-
Notifications
You must be signed in to change notification settings - Fork 0
200 lines (179 loc) · 6.85 KB
/
Copy pathrelease.yml
File metadata and controls
200 lines (179 loc) · 6.85 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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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