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
11 changes: 11 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ When in doubt about whether we will be interested in including a new feature, pl
7. Ensure all checks (tests, lint) are passing in GitHub.
8. Open a pull request with a detailed description of what is changing and why.

### Dev tooling

Shopify employees can use the root `dev.yml` from the repo root:

```bash
dev up
dev check
```

Platform-scoped commands are available as `dev android <command>`, `dev swift <command>`, and `dev react-native <command>` (or `dev rn`). Protocol schema/model commands are available as `dev protocol <command>`. For cross-platform changes, use `dev lint`, `dev test`, `dev check`, `dev format`, and `dev build`.

---

## Swift (`platforms/swift/`)
Expand Down
25 changes: 25 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,28 @@ protocol/ # cross-platform communication layer based on UCP
e2e/ # cross-platform end-to-end tests
.github/ # workflows, issue templates, CODEOWNERS
```

## Dev workflow

> **AI agents:** All commands require the `shadowenv exec --` prefix to run inside the shadowenv-managed environment.
>
> ```
> shadowenv exec --dir <repo_root> -- /opt/dev/bin/dev up
> shadowenv exec --dir <repo_root> -- /opt/dev/bin/dev test [ARGS]
> ```

Run `dev` commands from the repo root. Use `dev up` before running commands when
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.

Not sure if you've had success with this locally but I think telling it to use shadoenv and where to get dev from helped with certain ai sandboxing

All commands require `shadowenv exec --` prefix. 
shadowenv exec --dir DIR -- /opt/dev/bin/dev up
shadowenv exec --dir DIR -- /opt/dev/bin/dev test [ARGS]

the environment may not be provisioned.

For platform-scoped work, prefer the root `dev.yml` commands:

- Android: `dev android <command>`
- Swift: `dev swift <command>`
- React Native: `dev react-native <command>` or `dev rn <command>`

For protocol schema/model work, use `dev protocol <command>`.

For cross-platform changes, use the repo-wide aggregates: `dev lint`,
`dev test`, `dev check`, `dev format`, and `dev build`. Use
`dev <platform> format` for formatting; `fix` remains an alias for existing
workflows.
144 changes: 123 additions & 21 deletions dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ up:
- xcbeautify
- jq
- swiftlint
- swiftformat
- sccache
- ruby
- custom:
Expand Down Expand Up @@ -80,12 +81,23 @@ check:
android-detekt: cd platforms/android && ./gradlew detekt
android-lint: cd platforms/android && ./gradlew lintRelease
swift-lint: cd platforms/swift && ./Scripts/lint
swift-license-headers: cd platforms/swift && ./Scripts/ensure_license
react-native-lint-swift: cd platforms/react-native && ./scripts/lint_swift
react-native-lint-module: cd platforms/react-native && pnpm module lint
react-native-lint-sample: cd platforms/react-native && pnpm sample lint
react-native-license-headers: cd platforms/react-native && ./scripts/copy_license --check
protocol-build: swift build --target ShopifyCheckoutProtocol

commands:
# Repo-wide
build:
desc: Build all supported workspaces
run: |
set -e
/opt/dev/bin/dev android build
/opt/dev/bin/dev swift build
/opt/dev/bin/dev react-native build

codegen:
desc: "Generate UCP models. Usage: dev codegen <kotlin|swift>"
syntax: "<kotlin|swift>"
Expand All @@ -94,6 +106,33 @@ commands:
kotlin|swift) ./protocol/scripts/generate_models.sh --lang "$1" ;;
*) echo "Usage: dev codegen <kotlin|swift>"; exit 1 ;;
esac

format:
desc: Auto-format and apply safe lint autocorrections across supported workspaces
aliases: [fix]
run: |
set -e
/opt/dev/bin/dev android format
/opt/dev/bin/dev swift format
/opt/dev/bin/dev react-native format

lint:
desc: Run lint checks across supported workspaces
aliases: [style]
run: |
set -e
/opt/dev/bin/dev android lint
/opt/dev/bin/dev swift lint
/opt/dev/bin/dev react-native lint

test:
desc: Run tests across all supported workspaces
run: |
set -e
/opt/dev/bin/dev android test
/opt/dev/bin/dev swift test
/opt/dev/bin/dev react-native test

apollo:
subcommands:
download_schema:
Expand All @@ -115,6 +154,23 @@ commands:
*) echo "Usage: dev apollo codegen <android|swift> [accelerated|mobile-buy|all]"; exit 1 ;;
esac

# Protocol
protocol:
desc: "Checkout protocol commands"
subcommands:
build:
desc: Build the Swift protocol target
run: swift build --target ShopifyCheckoutProtocol
test:
desc: Run Swift protocol target tests
run: cd platforms/swift && ./Scripts/xcode_run test ShopifyCheckoutKit-Package ShopifyCheckoutProtocolTests
check:
desc: Build and test the Swift protocol target
run: |
set -e
/opt/dev/bin/dev protocol build
/opt/dev/bin/dev protocol test

# Android
android:
desc: "Android Checkout Kit commands"
Expand All @@ -127,6 +183,10 @@ commands:
desc: Build all sample applications
run: cd platforms/android/samples/MobileBuyIntegration && ./gradlew build

clean:
desc: Clean Android Gradle build outputs
run: cd platforms/android && ./gradlew clean

test:
desc: Run all tests with clean build
run: cd platforms/android && ./gradlew clean test --console=plain
Expand All @@ -141,8 +201,9 @@ commands:
aliases: [style]
run: cd platforms/android && ./gradlew detekt lintRelease

fix:
desc: Automatically fix format and lint issues where possible
format:
desc: Auto-format and apply safe lint autocorrections
aliases: [fix]
run: cd platforms/android && ./gradlew detekt --auto-correct

api:
Expand All @@ -152,6 +213,7 @@ commands:
echo ""
echo " check Verify public API matches the committed baseline"
echo " dump Regenerate the baseline after intentional public API changes"
exit 1
subcommands:
check:
desc: Verify public API matches the committed baseline
Expand All @@ -161,13 +223,11 @@ commands:
run: cd platforms/android && ./gradlew :lib:apiDump

check:
desc: Run all Android checks (license headers, detekt, android lint)
desc: Run all Android checks (license headers + lint)
run: |
set -e
cd platforms/android
./scripts/check_license_headers.rb
./gradlew detekt
./gradlew lintRelease
/opt/dev/bin/dev android check license-headers
/opt/dev/bin/dev android lint
subcommands:
license-headers:
desc: Check MIT license headers in source files
Expand All @@ -187,20 +247,39 @@ commands:
desc: Check format and lint issues using SwiftLint and SwiftFormat
aliases: [style]
run: cd platforms/swift && ./Scripts/lint
fix:
desc: Automatically fix format and lint issues where possible
format:
desc: Auto-format and apply safe lint autocorrections
aliases: [fix]
run: cd platforms/swift && ./Scripts/lint fix
clean:
desc: Clean Swift packages and sample app build artifacts
run: |
set -e
cd platforms/swift
# ShopifyCheckoutKit-Package is the SPM-wide scheme: it cleans all
# library targets in Package.swift (ShopifyCheckoutKit,
# ShopifyAcceleratedCheckouts, ShopifyCheckoutProtocol) in one pass.
./Scripts/xcode_run clean ShopifyCheckoutKit-Package
cd Samples
# Sample apps have a "Run Script" build phase that runs during clean
# and exits non-zero when there is nothing to delete. Tolerate it so
# the second sample still gets cleaned.
../Scripts/xcode_run clean MobileBuyIntegration || true
../Scripts/xcode_run clean ShopifyAcceleratedCheckoutsApp || true
build:
desc: Build Swift packages or sample apps
desc: Build all Swift packages and sample apps
run: |
echo "Usage: dev swift build {packages|samples}"
set -e
cd platforms/swift
# ShopifyCheckoutKit-Package builds every library target in
# Package.swift in one xcodebuild invocation. The sample apps act as
# integration compilation checks against the built libraries.
./Scripts/xcode_run build ShopifyCheckoutKit-Package
./Scripts/build_samples
subcommands:
packages:
desc: Build both ShopifyCheckoutKit and ShopifyAcceleratedCheckouts packages
run: |
cd platforms/swift
./Scripts/xcode_run build ShopifyCheckoutKit
./Scripts/xcode_run build ShopifyAcceleratedCheckouts
desc: Build Swift package targets
run: cd platforms/swift && ./Scripts/xcode_run build ShopifyCheckoutKit-Package
samples:
desc: Build all sample applications to verify integration
run: cd platforms/swift && ./Scripts/build_samples
Expand All @@ -210,12 +289,25 @@ commands:
`dev swift test <test_class_name>` - Run only the specified test class.
syntax: "[test_class_name]"
run: cd platforms/swift && ./Scripts/xcode_run test ShopifyCheckoutKit-Package "$1"
check:
desc: Run Swift lint and license header checks
run: |
set -e
/opt/dev/bin/dev swift lint
/opt/dev/bin/dev swift check license-headers
subcommands:
license-headers:
desc: Check MIT license headers in Swift source files
run: cd platforms/swift && ./Scripts/ensure_license

# React Native
react-native:
desc: "React Native Checkout Kit commands"
aliases: [rn]
subcommands:
build:
desc: Build the @shopify/checkout-kit-react-native module
run: cd platforms/react-native && pnpm module build
server:
desc: Start Metro development server
aliases: [s]
Expand All @@ -237,9 +329,6 @@ commands:
sccache --stop-server 2>/dev/null || true
fi
echo "Cleaned root, module and sample workspaces"
build:
desc: Build the @shopify/checkout-kit-react-native module
run: cd platforms/react-native && pnpm module build
lint:
desc: Run all React Native lint checks (Swift, module, sample)
aliases: [style]
Expand All @@ -254,12 +343,25 @@ commands:
desc: Lint Swift code via SwiftLint
run: cd platforms/react-native && ./scripts/lint_swift
module:
desc: Lint the @shopify/checkout-sheet-kit module
desc: Lint the @shopify/checkout-kit-react-native module
run: cd platforms/react-native && pnpm module lint
sample:
desc: Lint the sample app
run: cd platforms/react-native && pnpm sample lint
format:
desc: Auto-fix Swift lint and format issues
desc: Auto-format and apply safe lint autocorrections (Swift bridge only)
aliases: [fix]
run: cd platforms/react-native && ./scripts/lint_swift fix
test:
desc: Run React Native Jest tests
run: cd platforms/react-native && pnpm test --testPathPatterns="modules/@shopify/checkout-kit-react-native/tests"
check:
desc: Run React Native license header and lint checks
run: |
set -e
/opt/dev/bin/dev react-native check license-headers
/opt/dev/bin/dev react-native lint
subcommands:
license-headers:
desc: Check React Native module license headers
run: cd platforms/react-native && ./scripts/copy_license --check
4 changes: 2 additions & 2 deletions platforms/android/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ The sample is a separate Gradle composite (`samples/MobileBuyIntegration/setting

