Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 113 additions & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,91 @@ where they help consumers understand the change or migrate. Keep `None.` for
sections that do not apply. The generated list of included pull requests and
contributors remains below the curated sections.

### Coordinating ECP, Android, and React Native releases

When a feature needs changes across all three packages, release them in this order:

```text
Embedded Checkout Protocol (Maven Central)
→ Checkout Kit Android (Maven Central)
→ Checkout Kit React Native (npm)
```

Each downstream package must build and pass CI against its newly published
dependency before it is released. A GitHub tag or release alone is not enough:
wait for the upstream publish workflow and any registry approval/propagation to
finish, then let the downstream Gradle build resolve the artifact from Maven
Central. The release workflows run independently; they do not automatically
sequence these three stages.

The version declarations have different jobs. The Android catalog is
`platforms/android/gradle/libs.versions.toml`; the RN manifest is
`platforms/react-native/modules/@shopify/checkout-kit-react-native/package.json`.

| Package | Its release version | Dependency it consumes |
| --- | --- | --- |
| Kotlin ECP | Catalog: `embeddedCheckoutProtocolAndroid` | — |
| Android Kit | Catalog: `checkoutKitAndroid` | Catalog: `embeddedCheckoutProtocolAndroidDependency` |
| React Native | RN manifest: `version` | RN manifest: `checkoutKit.nativeSdkVersions.android` |

