feat(analysis): group near-duplicate findings instead of reporting ev… #13
Workflow file for this run
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 | |
| # Publishing happens here rather than from someone's laptop, for one reason that | |
| # matters more than convenience: npm provenance. A package published from a | |
| # GitHub Actions run with an OIDC token carries a signed, verifiable statement of | |
| # which commit and which workflow produced it, and npm shows that on the package | |
| # page. A package published from a laptop carries nobody's word but the | |
| # publisher's — which, for a tool that reads other people's configuration and | |
| # tells them what is unsafe, is the wrong way round. | |
| # | |
| # Triggered by pushing a tag. The tag is the decision; everything here is | |
| # mechanical. | |
| on: | |
| push: | |
| tags: | |
| # Release tags only, and "release tag" here means a version. The Action's | |
| # moving major tag is `v1`, which `v*` also matched, so pushing it sent | |
| # this workflow off to publish version "1" of three packages that are on | |
| # 2.0.0-beta.3. Requiring two dots keeps `v2.0.0-beta.3` and excludes | |
| # `v1`, which is the whole distinction. | |
| - "v*.*.*" | |
| workflow_dispatch: | |
| inputs: | |
| dry-run: | |
| description: "Build and pack, but do not publish" | |
| type: boolean | |
| default: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| verify: | |
| name: Verify the tag | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| prerelease: ${{ steps.version.outputs.prerelease }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - name: The tag and the manifests must agree | |
| id: version | |
| run: | | |
| set -euo pipefail | |
| # On a tag push the tag is the authority and the manifests must match | |
| # it. On a manual dispatch there is no tag — the ref is a branch — so | |
| # the manifests are the authority and what is being rehearsed is | |
| # everything after this check. | |
| if [ "$GITHUB_REF_TYPE" = "tag" ]; then | |
| version="${GITHUB_REF_NAME#v}" | |
| echo "tag says: $version" | |
| else | |
| version=$(node -p "require('./packages/core/package.json').version") | |
| echo "no tag (dispatch); manifests say: $version" | |
| fi | |
| for package in core cli agentfile; do | |
| manifest=$(node -p "require('./packages/$package/package.json').version") | |
| if [ "$manifest" != "$version" ]; then | |
| echo "::error::packages/$package is $manifest but the release is $version" | |
| exit 1 | |
| fi | |
| done | |
| # The changelog is the release notes, so its absence is a failure now | |
| # rather than an empty release body later. | |
| if ! grep -q "^## \\[$version\\]" CHANGELOG.md; then | |
| echo "::error::CHANGELOG.md has no section for $version" | |
| exit 1 | |
| fi | |
| # Anything with a hyphen is a pre-release: 2.0.0-beta.1 goes out under | |
| # the `next` dist-tag so `npm install @agentfile/cli` keeps resolving | |
| # to the last stable version. | |
| case "$version" in | |
| *-*) prerelease=true ;; | |
| *) prerelease=false ;; | |
| esac | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT" | |
| - run: npm ci | |
| - run: npm run lint | |
| - run: npm run build | |
| - run: npm test | |
| publish: | |
| name: Publish to npm | |
| needs: verify | |
| runs-on: ubuntu-latest | |
| environment: release | |
| permissions: | |
| contents: read | |
| # Required for npm provenance: the OIDC token is what signs the | |
| # attestation linking this package to this commit. | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| registry-url: "https://registry.npmjs.org" | |
| - run: npm ci | |
| - run: npm run build | |
| - name: Publish | |
| if: ${{ github.event_name == 'push' || inputs.dry-run == false }} | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| TAG: ${{ needs.verify.outputs.prerelease == 'true' && 'next' || 'latest' }} | |
| run: | | |
| set -euo pipefail | |
| # Order matters: each package's dependency must exist on the registry | |
| # before the package that depends on it is installable. | |
| for package in core cli agentfile; do | |
| echo "::group::publish @agentfile/$package under --tag $TAG" | |
| npm publish --workspace "packages/$package" --provenance --access public --tag "$TAG" | |
| echo "::endgroup::" | |
| done | |
| - name: Dry run | |
| if: ${{ github.event_name == 'workflow_dispatch' && inputs.dry-run }} | |
| run: | | |
| set -euo pipefail | |
| for package in core cli agentfile; do | |
| npm publish --workspace "packages/$package" --dry-run --access public | |
| done | |
| github-release: | |
| name: GitHub release | |
| needs: [verify, publish] | |
| if: github.event_name == 'push' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Release notes from the changelog | |
| run: | | |
| set -euo pipefail | |
| version="${{ needs.verify.outputs.version }}" | |
| # The changelog is the release notes. Keeping a second copy in the | |
| # release body is how the two drift apart. | |
| awk -v v="$version" ' | |
| $0 ~ "^## \\[" v "\\]" { inside = 1; next } | |
| inside && /^## \[/ { exit } | |
| inside { print } | |
| ' CHANGELOG.md > notes.md | |
| if [ ! -s notes.md ]; then | |
| echo "::error::CHANGELOG.md has no section for $version" | |
| exit 1 | |
| fi | |
| { | |
| echo | |
| echo "---" | |
| echo | |
| echo "Published to npm with provenance:" | |
| echo | |
| echo '```bash' | |
| if [ "${{ needs.verify.outputs.prerelease }}" = "true" ]; then | |
| echo "npm install --save-dev @agentfile/cli@next" | |
| else | |
| echo "npm install --save-dev @agentfile/cli" | |
| fi | |
| echo '```' | |
| } >> notes.md | |
| - name: Create the release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh release create "$GITHUB_REF_NAME" \ | |
| --title "$GITHUB_REF_NAME" \ | |
| --notes-file notes.md \ | |
| ${{ needs.verify.outputs.prerelease == 'true' && '--prerelease' || '' }} | |
| # A stable release leaves `next` pointing at the last prerelease, which is | |
| # older than what was just published. Reusing the maintenance workflow rather | |
| # than repeating the loop keeps one definition of what `next` means. | |
| dist-tags: | |
| name: Dist tags | |
| needs: [verify, publish] | |
| if: github.event_name == 'push' && needs.verify.outputs.prerelease != 'true' | |
| uses: ./.github/workflows/dist-tags.yml | |
| secrets: inherit |