Skip to content

Commit d126dcc

Browse files
[RN][CI] Setup release publishing flow (#248)
## What? - Adds `React Native` to workflow_dispatch for releases - Performs validation on the semver tag compared to package json - Creates a `react-native/X.Y.Z` tag - Builds and Publishes that tag - Updates the RN podspec source tag to use the `react-native/` namespace. - Changed workflow dispatch to avoid confusion between draft and dry-run settings Theres some scripts added to give us the ability to test tarbals of the release build against expo and react-native community cli installs, as well as with npm / yarn / bun / pnpm. | Before | After | | ------ | ----- | | <img width="307" height="388" alt="image" src="https://github.com/user-attachments/assets/c8553f5d-dedc-467c-9ce0-b8dda7ba348a" /> | <img width="310" height="418" alt="image" src="https://github.com/user-attachments/assets/d62fdb72-3ef6-4f98-abce-d227ff104dfc" /> | I also fixed some output on the release plan <img width="445" height="355" alt="image" src="https://github.com/user-attachments/assets/e8ede813-43ab-41d4-a940-a14178ffc4a3" /> ## Other In order to produce react-native `4.0.0-alpha.1` I needed to update podspec and gradle to point to the swift/android `4.0.0-alpha.1` versions published last week We have this awkward process for gradle, where the android sdk has to build and publish to `mavenLocal` for local dev, this is gated behind the `USE_LOCAL_SDK` flag This could fail silently if mavenLocal didn't have a 4.0.0-alpha.1 version, so I added an exclusiveContent filter which ensures the checkout-kit library, when the `USE_LOCAL_SDK` flag is true, will fail to build, ensuring we don't accidentally false positive test against a published SDK in maven. ```gradle if (useLocalSdk) { exclusiveContent { forRepository { mavenLocal() } filter { includeModule("com.shopify", "checkout-kit") } } } ```
1 parent 45f7312 commit d126dcc

16 files changed

Lines changed: 335 additions & 48 deletions

File tree

.github/CONTRIBUTING.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ Once merged, run the [Release package workflow](../../actions/workflows/release.
126126

127127
1. Select `iOS` as the platform.
128128
2. Enter the expected version. The workflow reads the SDK version from the checked-in files and fails if the typed version does not match.
129-
3. Leave `dry-run` enabled first to review the release plan.
130-
4. Rerun with `dry-run` disabled. By default this creates a draft GitHub Release with the bare semver tag (e.g. `3.8.1`) for human review.
129+
3. Select `Dry run` first to review the release plan without creating a release.
130+
4. Rerun with `Draft release` to create a draft GitHub Release with the bare semver tag (e.g. `3.8.1`) for human review.
131131
5. Publish the draft release when ready. Publishing the draft kicks off the [Swift publish workflow](../../actions/workflows/swift-publish.yml), which publishes the new version to CocoaPods.
132132

133133
---
@@ -174,8 +174,8 @@ Once merged, run the [Release package workflow](../../actions/workflows/release.
174174

175175
1. Select `Android` as the platform.
176176
2. Enter the expected version. The workflow reads the SDK version from `platforms/android/lib/build.gradle` and fails if the typed version does not match.
177-
3. Leave `dry-run` enabled first to review the release plan.
178-
4. Rerun with `dry-run` disabled. By default this creates a draft GitHub Release with the `android/`-prefixed tag (e.g. `android/3.0.1`) for human review.
177+
3. Select `Dry run` first to review the release plan without creating a release.
178+
4. Rerun with `Draft release` to create a draft GitHub Release with the `android/`-prefixed tag (e.g. `android/3.0.1`) for human review.
179179
5. Publish the draft release when ready. Publishing the draft kicks off the [Android publish workflow](../../actions/workflows/android-publish.yml). **A manual approval by a maintainer is required before publication to Maven Central.**
180180

181181
---
@@ -208,3 +208,20 @@ If your change intentionally modifies the public API:
208208
3. Commit the updated `.api.md` file in the same PR.
209209

210210
If you did *not* intend to change public API and `api:check` is failing, the diff shows what your change inadvertently affected — treat it as a signal that something in your PR has consumer-visible impact.
211+
212+
### Releasing a new React Native version
213+
214+
Open a pull request with the following changes:
215+
216+
1. Bump the `version` in `platforms/react-native/modules/@shopify/checkout-kit-react-native/package.json`.
217+
2. Add an entry to the React Native changelog.
218+
219+
Supported release versions are `X.Y.Z` and prerelease versions are `X.Y.Z-{alpha|beta|rc}.N`.
220+
221+
Once merged, run the [Release package workflow](../../actions/workflows/release.yml):
222+
223+
1. Select `React Native` as the platform.
224+
2. Enter the expected version. The workflow reads the SDK version from `platforms/react-native/modules/@shopify/checkout-kit-react-native/package.json` and fails if the typed version does not match.
225+
3. Select `Dry run` first to review the release plan without creating a release.
226+
4. From the dry-run job summary, copy the generated `gh workflow run` command to create a `Draft release` without retyping the validated version. Running it creates a draft GitHub Release with the `react-native/`-prefixed tag (e.g. `react-native/4.0.1`) for human review.
227+
5. Publish the draft release when ready. Publishing the draft kicks off the [React Native publish workflow](../../actions/workflows/rn-publish.yml), which publishes `@shopify/checkout-kit-react-native` to npm.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
file="${1:-}"
5+
6+
if [ -z "$file" ]; then
7+
echo "::error::Usage: package-json-version <package.json>" >&2
8+
exit 2
9+
fi
10+
11+
if [ ! -f "$file" ]; then
12+
echo "::error file=$file::Version source file does not exist." >&2
13+
exit 1
14+
fi
15+
16+
if ! version=$(jq -er '.version | strings | select(length > 0)' "$file"); then
17+
echo "::error file=$file::Could not extract version." >&2
18+
exit 1
19+
fi
20+
21+
printf '%s' "$version"

.github/scripts/validate-release-version

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Usage: validate-release-version <platform> [expected-version] [expected-tag]
88
Validates the selected SDK's checked-in version declarations, optional user
99
version input, optional git tag, and prints GitHub Actions outputs to stdout.
1010
11-
Platforms: iOS, Android
11+
Platforms: iOS, Android, React Native
1212
USAGE
1313
}
1414

@@ -42,6 +42,12 @@ extract_first_match() {
4242
printf '%s\n' "$value"
4343
}
4444

45+
json_version() {
46+
local file="$1"
47+
48+
.github/scripts/package-json-version "$file"
49+
}
50+
4551
check_same_version() {
4652
local expected="$1"
4753
local file="$2"
@@ -84,8 +90,19 @@ case "$PLATFORM_INPUT" in
8490
VERSION=$(extract_first_match "$ANDROID_VERSION_FILE" 's/^[[:space:]]*def[[:space:]]+versionName[[:space:]]*=[[:space:]]*"([^"]+)".*/\1/p')
8591
;;
8692

93+
"React Native"|react-native|ReactNative|rn|RN)
94+
PLATFORM="react-native"
95+
DISPLAY_PLATFORM="React Native"
96+
RELEASE_TITLE_PREFIX="React Native"
97+
TAG_PREFIX="react-native/"
98+
PUBLISH_WORKFLOW="rn-publish.yml"
99+
100+
RN_PACKAGE_FILE="platforms/react-native/modules/@shopify/checkout-kit-react-native/package.json"
101+
VERSION=$(json_version "$RN_PACKAGE_FILE")
102+
;;
103+
87104
*)
88-
echo "::error::Unsupported platform '$PLATFORM_INPUT'. Expected one of: iOS, Android." >&2
105+
echo "::error::Unsupported platform '$PLATFORM_INPUT'. Expected one of: iOS, Android, React Native." >&2
89106
exit 1
90107
;;
91108
esac
@@ -110,8 +127,10 @@ fi
110127

111128
if [[ "$VERSION" == *-* ]]; then
112129
PRERELEASE="true"
130+
NPM_TAG="next"
113131
else
114132
PRERELEASE="false"
133+
NPM_TAG="latest"
115134
fi
116135

117136
{
@@ -122,6 +141,7 @@ fi
122141
echo "release_title=$RELEASE_TITLE"
123142
echo "publish_workflow=$PUBLISH_WORKFLOW"
124143
echo "prerelease=$PRERELEASE"
144+
echo "npm_tag=$NPM_TAG"
125145
}
126146

127147
echo "${DISPLAY_PLATFORM} version '$VERSION' validates and maps to tag '$TAG'." >&2

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ jobs:
6666
- '.github/workflows/rn-build-ios.yml'
6767
- '.github/workflows/rn-check-packed-files.yml'
6868
- '.github/workflows/rn-lint.yml'
69+
- '.github/workflows/rn-publish.yml'
70+
- '.github/workflows/release.yml'
71+
- '.github/scripts/package-json-version'
72+
- '.github/scripts/validate-release-version'
73+
- '.github/actions/setup/**'
6974
- '.github/workflows/breaking-changes.yml'
7075
- '.github/workflows/ci.yml'
7176
web:

.github/workflows/release.yml

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@ on:
1010
options:
1111
- iOS
1212
- Android
13+
- React Native
1314
version:
1415
description: Expected SDK version. Must match the checked-in SDK version for the selected platform.
1516
required: true
1617
type: string
17-
dry-run:
18-
description: Dry run
18+
mode:
19+
description: Release mode
1920
required: false
20-
type: boolean
21-
default: true
22-
draft:
23-
description: Draft release
24-
required: false
25-
type: boolean
26-
default: true
21+
type: choice
22+
default: Dry run
23+
options:
24+
- Dry run
25+
- Draft release
26+
- Production release
2727

2828
permissions:
2929
contents: write
@@ -81,14 +81,13 @@ jobs:
8181
8282
- name: Print release plan
8383
env:
84-
DRY_RUN: ${{ inputs['dry-run'] && 'true' || 'false' }}
84+
MODE: ${{ inputs.mode }}
8585
DISPLAY_PLATFORM: ${{ steps.release.outputs.display_platform }}
8686
VERSION: ${{ steps.release.outputs.version }}
8787
TAG: ${{ steps.release.outputs.tag }}
8888
RELEASE_TITLE: ${{ steps.release.outputs.release_title }}
8989
PRERELEASE: ${{ steps.release.outputs.prerelease }}
9090
PUBLISH_WORKFLOW: ${{ steps.release.outputs.publish_workflow }}
91-
DRAFT: ${{ inputs.draft && 'true' || 'false' }}
9291
run: |
9392
set -euo pipefail
9493
echo "Release plan:"
@@ -98,21 +97,25 @@ jobs:
9897
echo " Title: ${RELEASE_TITLE}"
9998
echo " Prerelease: ${PRERELEASE}"
10099
echo " Publish workflow: ${PUBLISH_WORKFLOW}"
101-
echo " Dry run: ${DRY_RUN}"
102-
echo " Draft release: ${DRAFT}"
103-
if [ "$DRY_RUN" = "false" ] && [ "$DRAFT" = "true" ]; then
100+
echo " Mode: ${MODE}"
101+
if [ "$MODE" = "Dry run" ]; then
102+
echo " Release creation: skipped"
103+
echo " Publish dispatch: skipped"
104+
elif [ "$MODE" = "Draft release" ]; then
105+
echo " Release creation: draft GitHub Release"
104106
echo " Publish dispatch: skipped until the draft release is manually published"
105-
elif [ "$DRY_RUN" = "false" ]; then
107+
else
108+
echo " Release creation: published GitHub Release"
106109
echo " Publish dispatch: ${PUBLISH_WORKFLOW} will be dispatched after release creation"
107110
fi
108111
109112
- name: Create GitHub Release
110-
if: ${{ !inputs['dry-run'] }}
113+
if: ${{ inputs.mode != 'Dry run' }}
111114
env:
112115
TAG: ${{ steps.release.outputs.tag }}
113116
RELEASE_TITLE: ${{ steps.release.outputs.release_title }}
114117
PRERELEASE: ${{ steps.release.outputs.prerelease }}
115-
DRAFT: ${{ inputs.draft && 'true' || 'false' }}
118+
DRAFT: ${{ inputs.mode == 'Draft release' && 'true' || 'false' }}
116119
GH_TOKEN: ${{ github.token }}
117120
run: |
118121
set -euo pipefail
@@ -131,7 +134,7 @@ jobs:
131134
gh release create "${args[@]}"
132135
133136
- name: Dispatch publish workflow
134-
if: ${{ !inputs['dry-run'] && !inputs.draft }}
137+
if: ${{ inputs.mode == 'Production release' }}
135138
env:
136139
TAG: ${{ steps.release.outputs.tag }}
137140
PUBLISH_WORKFLOW: ${{ steps.release.outputs.publish_workflow }}
@@ -143,22 +146,50 @@ jobs:
143146
144147
- name: Summary
145148
env:
146-
DRY_RUN: ${{ inputs['dry-run'] && 'true' || 'false' }}
147-
DRAFT: ${{ inputs.draft && 'true' || 'false' }}
149+
MODE: ${{ inputs.mode }}
148150
DISPLAY_PLATFORM: ${{ steps.release.outputs.display_platform }}
149151
VERSION: ${{ steps.release.outputs.version }}
150152
TAG: ${{ steps.release.outputs.tag }}
151153
RELEASE_TITLE: ${{ steps.release.outputs.release_title }}
152154
PUBLISH_WORKFLOW: ${{ steps.release.outputs.publish_workflow }}
155+
REF_NAME: ${{ github.ref_name }}
156+
PLATFORM: ${{ inputs.platform }}
153157
run: |
158+
set -euo pipefail
159+
160+
WORKFLOW_URL="https://github.com/${GITHUB_REPOSITORY}/actions/workflows/release.yml"
161+
154162
cat >> "$GITHUB_STEP_SUMMARY" <<SUMMARY
155163
## Release plan
156164
157-
- Dry run: `${DRY_RUN}`
158-
- Draft release: `${DRAFT}`
165+
- Mode: \`${MODE}\`
159166
- Platform: ${DISPLAY_PLATFORM}
160167
- Version: ${VERSION}
161168
- Tag: \`${TAG}\`
162169
- Title: ${RELEASE_TITLE}
163170
- Publish workflow: \`${PUBLISH_WORKFLOW}\`
164171
SUMMARY
172+
173+
if [ "$MODE" = "Dry run" ]; then
174+
cat >> "$GITHUB_STEP_SUMMARY" <<SUMMARY
175+
176+
## Continue to draft release
177+
178+
GitHub does not currently support pre-filled \`workflow_dispatch\`
179+
form inputs via URL. To avoid retyping the validated version, run this
180+
GitHub CLI command:
181+
182+
\`\`\`bash
183+
gh workflow run release.yml \\
184+
--repo "${GITHUB_REPOSITORY}" \\
185+
--ref "${REF_NAME}" \\
186+
-f platform="${PLATFORM}" \\
187+
-f version="${VERSION}" \\
188+
-f mode="Draft release"
189+
\`\`\`
190+
191+
Or open the [Release package workflow](${WORKFLOW_URL}) and enter the
192+
same values manually.
193+
SUMMARY
194+
echo "::notice::GitHub CLI draft release command written to the job summary."
195+
fi

0 commit comments

Comments
 (0)