fix: resolve CI failures across all GitHub Actions workflows

- Fix ruff format issue in doc_scraper.py
- Add pytest skip markers for browser renderer tests when Playwright is
  not installed in CI
- Replace broken Python heredocs in 4 workflow YAML files
  (scheduled-updates, vector-db-export, quality-metrics, test-vector-dbs)
  with python3 -c calls to fix YAML parsing errors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
yusyus
2026-03-29 20:40:45 +03:00
parent 6fded977dd
commit 00c72ea4a3
6 changed files with 99 additions and 116 deletions

View File

@@ -88,48 +88,43 @@ jobs:
echo "🔍 Analyzing $SKILL_NAME..." echo "🔍 Analyzing $SKILL_NAME..."
# Run quality analysis # Run quality analysis
python3 << 'EOF' "$skill_dir" "$THRESHOLD" "$SKILL_NAME" python3 -c "
import sys import sys
from pathlib import Path from pathlib import Path
sys.path.insert(0, 'src')
from skill_seekers.cli.quality_metrics import QualityAnalyzer from skill_seekers.cli.quality_metrics import QualityAnalyzer
skill_dir = Path(sys.argv[1]) skill_dir = Path('$skill_dir')
threshold = float(sys.argv[2]) threshold = float('$THRESHOLD')
skill_name = sys.argv[3] skill_name = '$SKILL_NAME'
analyzer = QualityAnalyzer(skill_dir) analyzer = QualityAnalyzer(skill_dir)
report = analyzer.generate_report() report = analyzer.generate_report()
# Print formatted report formatted = analyzer.format_report(report)
formatted = analyzer.format_report(report) print(formatted)
print(formatted)
# Save individual report with open(f'quality_{skill_name}.txt', 'w') as f:
with open(f'quality_{skill_name}.txt', 'w') as f: f.write(formatted)
f.write(formatted)
# Add to summary score = report.overall_score.total_score
score = report.overall_score.total_score grade = report.overall_score.grade
grade = report.overall_score.grade status = 'PASS' if score >= threshold else 'FAIL'
status = "✅" if score >= threshold else "❌"
summary_line = f"{status} **{skill_name}**: {grade} ({score:.1f}/100)" summary_line = f'{status} **{skill_name}**: {grade} ({score:.1f}/100)'
print(f"\n{summary_line}") print(f'\n{summary_line}')
with open('quality_summary.md', 'a') as f: with open('quality_summary.md', 'a') as f:
f.write(f"{summary_line}\n") f.write(f'{summary_line}\n')
# Set metrics as annotations if score < threshold:
if score < threshold: print(f'::error file={skill_dir}/SKILL.md::Quality score {score:.1f} is below threshold {threshold}')
print(f"::error file={skill_dir}/SKILL.md::Quality score {score:.1f} is below threshold {threshold}") sys.exit(1)
sys.exit(1) elif score < 80:
elif score < 80: print(f'::warning file={skill_dir}/SKILL.md::Quality score {score:.1f} could be improved')
print(f"::warning file={skill_dir}/SKILL.md::Quality score {score:.1f} could be improved") else:
else: print(f'::notice file={skill_dir}/SKILL.md::Quality score {score:.1f} - Excellent!')
print(f"::notice file={skill_dir}/SKILL.md::Quality score {score:.1f} - Excellent!") "
EOF
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
ALL_PASSED=false ALL_PASSED=false

View File

