Repository navigation
165 lines (150 loc) · 6.39 KB
/
Copy pathrelease-please.yml
File metadata and controls
165 lines (150 loc) · 6.39 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
name: Release
# Publishes a STABLE release once its release PR lands on `production`.
#
# The release PR is built by .github/workflows/promote.yml: `dev`, with
# `production` merged in, plus a commit that sets the version in package.json and
# server.json and adds the CHANGELOG.md section. So a push to `production` that
# carries an untagged version is a release: this tags it, creates the GitHub
# Release from that changelog section, and chains into npm publish, the binary
# uploads and the back-merge into `dev`. A push whose version is already tagged
# is not a release, and does nothing.
#
# The file keeps its old name because npm's trusted publisher is registered
# against `release-please.yml`: npm checks the filename of the workflow that
# started the run, so renaming this means updating that registration first.
# release-please itself is gone. It needed a promotion PR, a `Release-As:` pin
# on it (or it based the version on a beta tag), and then its own Release PR.
#
# Betas are NOT handled here: they are cut by hand from
# .github/workflows/release-beta.yml.
on:
push:
branches: [production]
# Re-run lever: does the same as a push, so it releases `production`'s
# version if that is still untagged and does nothing otherwise.
workflow_dispatch:
permissions:
contents: read
jobs:
release:
if: github.ref_name == 'production'
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
version: ${{ steps.release.outputs.version }}
steps:
- uses: actions/checkout@v7
with:
# Tags, for the "already released" and "above every stable tag" checks.
fetch-depth: 0
- uses: actions/setup-node@v7
with:
node-version: '24.x'
# GITHUB_TOKEN, so the `release: published` event triggers no workflow;
# the publishing jobs below are chained with `needs:` instead.
- name: Tag and create the GitHub Release
id: release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
version=$(node -p "require('./package.json').version")
tag="v${version}"
if git rev-parse -q --verify "refs/tags/${tag}" >/dev/null; then
echo "${tag} is already tagged, so this push is not a release."
echo "release_created=false" >> "$GITHUB_OUTPUT"
exit 0
fi
node scripts/stable-release.mjs verify
node scripts/stable-release.mjs notes --version "$version" > "$RUNNER_TEMP/notes.md"
gh release create "$tag" \
--target "$GITHUB_SHA" \
--title "$tag" \
--notes-file "$RUNNER_TEMP/notes.md" \
--latest
{
echo "release_created=true"
echo "tag_name=${tag}"
echo "version=${version}"
} >> "$GITHUB_OUTPUT"
publish-npm-prod:
# Must be granted here too: a reusable workflow can never hold a
# permission its caller does not, and this workflow's top-level
# block has none. Without it npm-publish's own id-token: write is
# silently dropped and trusted publishing falls back to a token.
permissions:
contents: read
id-token: write
needs: release
if: needs.release.outputs.release_created == 'true'
uses: ./.github/workflows/npm-publish.yml
secrets: inherit
publish-binaries-prod:
# Granted here for the same reason: release-binaries.yml uploads the
# release assets, and this workflow's top-level block is read-only.
permissions:
contents: write
needs: release
if: needs.release.outputs.release_created == 'true'
uses: ./.github/workflows/release-binaries.yml
with:
tag_name: ${{ needs.release.outputs.tag_name }}
secrets: inherit
# Brings the release commit back to `dev`, so its package.json, server.json
# and CHANGELOG.md match what shipped (CHANGELOG.md on `dev` is the one the
# repo's front page shows). `dev` only allows squash merges, so this lands as
# one commit with `production`'s content but not its history. That is fine:
# promote.yml merges `production` into every release branch, which is what
# keeps the next release PR mergeable.
back-merge:
needs: release
if: needs.release.outputs.release_created == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
env:
BOT_APP_ID: ${{ secrets.BOT_APP_ID }}
steps:
# Prefers the App token: a PR opened by GITHUB_TOKEN does not trigger the
# required checks, so it could only be merged by an admin bypass.
- uses: actions/create-github-app-token@v3
id: app-token
if: env.BOT_APP_ID != ''
with:
app-id: ${{ secrets.BOT_APP_ID }}
private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }}
- uses: actions/checkout@v7
with:
ref: dev
fetch-depth: 0
token: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }}
- name: Open the back-merge PR
env:
GH_TOKEN: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }}
VERSION: ${{ needs.release.outputs.version }}
run: |
set -euo pipefail
BRANCH="chore/back-merge-${VERSION}"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -b "$BRANCH"
# A merge, so the PR's diff is exactly what `production` changed.
if ! git merge --no-ff origin/production -m "chore: merge production into dev after ${VERSION}"; then
echo "Back-merge of production into dev conflicts — resolve it by hand." >&2
git merge --abort || true
exit 1
fi
if git diff --quiet "origin/dev..HEAD"; then
echo "dev already contains production; nothing to back-merge."
exit 0
fi
git push origin "$BRANCH"
gh pr create \
--base dev \
--head "$BRANCH" \
--title "chore: merge production into dev after ${VERSION}" \
--body "Back-merge of the ${VERSION} stable release so \`dev\` carries the released package.json, server.json and CHANGELOG.md. Squash-merge it (the only method \`dev\` allows). Opened automatically by the Release workflow."