-
Notifications
You must be signed in to change notification settings - Fork 0
230 lines (209 loc) · 9.04 KB
/
Copy pathrelease.yml
File metadata and controls
230 lines (209 loc) · 9.04 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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# The release act. Pushing a vX.Y.Z tag runs this; docs/release.md is the
# narrative — what a release is, the version policy, and every step below in
# prose. CI (ci.yml) remains the gate every change passes; this workflow only
# ever runs on a commit that has already been through it.
name: Release
# Runs on a tag push — cut-release.yml's deploy-key push included. The manual
# dispatch exists for re-running against an existing tag; dispatched against
# anything that is not a release tag, the first job refuses.
on:
push:
tags: ['v*']
workflow_dispatch:
permissions:
contents: write
packages: write
env:
IMAGE: ghcr.io/meith-dev/meith
NEXT_TELEMETRY_DISABLED: '1'
jobs:
versions:
name: The tag and the tree agree
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- run: node scripts/release-check.mjs --tag "$GITHUB_REF_NAME"
# One job per architecture, on that architecture's own runner — no QEMU, so
# the boot tests below run the same binaries an operator will. Each job
# pushes an arch-suffixed tag; `publish` merges them into the real tags only
# after both architectures have booted in every role.
build:
name: Build and boot (${{ matrix.arch }})
needs: versions
strategy:
fail-fast: true
matrix:
include:
- arch: amd64
runner: ubuntu-24.04
- arch: arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: forum_release
ports: ['5432:5432']
options: >-
--health-cmd pg_isready --health-interval 10s
--health-timeout 5s --health-retries 5
steps:
- uses: actions/checkout@v7
- name: Build the image, stamped
run: |
VERSION="${GITHUB_REF_NAME#v}"
docker build -f docker/Dockerfile \
--build-arg MEITH_VERSION="$VERSION" \
--build-arg MEITH_COMMIT="$GITHUB_SHA" \
-t meith:release .
# The same boot checks as ci.yml's `image` job, compact. That job's
# comments carry the history of why each assertion exists.
- name: Apply migrations (migrate role)
run: |
docker run --rm --network host \
-e COMMUNITY_ROLE=migrate \
-e DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/forum_release \
-e DATA_SOURCE=postgres \
-e AUTH_SECRET=release-boot-test-auth-secret-long-enough \
-e TICK_SECRET=release-boot-test-tick-secret-long-enough \
meith:release
- name: Boot the web role, and it renders
run: |
docker run -d --name meith-web --network host \
-e DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/forum_release \
-e DATA_SOURCE=postgres \
-e AUTH_SECRET=release-boot-test-auth-secret-long-enough \
-e TICK_SECRET=release-boot-test-tick-secret-long-enough \
-e APP_URL=http://127.0.0.1:3000 \
meith:release
for i in $(seq 1 40); do
curl -fsS http://127.0.0.1:3000/api/health >/dev/null 2>&1 && break
test "$i" != 40 || { echo "::error::the web role never became healthy"; docker logs meith-web; exit 1; }
sleep 1
done
curl -fsS http://127.0.0.1:3000/ | grep -q '<main' \
|| { echo "::error::the board did not render"; docker logs meith-web; exit 1; }
- name: Boot the worker role, and it survives a tick
run: |
docker run -d --name meith-worker --network host --restart=no \
-e COMMUNITY_ROLE=worker \
-e DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/forum_release \
-e DATA_SOURCE=postgres \
-e AUTH_SECRET=release-boot-test-auth-secret-long-enough \
-e TICK_SECRET=release-boot-test-tick-secret-long-enough \
-e QUEUE_DRIVER=postgres -e CACHE_DRIVER=memory \
meith:release
sleep 90
docker ps --filter name=meith-worker --filter status=running | grep -q meith-worker \
|| { echo "::error::the worker exited instead of looping"; docker logs meith-worker; exit 1; }
starts=$(docker logs meith-worker 2>&1 | grep -c 'worker started' || true)
test "$starts" = "1" \
|| { echo "::error::expected one 'worker started', saw $starts"; docker logs meith-worker; exit 1; }
state=$(docker inspect -f '{{.State.Health.Status}}' meith-worker)
test "$state" = "healthy" \
|| { echo "::error::worker health is $state"; exit 1; }
docker run --rm --network host -e PGPASSWORD=postgres postgres:16-alpine \
psql -h 127.0.0.1 -U postgres -d forum_release -tAc 'select count(*) from tasks' \
| grep -qE '^[1-9]' \
|| { echo "::error::no tasks were registered"; docker logs meith-worker; exit 1; }
- name: Stop
if: always()
run: docker rm -f meith-web meith-worker 2>/dev/null || true
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push the architecture tag
run: |
VERSION="${GITHUB_REF_NAME#v}"
docker tag meith:release "$IMAGE:$VERSION-${{ matrix.arch }}"
docker push "$IMAGE:$VERSION-${{ matrix.arch }}"
# The npm half of the release: every non-private workspace package, in
# dependency order, at the tag's version. Gated on the boot-tested build so
# nothing reaches the registry from a release whose image did not boot, and
# re-runnable — a version already on the registry is skipped, not an error.
#
# No token: npm trusted publishing exchanges this job's OIDC identity for a
# short-lived credential, and provenance comes with it. Each package names
# this repository and workflow as its trusted publisher on npmjs.com — see
# docs/release.md § What publishes to npm.
npm:
name: Publish the packages
needs: build
runs-on: ubuntu-24.04
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: 22
cache: pnpm
# Trusted publishing lives in the npm CLI from 11.5.1; Node 22 bundles 10.x.
- run: npm install -g npm@latest
- run: pnpm install --frozen-lockfile
- run: node scripts/npm-publish.mjs
publish:
name: Tags, the release branch, and the draft notes
needs: [build, npm]
runs-on: ubuntu-24.04
steps:
# Full history, not the default shallow clone: pushing over an existing
# release branch needs that branch's current commit present locally, or
# the git client refuses with "fetch first" before the server can even
# check the fast-forward.
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# X.Y.Z never moves again and is what the compose file pins; X.Y and
# latest are floating conveniences for trying the image, and nothing
# this repository ships deploys either — see docs/release.md.
- name: Merge the architectures under the release tags
run: |
VERSION="${GITHUB_REF_NAME#v}"
LINE="${VERSION%.*}"
docker buildx imagetools create \
-t "$IMAGE:$VERSION" \
-t "$IMAGE:$LINE" \
-t "$IMAGE:latest" \
"$IMAGE:$VERSION-amd64" "$IMAGE:$VERSION-arm64"
# Fast-forward only: a tag not descended from `release` is a mistake this
# push refuses rather than rewrites.
- name: Move the release branch to the tag
run: git push origin "HEAD:refs/heads/release"
# Skipped when the release already exists — this job has proven it gets
# re-run, and a re-run must not clobber a draft a maintainer has edited.
- name: Draft the release notes
env:
GH_TOKEN: ${{ github.token }}
run: |
if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then
echo "the release for $GITHUB_REF_NAME already exists, leaving it alone"
exit 0
fi
VERSION="${GITHUB_REF_NAME#v}"
cat > /tmp/notes-header.md <<EOF
\`\`\`
docker pull $IMAGE:$VERSION
npm install @meith/plugin-kit@$VERSION @meith/theme-kit@$VERSION
\`\`\`
**Migrations:** _fill in before publishing — none / adds only / removes or renames._
Patch releases carry no migrations; docs/upgrading.md is the operator's side of that promise.
---
EOF
gh release create "$GITHUB_REF_NAME" \
--draft \
--title "$GITHUB_REF_NAME" \
--notes-file /tmp/notes-header.md \
--generate-notes