name: Skills Registry CI on: push: branches: ["main"] pull_request: branches: ["main"] workflow_dispatch: permissions: contents: read jobs: pr-policy: if: github.event_name == 'pull_request' runs-on: ubuntu-latest outputs: primary_category: ${{ steps.intake.outputs.primary_category }} categories: ${{ steps.intake.outputs.categories }} requires_references: ${{ steps.intake.outputs.requires_references }} direct_derived_changes_count: ${{ steps.intake.outputs.direct_derived_changes_count }} has_quality_checklist: ${{ steps.intake.outputs.has_quality_checklist }} has_issue_link: ${{ steps.intake.outputs.has_issue_link }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Node uses: actions/setup-node@v4 with: node-version: "lts/*" - name: Fetch base branch run: git fetch origin "${{ github.base_ref }}" - name: Intake PR change id: intake run: | node tools/scripts/pr_preflight.js \ --base "origin/${{ github.base_ref }}" \ --head "HEAD" \ --event-path "$GITHUB_EVENT_PATH" \ --no-run \ --write-github-output \ --write-step-summary - name: Enforce PR source-only contract run: | if [ "${{ steps.intake.outputs.direct_derived_changes_count }}" != "0" ]; then echo "Pull requests must stay source-only." echo "Remove derived files and let main regenerate them after merge." exit 1 fi if [ "${{ steps.intake.outputs.has_quality_checklist }}" != "true" ]; then echo "PR body must include the Quality Bar Checklist from the template." exit 1 fi if [ "${{ steps.intake.outputs.has_issue_link }}" != "true" ]; then echo "::notice::No Closes/Fixes issue link detected in the PR body." fi source-validation: if: github.event_name == 'pull_request' runs-on: ubuntu-latest needs: pr-policy steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install Python dependencies run: pip install pyyaml - name: Set up Node uses: actions/setup-node@v4 with: node-version: "lts/*" - name: Install npm dependencies run: npm ci - name: Verify directory structure run: | test -d skills/ test -d apps/web-app/ test -d tools/scripts/ test -d tools/lib/ test -f README.md test -f CONTRIBUTING.md - name: Validate source changes run: npm run validate - name: Validate references if: needs.pr-policy.outputs.requires_references == 'true' run: npm run validate:references - name: Audit npm dependencies run: npm audit --audit-level=high continue-on-error: true - name: Run tests env: ENABLE_NETWORK_TESTS: "1" run: npm run test - name: Run docs security checks run: npm run security:docs artifact-preview: if: github.event_name == 'pull_request' runs-on: ubuntu-latest needs: [pr-policy, source-validation] steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install Python dependencies run: pip install pyyaml - name: Set up Node uses: actions/setup-node@v4 with: node-version: "lts/*" - name: Install npm dependencies run: npm ci - name: Generate canonical artifacts preview run: | npm run chain npm run catalog - name: Report generated drift run: | managed_files=$(node tools/scripts/generated_files.js --shell --include-mixed) drift_files=$(git diff --name-only -- $managed_files) { echo "## Artifact Preview" echo echo "- Primary change: \`${{ needs.pr-policy.outputs.primary_category }}\`" echo "- Categories: \`${{ needs.pr-policy.outputs.categories }}\`" echo "- Derived-file policy: PRs remain source-only; main will canonicalize final generated outputs." echo } >> "$GITHUB_STEP_SUMMARY" if [ -z "$drift_files" ]; then echo "No generated drift detected after preview." echo "- Generated drift: none" >> "$GITHUB_STEP_SUMMARY" exit 0 fi echo "::notice::Generated drift detected in artifact preview." { echo "- Generated drift: detected" echo echo "Predicted file updates:" printf '%s\n' "$drift_files" | sed 's/^/- `/; s/$/`/' } >> "$GITHUB_STEP_SUMMARY" main-validation-and-sync: if: github.event_name != 'pull_request' runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install Python dependencies run: pip install pyyaml - name: Set up Node uses: actions/setup-node@v4 with: node-version: "lts/*" - name: Install npm dependencies run: npm ci - name: Verify directory structure run: | test -d skills/ test -d apps/web-app/ test -d tools/scripts/ test -d tools/lib/ test -f README.md test -f CONTRIBUTING.md - name: Validate skills run: npm run validate - name: Validate references run: npm run validate:references - name: Generate index run: npm run index - name: Update README run: npm run readme - name: Audit npm dependencies run: npm audit --audit-level=high continue-on-error: true - name: Run tests env: ENABLE_NETWORK_TESTS: "1" run: npm run test - name: Run docs security checks run: npm run security:docs - name: Build catalog run: npm run catalog - name: Set up GitHub credentials if: github.event_name == 'push' && github.ref == 'refs/heads/main' run: | git config user.name 'github-actions[bot]' git config user.email 'github-actions[bot]@users.noreply.github.com' git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git - name: Auto-commit canonical artifacts if: github.event_name == 'push' && github.ref == 'refs/heads/main' run: | managed_files=$(node tools/scripts/generated_files.js --shell --include-mixed) git diff --quiet && exit 0 git pull origin main --rebase || true git add $managed_files || true git diff --cached --quiet && exit 0 git commit -m "chore: sync generated registry files [ci skip]" git push origin HEAD - name: Check for uncommitted drift if: github.event_name == 'push' && github.ref == 'refs/heads/main' run: | if ! git diff --quiet; then echo "❌ Detected uncommitted changes produced by registry/readme/catalog scripts." echo echo "Main must be self-healing after the auto-sync step." echo "To fix locally, run the canonical maintainer flow:" echo " npm run release:preflight" echo " npm run chain" echo " npm run catalog" echo " git status" exit 1 fi