- **`-Xexplicit-api=strict`** is on (`lib/build.gradle`). Every public class, method, field, and property must have an explicit visibility modifier. "Accidentally public" is not a thing here. This is a consumer-protection rule — if you see a public-by-default declaration, it was deliberate.
- **Max line length: 140** (detekt-enforced). Detekt config: `lib/detekt.config.yml`.
- **MIT license header required on every new source file.** Format: copy the top comment of any existing `.kt` or `.java` file in `lib/src/main` or `lib/src/test`. Enforced in CI via the repo-root `scripts/check_license_headers.rb`.
- **MIT license header required on every new source file.** Format: copy the top comment of any existing `.kt` or `.java` file in `lib/src/main` or `lib/src/test`. Enforced in CI via `platforms/android/scripts/check_license_headers.rb`; check locally with `dev android check license-headers` from the repo root.
- **Library JVM target: 1.8.** Intentional for consumer compatibility; don't raise without a major-version discussion.
- **Library Kotlin version is pinned.** The `lib/build.gradle` plugin version and any `apiVersion` / `languageVersion` settings exist to keep consumer compatibility stable. A Kotlin major-version migration is a planned major-version event, not a casual dep bump.

Expand All @@ -63,7 +63,7 @@ If `apiCheck` fails and you did *not* intend to change public API, the diff tell
- Tests: `./gradlew :lib:test` (or `dev android test`)
- API surface: `./gradlew :lib:apiCheck` / `./gradlew :lib:apiDump` (or `dev android api check` / `dev android api dump`)
- Lint: `./gradlew detekt lintRelease` (or `dev android lint`)
- Auto-fix lint: `./gradlew detekt --auto-correct` (or `dev android fix`)
- Format: `./gradlew detekt --auto-correct` (or `dev android format`)
- Full local verification: `./gradlew :lib:clean :lib:test :lib:detekt :lib:lintRelease :lib:assembleRelease`
- Sample app build (from `samples/MobileBuyIntegration/`): `./gradlew assembleDebug`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ class RCTShopifyCheckoutKit: RCTEventEmitter, CheckoutDelegate {
return NSNumber(value: available)
}

