ci(build): add repo-wide verify:dist self-check; fix un-inlined JSON … #112
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| branches: [main] | |
| permissions: | |
| contents: write | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version-file: '.nvmrc' | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Run tests | |
| run: pnpm test 2>&1 | tee /tmp/test-output.log | |
| - name: Capture test counts | |
| id: tests | |
| run: | | |
| CLEAN=$(sed 's/\x1b\[[0-9;]*m//g' /tmp/test-output.log) | |
| TEST_COUNT=$(echo "$CLEAN" | sed -n 's/.*Tests *\([0-9][0-9]*\) passed.*/\1/p' | tail -1) | |
| TEST_FILES=$(echo "$CLEAN" | sed -n 's/.*Test Files *\([0-9][0-9]*\) passed.*/\1/p' | tail -1) | |
| echo "count=${TEST_COUNT:-0}" >> "$GITHUB_OUTPUT" | |
| echo "files=${TEST_FILES:-0}" >> "$GITHUB_OUTPUT" | |
| - name: Count agents | |
| id: agents | |
| run: | | |
| # Single source of truth: scripts/count-agents.ts walks the | |
| # workspace exactly the same way the CLI's discoverAgents() does. | |
| # The previous `find` undercounted by skipping agents-platform | |
| # and agents-tmc. | |
| AGENT_COUNT=$(pnpm tsx scripts/count-agents.ts --json | node -e "let s=''; process.stdin.on('data',c=>s+=c).on('end',()=>console.log(JSON.parse(s).total))") | |
| echo "count=${AGENT_COUNT}" >> "$GITHUB_OUTPUT" | |
| - name: Get version | |
| id: version | |
| run: | | |
| VERSION=$(node -p "require('./package.json').version") | |
| echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT" | |
| - name: Check if release exists | |
| id: check | |
| run: | | |
| TAG="${{ steps.version.outputs.tag }}" | |
| if curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GH_TOKEN" "https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG" | grep -q "200"; then | |
| echo "exists=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "exists=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build release notes | |
| if: steps.check.outputs.exists == 'false' | |
| id: notes | |
| run: | | |
| VERSION=$(node -p "require('./package.json').version") | |
| AGENT_COUNT="${{ steps.agents.outputs.count }}" | |
| TEST_COUNT="${{ steps.tests.outputs.count }}" | |
| TEST_FILES="${{ steps.tests.outputs.files }}" | |
| # Extract the current version's section from CHANGELOG.md. | |
| # Matches '## <version> — ...' through the next '## <digit>...' or EOF. | |
| # Require ' — ' after the version so '0.3.2' doesn't match '0.3.2.1'. | |
| CHANGELOG_BODY=$(node -e " | |
| const fs = require('fs'); | |
| const version = process.argv[1]; | |
| try { | |
| const content = fs.readFileSync('CHANGELOG.md', 'utf8'); | |
| const escaped = version.replace(/\./g, '\\\\.'); | |
| const re = new RegExp('## ' + escaped + ' — [^\\\\n]*\\\\n([\\\\s\\\\S]*?)(?=\\\\n## \\\\d|\$)'); | |
| const m = content.match(re); | |
| if (m) process.stdout.write(m[1].trim()); | |
| } catch (e) { /* CHANGELOG missing — fall through */ } | |
| " "$VERSION") | |
| { | |
| if [ -n "$CHANGELOG_BODY" ]; then | |
| # CHANGELOG has an entry for this version — use it verbatim. | |
| echo "$CHANGELOG_BODY" | |
| echo "" | |
| echo "---" | |
| echo "" | |
| fi | |
| echo "${AGENT_COUNT} agents · ${TEST_COUNT} tests passing across ${TEST_FILES} files · Apache 2.0" | |
| } > /tmp/release-notes.md | |
| - name: Create release | |
| if: steps.check.outputs.exists == 'false' | |
| # Why RELEASE_PAT instead of GITHUB_TOKEN: | |
| # GitHub Actions silently DOES NOT trigger downstream workflows | |
| # for events created by GITHUB_TOKEN (the default Actions token). | |
| # Without this, `gh release create` fires a `release: published` | |
| # event that the Publish-to-npm workflow never sees, and packages | |
| # never get published. Using a personal access token makes the | |
| # event "user-fired", which propagates correctly. | |
| # If RELEASE_PAT isn't configured the workflow falls back to | |
| # GITHUB_TOKEN so the release still gets created — but the | |
| # publish step won't auto-fire and someone will have to trigger | |
| # it manually via workflow_dispatch. | |
| env: | |
| GH_TOKEN: ${{ secrets.RELEASE_PAT || secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh release create "${{ steps.version.outputs.tag }}" \ | |
| --title "${{ steps.version.outputs.tag }}" \ | |
| --notes-file /tmp/release-notes.md \ | |
| --latest |