Merge pull request #729 from devswha/dev #23
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 | |
| on: | |
| push: | |
| tags: | |
| - 'v*.*.*' | |
| workflow_dispatch: | |
| inputs: | |
| publish: | |
| description: Publish npm packages instead of running a dry-run. | |
| type: boolean | |
| default: false | |
| publish_ghcr: | |
| description: Also publish the experimental GHCR image. Keep false for npm-only releases. | |
| type: boolean | |
| default: false | |
| permissions: | |
| contents: read | |
| jobs: | |
| verify: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up Node 24 | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 24 | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: ESLint, typecheck, and codespell | |
| run: npm run lint | |
| - name: Release metadata check | |
| run: npm run release:check | |
| - name: Unit and e2e tests | |
| run: npm test | |
| - name: Benchmark report schema | |
| run: npm run benchmark:report | |
| - name: Detector comparison harness | |
| # Runs before the drift check so the regenerated | |
| # docs/benchmarks/detector-comparison.{json,md} are drift-checked too. | |
| run: npm run benchmark:compare | |
| - name: Benchmark report drift check | |
| # Same deterministic regen as test.yml's quality job (timestamp and | |
| # node-version metadata lines ignored); a failure here means the | |
| # tagged commit ships a stale public benchmark page. | |
| run: | | |
| git diff --exit-code -I '"generatedAt":' -I '"benchmarkGeneratedAt":' -I 'Generated at:' -I '"nodeVersion":' -I '^- Node: ' -- docs/benchmarks || { | |
| echo '::error::docs/benchmarks is stale. Run `npm run benchmark:report && npm run benchmark:compare` and commit the result.' | |
| exit 1 | |
| } | |
| - name: Dogfood public docs | |
| run: npm run dogfood | |
| - name: Private asset leak gate | |
| run: npm run check:no-private-assets | |
| - name: Root npm package dry run | |
| run: npm pack --dry-run | |
| - name: Alias npm package dry run | |
| run: cd packages/patina-humanizer && npm pack --dry-run | |
| authorize-source: | |
| needs: verify | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && (inputs.publish == true || inputs.publish_ghcr == true)) | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Require a main-reachable release commit | |
| run: | | |
| git fetch --no-tags origin main:refs/remotes/origin/main | |
| if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ] && [ "$GITHUB_REF" != "refs/heads/main" ]; then | |
| echo "::error::Manual publication must be dispatched from main." | |
| exit 1 | |
| fi | |
| if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then | |
| echo "::error::Release commit is not reachable from origin/main." | |
| exit 1 | |
| fi | |
| npm: | |
| needs: [verify, authorize-source] | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && inputs.publish == true) | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up Node 24 for npm | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 24 | |
| registry-url: https://registry.npmjs.org | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Verify release metadata | |
| run: npm run release:check | |
| - name: Publish patina-cli | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: npm publish --access public --provenance | |
| - name: Publish patina-humanizer alias | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: cd packages/patina-humanizer && npm publish --access public --provenance | |
| github-release: | |
| needs: [verify, npm] | |
| runs-on: ubuntu-latest | |
| # Tag pushes only: workflow_dispatch dry-runs must not create releases, | |
| # and a release is only recorded after npm publish actually succeeded. | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Extract changelog notes for this version | |
| run: | | |
| node <<'EOF' > release-notes.md | |
| const { readFileSync } = require("node:fs"); | |
| const changelog = readFileSync("CHANGELOG.md", "utf8"); | |
| const version = process.env.GITHUB_REF_NAME.replace(/^v/, ""); | |
| const start = changelog.indexOf("## " + version + " — "); | |
| if (start === -1) { console.error("no CHANGELOG section for " + version); process.exit(1); } | |
| let end = changelog.indexOf("\n## ", start + 1); | |
| if (end === -1) end = changelog.length; | |
| console.log(changelog.slice(start, end).trim()); | |
| EOF | |
| cat release-notes.md | head -3 | |
| - name: Create GitHub release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| VERSION="${GITHUB_REF_NAME#v}" | |
| gh release create "$GITHUB_REF_NAME" \ | |
| --title "v${VERSION}" \ | |
| --notes-file release-notes.md \ | |
| --latest | |
| ghcr: | |
| needs: [verify, authorize-source] | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'workflow_dispatch' && inputs.publish_ghcr == true | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Docker metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/devswha/patina | |
| tags: | | |
| type=raw,value=latest | |
| type=semver,pattern={{version}} | |
| - name: Build and publish image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} |