-
Notifications
You must be signed in to change notification settings - Fork 4
183 lines (176 loc) · 7.79 KB
/
Copy pathrelease.yml
File metadata and controls
183 lines (176 loc) · 7.79 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
# Publishes @oxide/api and @oxide/openapi-gen-ts to npm via trusted publishers
# (OIDC) configured on npmjs.com for this repo + workflow filename + the
# `release` environment — no token, and provenance attestations are generated
# automatically (https://docs.npmjs.com/trusted-publishers).
#
# Two modes:
# Release: bump the version in a package's package.json and land it on main.
# The workflow publishes any version not yet on the registry after a
# required reviewer approves the `release` environment deployment. Full
# recipe in the README.
# Canary: Actions tab → Release → Run workflow → choose a package and enter
# a PR number. Publishes that PR's head as <version>-canary.<pr>.<sha>
# under the `canary` dist-tag and comments the version on the PR.
# Dispatching publishes the PR's code under @oxide after the same `release`
# environment approval — review the diff first. Fork PRs are rejected.
name: Release
on:
push:
branches: [main]
workflow_dispatch:
inputs:
package:
description: 'Package to publish a canary for'
required: true
type: choice
options: [oxide-api, oxide-openapi-gen-ts]
pr-number:
description: 'PR number to publish a canary for'
required: true
# A newer run cancels an older one in the same group — including one paused
# waiting for release-environment approval (tested; the docs don't say). So a
# stale unapproved release can't be approved once a newer merge lands, and the
# newest run publishes everything still unpublished. The cost: merging two
# bumps of the same package before the first is approved skips the first
# version. Canary runs are grouped per PR so they don't cancel releases or
# each other's PRs.
concurrency:
group: release-${{ inputs.pr-number || 'main' }}
cancel-in-progress: true
jobs:
# Main-branch validation runs here (rather than on validate.yml's own push
# trigger) so publishing can depend on it.
validate:
if: github.event_name == 'push'
uses: ./.github/workflows/validate.yml
check:
if: github.event_name == 'push'
needs: validate
runs-on: ubuntu-latest
outputs:
oxide-api: ${{ steps.oxide_api.outputs['needs-publishing'] }}
oxide-openapi-gen-ts: ${{ steps.oxide_openapi_gen_ts.outputs['needs-publishing'] }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Check whether @oxide/api version needs publishing
id: oxide_api
working-directory: oxide-api
run: |
NAME=$(jq --raw-output .name package.json)
VERSION=$(jq --raw-output .version package.json)
# empty output = version not on the registry. (npm view's exit code
# for a missing version has varied across npm versions; its output
# hasn't.)
if [ -n "$(npm view "${NAME}@${VERSION}" version 2>/dev/null)" ]; then
echo "needs-publishing=false" >> "$GITHUB_OUTPUT"
else
echo "needs-publishing=true" >> "$GITHUB_OUTPUT"
fi
- name: Check whether @oxide/openapi-gen-ts version needs publishing
id: oxide_openapi_gen_ts
working-directory: oxide-openapi-gen-ts
run: |
NAME=$(jq --raw-output .name package.json)
VERSION=$(jq --raw-output .version package.json)
# empty output = version not on the registry. (npm view's exit code
# for a missing version has varied across npm versions; its output
# hasn't.)
if [ -n "$(npm view "${NAME}@${VERSION}" version 2>/dev/null)" ]; then
echo "needs-publishing=false" >> "$GITHUB_OUTPUT"
else
echo "needs-publishing=true" >> "$GITHUB_OUTPUT"
fi
publish:
if: >-
needs.check.outputs['oxide-api'] == 'true' ||
needs.check.outputs['oxide-openapi-gen-ts'] == 'true'
needs: check
environment: release
timeout-minutes: 10
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # required for OIDC trusted publishing
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24 # npm >=11.5.1 required for trusted publishing
registry-url: 'https://registry.npmjs.org'
# npm recommends not restoring a dependency cache in release builds
package-manager-cache: false
# --ignore-scripts: a dependency install script running in this job
# could mint a publish credential (id-token: write is job-scoped).
# Nothing in either tree needs install scripts, and npm 12 will block
# them by default anyway.
- name: Install @oxide/api
if: needs.check.outputs['oxide-api'] == 'true'
working-directory: oxide-api
run: npm ci --ignore-scripts
- name: Publish @oxide/api # prepack runs the build
if: needs.check.outputs['oxide-api'] == 'true'
working-directory: oxide-api
run: npm publish
- name: Install @oxide/openapi-gen-ts
if: needs.check.outputs['oxide-openapi-gen-ts'] == 'true'
working-directory: oxide-openapi-gen-ts
run: npm ci --ignore-scripts
- name: Publish @oxide/openapi-gen-ts # prepack runs the build
if: needs.check.outputs['oxide-openapi-gen-ts'] == 'true'
working-directory: oxide-openapi-gen-ts
run: npm publish
canary:
if: github.event_name == 'workflow_dispatch'
environment: release
timeout-minutes: 10
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # required for OIDC trusted publishing
pull-requests: write # to comment the canary version on the PR
defaults:
run:
working-directory: ${{ inputs.package }}
steps:
# refs/pull/N/head resolves for fork PRs too, and this job's build
# scripts run with a publish credential, so only accept PRs from
# branches in this repo.
- name: Ensure PR is not from a fork
working-directory: .
env:
GH_TOKEN: ${{ github.token }}
run: |
CROSS_REPO=$(gh pr view ${{ inputs.pr-number }} --repo ${{ github.repository }} \
--json isCrossRepository --jq .isCrossRepository)
if [ "$CROSS_REPO" != "false" ]; then
echo "PR #${{ inputs.pr-number }} is from a fork; canaries can only be published for PRs from this repo" >&2
exit 1
fi
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The workflow definition comes from the ref the run is dispatched
# on, but the `release` environment only allows deployments from
# main, so in practice that's always main.
ref: refs/pull/${{ inputs.pr-number }}/head
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
registry-url: 'https://registry.npmjs.org'
package-manager-cache: false
- name: Install # --ignore-scripts for the same reason as the publish job
run: npm ci --ignore-scripts
- name: Set canary version
run: |
SHA=$(git rev-parse --short HEAD)
BASE=$(jq --raw-output .version package.json)
npm version "${BASE}-canary.${{ inputs.pr-number }}.${SHA}" --no-git-tag-version
- name: Publish canary # prepack runs the build
run: npm publish --tag canary
- name: Comment version on PR
env:
GH_TOKEN: ${{ github.token }}
run: |
NAME=$(jq --raw-output .name package.json)
VERSION=$(jq --raw-output .version package.json)
gh pr comment ${{ inputs.pr-number }} --repo ${{ github.repository }} \
--body "Canary published: \`${NAME}@${VERSION}\`"