-
Notifications
You must be signed in to change notification settings - Fork 15
170 lines (153 loc) · 7.08 KB
/
Copy pathrelease.yml
File metadata and controls
170 lines (153 loc) · 7.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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@v7
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"