Repository navigation
149 lines (136 loc) · 6.6 KB
/
Copy pathrelease.yml
File metadata and controls
149 lines (136 loc) · 6.6 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
name: Release
on:
push:
branches: [ beta, main, "1.x" ]
# A second merge into the same branch waits for this run instead of racing it for the version.
concurrency:
group: release-${{ github.ref_name }}
cancel-in-progress: false
jobs:
release:
runs-on: ubuntu-latest
# Runs take three to six minutes; a hung npm or GitHub call would otherwise hold the next merge into the branch behind the concurrency group for six hours.
timeout-minutes: 30
strategy:
matrix:
# Keep this resolving to 24.5.0 or newer: npm trusted publishing (OIDC) needs
# npm >= 11.5.1, and Node 24.5.0 is the first release whose bundled npm clears that.
node: [ '24' ]
name: Release with Node version ${{ matrix.node }}
permissions:
id-token: write # Required for OIDC
contents: write # pushes the release tag and its note, and creates the GitHub release
issues: write # needed by @semantic-release/github
pull-requests: write # needed if semantic-release comments on PRs
steps:
- name: Checkout CLI
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
repository: apimatic/apimatic-cli
token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
fetch-depth: 0
path: cli
# Promotion source ran on the PR's head; a tag main gained after that does not re-run it, and a beta that cannot reach the tag gets the wrong version.
- name: check that beta contains main
if: github.ref_name == 'beta'
working-directory: cli
run: |
if git merge-base --is-ancestor origin/main HEAD; then exit 0; elif [ $? -ne 1 ]; then exit 1; fi
{
echo "### beta does not contain main, so this beta would miss main's tag"
echo "Nothing was published. Merge main into dev (the last release run on main says how) and promote dev to beta again."
} >> "$GITHUB_STEP_SUMMARY"
exit 1
# Corepack is not distributed with Node 25 and newer.
- name: setup pnpm
uses: pnpm/action-setup@a15d269cd4658e1107c09f1fabf4cbd7bd1f308a # v4.4.0
with:
version: 11.0.8
standalone: true
- name: setup node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ matrix.node }}
cache: 'pnpm'
cache-dependency-path: cli/pnpm-lock.yaml
- name: Install dependencies
working-directory: cli
run: pnpm install --frozen-lockfile
- name: Check Build
working-directory: 'cli'
run: pnpm build
- name: Release
working-directory: cli
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: pnpm exec semantic-release
# The release tag sits on main's merge commit, and a beta cut from a dev that cannot reach it gets the wrong version.
back-merge:
name: Merge main back into dev
needs: release
# Also after a failed, timed-out or cancelled release: main holds the merge commit either way, and the tag may already be on it.
if: ${{ always() && github.ref_name == 'main' }}
runs-on: ubuntu-latest
# A hung push would otherwise hold the next release behind the concurrency group for six hours.
timeout-minutes: 10
# Holds the deploy key that dev's ruleset lets push; the environment admits only main.
environment: back-merge
permissions:
contents: read
env:
# Both summaries below end with this sentence, so the ruleset step is written once.
MERGE_COMMIT_STEP: "The merge button offers only **Squash and merge** until an admin adds **merge commit** to dev's ruleset; the admin merges the PR with **Create a merge commit** and sets the ruleset back to squash only."
steps:
- name: look for the back-merge key
id: key
env:
KEY: ${{ secrets.BACK_MERGE_DEPLOY_KEY }}
run: echo "present=$([ -n "$KEY" ] && echo true || echo false)" >> "$GITHUB_OUTPUT"
- name: checkout dev
if: steps.key.outputs.present == 'true'
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
ref: dev
fetch-depth: 0
ssh-key: ${{ secrets.BACK_MERGE_DEPLOY_KEY }}
- name: merge main into dev
if: steps.key.outputs.present == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
for _ in 1 2 3; do
git fetch --quiet origin dev main
git reset --quiet --hard origin/dev
if ! git merge --no-ff --no-edit -m "chore: merge main into dev" origin/main; then
git merge --abort
{
echo "### main conflicts with dev, so it was not merged back"
echo "Cut \`<name>/merge-main\` from dev, merge origin/main into it, resolve the conflicts, and open a PR into dev; a squash there leaves the release tag out of dev. Until it is merged, dev → beta is refused."
echo "$MERGE_COMMIT_STEP"
} >> "$GITHUB_STEP_SUMMARY"
exit 1
fi
if rejection=$(git push --quiet origin HEAD:dev 2>&1); then exit 0; fi
echo "$rejection"
# Only a push that lost a race with another merge into dev is worth another try.
case "$rejection" in
*"(fetch first)"*|*"(non-fast-forward)"*) ;;
*)
{
echo "### dev refused the back-merge push"
echo "Every ruleset that requires a pull request on dev must list deploy keys as a bypass actor (\`.ai/plans/release-pipeline.md\`, 7.3); then re-run this job."
} >> "$GITHUB_STEP_SUMMARY"
exit 1 ;;
esac
done
echo "### main was merged into dev, but dev kept moving and the push failed three times; re-run this job." >> "$GITHUB_STEP_SUMMARY"
exit 1
- name: link the back-merge
if: steps.key.outputs.present != 'true'
run: |
{
echo "### Next: merge main back into dev"
echo "Open $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/compare/dev...main?expand=1&title=chore%3A%20merge%20main%20into%20dev and merge it with **Create a merge commit**; a squash leaves the release tag out of dev."
echo "$MERGE_COMMIT_STEP"
} >> "$GITHUB_STEP_SUMMARY"