forked from facebook/react
-
Notifications
You must be signed in to change notification settings - Fork 1
222 lines (212 loc) · 10.5 KB
/
runtime_release_from_ci.yml
File metadata and controls
222 lines (212 loc) · 10.5 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
name: (Runtime) Release From CI
on:
workflow_dispatch:
inputs:
type:
required: true
description: Type of release to publish
type: choice
# The `─── ... ───` entries are visual separators in the dropdown.
# They're rejected by the `resolve` job below, so picking one fails
# the workflow up front instead of silently doing the wrong release.
# GitHub Actions requires choice values to be unique, so the strings
# differ by the number of dashes.
options:
# publishes canary + experimental.
- nightly
- "────────────────"
- "─────────────────"
# semver stable published with @latest.
- stable-latest
- "──────────────────"
- "───────────────────"
# semver stable published with @backport (used for patches to
# older release lines that shouldn't move @latest).
- stable-backport
- "────────────────────"
- "─────────────────────"
# only experimental is published.
- experimental_only
only_packages:
description: Packages to publish (space separated allow-list; empty means all)
type: string
dry:
description: Dry run
type: boolean
default: false
force_notify:
description: Force a Discord notification?
type: boolean
default: false
schedule:
# At 10 minutes past 16:00 on Mon, Tue, Wed, Thu, and Fri.
# Scheduled runs always publish a nightly (see `resolve` job).
- cron: 10 16 * * 1,2,3,4,5
permissions: {}
env:
TZ: /usr/share/zoneinfo/America/Los_Angeles
# https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
jobs:
resolve:
name: Resolve release inputs
runs-on: ubuntu-latest
outputs:
release_type: ${{ steps.resolve.outputs.release_type }}
steps:
- name: Resolve release inputs
id: resolve
run: |
# Scheduled runs always publish a nightly. Manual dispatches always
# supply `inputs.type`. Anything else is unsupported and fails fast.
if [ "${{ github.event_name }}" = "schedule" ]; then
release_type=nightly
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
release_type="${{ inputs.type }}"
else
echo "Unsupported event: ${{ github.event_name }}" >&2
exit 1
fi
# Reject the dropdown's separator entries (and anything else that
# isn't one of the four real release types). Without this, picking
# a separator would fall through to all of publish.js's `if:` gates
# being false and the job would succeed without doing anything.
case "$release_type" in
nightly|stable-latest|stable-backport|experimental_only) ;;
*)
echo "Invalid release type: '$release_type' — pick one of nightly, stable-latest, stable-backport, experimental_only." >&2
exit 1
;;
esac
echo "release_type=$release_type" >> "$GITHUB_OUTPUT"
notify_starting:
name: Notify Discord (release starting)
# Manual dispatches always notify before the release starts so the team
# has a heads-up that a release is incoming. Scheduled (nightly) runs
# don't notify up front; we only notify on failure (see `notify` job).
if: ${{ github.event_name == 'workflow_dispatch' && vars.DISABLE_DISCORD_NOTIFICATIONS != 'true' }}
runs-on: ubuntu-latest
steps:
- uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
embed-author-name: ${{ github.event.sender.login }}
embed-author-url: ${{ github.event.sender.html_url }}
embed-author-icon-url: ${{ github.event.sender.avatar_url }}
embed-title: "⚠️ Publishing ${{ inputs.type }} release from source"
embed-description: |
```json
${{ toJson(inputs) }}
```
embed-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
publish:
name: Publish release
needs: resolve
if: ${{ !cancelled() && needs.resolve.result == 'success' }}
runs-on: ubuntu-latest
# Protected environment — requires reviewer approval before the publish
# job starts running.
environment: npm
permissions:
id-token: write
contents: read
# `actions: read` lets prepare-release-from-ci.js fetch the
# runtime_build_and_test workflow's artifacts for github.sha.
actions: read
env:
# Required by scripts/release/shared-commands/download-build-artifacts.js.
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
# Always check out the commit the workflow file itself was loaded from.
# Crucially, no user-supplied ref is accepted, since this job runs in a
# protected environment.
- uses: actions/checkout@v4
with:
ref: ${{ github.sha }}
- uses: actions/setup-node@v4
with:
# Using modern Node.js that ships with NPM supporting trusted publishing
node-version: "24"
# scripts/release is the only thing this job actually needs to run —
# publish.js and prepare-release-from-ci.js both live there and use its
# own node_modules. Cache is owned by this workflow (this is the only
# consumer), so on a miss we install and save.
- name: Restore cached scripts/release node_modules
uses: actions/cache@v4
id: release_node_modules
with:
path: scripts/release/node_modules
key: release-scripts-node_modules-v1-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('scripts/release/yarn.lock') }}
- run: yarn --cwd scripts/release install --frozen-lockfile
if: steps.release_node_modules.outputs.cache-hit != 'true'
# ----- stable (semver) — either @latest or @backport -----
- name: Stage semver stable artifacts
if: ${{ startsWith(needs.resolve.outputs.release_type, 'stable-') }}
# BEFORE MERGE: {{ github.sha }}
run: scripts/release/prepare-release-from-ci.js --skipTests -r latest --commit=cbd1addfb629188804dc091f06ab3349d73516c4
- name: Publish semver stable to @latest
if: ${{ needs.resolve.outputs.release_type == 'stable-latest' }}
run: |
scripts/release/publish.js \
--tag=latest \
${{ inputs.only_packages && format('--onlyPackages={0}', inputs.only_packages) || '' }} \
${{ inputs.dry && '--dry' || '' }}
# Backport releases stay on the `@backport` dist-tag — we don't move
# @latest, and (under OIDC) we can't add/remove dist-tags after publish
# anyway. The tag goes on at publish time and is left in place.
- name: Publish semver stable to @backport
if: ${{ needs.resolve.outputs.release_type == 'stable-backport' }}
run: |
scripts/release/publish.js \
--tag=backport \
${{ inputs.only_packages && format('--onlyPackages={0}', inputs.only_packages) || '' }} \
${{ inputs.dry && '--dry' || '' }}
# ----- nightly: canary first, then experimental -----
# NOTE: Intentionally running sequentially because npm will sometimes
# fail if you try to concurrently publish two different versions of the
# same package, even if they use different dist tags.
- name: Stage canary artifacts
if: ${{ needs.resolve.outputs.release_type == 'nightly' }}
# BEFORE MERGE: {{ github.sha }}
run: scripts/release/prepare-release-from-ci.js --skipTests -r stable --commit=cbd1addfb629188804dc091f06ab3349d73516c4
# Single tag only — OIDC publish tokens can't add additional dist-tags
# after publish, so the historical `canary,next` aliasing is gone.
- name: Publish canary to @canary
if: ${{ needs.resolve.outputs.release_type == 'nightly' }}
run: |
scripts/release/publish.js \
--tag=canary \
${{ inputs.only_packages && format('--onlyPackages={0}', inputs.only_packages) || '' }} \
${{ inputs.dry && '--dry' || '' }}
# ----- experimental (nightly + experimental_only) -----
- name: Stage experimental artifacts
if: ${{ needs.resolve.outputs.release_type == 'nightly' || needs.resolve.outputs.release_type == 'experimental_only' }}
# BEFORE MERGE: {{ github.sha }}
run: scripts/release/prepare-release-from-ci.js --skipTests -r experimental --commit=cbd1addfb629188804dc091f06ab3349d73516c4
- name: Publish experimental to @experimental
if: ${{ needs.resolve.outputs.release_type == 'nightly' || needs.resolve.outputs.release_type == 'experimental_only' }}
run: |
scripts/release/publish.js \
--tag=experimental \
${{ inputs.only_packages && format('--onlyPackages={0}', inputs.only_packages) || '' }} \
${{ inputs.dry && '--dry' || '' }}
notify:
name: Notify Discord on failure
needs: [resolve, publish]
# Runs for every workflow run (manual + scheduled) and only fires when
# something didn't complete successfully — i.e. an actual failure or a
# cancellation. Successful runs stay silent.
if: ${{ always() && (needs.resolve.result == 'failure' || needs.resolve.result == 'cancelled' || needs.publish.result == 'failure' || needs.publish.result == 'cancelled') && vars.DISABLE_DISCORD_NOTIFICATIONS != 'true' }}
runs-on: ubuntu-latest
steps:
- name: Discord Webhook Action
uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
embed-author-name: "GitHub Actions"
embed-title: "❌ [Runtime] Release from source failed (${{ needs.resolve.outputs.release_type || inputs.type || 'nightly' }})"
embed-description: |
resolve: `${{ needs.resolve.result }}`
publish: `${{ needs.publish.result }}`
event: `${{ github.event_name }}`
embed-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/attempts/${{ github.run_attempt }}