Skip to content
Merged
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
20 changes: 19 additions & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,31 @@ If your change intentionally modifies the public API:

If you did _not_ intend to change public API and `apiCheck` is failing, the diff shows what your change inadvertently affected — treat it as a signal that something in your PR has consumer-visible impact.

### Releasing a new Embedded Checkout Protocol version

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.

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):

1. Select `Embedded Checkout Protocol` as the platform.
2. Enter the expected version. The workflow reads the protocol version from `platforms/android/gradle/libs.versions.toml` and fails if the typed version does not match.
3. Select `Dry run` first to review the release plan without creating a release.
4. Rerun with `Draft release` to create a draft GitHub Release with the `embedded-checkout-protocol/`-prefixed tag (e.g. `embedded-checkout-protocol/2026.04.08.1-alpha.1`) for human review.
5. Publish the draft release when ready. Publishing the draft kicks off the [Embedded Checkout Protocol publish workflow](../../actions/workflows/android-protocol-publish.yml). **A manual approval by a maintainer is required before publication to Maven Central.**

### Releasing a new Android version

Open a pull request with the following changes:

1. Bump `checkoutKitAndroid` in `platforms/android/gradle/libs.versions.toml`.
2. Add an entry to the top of `platforms/android/CHANGELOG.md`.
3. Update `checkoutKit.nativeSdkVersions.android` in `platforms/react-native/modules/@shopify/checkout-kit-react-native/package.json` to the same lowercase SemVer.
4. If the Android Kit release depends on a new protocol version, release `embeddedCheckoutProtocolAndroid` first.

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

Expand All @@ -181,7 +199,7 @@ Once merged, run the [Release package workflow](../../actions/workflows/release.
2. Enter the expected version. The workflow reads the SDK version from `platforms/android/gradle/libs.versions.toml` and fails if the typed version does not match.
3. Select `Dry run` first to review the release plan without creating a release.
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.
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.**
5. Publish the draft release when ready. Publishing the draft kicks off the [Android publish workflow](../../actions/workflows/android-publish.yml). The workflow verifies that `com.shopify:embedded-checkout-protocol` is already available on Maven Central before publishing `com.shopify:checkout-kit`. **A manual approval by a maintainer is required before publication to Maven Central.**

---

Expand Down
8 changes: 8 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@

</details>

<details>
<summary>Releasing a new Embedded Checkout Protocol version?</summary>

- [ ] 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

</details>

<details>
<summary>Releasing a new Android version?</summary>

Expand Down
41 changes: 35 additions & 6 deletions .github/scripts/validate-release-version
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Usage: validate-release-version <platform> [expected-version] [expected-tag]
Validates the selected SDK's checked-in version declarations, optional user
version input, optional git tag, and prints GitHub Actions outputs to stdout.

Platforms: iOS, Android, React Native
Platforms: iOS, Android, Embedded Checkout Protocol, React Native
USAGE
}

Expand All @@ -22,6 +22,12 @@ EXPECTED_VERSION="${2:-}"
EXPECTED_TAG="${3:-}"

SEMVER_REGEX='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-(alpha|beta|rc)\.(0|[1-9][0-9]*))?$'
CALVER_REGEX='^[0-9]{4}\.(0[1-9]|1[0-2])\.(0[1-9]|[12][0-9]|3[01])\.(0|[1-9][0-9]*)(-(alpha|beta|rc)\.(0|[1-9][0-9]*))?$'

# Most release targets are SemVer. Embedded Checkout Protocol overrides these
# defaults to CalVer in its platform branch below.
VERSION_REGEX="$SEMVER_REGEX"
VERSION_FORMAT="X.Y.Z or X.Y.Z-{alpha|beta|rc}.N"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to update this?


