-
Notifications
You must be signed in to change notification settings - Fork 1.4k
198 lines (181 loc) · 6.64 KB
/
release.yml
File metadata and controls
198 lines (181 loc) · 6.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
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
name: Release
on:
workflow_dispatch:
inputs:
ref:
description: 'Commit ref or existing version tag (e.g., abc1234 or v1.2.3)'
required: true
type: string
new_version:
description: 'New version for npm version (e.g., patch, minor, major, 1.2.3). Must be empty when providing an existing tag.'
required: false
type: string
skip_ci_check:
description: 'Skip CI status check'
required: false
type: boolean
default: false
jobs:
preflight:
name: Validate
runs-on: ubuntu-slim
permissions:
actions: read
contents: read
outputs:
ref_is_tag: ${{steps.validation.outputs.ref_is_tag}}
steps:
- name: Validate inputs
id: validation
env:
REF: ${{inputs.ref}}
NEW_VERSION: ${{inputs.new_version}}
run: |
if [[ "$REF" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then
echo "ref_is_tag=true" >> "$GITHUB_OUTPUT"
if [[ -n "$NEW_VERSION" ]]; then
echo "::error::new_version must be empty when an existing tag is provided"
exit 1
fi
else
echo "ref_is_tag=false" >> "$GITHUB_OUTPUT"
if [[ -z "$NEW_VERSION" ]]; then
echo "::error::new_version is required when a commit ref is provided"
exit 1
fi
fi
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{inputs.ref}}
fetch-depth: 0
- name: Verify ref is HEAD of main
if: steps.validation.outputs.ref_is_tag == 'false'
env:
REF: ${{inputs.ref}}
run: |
MAIN_SHA=$(git rev-parse origin/main)
CURRENT_SHA=$(git rev-parse HEAD)
if [[ "$CURRENT_SHA" != "$MAIN_SHA" ]]; then
echo "::error::ref ${REF} (${CURRENT_SHA}) is not the HEAD of main (${MAIN_SHA})"
exit 1
fi
- name: Verify tag matches package.json version
if: steps.validation.outputs.ref_is_tag == 'true'
env:
REF: ${{inputs.ref}}
run: |
jq --raw-output --exit-status --arg tag "$REF" '
if (.version == ($tag | ltrimstr("v"))) then
"Package version (\(.version)) matches tag version (\($tag | ltrimstr(\"v\")))"
else
"Package version (\(.version)) does not match tag version (\($tag | ltrimstr(\"v\")))" | halt_error(1)
end' package.json
- name: Check CI status
if: '!inputs.skip_ci_check'
run: |
gh run list --commit "$(git rev-parse HEAD)" --workflow ci.yml --status success --json databaseId \
| jq --raw-output --exit-status '
if (length > 0) then
"CI checks have passed"
else
"CI has not completed successfully for this commit" | halt_error(1)
end'
env:
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
release:
name: Release
runs-on: ubuntu-latest
needs: preflight
environment: npm
permissions:
contents: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{inputs.ref}}
fetch-depth: 0
- name: Verify ref is still HEAD of main
if: needs.preflight.outputs.ref_is_tag == 'false'
env:
REF: ${{inputs.ref}}
run: |
MAIN_SHA=$(git rev-parse origin/main)
CURRENT_SHA=$(git rev-parse HEAD)
if [[ "$CURRENT_SHA" != "$MAIN_SHA" ]]; then
echo "::error::ref ${REF} (${CURRENT_SHA}) is no longer the HEAD of main (${MAIN_SHA})"
exit 1
fi
- name: Generate app token
if: needs.preflight.outputs.ref_is_tag == 'false'
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{vars.LAUNCHBOT_ID}}
private-key: ${{secrets.LAUNCHBOT_PRIVATE_KEY}}
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: package.json
cache: npm
registry-url: https://registry.npmjs.org
- name: Bump version
if: needs.preflight.outputs.ref_is_tag == 'false'
env:
NEW_VERSION: ${{inputs.new_version}}
run: npm version "$NEW_VERSION" --no-git-tag-version
- name: Push version commit
if: needs.preflight.outputs.ref_is_tag == 'false'
id: push-commit
env:
GH_TOKEN: ${{steps.app-token.outputs.token}}
run: |
RELEASE_TAG="v$(jq --raw-output .version package.json)"
PARENT_SHA=$(git rev-parse HEAD)
jq --null-input \
--arg repo "$GITHUB_REPOSITORY" \
--arg parentSha "$PARENT_SHA" \
--arg headline "$RELEASE_TAG" \
--rawfile pkgContent package.json \
--rawfile lockContent package-lock.json \
'{
query: "mutation($input: CreateCommitOnBranchInput!) { createCommitOnBranch(input: $input) { commit { oid } } }",
variables: {
input: {
branch: { repositoryNameWithOwner: $repo, branchName: "main" },
message: { headline: $headline },
expectedHeadOid: $parentSha,
fileChanges: {
additions: [
{ path: "package.json", contents: ($pkgContent | @base64) },
{ path: "package-lock.json", contents: ($lockContent | @base64) }
]
}
}
}
}' > /tmp/request.json
COMMIT_SHA=$(gh api graphql --input /tmp/request.json \
--jq '.data.createCommitOnBranch.commit.oid')
echo "release_tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
echo "version_commit_sha=$COMMIT_SHA" >> "$GITHUB_OUTPUT"
- name: Create version tag
if: needs.preflight.outputs.ref_is_tag == 'false'
run: |
gh api "repos/$GITHUB_REPOSITORY/git/refs" \
-f ref="refs/tags/${{steps.push-commit.outputs.release_tag}}" \
-f sha="${{steps.push-commit.outputs.version_commit_sha}}"
env:
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Publish to npm with provenance
run: npm publish --provenance
- name: Create GitHub Release
run: |
RELEASE_TAG="v$(jq --raw-output .version package.json)"
gh release create "$RELEASE_TAG" \
--title "$RELEASE_TAG" \
--draft \
--generate-notes
env:
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}