1. **Release ECP.** Merge the protocol changes, any API baseline changes, and
`embeddedCheckoutProtocolAndroid` bump after protocol tests/API checks pass.
Leave Kit's dependency pin and RN's native SDK pins at their existing published
versions. Follow [the ECP release steps](#releasing-a-new-embedded-checkout-protocol-version)
and wait for the new protocol JAR and metadata to be available on Maven Central.
2. **Adopt ECP and release Android.** In the Kit PR, update
`embeddedCheckoutProtocolAndroidDependency`, make the SDK changes, and bump
`checkoutKitAndroid` and the installation snippets. Run normal SDK and sample
tests/builds and API checks against the published ECP dependency. Merge with
passing CI, follow [the Android release steps](#releasing-a-new-android-version),
and wait for the Kit AAR and metadata to be available on Maven Central.
3. **Adopt Android and release RN.** In the RN PR, update
`checkoutKit.nativeSdkVersions.android` to the published Kit version, make the
wrapper changes, and bump the RN package's own `version`. Run the normal RN
Android tests and sample build, plus the other required RN checks, before
merging. Follow [the RN release steps](#releasing-a-new-react-native-version)
to publish the npm package. RN resolves ECP transitively through Kit; it does
not need a separate ECP version pin. Update the iOS pin only when needed, after
the required Swift release is available on CocoaPods.

This is a dependency waterfall, not a requirement to release every package every
time. An Android-only fix can retain its ECP pin and start at stage 2. An RN-only
fix can retain its native SDK pins and start at stage 3. An ECP release does not
force Kit or RN to adopt it immediately, and the package versions need not match.

#### Why publish in this order?

- **Each layer tests the artifacts it declares.** Kit compiles against the same
published ECP API it asks consumers to resolve. RN then compiles against the
released Kit and its transitive protocol dependency. Missing types or methods
can fail those builds before a downstream release ships.
- **Versions form an explicit boundary.** A protocol source change does not
silently change Kit's dependency, and a Kit source change does not silently
change RN's dependency. Each adoption is a reviewable pin update with CI results.
- **The packages remain composable.** Apps and other SDKs can use standalone ECP
or Kit's normal transitive dependency without Kit embedding another copy of the
protocol classes. Gradle can resolve the shared dependency through its metadata.

The cost is waiting for upstream publication and running downstream CI between
releases. That waiting is deliberate: a successful local source build does not
prove that the declared published dependency supports the new code. The RN npm
publish job builds and packs the JavaScript module; it does not rerun Android
compilation, so passing RN Android CI against the published SDK is required before
the RN release.

#### Developing changes across packages

Develop the changes together on stacked branches using `dev android test --local`
or `dev android api check --local` for Kit/ECP, and `dev rn test android --local`
or `dev rn android --local` for RN/native changes. These explicit overrides allow
early integration before publication. On the RN development branch, set
`checkoutKit.nativeSdkVersions.android` to the in-repo `checkoutKitAndroid` version
so Maven Local resolves the artifact just built. Keep downstream adoption PRs open
until their upstream artifacts are published, then rerun normal builds and CI without
the overrides. Clear `USE_LOCAL_SDK` and `ORG_GRADLE_PROJECT_useLocalProtocol` if
they were exported in your shell. Local success does not replace the published
dependency checks or change the release order.

---

## Swift (`platforms/swift/`)
Expand Down Expand Up @@ -255,6 +340,10 @@ Open a pull request with the following changes:
1. Bump `embeddedCheckoutProtocolAndroid` in `platforms/android/gradle/libs.versions.toml`.
2. Update `protocol/languages/kotlin/embedded-checkout-protocol/api/embedded-checkout-protocol.api` if the public protocol API changed.

Keep `embeddedCheckoutProtocolAndroidDependency` at the existing published version
until the new protocol artifact is available. This lets the protocol release PR
merge while Kit continues building against its current dependency.

Supported protocol release versions are `YYYY.MM.DD.PATCH` and prerelease versions are `YYYY.MM.DD.PATCH-{alpha|beta|rc}.N`.

Once merged, run the [Release package workflow](../../actions/workflows/release.yml):
Expand All @@ -270,7 +359,26 @@ Once merged, run the [Release package workflow](../../actions/workflows/release.
Open a pull request with the following changes:

1. Bump `checkoutKitAndroid` in `platforms/android/gradle/libs.versions.toml`.
2. If the Android Kit release depends on a new protocol version, release `embeddedCheckoutProtocolAndroid` first.
2. If Kit needs a new protocol version, publish that protocol release first, then
update `embeddedCheckoutProtocolAndroidDependency` to it in the same catalog.

The Android library and sample compile against the protocol artifact pinned in
`platforms/android/gradle/libs.versions.toml` from Maven Central. CI uses the same
dependency, and the publish workflow runs unit tests and API checks before uploading.
Unit tests and remote publication also run `:lib:verifyPublishedProtocol`, which
checks that the release and unit-test classpaths resolve ECP as an external module
at exactly the catalog-pinned dependency version. This rejects accidental project
substitution or version changes introduced by dependency resolution. Explicit local
mode skips this assertion; remote publication still rejects local mode.
Changes to protocol source are tested separately with `dev protocol test kotlin`.

For joint development against unreleased protocol changes, run `dev android test --local`,
`dev android build --local`, or `dev android start --local`. The `--local` flag works with
Android build, test, lint, format, check, and API commands and sets the Gradle property
`useLocalProtocol=true` for that invocation. When running Gradle directly, pass `-PuseLocalProtocol=true`.
Remote publication rejects local mode; React Native's explicit `--local` flow can
still publish both artifacts to Maven Local. Kit changes that need a new protocol
version become mergeable once that version is published and normal CI passes.

Supported release versions are `X.Y.Z` and prerelease versions are `X.Y.Z-{alpha|beta|rc}.N`.

Expand Down Expand Up @@ -303,6 +411,10 @@ When updating the Swift or Android SDK version that React Native should consume,

For coordinated native and React Native releases, publish Android and Swift first, then update these React Native native SDK version pointers and publish React Native.

For an Android change that also needs a new protocol version, use the full
[ECP → Android → RN release sequence](#coordinating-ecp-android-and-react-native-releases).
Keep an unchanged iOS dependency pinned to its existing published release.

### Public API surface

The library's public API is tracked via a committed report at `platforms/react-native/modules/@shopify/checkout-kit-react-native/api/checkout-kit-react-native.api.md`, generated by [@microsoft/api-extractor](https://api-extractor.com/) from the bob-produced `.d.ts` files. The unified `Breaking Changes` CI workflow runs `dev rn api check` on every PR that touches React Native sources and fails if the regenerated report diverges from the committed one.
Expand Down
12 changes: 12 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

- [ ] I have bumped `embeddedCheckoutProtocolAndroid` in `platforms/android/gradle/libs.versions.toml`
- [ ] I have updated `protocol/languages/kotlin/embedded-checkout-protocol/api/embedded-checkout-protocol.api` if the public API changed
- [ ] Kit's `embeddedCheckoutProtocolAndroidDependency` still references an available Maven Central release

</details>

Expand All @@ -41,8 +42,19 @@

- [ ] I have bumped `checkoutKitAndroid` in `platforms/android/gradle/libs.versions.toml`
- [ ] I have updated the Gradle/Maven version snippets in `platforms/android/README.md`
- [ ] The ECP version in `embeddedCheckoutProtocolAndroidDependency` is published, and normal Android CI passes against it

</details>

<details>
<summary>Releasing a new React Native version?</summary>

- [ ] I have bumped `version` in `platforms/react-native/modules/@shopify/checkout-kit-react-native/package.json`
- [ ] Any updated `checkoutKit.nativeSdkVersions` pins are available on Maven Central/CocoaPods
- [ ] Normal RN Android tests and the sample build pass against the published SDK, without `--local` or `USE_LOCAL_SDK=1`

</details>

> [!TIP]
> See the [Contributing documentation](./CONTRIBUTING.md) for the full release process per platform.
> Changes spanning ECP, Android, and RN follow the [dependency release sequence](./CONTRIBUTING.md#coordinating-ecp-android-and-react-native-releases).
2 changes: 1 addition & 1 deletion .github/scripts/validate-release-version
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ case "$PLATFORM_INPUT" in
ANDROID_VERSION_FILE="platforms/android/gradle/libs.versions.toml"

VERSION=$(version_catalog_value "$ANDROID_VERSION_FILE" "checkoutKitAndroid")
ANDROID_PROTOCOL_VERSION=$(version_catalog_value "$ANDROID_VERSION_FILE" "embeddedCheckoutProtocolAndroid")
ANDROID_PROTOCOL_VERSION=$(version_catalog_value "$ANDROID_VERSION_FILE" "embeddedCheckoutProtocolAndroidDependency")
;;

"Embedded Checkout Protocol"|embedded-checkout-protocol|EmbeddedCheckoutProtocol|ecp|ECP)
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/android-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ jobs:
distribution: temurin
java-version: 17

- name: Test and check API against the published protocol
run: ./gradlew :lib:testDebugUnitTest :lib:apiCheck --console=plain

- name: Publish Package
run: |
./gradlew :lib:publishReleasePublicationToOssrh-staging-apiRepository
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/android-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ jobs:
- name: Run Tests
run: ./gradlew test --console=plain

- name: Run Protocol Unit Tests
working-directory: protocol/languages/kotlin
run: ./gradlew test --console=plain

- name: Setup sample app environment
id: sample_env
if: ${{ !cancelled() }}
Expand Down Expand Up @@ -147,6 +151,10 @@ jobs:
- name: Kotlin Lint
run: ./gradlew detekt

- name: Protocol Kotlin Lint
working-directory: protocol/languages/kotlin
run: ./gradlew detekt

- name: Setup sample app environment
run: ${{ github.workspace }}/scripts/setup_storefront_env
env:
Expand Down
Loading
Loading