@@ -86,32 +86,28 @@ jobs:
SKILL_DIR="output/$FRAMEWORK" SKILL_DIR="output/$FRAMEWORK"
# Detect changes using incremental updater # Detect changes using incremental updater
python3 << 'EOF' python3 -c "
import sys import sys, os
from pathlib import Path from pathlib import Path
sys.path.insert(0, 'src')
from skill_seekers.cli.incremental_updater import IncrementalUpdater from skill_seekers.cli.incremental_updater import IncrementalUpdater
import os
framework = os.environ['FRAMEWORK'] framework = os.environ['FRAMEWORK']
skill_dir = Path(f'output/{framework}') skill_dir = Path(f'output/{framework}')
updater = IncrementalUpdater(skill_dir) updater = IncrementalUpdater(skill_dir)
changes = updater.detect_changes() changes = updater.detect_changes()
if changes.has_changes: if changes.has_changes:
print(f"🔄 Changes detected:") print(f'Changes detected:')
print(f" Added: {len(changes.added)}") print(f' Added: {len(changes.added)}')
print(f" Modified: {len(changes.modified)}") print(f' Modified: {len(changes.modified)}')
print(f" Deleted: {len(changes.deleted)}") print(f' Deleted: {len(changes.deleted)}')
updater.current_versions = updater._scan_documents()
# Save current versions for next run updater.save_current_versions()
updater.current_versions = updater._scan_documents() else:
updater.save_current_versions() print('No changes detected, skill is up to date')
else: "
print("✓ No changes detected, skill is up to date")
EOF
- name: Full scrape (if new or manual) - name: Full scrape (if new or manual)
if: steps.should_update.outputs.update == 'true' && steps.check_existing.outputs.exists == 'false' if: steps.should_update.outputs.update == 'true' && steps.check_existing.outputs.exists == 'false'
@@ -140,26 +136,24 @@ EOF
echo "📊 Generating quality metrics..." echo "📊 Generating quality metrics..."
python3 << 'EOF' python3 -c "
import sys import sys, os
import os from pathlib import Path
from pathlib import Path
sys.path.insert(0, 'src')
from skill_seekers.cli.quality_metrics import QualityAnalyzer from skill_seekers.cli.quality_metrics import QualityAnalyzer
framework = os.environ['FRAMEWORK'] framework = os.environ['FRAMEWORK']
skill_dir = Path(f'output/{framework}') skill_dir = Path(f'output/{framework}')
analyzer = QualityAnalyzer(skill_dir) analyzer = QualityAnalyzer(skill_dir)
report = analyzer.generate_report() report = analyzer.generate_report()
print(f"\n📊 Quality Score: {report.overall_score.grade} ({report.overall_score.total_score:.1f}/100)") print(f'Quality Score: {report.overall_score.grade} ({report.overall_score.total_score:.1f}/100)')
print(f" Completeness: {report.overall_score.completeness:.1f}%") print(f' Completeness: {report.overall_score.completeness:.1f}%')
print(f" Accuracy: {report.overall_score.accuracy:.1f}%") print(f' Accuracy: {report.overall_score.accuracy:.1f}%')
print(f" Coverage: {report.overall_score.coverage:.1f}%") print(f' Coverage: {report.overall_score.coverage:.1f}%')
print(f" Health: {report.overall_score.health:.1f}%") print(f' Health: {report.overall_score.health:.1f}%')
EOF "
- name: Package for Claude - name: Package for Claude
if: steps.should_update.outputs.update == 'true' if: steps.should_update.outputs.update == 'true'

View File

@@ -63,23 +63,17 @@ jobs:
echo "# Reference" > test_skill/references/ref.md echo "# Reference" > test_skill/references/ref.md
# Test adaptor packaging # Test adaptor packaging
python3 << 'EOF' python3 -c "
import sys import os
import os from pathlib import Path
from pathlib import Path from skill_seekers.cli.adaptors import get_adaptor
sys.path.insert(0, 'src') adaptor_name = os.environ['ADAPTOR_NAME']
adaptor = get_adaptor(adaptor_name)
from skill_seekers.cli.adaptors import get_adaptor package_path = adaptor.package(Path('test_skill'), Path('.'))
print(f'Package created: {package_path}')
adaptor_name = os.environ['ADAPTOR_NAME'] assert package_path.exists(), 'Package file not created'
adaptor = get_adaptor(adaptor_name) print(f'Package size: {package_path.stat().st_size} bytes')
package_path = adaptor.package(Path('test_skill'), Path('.')) "
print(f"✅ Package created: {package_path}")
# Verify package exists
assert package_path.exists(), "Package file not created"
print(f"📦 Package size: {package_path.stat().st_size} bytes")
EOF
- name: Upload test package - name: Upload test package
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3

View File

