44 workflow_dispatch :
55 inputs :
66 version :
7- description : " Exact prepared version, such as 10.0.1 or 9.8.3 "
7+ description : " patch, minor, major, or an exact stable version "
88 required : true
9+ default : " patch"
910 type : string
11+ announcement :
12+ description : " Optional announcement, saved before the generated changelog"
13+ required : false
14+ type : string
15+ release_tag :
16+ description : " Existing release tag to resume publication without another version bump"
17+ required : false
18+ type : string
19+
20+ permissions : {}
1021
11- permissions :
12- contents : write
13- # Both npm packages must trust margelo/react-native-nitro-sqlite's release.yml.
14- id-token : write
22+ env :
23+ GH_REPO : ${{ github.repository }}
1524
1625concurrency :
26+ # Serialize releases across branches so maintenance releases cannot race latest.
1727 group : release
1828 cancel-in-progress : false
1929
@@ -26,112 +36,191 @@ jobs:
2636 with :
2737 full : true
2838
29- release :
30- name : Publish packages and GitHub release
39+ prepare :
40+ name : Prepare release PR and lockfiles
3141 needs : preflight
32- runs-on : ubuntu-24.04
42+ if : inputs.release_tag == ''
43+ runs-on : macos-26
44+ timeout-minutes : 40
45+ permissions :
46+ contents : write
47+ pull-requests : write
48+ issues : write
49+ outputs :
50+ pr : ${{ steps.prepare.outputs.pr }}
51+ version : ${{ steps.prepare.outputs.version }}
52+ env :
53+ # A separate token makes GitHub run the usual PR checks on automated commits.
54+ GH_TOKEN : ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.RELEASE_IT_GITHUB_TOKEN }}
55+ RELEASE_INCREMENT : ${{ inputs.version }}
3356 steps :
57+ - name : Check release credentials
58+ run : |
59+ if [ -z "$GH_TOKEN" ]; then
60+ echo "Configure RELEASE_PLEASE_TOKEN with contents, pull requests, and issues write access." >&2
61+ exit 1
62+ fi
3463 - uses : actions/checkout@v7
3564 with :
3665 fetch-depth : 0
3766 ref : ${{ github.sha }}
38-
39- - name : Validate release version
67+ persist-credentials : false
68+ - uses : actions/setup-node@v7
69+ with :
70+ node-version : 24
71+ package-manager-cache : false
72+ - uses : oven-sh/setup-bun@v2
73+ with :
74+ bun-version : 1.3.1
75+ - name : Install release tooling
76+ run : bun install --frozen-lockfile
77+ - name : Test release automation
78+ run : |
79+ bun run test:release
80+ bun run typecheck:release
81+ - name : Create or refresh release PR
82+ id : prepare
83+ run : node scripts/prepare-release.ts
84+ - name : Check out release PR
85+ uses : actions/checkout@v7
86+ with :
87+ ref : ${{ steps.prepare.outputs.branch }}
88+ token : ${{ env.GH_TOKEN }}
89+ fetch-depth : 0
90+ persist-credentials : false
91+ - name : Synchronize workspace lockfile
92+ run : |
93+ bun install --lockfile-only --ignore-scripts
94+ bun run sync:lockfile
95+ bun run check:lockfile
96+ bun install --frozen-lockfile
97+ - uses : ruby/setup-ruby@v1
98+ with :
99+ ruby-version : 3.3.0
100+ bundler-cache : true
101+ working-directory : example
102+ - name : Select Xcode
103+ run : sudo xcode-select -s "/Applications/Xcode_26.5.app/Contents/Developer"
104+ - name : Update iOS and macOS lockfiles
105+ run : |
106+ bun example pods
107+ bun example pods:macos
108+ - name : Commit lockfiles and optional announcement
109+ run : node scripts/commit-release-lockfiles.ts
40110 env :
41- RELEASE_VERSION : ${{ inputs.version }}
111+ RELEASE_BRANCH : ${{ steps.prepare.outputs.branch }}
112+ RELEASE_ANNOUNCEMENT : ${{ inputs.announcement }}
113+
114+ merge :
115+ name : Wait for checks, merge and tag release
116+ needs : prepare
117+ runs-on : ubuntu-24.04
118+ timeout-minutes : 70
119+ permissions :
120+ contents : write
121+ pull-requests : write
122+ issues : write
123+ outputs :
124+ tag : ${{ steps.release.outputs.tag }}
125+ env :
126+ GH_TOKEN : ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.RELEASE_IT_GITHUB_TOKEN }}
127+ RELEASE_PR : ${{ needs.prepare.outputs.pr }}
128+ RELEASE_VERSION : ${{ needs.prepare.outputs.version }}
129+ steps :
130+ - uses : actions/checkout@v7
131+ with :
132+ ref : ${{ github.sha }}
133+ persist-credentials : false
134+ - uses : actions/setup-node@v7
135+ with :
136+ node-version : 24
137+ package-manager-cache : false
138+ - uses : oven-sh/setup-bun@v2
139+ with :
140+ bun-version : 1.3.1
141+ - run : bun install --frozen-lockfile
142+ - name : Merge when GitHub permits, then create a tagged draft
143+ id : release
144+ run : node scripts/merge-release.ts
145+
146+ publish :
147+ name : Publish both packages from the release tag
148+ needs : [preflight, merge]
149+ if : ${{ always() && needs.preflight.result == 'success' && (needs.merge.result == 'success' || inputs.release_tag != '') }}
150+ runs-on : ubuntu-24.04
151+ timeout-minutes : 30
152+ permissions :
153+ contents : write
154+ id-token : write
155+ outputs :
156+ tag : ${{ steps.validate.outputs.tag }}
157+ latest : ${{ steps.validate.outputs.latest }}
158+ env :
159+ GH_TOKEN : ${{ github.token }}
160+ RELEASE_TAG : ${{ needs.merge.outputs.tag || inputs.release_tag }}
161+ NPM_CONFIG_PROVENANCE : " true"
162+ steps :
163+ - name : Validate tag input
42164 run : |
43- if [[ ! "$RELEASE_VERSION " =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
44- echo "An exact stable release version is required." >&2
165+ if [[ ! "$RELEASE_TAG " =~ ^v [0-9]+\.[0-9]+\.[0-9]+$ ]]; then
166+ echo "An existing stable release tag is required." >&2
45167 exit 1
46168 fi
47-
169+ - uses : actions/checkout@v7
170+ with :
171+ ref : ${{ env.RELEASE_TAG }}
172+ fetch-depth : 0
173+ persist-credentials : false
48174 - uses : actions/setup-node@v7
49175 with :
50176 node-version : 24
51177 package-manager-cache : false
52-
53178 - uses : oven-sh/setup-bun@v2
54179 with :
55180 bun-version : 1.3.1
56-
57- - name : Install npm dependencies (bun)
58- run : bun install
59-
181+ - run : bun install --frozen-lockfile
182+ - name : Validate release and publication channel
183+ id : validate
184+ run : node scripts/validate-release.ts
60185 - name : Test release preflight
61- run : bun run test:release
62-
63- - name : Check trusted publishing for both packages
64186 run : |
65- node --input-type=module <<'NODE'
66- const requestUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL
67- const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN
68- if (!requestUrl || !requestToken) {
69- throw new Error('GitHub OIDC is unavailable to the release job')
70- }
71-
72- const oidcUrl = new URL(requestUrl)
73- oidcUrl.searchParams.set('audience', 'npm:registry.npmjs.org')
74- for (const packageName of [
75- 'react-native-nitro-sqlite',
76- 'react-native-nitro-sqlite-vec',
77- ]) {
78- const oidcResponse = await fetch(oidcUrl, {
79- headers: { Authorization: `Bearer ${requestToken}` },
80- })
81- if (!oidcResponse.ok) {
82- throw new Error(`GitHub OIDC request failed: ${oidcResponse.status}`)
83- }
84- const { value: idToken } = await oidcResponse.json()
85- if (!idToken) {
86- throw new Error('GitHub OIDC response did not contain a token')
87- }
88-
89- const exchangeUrl = new URL(
90- `/-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`,
91- 'https://registry.npmjs.org',
92- )
93- const response = await fetch(exchangeUrl, {
94- method: 'POST',
95- headers: { Authorization: `Bearer ${idToken}` },
96- })
97- if (!response.ok) {
98- throw new Error(`${packageName}: npm OIDC exchange failed (${response.status})`)
99- }
100- const { token } = await response.json()
101- if (!token) {
102- throw new Error(`${packageName}: npm OIDC exchange returned no token`)
103- }
104- console.log(`${packageName}: npm trusted publishing is available`)
105- }
106- NODE
107-
108- - name : Generate release notes
109- run : node scripts/generate-release-notes.ts "$RELEASE_VERSION" "$RUNNER_TEMP/release-notes.md"
110- env :
111- GH_TOKEN : ${{ github.token }}
112- RELEASE_VERSION : ${{ inputs.version }}
113-
114- - name : Publish prepared release
115- run : ./scripts/release.sh --publish-prepared "$RELEASE_VERSION"
187+ bun run test:release
188+ bun run typecheck:release
189+ - name : Assemble announcement, changelog and contributors
190+ run : node scripts/generate-release-notes.ts "${RELEASE_TAG#v}" "$RUNNER_TEMP/release-notes.md"
191+ - name : Publish packages with trusted publishing
192+ # Both packages keep their existing trusted publisher for release.yml.
193+ run : node scripts/publish-release.ts "${RELEASE_TAG#v}"
116194 env :
117- RELEASE_VERSION : ${{ inputs.version }}
118195 NPM_CONFIG_CACHE : ${{ runner.temp }}/npm-cache
119- NPM_CONFIG_PROVENANCE : " true"
120-
121- - name : Create GitHub release
122- run : |
123- version="$RELEASE_VERSION"
124- latest=true
125- if [[ "${version%%.*}" == 9 ]]; then
126- latest=false
127- fi
128- gh release create "v${version}" --target "$(git rev-parse HEAD)" --title "NitroSQLite ${version}" --latest="$latest" --notes-file "$RUNNER_TEMP/release-notes.md"
129- env :
130- GH_TOKEN : ${{ github.token }}
131- RELEASE_VERSION : ${{ inputs.version }}
196+ - uses : actions/upload-artifact@v7
197+ with :
198+ name : release-notes
199+ path : ${{ runner.temp }}/release-notes.md
132200
133201 build-android :
134- needs : release
202+ needs : publish
203+ permissions :
204+ contents : write
135205 uses : ./.github/workflows/build-android-release.yml
136206 with :
137- tag : v${{ inputs.version }}
207+ tag : ${{ needs.publish.outputs.tag }}
208+
209+ announce :
210+ name : Publish GitHub release with notes and APK
211+ needs : [publish, build-android]
212+ runs-on : ubuntu-24.04
213+ permissions :
214+ contents : write
215+ steps :
216+ - uses : actions/download-artifact@v8
217+ with :
218+ name : release-notes
219+ - name : Publish draft release
220+ env :
221+ GH_TOKEN : ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.RELEASE_IT_GITHUB_TOKEN }}
222+ GH_REPO : ${{ github.repository }}
223+ RELEASE_TAG : ${{ needs.publish.outputs.tag }}
224+ RELEASE_LATEST : ${{ needs.publish.outputs.latest }}
225+ run : |
226+ gh release edit "$RELEASE_TAG" --draft=false --title "NitroSQLite ${RELEASE_TAG#v}" --latest="$RELEASE_LATEST" --notes-file release-notes.md
0 commit comments