@objc func initiateGeolocationRequest(_ allow: Bool) {
@objc func initiateGeolocationRequest(_: Bool) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Root dev check surfaced an existing SwiftFormat issue. The parameter is intentionally unused, so _ is the correct shape here.

// No-op on iOS — geolocation permission is handled natively
}

Expand Down
1 change: 1 addition & 0 deletions platforms/react-native/scripts/copy_license
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def copy_license(dir, license_block, normalized_license, check_only: false)
supported_exts = %w[.swift .h .mm .java .js .ts .tsx]

Find.find(dir) do |path|
next if path.include?('/build/generated/')
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This keeps the RN license check focused on source-controlled files. Generated Android build output can exist locally after builds/codegen and does not reliably include our license header, which made dev check depend on local build state

next unless File.file?(path) && supported_exts.any? { |ext| path.end_with?(ext) }

changed = process_file(path, license_block, normalized_license, write: !check_only)
Expand Down
8 changes: 4 additions & 4 deletions platforms/swift/.cursor/rules/swift-development.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ alwaysApply: true

Key commands for verification:
- `dev swift lint` (alias: `dev swift style`) - Check format & lint issues
- `dev swift fix` - Auto-fix formatting and linting issues
- `dev swift build packages` - Build both packages
- `dev swift format` - Auto-format and apply safe lint autocorrections
- `dev swift build packages` - Build Swift package targets
- `dev swift test` - Run all tests

## Concurrency Best Practices
Expand All @@ -50,15 +50,15 @@ actor QueryCache {
✅ DO: Run the appropriate dev command to verify

### After making changes:
- ALWAYS run `dev swift fix && dev swift lint` to check your code is formatted and written in our style.
- ALWAYS run `dev swift format && dev swift lint` to check your code is formatted and written in our style.

## Example Workflow

```bash
# Make your Swift changes...

# Fix any auto-fixable issues
dev swift fix
dev swift format

# Check for any remaining lint issues
dev swift lint
Expand Down
Loading
Loading