extract_first_match() {
local file="$1"
Expand Down Expand Up @@ -109,11 +115,26 @@ case "$PLATFORM_INPUT" in
RN_PACKAGE_FILE="platforms/react-native/modules/@shopify/checkout-kit-react-native/package.json"

VERSION=$(version_catalog_value "$ANDROID_VERSION_FILE" "checkoutKitAndroid")
ANDROID_PROTOCOL_VERSION=$(version_catalog_value "$ANDROID_VERSION_FILE" "embeddedCheckoutProtocolAndroid")
RN_ANDROID_VERSION=$(json_string_field "$RN_PACKAGE_FILE" '.checkoutKit.nativeSdkVersions.android')

check_same_version "$VERSION" "$RN_PACKAGE_FILE" "$RN_ANDROID_VERSION"
;;

"Embedded Checkout Protocol"|embedded-checkout-protocol|EmbeddedCheckoutProtocol|ecp|ECP)
PLATFORM="embedded-checkout-protocol"
DISPLAY_PLATFORM="Embedded Checkout Protocol"
RELEASE_TITLE_PREFIX="Embedded Checkout Protocol"
TAG_PREFIX="embedded-checkout-protocol/"
PUBLISH_WORKFLOW="android-protocol-publish.yml"
VERSION_REGEX="$CALVER_REGEX"
VERSION_FORMAT="YYYY.MM.DD.PATCH or YYYY.MM.DD.PATCH-{alpha|beta|rc}.N"

ANDROID_VERSION_FILE="platforms/android/gradle/libs.versions.toml"

VERSION=$(version_catalog_value "$ANDROID_VERSION_FILE" "embeddedCheckoutProtocolAndroid")
;;

"React Native"|react-native|ReactNative|rn|RN)
PLATFORM="react-native"
DISPLAY_PLATFORM="React Native"
Expand All @@ -126,26 +147,31 @@ case "$PLATFORM_INPUT" in
;;

*)
echo "::error::Unsupported platform '$PLATFORM_INPUT'. Expected one of: iOS, Android, React Native." >&2
echo "::error::Unsupported platform '$PLATFORM_INPUT'. Expected one of: iOS, Android, Embedded Checkout Protocol, React Native." >&2
exit 1
;;
esac

if [[ ! "$VERSION" =~ $SEMVER_REGEX ]]; then
echo "::error::${DISPLAY_PLATFORM} SDK version '$VERSION' is invalid. Expected X.Y.Z or X.Y.Z-{alpha|beta|rc}.N." >&2
if [[ ! "$VERSION" =~ $VERSION_REGEX ]]; then
echo "::error::${DISPLAY_PLATFORM} version '$VERSION' is invalid. Expected $VERSION_FORMAT." >&2
exit 1
fi

if [ -n "${ANDROID_PROTOCOL_VERSION:-}" ] && [[ ! "$ANDROID_PROTOCOL_VERSION" =~ $CALVER_REGEX ]]; then
echo "::error::Embedded Checkout Protocol version '$ANDROID_PROTOCOL_VERSION' is invalid. Expected YYYY.MM.DD.PATCH or YYYY.MM.DD.PATCH-{alpha|beta|rc}.N." >&2
exit 1
fi

if [ -n "$EXPECTED_VERSION" ] && [ "$EXPECTED_VERSION" != "$VERSION" ]; then
echo "::error::Requested version '$EXPECTED_VERSION' does not match ${DISPLAY_PLATFORM} SDK version '$VERSION'. Bump the SDK version in a PR first, or rerun with '$VERSION'." >&2
echo "::error::Requested version '$EXPECTED_VERSION' does not match ${DISPLAY_PLATFORM} version '$VERSION'. Bump the version in a PR first, or rerun with '$VERSION'." >&2
exit 1
fi

TAG="${TAG_PREFIX}${VERSION}"
RELEASE_TITLE="[${RELEASE_TITLE_PREFIX}] ${VERSION}"

if [ -n "$EXPECTED_TAG" ] && [ "$EXPECTED_TAG" != "$TAG" ]; then
echo "::error::Git tag '$EXPECTED_TAG' does not match ${DISPLAY_PLATFORM} SDK version '$VERSION'. Expected tag '$TAG'." >&2
echo "::error::Git tag '$EXPECTED_TAG' does not match ${DISPLAY_PLATFORM} version '$VERSION'. Expected tag '$TAG'." >&2
exit 1
fi

Expand All @@ -166,6 +192,9 @@ fi
echo "publish_workflow=$PUBLISH_WORKFLOW"
echo "prerelease=$PRERELEASE"
echo "npm_tag=$NPM_TAG"
if [ -n "${ANDROID_PROTOCOL_VERSION:-}" ]; then
echo "android_protocol_version=$ANDROID_PROTOCOL_VERSION"
fi
}

echo "✓ ${DISPLAY_PLATFORM} version '$VERSION' validates and maps to tag '$TAG'." >&2
91 changes: 91 additions & 0 deletions .github/workflows/android-protocol-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: Embedded Checkout Protocol — Publish to Maven Central

on:
release:
types:
- published
workflow_dispatch:

