From 92de1c26a86e5bfb33839f119e00fbff2fa91034 Mon Sep 17 00:00:00 2001 From: Kyle Schellen Date: Wed, 20 May 2026 19:30:31 -0400 Subject: [PATCH] Add repo setup orchestration commands --- .env.example | 6 +- .github/CONTRIBUTING.md | 24 +- AGENTS.md | 14 + dev.yml | 359 +++++++++++------- .../samples/MobileBuyIntegration/.env.example | 2 +- .../samples/MobileBuyIntegration/README.md | 60 +-- platforms/android/samples/README.md | 23 +- platforms/react-native/CONTRIBUTING.md | 21 +- .../ios/ShopifyCheckoutKit.swift | 8 +- platforms/react-native/sample/.env.example | 2 +- .../Samples/MobileBuyIntegration/README.md | 52 +-- .../Storefront.xcconfig.example | 2 +- platforms/swift/Samples/README.md | 60 +-- .../Storefront.xcconfig.example | 2 +- platforms/web/.gitignore | 1 + platforms/web/.oxlintrc.json | 1 + platforms/web/package.json | 6 +- scripts/setup_dev_workspace | 281 ++++++++++++++ 18 files changed, 676 insertions(+), 248 deletions(-) create mode 100755 scripts/setup_dev_workspace diff --git a/.env.example b/.env.example index 00f2b847..75114f8f 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,9 @@ # Checkout Kit sample storefront configuration. -# Copy this file to .env, fill in local values, then run: +# Copy this file to .env, fill in local values, then run dev bootstrap or: # scripts/setup_storefront_env -# Optional Apple Pay and Customer Account API values can stay blank. +# Direct interactive setup prompts for optional Apple Pay and Customer Account +# API values by default. dev up/dev bootstrap use non-interactive setup, so +# missing optional values can stay blank. # # Do not commit real values from .env or generated platform config files. diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ae63d3ed..e6005706 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,7 +2,7 @@ The following is a set of guidelines for contributing to this project. Please take a moment to read through them before submitting your first PR. -This is a monorepo containing the iOS/Swift, Android, and (forthcoming) React Native implementations of the Shopify Checkout Kit. Each platform has its own conventions, tooling, and release process; the shared guidelines below apply to all of them. +This is a monorepo containing the Swift, Android, React Native, and Web implementations of the Shopify Checkout Kit. Each platform has its own conventions, tooling, and release process; the shared guidelines below apply to all of them. ## Code of Conduct @@ -48,7 +48,25 @@ dev up dev check ``` -Platform-scoped commands are available as `dev android `, `dev swift `, and `dev react-native ` (or `dev rn`). Protocol schema/model commands are available as `dev protocol `. For cross-platform changes, use `dev lint`, `dev test`, `dev check`, `dev format`, and `dev build`. +`dev up` performs full DevHub provisioning, then runs Checkout Kit's repo-owned +setup steps. Those repo-owned steps are summarized at the end so a Swift, +Android, React Native, or Web setup failure is visible without hiding later +platform results. If a platform-specific setup step fails, fix it and rerun that +platform directly, for example `dev swift setup`. To rerun the repo-owned setup +summary without DevHub's native provisioning, use `dev bootstrap`. + +Setup creates or syncs sample app storefront configuration from the repo-root +`.env`. If `.env` is missing, setup prompts for required storefront values and +then generates the Android, Swift, and React Native sample config files. +Optional Apple Pay and Customer Account API values are preserved if already set, +but `dev up` leaves missing optional values blank instead of prompting. To fill +optional values interactively, run `dev storefront-env sync --prompt-optional`. + +Platform-scoped commands are available as `dev android `, `dev swift `, `dev react-native ` (or `dev rn`), and `dev web `. Each platform also supports `dev setup` and `dev up` for platform-local setup when the full repo does not need to be reprovisioned. Protocol schema/model commands are available as `dev protocol `. For cross-platform changes, use `dev lint`, `dev test`, `dev check`, `dev format`, and `dev build`. + +React Native sample apps can be run against local in-repo SDK sources with +`dev rn ios --local` or `dev rn android --local`. The Web sample accepts a +checkout URL directly and does not use the shared storefront credential files. --- @@ -136,7 +154,7 @@ If your change intentionally modifies the public API: 2. Review the diff in `platforms/android/lib/api/lib.api` alongside your code changes. 3. Commit the updated `.api` file in the same PR. -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. +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 Android version diff --git a/AGENTS.md b/AGENTS.md index f9ef5375..3259adef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,6 +5,7 @@ platforms/ swift/ # iOS Swift Package and CocoaPods sources android/ # Android library and sample apps react-native/ # React Native wrapper + web/ # Web component package and sample app protocol/ # cross-platform communication layer based on UCP e2e/ # cross-platform end-to-end tests .github/ # workflows, issue templates, CODEOWNERS @@ -27,6 +28,11 @@ For platform-scoped work, prefer the root `dev.yml` commands: - Android: `dev android ` - Swift: `dev swift ` - React Native: `dev react-native ` or `dev rn ` +- Web: `dev web ` + +Use `dev setup` or `dev up` for platform-local setup when +the full repo has already been provisioned. Use `dev bootstrap` to rerun the +repo-owned setup aggregator without rerunning DevHub's native provisioning. For protocol schema/model work, use `dev protocol `. @@ -34,3 +40,11 @@ For cross-platform changes, use the repo-wide aggregates: `dev lint`, `dev test`, `dev check`, `dev format`, and `dev build`. Use `dev format` for formatting; `fix` remains an alias for existing workflows. + +## Sensitive configuration + +Treat storefront environment and generated sample app configuration values as +sensitive. Never print, commit, paste, or document real values from `.env`, +generated platform config, access tokens, merchant identifiers, shop IDs, +account IDs, or storefront domains. Use synthetic placeholders for docs and +verification. diff --git a/dev.yml b/dev.yml index 53513b8b..f43e2817 100644 --- a/dev.yml +++ b/dev.yml @@ -11,48 +11,6 @@ up: - swiftformat - sccache - ruby - - custom: - name: Install bundle packages - met?: BUNDLE_GEMFILE=platforms/swift/Gemfile bundle check - meet: BUNDLE_GEMFILE=platforms/swift/Gemfile bundle install - # Android - - custom: - name: Ensure Android sample app .env files exist - met?: | - ([ -f "./platforms/android/samples/MobileBuyIntegration/.env" ] || exit 1) - meet: cd platforms/android && ./scripts/setup_env.sh - - - custom: - name: Bootstrap Mint packages - met?: | - set -e - cd platforms/swift - expected_swiftlint="$(sed -n 's#^realm/SwiftLint@##p' Mintfile)" - expected_swiftformat="$(sed -n 's#^nicklockwood/SwiftFormat@##p' Mintfile)" - swiftlint_bin="$(mint which swiftlint)" - swiftformat_bin="$(mint which swiftformat)" - test -n "$expected_swiftlint" - test -n "$expected_swiftformat" - test "$("$swiftlint_bin" version)" = "$expected_swiftlint" - test "$("$swiftformat_bin" --version)" = "$expected_swiftformat" - meet: cd platforms/swift && mint bootstrap - - xcode: - version: "26.2" - runtimes: - ios: - - version: 23C54 # 26.2 - architecture_variant: arm64 - - custom: - name: Ensure Storefront.xcconfig file - met?: | - ([ -f "./platforms/swift/Samples/MobileBuyIntegration/Storefront.xcconfig" ] || exit 1) - meet: cd platforms/swift && ./Scripts/ensure_storefront_config - - custom: - name: Setup entitlements - met?: | - ([ -f "./platforms/swift/Samples/MobileBuyIntegration/MobileBuyIntegration/MobileBuyIntegration.entitlements" ] || exit 1;) - meet: cd platforms/swift && ./Scripts/setup_entitlements - - node: version: v22.14.0 package_manager: pnpm@10.33.1 @@ -60,17 +18,15 @@ up: - platforms/react-native - platforms/web - custom: - name: Install NPM dependencies (React Native) - met?: ls -l platforms/react-native | grep node_modules - meet: cd platforms/react-native && pnpm install - - custom: - name: Install NPM dependencies (Web) - met?: ls -l platforms/web | grep node_modules - meet: cd platforms/web && pnpm install - - custom: - name: Install gems (React Native sample) - met?: (cd platforms/react-native/sample/ios && bundle check) - meet: cd platforms/react-native/sample/ios && bundle install + name: Run Checkout Kit workspace setup + met?: ./scripts/setup_dev_workspace --check --skip-optional-prompts all + meet: ./scripts/setup_dev_workspace --skip-optional-prompts all + - xcode: + version: "26.2" + runtimes: + ios: + - version: 23C54 # 26.2 + architecture_variant: arm64 open: "GitHub": "https://github.com/Shopify/checkout-kit" @@ -78,6 +34,7 @@ open: "PRs": "https://github.com/Shopify/checkout-kit/pulls" check: + storefront-env-tests: ./scripts/test_setup_storefront_env android-detekt: cd platforms/android && ./gradlew detekt android-lint: cd platforms/android && ./gradlew lintRelease swift-lint: cd platforms/swift && ./Scripts/lint @@ -89,6 +46,18 @@ check: commands: # Repo-wide + bootstrap: + desc: Prepare repo-owned setup steps and summarize platform failures + long_desc: | + Runs Checkout Kit repo-owned setup without rerunning DevHub native + provisioning. Use dev up first if shared tools from packages, ruby, node, + or xcode provisioning are missing. + --prompt-optional only affects storefront configuration setup for + Android, Swift, and React Native; web-only setup has no optional prompts. + syntax: + optional: "[--check] [--prompt-optional] [all|android|swift|react-native|rn|web]" + run: ./scripts/setup_dev_workspace "$@" + build: desc: Build all supported workspaces run: | @@ -107,6 +76,23 @@ commands: *) echo "Usage: dev codegen "; exit 1 ;; esac + storefront-env: + desc: "Sample app storefront configuration commands" + subcommands: + sync: + desc: Create or sync generated sample config from the repo-root .env + long_desc: | + Creates or syncs generated platform sample config from the repo-root + .env. dev up skips optional prompts for automation; run + dev storefront-env sync --prompt-optional to fill optional Apple Pay + and Customer Account API values interactively. + syntax: + optional: "[--skip-optional-prompts|--prompt-optional]" + run: ./scripts/setup_storefront_env "$@" + check: + desc: Verify generated sample config is up to date + run: ./scripts/setup_storefront_env --check + format: desc: Auto-format and apply safe lint autocorrections across supported workspaces aliases: [fix] @@ -137,6 +123,7 @@ commands: /opt/dev/bin/dev web test apollo: + desc: "Apollo GraphQL schema and code generation commands" subcommands: download_schema: desc: "Download GraphQL schema. Usage: dev apollo download_schema [accelerated|mobile-buy|all]" @@ -178,6 +165,17 @@ commands: android: desc: "Android Checkout Kit commands" subcommands: + setup: + desc: Prepare Android sample configuration without running full repo setup + long_desc: | + Runs only Checkout Kit repo-owned Android setup. It does not run + DevHub native provisioning for shared tools. Use dev up first if + base tools are missing. + aliases: [up] + syntax: + optional: "[--check] [--prompt-optional]" + run: ./scripts/setup_dev_workspace android "$@" + build: desc: Build the library run: cd platforms/android && ./gradlew :lib:build @@ -186,10 +184,6 @@ 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 @@ -209,6 +203,24 @@ commands: aliases: [fix] run: cd platforms/android && ./gradlew detekt --auto-correct + check: + desc: Run all Android checks (detekt, Android lint) + run: | + set -e + /opt/dev/bin/dev android check detekt + /opt/dev/bin/dev android check android-lint + subcommands: + detekt: + desc: Run detekt static analysis + run: cd platforms/android && ./gradlew detekt + android-lint: + desc: Run Android lint + run: cd platforms/android && ./gradlew lintRelease + + clean: + desc: Clean Android Gradle build outputs + run: cd platforms/android && ./gradlew clean + api: desc: Validate or update the public API baseline (lib/api/lib.api) run: | @@ -225,47 +237,21 @@ commands: desc: Regenerate the baseline after intentional public API changes run: cd platforms/android && ./gradlew :lib:apiDump - check: - desc: Run all Android checks (detekt, Android lint) - run: | - set -e - /opt/dev/bin/dev android check detekt - /opt/dev/bin/dev android check android-lint - subcommands: - detekt: - desc: Run detekt static analysis - run: cd platforms/android && ./gradlew detekt - android-lint: - desc: Run Android lint - run: cd platforms/android && ./gradlew lintRelease - # Swift swift: desc: "Swift Checkout Kit commands" subcommands: - lint: - desc: Check format and lint issues using SwiftLint and SwiftFormat - aliases: [style] - run: cd platforms/swift && ./Scripts/lint - 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 + setup: + desc: Prepare Swift sample configuration, gems, Mint tools, and entitlements + long_desc: | + Runs only Checkout Kit repo-owned Swift setup. It does not run DevHub + native provisioning for shared tools or Xcode runtimes. Use dev up + first if base tools are missing. + aliases: [up] + syntax: + optional: "[--check] [--prompt-optional]" + run: ./scripts/setup_dev_workspace swift "$@" + build: desc: Build all Swift packages and sample apps run: | @@ -304,63 +290,62 @@ commands: dump: desc: Regenerate the Swift API baselines after intentional public API changes run: cd platforms/swift && ./Scripts/api dump + + lint: + desc: Check format and lint issues using SwiftLint and SwiftFormat + aliases: [style] + run: cd platforms/swift && ./Scripts/lint + + format: + desc: Auto-format and apply safe lint autocorrections + aliases: [fix] + run: cd platforms/swift && ./Scripts/lint fix + check: desc: Run Swift lint checks run: /opt/dev/bin/dev swift lint + 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 + # React Native react-native: desc: "React Native Checkout Kit commands" aliases: [rn] subcommands: + setup: + desc: Prepare React Native packages, sample config, and iOS Ruby gems + long_desc: | + Runs only Checkout Kit repo-owned React Native setup. It does not run + DevHub native provisioning for shared tools or CocoaPods install. Use + dev up first if base tools are missing, and use dev rn pod-install + when iOS pods need to be installed. + aliases: [up] + syntax: + optional: "[--check] [--prompt-optional]" + run: ./scripts/setup_dev_workspace react-native "$@" + 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] - run: cd platforms/react-native && pnpm sample start --reset-cache - ios: - desc: Run the iOS sample app in the simulator. - long_desc: | - Builds and runs the iOS sample app on a simulator. - --local - Build against in-repo Swift SDK sources (runs pod install first). - syntax: - optional: --local - run: cd platforms/react-native && pnpm sample ios "$@" - android: - desc: Run the Android sample app in the emulator. - long_desc: | - Builds and runs the Android sample app on an emulator. - - --local - Build against in-repo SDK sources (publishes a local Maven snapshot first). - syntax: - optional: --local - run: cd platforms/react-native && pnpm sample android "$@" - pod-install: - desc: Install CocoaPods for the iOS sample. - long_desc: | - Runs pod install for the iOS sample app. + test: + desc: Run React Native Jest tests + run: cd platforms/react-native && pnpm test --testPathPatterns="modules/@shopify/checkout-kit-react-native/tests" - --local - Wire the Podfile against in-repo Swift SDK sources. - syntax: - optional: --local - run: cd platforms/react-native && pnpm run pod-install -- "$@" - clean: - desc: Remove generated directories and stop sccache - run: | - cd platforms/react-native - pnpm module clean - pnpm sample clean - pnpm clean - if command -v sccache >/dev/null 2>&1; then - sccache --stop-server 2>/dev/null || true - fi - echo "Cleaned root, module and sample workspaces" lint: desc: Run all React Native lint checks (Swift, module, sample) aliases: [style] @@ -380,6 +365,7 @@ commands: sample: desc: Lint the sample app run: cd platforms/react-native && pnpm sample lint + format: desc: Auto-format and apply safe lint autocorrections (Swift bridge only) aliases: [fix] @@ -399,13 +385,61 @@ commands: dump: desc: Regenerate the RN API report after intentional public API changes run: cd platforms/react-native && pnpm module api:dump - 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 lint checks run: /opt/dev/bin/dev react-native lint + clean: + desc: Remove generated directories and stop sccache + run: | + cd platforms/react-native + pnpm module clean + pnpm sample clean + pnpm clean + if command -v sccache >/dev/null 2>&1; then + sccache --stop-server 2>/dev/null || true + fi + echo "Cleaned root, module and sample workspaces" + + server: + desc: Start Metro development server + aliases: [s] + run: cd platforms/react-native && pnpm sample start --reset-cache + + ios: + desc: Run the iOS sample app in the simulator. + long_desc: | + Builds and runs the iOS sample app on a simulator. + + --local + Build against in-repo Swift SDK sources (runs pod install first). + syntax: + optional: --local + run: cd platforms/react-native && pnpm sample ios "$@" + + android: + desc: Run the Android sample app in the emulator. + long_desc: | + Builds and runs the Android sample app on an emulator. + + --local + Build against in-repo SDK sources (publishes a local Maven snapshot first). + syntax: + optional: --local + run: cd platforms/react-native && pnpm sample android "$@" + + pod-install: + desc: Install CocoaPods for the iOS sample. + long_desc: | + Runs pod install for the iOS sample app. + + --local + Wire the Podfile against in-repo Swift SDK sources. + syntax: + optional: --local + run: cd platforms/react-native && pnpm run pod-install -- "$@" + rn: desc: "Alias for React Native Checkout Kit commands" long_desc: | @@ -419,13 +453,21 @@ commands: web: desc: "Web Checkout Kit commands" subcommands: - sample: - desc: Start the sample app dev server - aliases: [s] - run: cd platforms/web && pnpm sample + setup: + desc: Prepare Web package dependencies + long_desc: | + Runs only Checkout Kit repo-owned Web setup. It does not run DevHub + native provisioning for shared tools. Use dev up first if base tools + are missing. + aliases: [up] + syntax: + optional: "[--check]" + run: ./scripts/setup_dev_workspace web "$@" + build: desc: Build the @shopify/checkout-kit package run: cd platforms/web && pnpm build + test: desc: Run unit tests with coverage run: cd platforms/web && pnpm test @@ -433,10 +475,12 @@ commands: watch: desc: Run tests in watch mode run: cd platforms/web && pnpm test:watch + lint: desc: Run typecheck, oxlint, and format checks aliases: [style] run: cd platforms/web && pnpm lint + format: desc: Auto-fix JS lint and formatting issues aliases: [fix] @@ -445,6 +489,33 @@ commands: cd platforms/web pnpm run lint:js:fix pnpm run format + + check: + desc: Run Web lint, unit tests, build, package verification, and sample build checks + run: | + set -e + /opt/dev/bin/dev web lint + /opt/dev/bin/dev web test + /opt/dev/bin/dev web build + /opt/dev/bin/dev web verify + /opt/dev/bin/dev web sample build + clean: desc: Remove dist and coverage directories run: cd platforms/web && pnpm clean + + sample: + desc: Start the sample app dev server + aliases: [s] + run: cd platforms/web && pnpm sample + subcommands: + build: + desc: Build the sample app + run: cd platforms/web && pnpm sample:build + preview: + desc: Preview the built sample app + run: cd platforms/web && pnpm sample:preview + + verify: + desc: Run Web package verification + run: cd platforms/web && pnpm verify diff --git a/platforms/android/samples/MobileBuyIntegration/.env.example b/platforms/android/samples/MobileBuyIntegration/.env.example index 5a393043..c1b2d88d 100644 --- a/platforms/android/samples/MobileBuyIntegration/.env.example +++ b/platforms/android/samples/MobileBuyIntegration/.env.example @@ -1,5 +1,5 @@ # Platform-local example for the Android sample. Prefer the repo-root -# .env.example for shared setup, then run scripts/setup_storefront_env. +# .env.example for shared setup, then run dev up or dev bootstrap. STOREFRONT_DOMAIN= STOREFRONT_ACCESS_TOKEN= API_VERSION=2026-04 diff --git a/platforms/android/samples/MobileBuyIntegration/README.md b/platforms/android/samples/MobileBuyIntegration/README.md index ded94669..ec15e56c 100644 --- a/platforms/android/samples/MobileBuyIntegration/README.md +++ b/platforms/android/samples/MobileBuyIntegration/README.md @@ -66,46 +66,54 @@ Do not edit files in `app/build/generated/source/apollo/` by hand. Update `.grap ## Setup -From this directory: +1. From the repo root, create or sync the shared sample configuration: -```sh -cp .env.example .env -``` + ```bash + dev up + ``` -Edit `.env`: + If the repo is already provisioned and you only need Android sample setup, + run: -```text -STOREFRONT_DOMAIN=your-store.myshopify.com -STOREFRONT_ACCESS_TOKEN=your-token -API_VERSION=2026-04 -``` + ```bash + dev android setup + ``` + + If you are not using `dev`, copy the repo-root `.env.example` to `.env`, + fill in local values, then run: + + ```bash + scripts/setup_storefront_env + ``` Optional values enable Customer Account API and buyer identity demo flows: ```text CUSTOMER_ACCOUNT_API_CLIENT_ID=your-client-id -CUSTOMER_ACCOUNT_API_REDIRECT_URI=shop..app://callback -CUSTOMER_ACCOUNT_API_GRAPHQL_BASE_URL=https://shopify.com//account/customer/api//graphql -CUSTOMER_ACCOUNT_API_AUTH_BASE_URL=https://shopify.com/authentication/ -PREFILL_EMAIL=test.buyer@example.com -PREFILL_PHONE=+16135550123 +CUSTOMER_ACCOUNT_API_SHOP_ID=your-shop-id +CUSTOMER_ACCOUNT_API_VERSION=unstable +EMAIL=test.buyer@example.com +PHONE=+14165550100 ``` Open the project in Android Studio, sync Gradle, then build and run. ## Updating the Storefront API version -1. Update `API_VERSION` in `.env`. -2. Download the schema with Rover. This introspects your store's Storefront API and writes `schema.graphqls` into `app/src/main/graphql/`. +1. Update `API_VERSION` in the repo-root `.env`. +2. Sync the generated platform config: + + ```sh + dev storefront-env sync + ``` + +3. Download the schema. This introspects your store's Storefront API and writes `schema.graphqls` into `app/src/main/graphql/`. ```sh - rover graph introspect \ - "https://$STOREFRONT_DOMAIN/api/$API_VERSION/graphql" \ - --header="X-Shopify-Storefront-Access-Token: $STOREFRONT_ACCESS_TOKEN" \ - --output "app/src/main/graphql/schema.graphqls" + dev apollo download_schema android ``` -3. Update GraphQL operations in `app/src/main/graphql/` if the schema changed. For example, add a product field to `FetchProducts.graphql` before regenerating types: +4. Update GraphQL operations in `app/src/main/graphql/` if the schema changed. For example, add a product field to `FetchProducts.graphql` before regenerating types: ```graphql query FetchProducts(...) { @@ -119,13 +127,13 @@ Open the project in Android Studio, sync Gradle, then build and run. } ``` -4. Regenerate Kotlin types with Apollo Kotlin. This reads the schema and `.graphql` files, then regenerates Kotlin code in `app/build/generated/source/apollo/`. +5. Regenerate Kotlin types with Apollo Kotlin. This reads the schema and `.graphql` files, then regenerates Kotlin code in `app/build/generated/source/apollo/`. ```sh - ./gradlew :app:generateApolloSources + dev apollo codegen android ``` -5. Build and fix any compile errors from schema changes: +6. Build and fix any compile errors from schema changes: ```sh ./gradlew :app:assembleDebug @@ -135,7 +143,7 @@ Open the project in Android Studio, sync Gradle, then build and run. | File | Purpose | | --- | --- | -| `.env` | Store credentials, API version, Customer Account API values, and demo buyer identity. | +| `.env` | Generated sample config from the repo-root `.env` (not checked into git). | | `app/build.gradle` | Apollo plugin configuration and `BuildConfig` values from `.env`. | | `app/src/main/graphql/schema.graphqls` | Storefront API schema. | | `common/client/StorefrontApiClient.kt` | Apollo client setup and Storefront API auth header. | diff --git a/platforms/android/samples/README.md b/platforms/android/samples/README.md index 42f62677..8fa84b14 100644 --- a/platforms/android/samples/README.md +++ b/platforms/android/samples/README.md @@ -2,24 +2,29 @@ This directory contains Android sample apps for Checkout Kit. +Sample app storefront configuration is generated from the repo-root `.env`. +Run `dev up` from the repo root to provision the repo and create or sync the +generated sample config files. If the repo is already provisioned, `dev android +setup` or `dev android up` refreshes the Android sample setup directly. + | Sample | Purpose | | --- | --- | | `MobileBuyIntegration` | Storefront API cart flow with Apollo Kotlin, checkout presentation, typed protocol lifecycle events, file chooser handling, geolocation callbacks, buyer identity demo data, and Customer Account API sign-in. | ## Setup -From `platforms/android/samples/MobileBuyIntegration`: +From the repo root: ```sh -cp .env.example .env +dev up ``` -Fill in: +If you are not using `dev`, copy the repo-root `.env.example` to `.env`, fill +in local values, then run: -- `STOREFRONT_DOMAIN` -- `STOREFRONT_ACCESS_TOKEN` -- `API_VERSION` -- Optional Customer Account API values -- Optional demo buyer identity values +```sh +scripts/setup_storefront_env +``` -Open `MobileBuyIntegration` in Android Studio, sync Gradle, then build and run the `app` target. +Open `MobileBuyIntegration` in Android Studio, sync Gradle, then build and run +the `app` target. diff --git a/platforms/react-native/CONTRIBUTING.md b/platforms/react-native/CONTRIBUTING.md index d2c5f691..2ce5a0f0 100644 --- a/platforms/react-native/CONTRIBUTING.md +++ b/platforms/react-native/CONTRIBUTING.md @@ -18,7 +18,10 @@ specific to each workspace. If you've cloned the repo and want to run the sample app, Shopify employees can run `dev up` and `dev react-native ` from the repo root (`dev rn` is -an alias). The underlying `pnpm` commands below are run from +an alias). If the repo is already provisioned, `dev rn setup` or `dev rn up` +refreshes React Native packages, sample config, and iOS Ruby gems directly. +Run `dev rn pod-install` when iOS pods need to be installed. The underlying +`pnpm` commands below are run from `platforms/react-native`: 1. Install the NPM dependencies @@ -30,7 +33,7 @@ an alias). The underlying `pnpm` commands below are run from 2. Install iOS dependencies. (N.b. Android dependencies are automatically installed by Gradle) ```sh - pnpm pod-install sample/ios + pnpm run pod-install ``` 3. Build the Native Module @@ -179,7 +182,7 @@ pnpm install ### Install Cocoapods ```sh -pnpm pod-install sample/ios +pnpm run pod-install ``` ### Build the local module @@ -190,14 +193,20 @@ pnpm module build ### Update the dotenv file -Replace the details in the `sample/.env.example` file and rename it to -`sample/.env` +From the repo root, run `dev up` to create or sync the sample app dotenv file +from the root `.env`. + +If the repo is already provisioned and you only need React Native setup, run +`dev rn setup` or `dev rn up`. + +If you are not using `dev`, copy `.env.example` from the repo root to `.env`, +fill in local values, then run `scripts/setup_storefront_env`. ``` # Storefront Details STOREFRONT_DOMAIN="YOUR_STORE.myshopify.com" STOREFRONT_ACCESS_TOKEN="YOUR_PUBLIC_STOREFRONT_ACCESS_TOKEN" -STOREFRONT_VERSION="2025-07" +API_VERSION="2025-07" ``` ### Start the sample app diff --git a/platforms/react-native/modules/@shopify/checkout-kit-react-native/ios/ShopifyCheckoutKit.swift b/platforms/react-native/modules/@shopify/checkout-kit-react-native/ios/ShopifyCheckoutKit.swift index 1f64ec2d..ea57f34e 100644 --- a/platforms/react-native/modules/@shopify/checkout-kit-react-native/ios/ShopifyCheckoutKit.swift +++ b/platforms/react-native/modules/@shopify/checkout-kit-react-native/ios/ShopifyCheckoutKit.swift @@ -242,7 +242,7 @@ class RCTShopifyCheckoutKit: NSObject { return NSNumber(value: available) } - @objc func respondToGeolocationRequest(_ allow: Bool) { + @objc func respondToGeolocationRequest(_: Bool) { // No-op on iOS — geolocation permission is handled natively } @@ -319,12 +319,12 @@ extension RCTShopifyCheckoutKit: CheckoutDelegate { // MARK: - Dispatch envelope helpers -private extension RCTShopifyCheckoutKit { +extension RCTShopifyCheckoutKit { /// Builds a `{ "type": ..., "payload": ... }` envelope and forwards /// it to the pending JS dispatcher. SDK lifecycle envelopes are /// single-shot: the callback is released after emission so the same /// presentation can only fire one terminal event. - func emitDispatchEnvelope(type: DispatchEventType, payload: [String: Any]?) { + private func emitDispatchEnvelope(type: DispatchEventType, payload: [String: Any]?) { guard let dispatch = pendingDispatchCallback else { return } // Single-shot for SDK lifecycle events — release before invoking // so a delegate callback that re-enters this code path (e.g. via @@ -353,7 +353,7 @@ private extension RCTShopifyCheckoutKit { /// shape the JS dispatcher expects. Field names match Android's /// `CustomCheckoutListener.populateErrorDetails` so the JS-side /// `parseCheckoutError` works identically on both platforms. - static func errorPayload(from error: CheckoutError) -> [String: Any] { + fileprivate static func errorPayload(from error: CheckoutError) -> [String: Any] { switch error { case let .sdkError(underlying): return [ diff --git a/platforms/react-native/sample/.env.example b/platforms/react-native/sample/.env.example index abf479fd..cb88f943 100644 --- a/platforms/react-native/sample/.env.example +++ b/platforms/react-native/sample/.env.example @@ -1,5 +1,5 @@ # Platform-local example for the React Native sample. Prefer the repo-root -# .env.example for shared setup, then run scripts/setup_storefront_env. +# .env.example for shared setup, then run dev up or dev bootstrap. # Storefront details STOREFRONT_DOMAIN="your-store.myshopify.com" diff --git a/platforms/swift/Samples/MobileBuyIntegration/README.md b/platforms/swift/Samples/MobileBuyIntegration/README.md index 4f5c3de0..f207e6bf 100644 --- a/platforms/swift/Samples/MobileBuyIntegration/README.md +++ b/platforms/swift/Samples/MobileBuyIntegration/README.md @@ -63,20 +63,25 @@ Do not edit files in `Generated/` by hand. Update `.graphql` files and regenerat ## Setup -From `platforms/swift`: +1. From the repo root, create or sync the shared sample configuration: -```sh -cp Samples/MobileBuyIntegration/Storefront.xcconfig.example \ - Samples/MobileBuyIntegration/Storefront.xcconfig -``` + ```bash + dev up + ``` -Edit `Storefront.xcconfig`: + If the repo is already provisioned and you only need Swift sample setup, + run: -```text -STOREFRONT_DOMAIN = your-store.myshopify.com -STOREFRONT_ACCESS_TOKEN = your-token -API_VERSION = 2026-04 -``` + ```bash + dev swift setup + ``` + + If you are not using `dev`, copy the repo-root `.env.example` to `.env`, + fill in local values, then run: + + ```bash + scripts/setup_storefront_env + ``` Optional values enable Customer Account API and buyer identity demo flows: @@ -91,17 +96,20 @@ Open the project in Xcode, let Swift Package Manager resolve dependencies, then ## Updating the Storefront API version -1. Update `API_VERSION` in `Storefront.xcconfig`. -2. Download the schema with Rover. This introspects your store's Storefront API and writes `schema..graphqls` into the sample app directory. +1. Update `API_VERSION` in the repo-root `.env`. +2. Sync the generated platform config: + + ```sh + dev storefront-env sync + ``` + +3. Download the schema. This introspects your store's Storefront API and writes `schema..graphqls` into the sample app directory. ```sh - rover graph introspect \ - "https://$STOREFRONT_DOMAIN/api/$API_VERSION/graphql" \ - --header="X-Shopify-Storefront-Access-Token: $STOREFRONT_ACCESS_TOKEN" \ - --output "schema.$API_VERSION.graphqls" + dev apollo download_schema swift mobile-buy ``` -3. Update `.graphql` operations if the schema changed. For example, add a product field to `MobileBuyIntegration/Sources/Api/Queries/GetProducts.graphql` before regenerating types: +4. Update `.graphql` operations if the schema changed. For example, add a product field to `MobileBuyIntegration/Sources/Api/Queries/GetProducts.graphql` before regenerating types: ```graphql query GetProducts(...) { @@ -115,13 +123,13 @@ Open the project in Xcode, let Swift Package Manager resolve dependencies, then } ``` -4. Regenerate Swift types with the Apollo iOS CLI and this sample's `apollo-codegen-config.json`. This reads the schema and `.graphql` files, then regenerates Swift code in `MobileBuyIntegration/Sources/Generated/`. +5. Regenerate Swift types with the Apollo iOS CLI and this sample's `apollo-codegen-config.json`. This reads the schema and `.graphql` files, then regenerates Swift code in `MobileBuyIntegration/Sources/Generated/`. ```sh - ./apollo-ios-cli generate --path apollo-codegen-config.json + dev apollo codegen swift mobile-buy ``` -5. Build in Xcode and fix any compile errors from schema changes. +6. Build in Xcode and fix any compile errors from schema changes. ## Dev commands reference @@ -140,7 +148,7 @@ All commands are run from the **repo root** (`checkout-kit/`): | File | Purpose | | --- | --- | -| `Storefront.xcconfig` | Store credentials, API version, Customer Account API values, and demo buyer identity. | +| `Storefront.xcconfig` | Generated sample config from the repo-root `.env` (not checked into git). | | `schema..graphqls` | Storefront API schema downloaded with the Apollo iOS CLI. | | `apollo-codegen-config.json` | Apollo code generation configuration. | | `MobileBuyIntegration/Sources/Api/Network.swift` | Apollo client setup and authentication interceptor. | diff --git a/platforms/swift/Samples/MobileBuyIntegration/Storefront.xcconfig.example b/platforms/swift/Samples/MobileBuyIntegration/Storefront.xcconfig.example index b8714214..648e8988 100644 --- a/platforms/swift/Samples/MobileBuyIntegration/Storefront.xcconfig.example +++ b/platforms/swift/Samples/MobileBuyIntegration/Storefront.xcconfig.example @@ -1,7 +1,7 @@ // --- Storefront // --- Find your Storefront API access token under: // ----- https://admin.shopify.com/store/{STOREFRONT}/settings/apps/development/{APP_ID}/api_credentials -// --- Prefer the repo-root .env.example for shared setup, then run scripts/setup_storefront_env. +// --- Prefer the repo-root .env.example for shared setup, then run dev up or dev bootstrap. STOREFRONT_DOMAIN = your-store.myshopify.com STOREFRONT_ACCESS_TOKEN = your-public-storefront-access-token diff --git a/platforms/swift/Samples/README.md b/platforms/swift/Samples/README.md index bf27c831..6ec702c0 100644 --- a/platforms/swift/Samples/README.md +++ b/platforms/swift/Samples/README.md @@ -2,6 +2,11 @@ This directory contains iOS sample apps for Checkout Kit. +The sample apps read generated `Storefront.xcconfig` files. From the repo root, +run `dev up` to provision the repo and create or sync them from the shared +`.env`. If the repo is already provisioned, `dev swift setup` or `dev swift up` +refreshes Swift sample setup directly. + | Sample | Purpose | | --- | --- | | `MobileBuyIntegration` | Storefront API cart flow with Apollo iOS, checkout presentation, buyer identity modes, Customer Account API sign-in, and protocol lifecycle events. | @@ -16,48 +21,53 @@ This directory contains iOS sample apps for Checkout Kit. ## MobileBuyIntegration -From `platforms/swift`: +### Getting Started + +1. Create or sync the shared configuration from the repo root: ```sh -cp Samples/MobileBuyIntegration/Storefront.xcconfig.example \ - Samples/MobileBuyIntegration/Storefront.xcconfig +dev up ``` -Fill in: - -- `STOREFRONT_DOMAIN` -- `STOREFRONT_ACCESS_TOKEN` -- `API_VERSION` -- Optional Customer Account API values -- Optional demo buyer identity values +2. If the repo is already provisioned and you only need Swift setup, run +`dev swift setup`. +3. If you are not using `dev`, copy the repo-root `.env.example` to `.env`, +fill in local values, then run `scripts/setup_storefront_env`. +4. Build and run. Entitlements are auto-generated via a build PreAction. -Open `Samples/Samples.xcworkspace` or `Samples/MobileBuyIntegration/MobileBuyIntegration.xcodeproj` in Xcode, then build and run the `MobileBuyIntegration` scheme. +Open `Samples/Samples.xcworkspace` or +`Samples/MobileBuyIntegration/MobileBuyIntegration.xcodeproj` in Xcode, then +build and run the `MobileBuyIntegration` scheme. -The project generates associated-domain entitlements from `Storefront.xcconfig` during the Xcode build pre-action. +The project generates associated-domain entitlements from +`Storefront.xcconfig` during the Xcode build pre-action. ## ShopifyAcceleratedCheckoutsApp -From `platforms/swift`: +To get started: + +1. Create or sync the shared configuration from the repo root: ```sh -cp Samples/ShopifyAcceleratedCheckoutsApp/Storefront.xcconfig.example \ - Samples/ShopifyAcceleratedCheckoutsApp/Storefront.xcconfig +dev up ``` -Fill in: - -- `STOREFRONT_DOMAIN` -- `STOREFRONT_ACCESS_TOKEN` -- `API_VERSION` +2. If the repo is already provisioned and you only need Swift setup, run +`dev swift setup`. +3. If you are not using `dev`, copy the repo-root `.env.example` to `.env`, +fill in local values, then run `scripts/setup_storefront_env`. -Open `Samples/Samples.xcworkspace` or `Samples/ShopifyAcceleratedCheckoutsApp/ShopifyAcceleratedCheckoutsApp.xcodeproj` in Xcode, then build and run the `ShopifyAcceleratedCheckoutsApp` scheme. +Open `Samples/Samples.xcworkspace` or +`Samples/ShopifyAcceleratedCheckoutsApp/ShopifyAcceleratedCheckoutsApp.xcodeproj` +in Xcode, then build and run the `ShopifyAcceleratedCheckoutsApp` scheme. ## Troubleshooting -If the build pre-action fails, Xcode usually shows `exited with status code 1`. Open the build log and check the script output. +If the build pre-action fails, Xcode usually shows `exited with status code 1`. +Open the build log and check the script output. | Build log output | Cause | Fix | | --- | --- | --- | -| `grep: Storefront.xcconfig: No such file or directory` | The sample config file is missing. | Copy `.xcconfig.example` to `Storefront.xcconfig`. | -| `Error: STOREFRONT_DOMAIN is not set in Storefront.xcconfig` | `STOREFRONT_DOMAIN` is blank. | Set your shop domain without `https://`. | -| Associated domains do not work at runtime | Domain or app association is wrong. | Verify your custom storefront domain, app ID, and Universal Links setup. | +| `grep: Storefront.xcconfig: No such file or directory` | `Storefront.xcconfig` file is missing. | Run `dev swift setup` from the repo root. | +| `Error: STOREFRONT_DOMAIN is not set in Storefront.xcconfig` | `Storefront.xcconfig` exists but `STOREFRONT_DOMAIN` is blank. | Update root `.env`, then run `dev storefront-env sync`. | +| Associated domains not working at runtime | Domain value is incorrect. | Update root `.env`, then run `dev storefront-env sync`. | diff --git a/platforms/swift/Samples/ShopifyAcceleratedCheckoutsApp/Storefront.xcconfig.example b/platforms/swift/Samples/ShopifyAcceleratedCheckoutsApp/Storefront.xcconfig.example index bff231f4..9aca89ee 100644 --- a/platforms/swift/Samples/ShopifyAcceleratedCheckoutsApp/Storefront.xcconfig.example +++ b/platforms/swift/Samples/ShopifyAcceleratedCheckoutsApp/Storefront.xcconfig.example @@ -1,7 +1,7 @@ // --- Storefront // --- Find your Storefront API access token under: // ----- https://admin.shopify.com/store/{STOREFRONT}/settings/apps/development/{APP_ID}/api_credentials -// --- Prefer the repo-root .env.example for shared setup, then run scripts/setup_storefront_env. +// --- Prefer the repo-root .env.example for shared setup, then run dev up or dev bootstrap. // Configuration settings file format documentation can be found at: // https://help.apple.com/xcode/#/dev745c5c974 diff --git a/platforms/web/.gitignore b/platforms/web/.gitignore index 5c31f727..427cbdac 100644 --- a/platforms/web/.gitignore +++ b/platforms/web/.gitignore @@ -4,6 +4,7 @@ node_modules/ # Build output dist/ +sample/dist/ build/ coverage/ custom-elements.json diff --git a/platforms/web/.oxlintrc.json b/platforms/web/.oxlintrc.json index 7bf618d7..ff76f55a 100644 --- a/platforms/web/.oxlintrc.json +++ b/platforms/web/.oxlintrc.json @@ -24,6 +24,7 @@ }, "ignorePatterns": [ "dist/**", + "sample/dist/**", "coverage/**", "node_modules/**" ], diff --git a/platforms/web/package.json b/platforms/web/package.json index 04064196..4302bd12 100644 --- a/platforms/web/package.json +++ b/platforms/web/package.json @@ -53,7 +53,7 @@ "ecommerce" ], "scripts": { - "clean": "rm -rf dist coverage", + "clean": "rm -rf dist sample/dist coverage", "build": "vite build && pnpm run build:manifest", "build:manifest": "cem analyze --globs 'src/**/*.ts' --exclude 'src/**/*.test.ts' --outdir dist", "dev": "vite build --watch", @@ -62,8 +62,8 @@ "lint": "pnpm run typecheck && pnpm run sample:typecheck && pnpm run lint:js && pnpm run format:check", "lint:js": "oxlint --report-unused-disable-directives --max-warnings 0 src sample", "lint:js:fix": "oxlint --fix src sample", - "format": "oxfmt src sample", - "format:check": "oxfmt --check src sample", + "format": "oxfmt src sample '!sample/dist/**'", + "format:check": "oxfmt --check src sample '!sample/dist/**'", "typecheck": "tsc --noEmit", "sample": "vite --config sample/vite.config.ts", "sample:build": "vite build --config sample/vite.config.ts", diff --git a/scripts/setup_dev_workspace b/scripts/setup_dev_workspace new file mode 100755 index 00000000..443506f6 --- /dev/null +++ b/scripts/setup_dev_workspace @@ -0,0 +1,281 @@ +#!/usr/bin/env bash + +set -o pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +mode="sync" +storefront_prompt_arg="--skip-optional-prompts" +platforms=() +platform_local_invocation="false" + +step_names=() +step_statuses=() +step_details=() +failure_count=0 +last_status=0 +storefront_ran=0 +storefront_status=0 + +usage() { + cat <&2 + exit 1 + ;; + esac + shift +done + +if [[ "${#platforms[@]}" -eq 0 ]]; then + add_platform all +fi + +record_step() { + local name="$1" + local status="$2" + local detail="$3" + + step_names+=("$name") + step_statuses+=("$status") + step_details+=("$detail") + + if [[ "$status" != "PASS" ]]; then + failure_count=$((failure_count + 1)) + fi +} + +status_icon() { + case "$1" in + PASS) printf '✅' ;; + FAIL) printf '❌' ;; + *) printf '•' ;; + esac +} + +run_shell() { + local name="$1" + local command="$2" + local status + + printf '\n🔧 %s\n' "$name" + (cd "$ROOT_DIR" && bash -c "$command") + status=$? + + if [[ "$status" -eq 0 ]]; then + printf '✅ PASS %s\n' "$name" + record_step "$name" "PASS" "" + else + printf '❌ FAIL %s (exit %s)\n' "$name" "$status" + record_step "$name" "FAIL" "exit $status" + fi + + last_status="$status" + return "$status" +} + +ensure_storefront_env() { + if [[ "$storefront_ran" -eq 1 ]]; then + return "$storefront_status" + fi + + if [[ "$mode" == "check" ]]; then + run_shell "Sample storefront config" './scripts/setup_storefront_env --check' + else + run_shell "Sample storefront config" "./scripts/setup_storefront_env ${storefront_prompt_arg}" + fi + + storefront_status="$last_status" + storefront_ran=1 + return "$storefront_status" +} + +setup_android() { + ensure_storefront_env || true +} + +setup_swift() { + ensure_storefront_env || true + + if [[ "$mode" == "check" ]]; then + run_shell "Swift bundle packages" 'BUNDLE_GEMFILE=platforms/swift/Gemfile bundle check' || true + run_shell "Swift Mint packages" ' + set -e + cd platforms/swift + expected_swiftlint="$(sed -n "s#^realm/SwiftLint@##p" Mintfile)" + expected_swiftformat="$(sed -n "s#^nicklockwood/SwiftFormat@##p" Mintfile)" + swiftlint_bin="$(mint which swiftlint)" + swiftformat_bin="$(mint which swiftformat)" + test -n "$expected_swiftlint" + test -n "$expected_swiftformat" + test "$("$swiftlint_bin" version)" = "$expected_swiftlint" + test "$("$swiftformat_bin" --version)" = "$expected_swiftformat" + ' || true + run_shell "Swift sample entitlements" 'test -f platforms/swift/Samples/MobileBuyIntegration/MobileBuyIntegration/MobileBuyIntegration.entitlements' || true + else + run_shell "Swift bundle packages" 'BUNDLE_GEMFILE=platforms/swift/Gemfile bundle check || BUNDLE_GEMFILE=platforms/swift/Gemfile bundle install' || true + run_shell "Swift Mint packages" ' + set -e + cd platforms/swift + if ( + expected_swiftlint="$(sed -n "s#^realm/SwiftLint@##p" Mintfile)" + expected_swiftformat="$(sed -n "s#^nicklockwood/SwiftFormat@##p" Mintfile)" + swiftlint_bin="$(mint which swiftlint)" + swiftformat_bin="$(mint which swiftformat)" + test -n "$expected_swiftlint" + test -n "$expected_swiftformat" + test "$("$swiftlint_bin" version)" = "$expected_swiftlint" + test "$("$swiftformat_bin" --version)" = "$expected_swiftformat" + ); then + exit 0 + fi + mint bootstrap + ' || true + run_shell "Swift sample entitlements" 'test -f platforms/swift/Samples/MobileBuyIntegration/MobileBuyIntegration/MobileBuyIntegration.entitlements || (cd platforms/swift && ./Scripts/setup_entitlements)' || true + fi +} + +setup_react_native() { + ensure_storefront_env || true + + if [[ "$mode" == "check" ]]; then + run_shell "React Native packages" 'test -d platforms/react-native/node_modules' || true + run_shell "React Native iOS gems" 'cd platforms/react-native/sample/ios && bundle check' || true + else + run_shell "React Native packages" 'test -d platforms/react-native/node_modules || (cd platforms/react-native && pnpm install)' || true + run_shell "React Native iOS gems" 'cd platforms/react-native/sample/ios && (bundle check || bundle install)' || true + fi +} + +setup_web() { + if [[ "$mode" == "check" ]]; then + run_shell "Web packages" 'test -d platforms/web/node_modules' || true + else + run_shell "Web packages" 'test -d platforms/web/node_modules || (cd platforms/web && pnpm install)' || true + fi +} + +print_platform_local_notice() { + if [[ "$platform_local_invocation" != "true" ]]; then + return 0 + fi + + cat < setup\n' + printf 'If a base tool is missing, run dev up first so DevHub can provision shared tools.\n' + else + printf '\n✅ All requested setup steps passed.\n' + fi +} + +print_platform_local_notice + +for platform in "${platforms[@]}"; do + case "$platform" in + android) setup_android ;; + swift) setup_swift ;; + react-native) setup_react_native ;; + web) setup_web ;; + *) + echo "Unknown platform: $platform" >&2 + exit 1 + ;; + esac +done + +print_summary + +if [[ "$failure_count" -gt 0 ]]; then + exit 1 +fi