Skip to content

0.28.8

0.28.8 #6

Workflow file for this run

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
# Writes the section between `## [VERSION]` and the next `## [` heading
# to a file. Avoids using `${{ }}` interpolation for multi-line content
# in subsequent shell commands — markdown bullets/parens/backticks would
# be mis-parsed as shell syntax (subshells, command substitution, etc).
run: |
VERSION="${{ steps.version.outputs.tag_version }}"
awk -v v="^## \\\\[$VERSION\\\\]" '
$0 ~ v { found=1; next }
found && /^## \[/ { exit }
found { print }
' CHANGELOG.md > release-notes.md
if [ ! -s release-notes.md ]; then
echo "::warning::No CHANGELOG section found for version $VERSION; release notes will be sparse."
echo "See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details." > release-notes.md
fi
echo "--- release-notes.md preview ---"
head -20 release-notes.md
echo "..."
echo "(total lines: $(wc -l < release-notes.md))"
- 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
# Use --notes-file (not --notes "$VAR") so multi-line markdown with
# backticks/parens/asterisks isn't subjected to shell parsing.
gh release create "$TAG" \
--title "$TAG" \
--notes-file release-notes.md \
--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"