jobs:
build:
# Only run for Embedded Checkout Protocol releases. Protocol releases are
# tagged `embedded-checkout-protocol/YYYY.MM.DD.PATCH[-prerelease.N]`.
if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.event.release.tag_name, 'embedded-checkout-protocol/') }}
environment:
name: Central Repository Deployment

runs-on: ubuntu-latest
permissions:
contents: read

env:
OSSRH_API_BASE_URL: https://ossrh-staging-api.central.sonatype.com

defaults:
run:
working-directory: protocol/languages/kotlin

steps:
- name: Checkout Repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
submodules: true

- name: Validate release tag matches protocol version
working-directory: ${{ github.workspace }}
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
set -euo pipefail
if [ "$GITHUB_EVENT_NAME" = "release" ]; then
TAG="$RELEASE_TAG"
else
TAG="$GITHUB_REF_NAME"
fi

.github/scripts/validate-release-version "Embedded Checkout Protocol" "" "$TAG"

- name: Install JDK 1.17
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: zulu
java-version: 17

- name: Publish Package
run: ./gradlew :embedded-checkout-protocol:publishReleasePublicationToOssrh-staging-apiRepository
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.OSSRH_GPG_SECRET_KEY_ID }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSSRH_GPG_SECRET_KEY }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }}

- name: Find OSSRH Repository
id: find-repo
run: |
TOKEN=$(echo -n "${{ secrets.OSSRH_USERNAME }}:${{ secrets.OSSRH_PASSWORD }}" | base64)
RESPONSE=$(curl -s -H "Authorization: Bearer $TOKEN" \
"${OSSRH_API_BASE_URL}/manual/search/repositories?ip=any&state=open")
echo "Response: $RESPONSE"

REPO_KEY=$(echo "$RESPONSE" | jq -r \
'.repositories[] | .key' | head -1)
echo "Repository key: $REPO_KEY"

if [ -z "$REPO_KEY" ]; then
echo "Error: No open repository found"
exit 1
fi

echo "repo_key=$REPO_KEY" >> $GITHUB_OUTPUT

- name: Upload Repository to Central Portal
run: |
TOKEN=$(echo -n "${{ secrets.OSSRH_USERNAME }}:${{ secrets.OSSRH_PASSWORD }}" | base64)
REPO_KEY="${{ steps.find-repo.outputs.repo_key }}"

echo "Uploading repository: $REPO_KEY"
curl -v -X POST -H "Authorization: Bearer $TOKEN" \
"${OSSRH_API_BASE_URL}/manual/upload/repository/$REPO_KEY"

echo "Upload completed successfully"
18 changes: 16 additions & 2 deletions .github/workflows/android-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
submodules: true

- name: Validate release tag matches Android version
id: release
working-directory: ${{ github.workspace }}
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
Expand All @@ -43,7 +44,21 @@ jobs:
TAG="$GITHUB_REF_NAME"
fi

.github/scripts/validate-release-version Android "" "$TAG"
.github/scripts/validate-release-version Android "" "$TAG" >> "$GITHUB_OUTPUT"

- name: Verify Embedded Checkout Protocol is published
env:
PROTOCOL_VERSION: ${{ steps.release.outputs.android_protocol_version }}
run: |
set -euo pipefail
POM_URL="https://repo.maven.apache.org/maven2/com/shopify/embedded-checkout-protocol/${PROTOCOL_VERSION}/embedded-checkout-protocol-${PROTOCOL_VERSION}.pom"

if ! curl -fsSL --retry 5 --retry-delay 10 --retry-all-errors -o /dev/null "$POM_URL"; then
echo "::error::com.shopify:embedded-checkout-protocol:${PROTOCOL_VERSION} is not available on Maven Central. Publish embedded-checkout-protocol/${PROTOCOL_VERSION} before releasing Android Kit."
exit 1
fi

echo "::notice::Found com.shopify:embedded-checkout-protocol:${PROTOCOL_VERSION} on Maven Central."

