Release #3
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 | |
| # Triggered by tag pushes (preferred) or manual workflow_dispatch (for re-runs). | |
| # | |
| # Authentication: Trusted Publishing via OIDC. No NPM_TOKEN secret needed. | |
| # Configured at: https://www.npmjs.com/package/taskplane/access | |
| # Trusted publisher: HenryLach/taskplane @ release.yml | |
| # | |
| # Flow: | |
| # 1. Checkout the tagged commit | |
| # 2. Validate tag name matches package.json version (no drift allowed) | |
| # 3. Re-run tests (belt-and-suspenders; PR CI already validated main) | |
| # 4. Publish to npm with --provenance attestation | |
| # 5. Create GitHub release with notes extracted from CHANGELOG.md | |
| # | |
| # Manual override: trigger via Actions UI with `tag` input (e.g. `v0.28.5`). | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Existing tag to release (e.g., v0.28.5)" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write # for creating GitHub release | |
| id-token: write # for npm OIDC trusted publishing | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Determine tag ref | |
| id: ref | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| echo "ref=${{ github.event.inputs.tag }}" >> "$GITHUB_OUTPUT" | |
| echo "tag=${{ github.event.inputs.tag }}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "ref=${GITHUB_REF}" >> "$GITHUB_OUTPUT" | |
| echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Checkout tagged commit | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ steps.ref.outputs.ref }} | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "24" | |
| registry-url: "https://registry.npmjs.org" | |
| cache: npm | |
| cache-dependency-path: extensions/package-lock.json | |
| - name: Verify npm version supports OIDC Trusted Publishing | |
| # OIDC Trusted Publishing for npm requires npm 11.5.1+. | |
| # Node 24 LTS ships with npm 11.x natively, so this is just a | |
| # belt-and-suspenders check + future-proofing in case a Node 24 | |
| # patch ever ships with an older npm. The `install -g npm@latest` | |
| # is a no-op when the bundled version is already current. | |
| run: | | |
| echo "Node version: $(node --version)" | |
| echo "npm version (pre): $(npm --version)" | |
| npm install -g npm@latest | |
| echo "npm version (post): $(npm --version)" | |
| - name: Validate tag matches package.json version | |
| id: version | |
| run: | | |
| PKG_VERSION=$(node -p "require('./package.json').version") | |
| TAG_NAME="${{ steps.ref.outputs.tag }}" | |
| TAG_VERSION="${TAG_NAME#v}" | |
| echo "package_version=$PKG_VERSION" >> "$GITHUB_OUTPUT" | |
| echo "tag_version=$TAG_VERSION" >> "$GITHUB_OUTPUT" | |
| echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT" | |
| if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then | |
| echo "::error::Tag $TAG_NAME (version $TAG_VERSION) does not match package.json version $PKG_VERSION. Refusing to publish a mismatched release." | |
| exit 1 | |
| fi | |
| echo "✅ Version match: $PKG_VERSION" | |
| - name: Install extension dependencies | |
| run: npm ci --prefix extensions | |
| - name: Run tests (release gate) | |
| run: | | |
| cd extensions | |
| # Same fast-suite invocation CI uses on PR/push to main. | |
| node --experimental-strip-types --experimental-test-module-mocks --no-warnings \ | |
| --import ./tests/loader.mjs \ | |
| --test $(ls tests/*.test.ts | grep -v '\.integration\.test\.ts\|execution-path-resolution\|orch-pure-functions\|project-config-loader' | tr '\n' ' ') | |
| - name: CLI smoke checks | |
| run: | | |
| node bin/taskplane.mjs help | |
| node bin/taskplane.mjs version | |
| - name: Publish to npm with provenance | |
| run: npm publish --provenance --access public | |
| - name: Verify npm publish landed | |
| run: | | |
| # npm registry can lag a few seconds; poll for up to ~30s. | |
| for i in 1 2 3 4 5 6; do | |
| REGISTRY_VERSION=$(npm view taskplane version 2>/dev/null || echo "") | |
| if [ "$REGISTRY_VERSION" = "${{ steps.version.outputs.tag_version }}" ]; then | |
| echo "✅ npm registry shows version $REGISTRY_VERSION" | |
| exit 0 | |
| fi | |
| echo "Registry shows '$REGISTRY_VERSION', expected '${{ steps.version.outputs.tag_version }}'. Retry $i/6 in 5s..." | |
| sleep 5 | |
| done | |
| echo "::error::npm registry did not propagate the new version within 30s. Check https://www.npmjs.com/package/taskplane manually." | |
| exit 1 | |
| - name: Extract changelog section for this version | |
| id: changelog | |
| run: | | |
| VERSION="${{ steps.version.outputs.tag_version }}" | |
| # Extract the section between `## [VERSION]` and the next `## [` heading. | |
| NOTES=$(awk -v v="^## \\\\[$VERSION\\\\]" ' | |
| $0 ~ v { found=1; next } | |
| found && /^## \[/ { exit } | |
| found { print } | |
| ' CHANGELOG.md) | |
| if [ -z "$NOTES" ]; then | |
| echo "::warning::No CHANGELOG section found for version $VERSION; release notes will be sparse." | |
| NOTES="See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details." | |
| fi | |
| { | |
| echo 'notes<<NOTES_EOF' | |
| echo "$NOTES" | |
| echo NOTES_EOF | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Create GitHub release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| TAG="${{ steps.version.outputs.tag_name }}" | |
| # Idempotent: if the release already exists (e.g. retry after a partial | |
| # failure), skip rather than fail the workflow. | |
| if gh release view "$TAG" >/dev/null 2>&1; then | |
| echo "::notice::GitHub release $TAG already exists; skipping creation." | |
| exit 0 | |
| fi | |
| gh release create "$TAG" \ | |
| --title "$TAG" \ | |
| --notes "${{ steps.changelog.outputs.notes }}" \ | |
| --verify-tag | |
| - name: Summary | |
| run: | | |
| echo "### 🚀 Released ${{ steps.version.outputs.tag_name }}" >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "- **npm**: https://www.npmjs.com/package/taskplane/v/${{ steps.version.outputs.tag_version }}" >> "$GITHUB_STEP_SUMMARY" | |
| echo "- **GitHub**: https://github.com/${{ github.repository }}/releases/tag/${{ steps.version.outputs.tag_name }}" >> "$GITHUB_STEP_SUMMARY" | |
| echo "- **Provenance**: attached (verifiable via \`npm audit signatures\`)" >> "$GITHUB_STEP_SUMMARY" |