Repository navigation
172 lines (160 loc) · 7.64 KB
/
Copy pathrelease.yml
File metadata and controls
172 lines (160 loc) · 7.64 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
# ─────────────────────────────────────────────────────────────────────────────
# Every version that lands on main gets a tag and a draft release, and dev is
# brought up to date with main (the sync-dev job at the end).
#
# A push to main whose version has no tag yet is a release. CI on that push builds
# the engines on all five platforms and runs the five-platform e2e; only when that
# run is GREEN does this workflow tag the commit it tested v<version> and open a
# DRAFT GitHub release with notes generated
# from the pull requests since the previous tag. Nothing is published here.
#
# Publishing is a person's decision: review the draft, then press Publish. That
# `release: published` event runs publish-npm.yml, which ships the binaries and
# the tarball of that same green CI run: nothing is compiled twice (#1350).
#
# A version that is already tagged finds its tag present and does nothing.
# ─────────────────────────────────────────────────────────────────────────────
name: release
on:
push:
branches: [main] # sync-dev
workflow_run: # tag: after CI on main finished
workflows: [CI]
types: [completed]
branches: [main]
workflow_dispatch:
concurrency:
group: release-main
cancel-in-progress: false
permissions:
contents: write
jobs:
tag:
# the CI run of a PUSH to main, and only a green one: a red release build drafts nothing
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_run' && github.event.workflow_run.event == 'push' &&
github.event.workflow_run.conclusion == 'success')
runs-on: ubuntu-24.04
env:
SHA: ${{ github.event.workflow_run.head_sha || github.sha }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ env.SHA }}
fetch-depth: 0
- name: the bot this job writes as
id: bot
uses: ./.github/actions/bot
with:
app-id: ${{ vars.AXIOMCODE_BOT_APP_ID }}
private-key: ${{ secrets.AXIOMCODE_BOT_PRIVATE_KEY }}
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: every manifest carries the same version
run: node .github/scripts/version.mjs check
- name: tag and draft the release
env:
GH_TOKEN: ${{ steps.bot.outputs.token }}
BOT_NAME: ${{ steps.bot.outputs.name }}
BOT_EMAIL: ${{ steps.bot.outputs.email }}
run: |
set -euo pipefail
version="$(node .github/scripts/version.mjs get)"
tag="v$version"
if git ls-remote --exit-code --tags origin "refs/tags/$tag" >/dev/null; then
echo "$tag already exists — this push did not change the version"
exit 0
fi
prerelease=""
case "$version" in *-*) prerelease="--prerelease";; esac
# The previous tag bounds the generated notes; the first release has none.
prev="$(git tag --list 'v*' --sort=-v:refname | head -1)"
start=""; [ -n "$prev" ] && start="--notes-start-tag $prev"
# The tag is pushed here rather than left to the release: a DRAFT release
# does not create its tag until it is published, so the check above would
# never see it and every later push would draft again. A tag pushed with
# the bot's token starts no workflow here either: nothing runs on a tag push, and
# publishing waits for a person to publish the draft.
git config user.name "$BOT_NAME"
git config user.email "$BOT_EMAIL"
git tag -a "$tag" -m "$tag" "$SHA"
git -c http.https://github.com/.extraheader= push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "refs/tags/$tag"
gh release create "$tag" --draft --verify-tag --title "$tag" \
--generate-notes $start $prerelease
echo "::notice::drafted $tag — review it under Releases and publish it to ship to npm"
# ── keep dev on top of main ─────────────────────────────────────────────────
# A promotion from dev lands on main as a merge commit that dev does not have
# yet, and a hotfix straight to main is a commit dev lacks too; merging main
# back into dev records that they are already there. A promotion merges cleanly (same content both sides); a hotfix
# that went straight to main and touches lines dev has since changed does not,
# and then nothing is pushed: an issue says how to resolve it by hand.
sync-dev:
if: github.event_name == 'push'
runs-on: ubuntu-24.04
permissions:
contents: write
issues: write
steps:
- uses: actions/checkout@v4
with:
ref: dev
fetch-depth: 0
- name: the bot this job writes as
id: bot
uses: ./.github/actions/bot
with:
app-id: ${{ vars.AXIOMCODE_BOT_APP_ID }}
private-key: ${{ secrets.AXIOMCODE_BOT_PRIVATE_KEY }}
- name: merge main into dev
id: merge
env:
BOT_TOKEN: ${{ steps.bot.outputs.token }}
BOT_NAME: ${{ steps.bot.outputs.name }}
BOT_EMAIL: ${{ steps.bot.outputs.email }}
run: |
set -uo pipefail
git config user.name "$BOT_NAME"
git config user.email "$BOT_EMAIL"
git fetch origin main
if git merge-base --is-ancestor origin/main HEAD; then
echo "dev already contains main"; exit 0
fi
if git merge --no-edit -m "Merge main into dev (${GITHUB_SHA::8})" origin/main; then
git -c http.https://github.com/.extraheader= push "https://x-access-token:${BOT_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:dev
echo "dev now contains main at ${GITHUB_SHA::8}"
else
git diff --name-only --diff-filter=U > /tmp/conflicts.txt
git merge --abort
echo "conflict=1" >> "$GITHUB_OUTPUT"
echo "::error::main does not merge cleanly into dev"
exit 1
fi
# One open issue at most: a later conflicting push comments on it.
- name: say how to resolve it
if: failure() && steps.merge.outputs.conflict == '1'
env:
GH_TOKEN: ${{ steps.bot.outputs.token }}
run: |
set -uo pipefail
title="main does not merge cleanly into dev"
{
echo "Merging main (${GITHUB_SHA::8}) into dev conflicts in:"
echo; echo '```'; cat /tmp/conflicts.txt; echo '```'; echo
echo "Resolve it locally (dev takes direct pushes):"
echo; echo '```'
echo "git fetch origin && git checkout dev && git pull"
echo "git merge origin/main"
echo "# fix the files above, then"
echo "git add -A && git commit && git push origin dev"
echo '```'; echo
echo "Close this once dev is pushed; the next push to main retries on its own."
echo; echo "Run: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
} > /tmp/body.md
existing="$(gh issue list --state open --search "\"$title\" in:title" --json number --jq '.[0].number' || true)"
if [ -n "$existing" ]; then
gh issue comment "$existing" --body-file /tmp/body.md
else
gh issue create --title "$title" --body-file /tmp/body.md
fi