@@ -106,16 +106,12 @@ jobs:
echo "🔹 Exporting to $target..." echo "🔹 Exporting to $target..."
# Use adaptor directly via CLI # Use adaptor directly via CLI
python -c " python3 -c "
import sys from pathlib import Path
from pathlib import Path from skill_seekers.cli.adaptors import get_adaptor
sys.path.insert(0, 'src') adaptor = get_adaptor('$target')
package_path = adaptor.package(Path('$SKILL_DIR'), Path('output'))
from skill_seekers.cli.adaptors import get_adaptor print(f'Exported to {package_path}')
adaptor = get_adaptor('$target')
package_path = adaptor.package(Path('$SKILL_DIR'), Path('output'))
print(f'✅ Exported to {package_path}')
" "
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
@@ -133,21 +129,15 @@ print(f'✅ Exported to {package_path}')
if [ -d "$SKILL_DIR" ]; then if [ -d "$SKILL_DIR" ]; then
echo "📊 Generating quality metrics..." echo "📊 Generating quality metrics..."
python -c " python3 -c "
import sys from pathlib import Path
from pathlib import Path from skill_seekers.cli.quality_metrics import QualityAnalyzer
sys.path.insert(0, 'src') analyzer = QualityAnalyzer(Path('$SKILL_DIR'))
report = analyzer.generate_report()
from skill_seekers.cli.quality_metrics import QualityAnalyzer formatted = analyzer.format_report(report)
print(formatted)
analyzer = QualityAnalyzer(Path('$SKILL_DIR')) with open('quality_report_${SKILL_NAME}.txt', 'w') as f:
report = analyzer.generate_report() f.write(formatted)
formatted = analyzer.format_report(report)
print(formatted)
# Save to file
with open('quality_report_${SKILL_NAME}.txt', 'w') as f:
f.write(formatted)
" "
fi fi
continue-on-error: true continue-on-error: true

View File

@@ -819,9 +819,7 @@ class DocToSkillConverter:
if self.browser_mode and not self._has_md_extension(url): if self.browser_mode and not self._has_md_extension(url):
# Use Playwright in executor (sync API in async context) # Use Playwright in executor (sync API in async context)
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
html = await loop.run_in_executor( html = await loop.run_in_executor(None, self._render_with_browser, url)
None, self._render_with_browser, url
)
soup = BeautifulSoup(html, "html.parser") soup = BeautifulSoup(html, "html.parser")
page = self.extract_content(soup, url) page = self.extract_content(soup, url)
else: else:

View File

@@ -5,13 +5,23 @@ Real end-to-end tests using actual Playwright + Chromium.
from __future__ import annotations from __future__ import annotations
import pytest
from skill_seekers.cli.browser_renderer import ( from skill_seekers.cli.browser_renderer import (
BrowserRenderer, BrowserRenderer,
_auto_install_chromium, _auto_install_chromium,
_check_playwright_available, _check_playwright_available,
) )
# Skip all real browser tests when Playwright is not installed
_has_playwright = _check_playwright_available()
requires_playwright = pytest.mark.skipif(
not _has_playwright,
reason="Playwright not installed (pip install 'skill-seekers[browser]')",
)
@requires_playwright
class TestPlaywrightAvailability: class TestPlaywrightAvailability:
"""Test that playwright is properly detected.""" """Test that playwright is properly detected."""
@@ -23,6 +33,7 @@ class TestPlaywrightAvailability:
assert _auto_install_chromium() is True assert _auto_install_chromium() is True
@requires_playwright
class TestBrowserRendererReal: class TestBrowserRendererReal:
"""Real end-to-end tests with actual Chromium.""" """Real end-to-end tests with actual Chromium."""
@@ -113,6 +124,7 @@ class TestDocScraperBrowserIntegration:
scraper = DocToSkillConverter(config) scraper = DocToSkillConverter(config)
assert scraper.browser_mode is False assert scraper.browser_mode is False
@requires_playwright
def test_render_with_browser_returns_html(self): def test_render_with_browser_returns_html(self):
"""Test the _render_with_browser helper directly.""" """Test the _render_with_browser helper directly."""
from skill_seekers.cli.doc_scraper import DocToSkillConverter from skill_seekers.cli.doc_scraper import DocToSkillConverter