- name: Install JDK 1.17
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
Expand All @@ -54,7 +69,6 @@ jobs:
- name: Publish Package
run: |
./gradlew \
:embedded-checkout-protocol:publishReleasePublicationToOssrh-staging-apiRepository \
:lib:publishReleasePublicationToOssrh-staging-apiRepository
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@ jobs:
android:
- 'platforms/android/**'
- 'protocol/**'
- '.github/workflows/android-publish.yml'
- '.github/workflows/android-protocol-publish.yml'
- '.github/workflows/android-test.yml'
- '.github/workflows/breaking-changes.yml'
- '.github/workflows/release.yml'
- '.github/scripts/validate-release-version'
- '.github/workflows/ci.yml'
swift:
- 'platforms/swift/**'
Expand Down
14 changes: 13 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ on:
options:
- iOS
- Android
- Embedded Checkout Protocol
- React Native
version:
description: Expected SDK version. Must match the checked-in SDK version for the selected platform.
description: Expected package version. Must match the checked-in version for the selected platform.
required: true
type: string
mode:
Expand Down Expand Up @@ -88,11 +89,15 @@ jobs:
RELEASE_TITLE: ${{ steps.release.outputs.release_title }}
PRERELEASE: ${{ steps.release.outputs.prerelease }}
PUBLISH_WORKFLOW: ${{ steps.release.outputs.publish_workflow }}
ANDROID_PROTOCOL_VERSION: ${{ steps.release.outputs.android_protocol_version }}
run: |
set -euo pipefail
echo "Release plan:"
echo " Platform: ${DISPLAY_PLATFORM}"
echo " Version: ${VERSION}"
if [ -n "$ANDROID_PROTOCOL_VERSION" ]; then
echo " Embedded Checkout Protocol version: ${ANDROID_PROTOCOL_VERSION}"
fi
echo " Tag: ${TAG}"
echo " Title: ${RELEASE_TITLE}"
echo " Prerelease: ${PRERELEASE}"
Expand Down Expand Up @@ -152,6 +157,7 @@ jobs:
TAG: ${{ steps.release.outputs.tag }}
RELEASE_TITLE: ${{ steps.release.outputs.release_title }}
PUBLISH_WORKFLOW: ${{ steps.release.outputs.publish_workflow }}
ANDROID_PROTOCOL_VERSION: ${{ steps.release.outputs.android_protocol_version }}
REF_NAME: ${{ github.ref_name }}
PLATFORM: ${{ inputs.platform }}
run: |
Expand All @@ -170,6 +176,12 @@ jobs:
- Publish workflow: \`${PUBLISH_WORKFLOW}\`
SUMMARY

if [ -n "$ANDROID_PROTOCOL_VERSION" ]; then
cat >> "$GITHUB_STEP_SUMMARY" <<SUMMARY
- Embedded Checkout Protocol version: \`${ANDROID_PROTOCOL_VERSION}\`
SUMMARY
fi

if [ "$MODE" = "Dry run" ]; then
cat >> "$GITHUB_STEP_SUMMARY" <<SUMMARY

Expand Down
4 changes: 2 additions & 2 deletions platforms/android/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ Published Android artifact versions are bumped via:
2. The install snippets in `README.md` (Gradle and Maven).
3. `platforms/react-native/modules/@shopify/checkout-kit-react-native/package.json` (`checkoutKit.nativeSdkVersions.android`) for the published `com.shopify:checkout-kit` SemVer that RN CI resolves from Maven.

Android releases are tagged `android/X.Y.Z` (Swift releases use bare `X.Y.Z`). The publish workflow filters on the `android/` prefix — without it, nothing publishes on the Android side.
Embedded Checkout Protocol releases are tagged `embedded-checkout-protocol/YYYY.MM.DD.PATCH[-prerelease.N]` and publish only `com.shopify:embedded-checkout-protocol`. Android Kit releases are tagged `android/X.Y.Z` and publish only `com.shopify:checkout-kit`; the Android publish workflow fails if the referenced protocol version is not already available on Maven Central. Swift releases use bare `X.Y.Z`.

Publishing goes through GitHub Releases → the repo-root `.github/workflows/android-publish.yml` → manual approval gate before Maven Central deploy. Full procedure: the repo-root `.github/CONTRIBUTING.md` "Releasing a new version".
Publishing goes through GitHub Releases → the relevant repo-root publish workflow → manual approval gate before Maven Central deploy. Full procedure: the repo-root `.github/CONTRIBUTING.md` release sections.

## Things not to touch without discussion

Expand Down
2 changes: 1 addition & 1 deletion platforms/android/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[versions]
checkoutKitAndroid = "4.0.0-alpha.1"
embeddedCheckoutProtocolAndroid = "2026.04.08-alpha.1"
embeddedCheckoutProtocolAndroid = "2026.04.08.1-alpha.1"

androidApplicationGradlePlugin = "9.2.1"
androidLibraryGradlePlugin = "9.1.1"
Expand Down
Loading