diff --git a/.envrc b/.envrc index 2f05af981..f57ec2049 100644 --- a/.envrc +++ b/.envrc @@ -1,9 +1,10 @@ #!/bin/bash # Automatically sets up your devbox environment whenever you cd into this -# directory via our direnv integration: +# directory via our direnv integration. +# Uses omit-nix-env as a temporary workaround: https://github.com/jetify-com/devbox/issues/1509 -eval "$(devbox generate direnv --print-envrc)" +eval "$(devbox shellenv --init-hook --install --no-refresh-alias --omit-nix-env=true)" # check out https://www.jetpack.io/devbox/docs/ide_configuration/direnv/ # for more details diff --git a/.github/workflows/ci-e2e-nightly.yml b/.github/workflows/ci-e2e-full.yml similarity index 94% rename from .github/workflows/ci-e2e-nightly.yml rename to .github/workflows/ci-e2e-full.yml index 5385b88b1..b973cee8e 100644 --- a/.github/workflows/ci-e2e-nightly.yml +++ b/.github/workflows/ci-e2e-full.yml @@ -1,4 +1,4 @@ -name: E2E (Nightly) +name: E2E (Full) on: schedule: @@ -6,7 +6,7 @@ on: workflow_dispatch: concurrency: - group: e2e-nightly-${{ github.ref }} + group: e2e-full-${{ github.ref }} cancel-in-progress: false jobs: @@ -54,7 +54,7 @@ jobs: echo "IOS_RUNTIME=${PLATFORM_IOS_MAX_RUNTIME}" >> "$GITHUB_ENV" fi - name: iOS E2E Tests - run: devbox shell --omit-nix-env -- devbox run test-ios + run: devbox run --config=shells/devbox-ios.json test-ios run-e2e-android: runs-on: ubuntu-24.04-arm @@ -102,4 +102,4 @@ jobs: avd_name="${device}_API${api}_arm64_v8a" echo "DETOX_AVD=${avd_name}" >> "$GITHUB_ENV" - name: Android E2E Tests - run: devbox run test-android + run: devbox run --config=shells/devbox-android.json test-android diff --git a/.github/workflows/ci-e2e-optional.yml b/.github/workflows/ci-e2e-latest.yml similarity index 93% rename from .github/workflows/ci-e2e-optional.yml rename to .github/workflows/ci-e2e-latest.yml index 5e04ae5cd..148328e61 100644 --- a/.github/workflows/ci-e2e-optional.yml +++ b/.github/workflows/ci-e2e-latest.yml @@ -1,12 +1,10 @@ -name: E2E (On-Demand) +name: E2E (Latest) on: workflow_dispatch: - push: - branches: [ci2] concurrency: - group: e2e-optional-${{ github.ref }} + group: e2e-latest-${{ github.ref }} cancel-in-progress: false jobs: @@ -44,7 +42,7 @@ jobs: echo "DETOX_IOS_DEVICE=${PLATFORM_IOS_MAX_DEVICE}" >> "$GITHUB_ENV" echo "IOS_RUNTIME=${PLATFORM_IOS_MAX_RUNTIME}" >> "$GITHUB_ENV" - name: iOS E2E Tests (latest) - run: devbox shell --omit-nix-env -- devbox run test-ios + run: devbox run --config=shells/devbox-ios.json test-ios run-e2e-android: runs-on: ubuntu-24.04-arm @@ -80,4 +78,4 @@ jobs: avd_name="${device}_API${api}_arm64_v8a" echo "DETOX_AVD=${avd_name}" >> "$GITHUB_ENV" - name: Android E2E Tests (latest) - run: devbox run test-android + run: devbox run --config=shells/devbox-android.json test-android diff --git a/.github/workflows/ci-fast.yml b/.github/workflows/ci-fast.yml index fc37487a8..7e767fa9c 100644 --- a/.github/workflows/ci-fast.yml +++ b/.github/workflows/ci-fast.yml @@ -15,27 +15,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Aggressive disk cleanup (Ubuntu) - run: | - sudo rm -rf /usr/share/dotnet - sudo rm -rf /opt/ghc - sudo rm -rf "$AGENT_TOOLSDIRECTORY" - sudo rm -rf /opt/hostedtoolcache/CodeQL - sudo rm -rf /usr/local/lib/android - sudo rm -rf /usr/local/lib/node_modules - sudo rm -rf /usr/local/share/boost - sudo rm -rf /usr/local/share/chromium - sudo rm -rf /usr/local/share/powershell - sudo rm -rf /usr/local/share/edge_driver - sudo rm -rf /usr/local/share/gecko_driver - sudo rm -rf /usr/local/share/phantomjs - sudo rm -rf "$HOME/.cache" - df -H - - name: Free Disk Space (Ubuntu) - uses: jlumbroso/free-disk-space@v1.3.1 - name: devbox installer uses: jetify-com/devbox-install-action@v0.14.0 with: enable-cache: 'false' - name: build - run: devbox run build + run: devbox run --config=shells/devbox-fast.json build diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 42e8ef3fd..d49d03f41 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,9 +9,118 @@ on: required: true jobs: + fast-checks: + name: Build + Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: devbox installer + uses: jetify-com/devbox-install-action@v0.14.0 + with: + enable-cache: 'false' + - name: build + run: devbox run --config=shells/devbox-fast.json build + + e2e-ios: + name: E2E iOS (min/max) + runs-on: macos-26 + env: + YARN_ENABLE_HARDENED_MODE: 0 + XCODE_VERSION: '26.2' + strategy: + matrix: + include: + - name: ios-min + - name: ios-latest + steps: + - uses: actions/checkout@v4 + - name: Aggressive disk cleanup (macOS) + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf "/usr/local/share/boost" + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + sudo rm -rf /Applications/Android\ Studio.app + sudo rm -rf /usr/local/share/miniconda + sudo rm -rf /opt/homebrew + sudo rm -rf "$HOME/Library/Android" + sudo rm -rf "$HOME/.gradle" + sudo rm -rf "$HOME/Library/Developer/CoreSimulator/Devices" + sudo rm -rf "$HOME/Library/Developer/Xcode/DerivedData" + df -H + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '26.2' + - name: devbox installer + uses: jetify-com/devbox-install-action@v0.14.0 + with: + enable-cache: 'false' + - name: Resolve iOS targets + run: | + . scripts/platform-versions.sh + if [ "${{ matrix.name }}" = "ios-min" ]; then + echo "DETOX_IOS_DEVICE=${PLATFORM_IOS_MIN_DEVICE}" >> "$GITHUB_ENV" + echo "IOS_RUNTIME=${PLATFORM_IOS_MIN_RUNTIME}" >> "$GITHUB_ENV" + else + echo "DETOX_IOS_DEVICE=${PLATFORM_IOS_MAX_DEVICE}" >> "$GITHUB_ENV" + echo "IOS_RUNTIME=${PLATFORM_IOS_MAX_RUNTIME}" >> "$GITHUB_ENV" + fi + - name: iOS E2E Tests + run: devbox run --config=shells/devbox-ios.json test-ios + + e2e-android: + name: E2E Android (min/max) + runs-on: ubuntu-24.04-arm + env: + EMU_HEADLESS: 1 + strategy: + matrix: + include: + - name: android-min + target: min + - name: android-latest + target: max + steps: + - uses: actions/checkout@v4 + - name: Aggressive disk cleanup (Ubuntu) + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + sudo rm -rf /opt/hostedtoolcache/CodeQL + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/local/lib/node_modules + sudo rm -rf /usr/local/share/boost + sudo rm -rf /usr/local/share/chromium + sudo rm -rf /usr/local/share/powershell + sudo rm -rf /usr/local/share/edge_driver + sudo rm -rf /usr/local/share/gecko_driver + sudo rm -rf /usr/local/share/phantomjs + sudo rm -rf "$HOME/.cache" + df -H + - name: devbox installer + uses: jetify-com/devbox-install-action@v0.14.0 + with: + enable-cache: 'false' + - name: Resolve Android targets + run: | + . scripts/platform-versions.sh + if [ "${{ matrix.target }}" = "min" ]; then + api="$PLATFORM_ANDROID_MIN_API" + device="$PLATFORM_ANDROID_MIN_DEVICE" + else + api="$PLATFORM_ANDROID_MAX_API" + device="$PLATFORM_ANDROID_MAX_DEVICE" + fi + avd_name="${device}_API${api}_arm64_v8a" + echo "DETOX_AVD=${avd_name}" >> "$GITHUB_ENV" + - name: Android E2E Tests + run: devbox run --config=shells/devbox-android.json test-android + publish: name: Publish to npm environment: Publish + needs: [fast-checks, e2e-ios, e2e-android] runs-on: ubuntu-latest permissions: contents: write # to be able to publish a GitHub release @@ -23,22 +132,10 @@ jobs: fetch-depth: 0 token: ${{ secrets.GH_TOKEN }} - # Workaround for corepack enable in node - # Source: (https://github.com/actions/setup-node/issues/899#issuecomment-1828798029) - - uses: actions/setup-node@v4 - with: - node-version: 20 - - run: corepack enable - - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: yarn - # End workaround - - name: devbox installer uses: jetify-com/devbox-install-action@v0.14.0 with: - enable-cache: 'true' + enable-cache: 'false' - name: Config, Build, Release run: devbox run release @@ -49,6 +146,4 @@ jobs: - name: Update Apps run: | - yarn install --no-immutable - yarn e2e install --no-immutable - yarn example install --no-immutable + devbox run update-apps diff --git a/.gitignore b/.gitignore index f144e2ac5..73d538dbc 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,6 @@ tsconfig.tsbuildinfo packages/core/src/info.ts .pnpm/ + +AGENTS.md + diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index c0372124b..000000000 --- a/AGENTS.md +++ /dev/null @@ -1,35 +0,0 @@ -# Repository Guidelines - -## Project Structure & Module Organization - -- Yarn workspaces live under `packages`: `core` (SDK runtime), `sovran` (state store), `shared` (cross-package utilities), and `plugins/*` (destination and helper plugins). Native iOS/Android sources reside in each package’s `ios` and `android` folders. -- Example apps sit in `examples/AnalyticsReactNativeExample` (manual QA) and `examples/E2E` (Detox). Scripts are in `scripts/`; configuration lives alongside packages (e.g., `tsconfig.json`, `babel.config.js`, `jest.config.js`). - -## Build, Test, and Development Commands - -- `yarn bootstrap`: install root + workspace deps and pod install for example/e2e apps. -- `yarn build`: run workspace builds in topo order. -- `yarn testAll` / `yarn test`: workspace Jest suite or root Jest run. -- `yarn lint`; `yarn lint --fix`: ESLint across the monorepo. -- `yarn typescript`: type-check without emitting. -- Example app: `yarn example start | ios | android`. Detox: `yarn e2e start:e2e` then platform builds/tests (e.g., `yarn e2e e2e:build:ios` + `yarn e2e e2e:test:ios`). - -## Coding Style & Naming Conventions - -- TypeScript-first; native code should mirror existing Swift/Obj-C/Kotlin style. Two-space indentation and Prettier formatting via ESLint rules. -- Prefer camelCase for variables/functions, PascalCase for React components/classes, and UPPER_SNAKE for constants. Plugin packages follow `plugin-*` folder naming and publish as scoped `@segment/*`. -- Keep public APIs typed and documented; colocate utilities with their feature module (e.g., `src/plugins`, `src/__tests__`). - -## Testing Guidelines - -- Unit tests use Jest with tests under `__tests__` near source; snapshots live in `__tests__/__snapshots__`. -- End-to-end coverage uses Detox in `examples/E2E`; build and run per platform before pushing. Add regression tests for new behaviors and keep existing snapshots updated only when intentional. - -## Commit & Pull Request Guidelines - -- Commit messages follow Conventional Commits (`feat`, `fix`, `chore`, etc.); enforced by commitlint and release automation. -- For PRs, keep scope narrow, link issues when relevant, and note user-facing changes. Ensure `yarn lint`, `yarn typescript`, and the relevant `yarn test*`/Detox flows pass. Include screenshots only when UI changes affect the example app. - -## Security & Configuration Tips - -- Do not commit real Segment write keys or private endpoints; use placeholder values in examples and tests. Keep secrets out of `examples/` and CI config. When testing proxies/CDN settings, prefer environment-driven config rather than hardcoding. diff --git a/devbox.json b/devbox.json index 9ec933d26..30bc30372 100644 --- a/devbox.json +++ b/devbox.json @@ -12,12 +12,15 @@ "jdk17": "latest", "gradle": "latest", "jq": "latest", + "netcat": "latest", "path:./nix#android-sdk": "" }, "shell": { "init_hook": [ "echo 'Welcome to analytics-react-native devbox!' > /dev/null", - ". $DEVBOX_PROJECT_ROOT/scripts/android-env.sh", + ". $DEVBOX_PROJECT_ROOT/scripts/shared/common.sh", + "if [ \"$(uname -s)\" = \"Darwin\" ]; then . $DEVBOX_PROJECT_ROOT/scripts/ios/env.sh; fi", + ". $DEVBOX_PROJECT_ROOT/scripts/android/env.sh", "echo 'Android SDK env configured (details: wiki/devbox.md#devbox-android).'" ], "scripts": { @@ -28,53 +31,40 @@ "yarn cache clean", "find $DEVBOX_PROJECT_DIR -type d -name node_modules -exec rmdir {} \\;" ], - "build": [ - "yarn install --immutable", - "yarn build", - "yarn lint", - "yarn test --coverage" - ], - "test-android": [ - "devbox run setup-android", - "yarn install", - "yarn e2e install", - "yarn build", - "yarn e2e build:android", - "yarn e2e test:android" - ], - "test-ios": [ - "devbox run setup-ios", - "yarn install", - "yarn e2e install", - "yarn e2e pods", - "yarn build", - "yarn e2e build:ios", - "yarn e2e test:ios" - ], + "build": ["bash $SCRIPTS_DIR/build.sh"], + "format": ["treefmt"], + "lint": ["treefmt --fail-on-change"], + "test-android": ["bash $SCRIPTS_DIR/android/test.sh"], + "test-ios": ["bash $SCRIPTS_DIR/ios/test.sh"], "act-ci": [ - "bash $DEVBOX_PROJECT_ROOT/scripts/act-ci.sh --platform ubuntu-latest=node:20-bullseye" - ], - "setup-android": ["bash $DEVBOX_PROJECT_ROOT/scripts/android-setup.sh"], - "setup-ios": ["bash $DEVBOX_PROJECT_ROOT/scripts/ios-setup.sh"], - "start-emulator": [ - "bash $DEVBOX_PROJECT_ROOT/scripts/android-manager.sh start" - ], - "start-ios": ["bash $DEVBOX_PROJECT_ROOT/scripts/ios-manager.sh start"], - "start-android-minsdk": [ - "bash $DEVBOX_PROJECT_ROOT/scripts/android-manager.sh start" + "bash $SCRIPTS_DIR/act-ci.sh --platform ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-24.04" ], + "setup-android": ["bash $SCRIPTS_DIR/android/setup.sh"], + "setup-ios": ["bash $SCRIPTS_DIR/ios/setup.sh"], + "start-emulator": ["bash $SCRIPTS_DIR/android/manager.sh start"], + "start-ios": ["bash $SCRIPTS_DIR/ios/manager.sh start"], + "start-android-minsdk": ["bash $SCRIPTS_DIR/android/manager.sh start"], "start-android-latest": [ - "AVD_FLAVOR=latest bash $DEVBOX_PROJECT_ROOT/scripts/android-manager.sh start" - ], - "start-android": [ - "bash $DEVBOX_PROJECT_ROOT/scripts/android-manager.sh start" + "AVD_FLAVOR=latest bash $SCRIPTS_DIR/android/manager.sh start" ], + "start-android": ["bash $SCRIPTS_DIR/android/manager.sh start"], "release": [ "npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}", "yarn install --immutable", "yarn build", "yarn release" ], + "update-apps": [ + "yarn install --no-immutable", + "yarn e2e install --no-immutable", + "yarn example install --no-immutable" + ], + "update-shells": [ + "devbox update", + "devbox update --config=shells/devbox-fast.json", + "devbox update --config=shells/devbox-android.json", + "devbox update --config=shells/devbox-ios.json" + ], "reset-android": [ "rm -rf ~/.android/avd", "rm -f ~/.android/adbkey*", diff --git a/devbox.lock b/devbox.lock index 7dda352e7..81129d34f 100644 --- a/devbox.lock +++ b/devbox.lock @@ -226,6 +226,126 @@ } } }, + "netcat@latest": { + "last_modified": "2026-01-20T02:11:35Z", + "resolved": "github:NixOS/nixpkgs/ed142ab1b3a092c4d149245d0c4126a5d7ea00b0#netcat", + "source": "devbox-search", + "version": "4.2.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "nc", + "path": "/nix/store/y2i4f3bwmgpxw4m6dl99dz9d7zp5axz2-libressl-4.2.1-nc", + "default": true + }, + { + "name": "bin", + "path": "/nix/store/ycvwxl29jb6ajjzgkq2jgy1nqpahq5k4-libressl-4.2.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/2b61bckfnaiw3n5ppjg70avgd9rn60bp-libressl-4.2.1-man", + "default": true + }, + { + "name": "out", + "path": "/nix/store/fm6zdqw6856i2snd4fikcsi9d1qagj5j-libressl-4.2.1" + }, + { + "name": "dev", + "path": "/nix/store/bz5h2baa7bkpz8sgjc9ld8fr7cg5wapg-libressl-4.2.1-dev" + } + ], + "store_path": "/nix/store/y2i4f3bwmgpxw4m6dl99dz9d7zp5axz2-libressl-4.2.1-nc" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "nc", + "path": "/nix/store/0dzxkwilv9lgd7j0429s2rmshy7p8gw7-libressl-4.2.1-nc", + "default": true + }, + { + "name": "bin", + "path": "/nix/store/5ama6wp3yi03hbixdcm5jy2ya9ikvzjz-libressl-4.2.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/vr4wlcvj5ba6wdmhj2aidalzqk33lrph-libressl-4.2.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/hs5c0yvhf7n60xijr7bmpi592n45pb6i-libressl-4.2.1-dev" + }, + { + "name": "out", + "path": "/nix/store/fiy9987ph2kqr5drlr136w7hvm3v6rvg-libressl-4.2.1" + } + ], + "store_path": "/nix/store/0dzxkwilv9lgd7j0429s2rmshy7p8gw7-libressl-4.2.1-nc" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "nc", + "path": "/nix/store/vmrm6nr9hfhw7x8ln3ms98gszq709bfa-libressl-4.2.1-nc", + "default": true + }, + { + "name": "bin", + "path": "/nix/store/7kafgvpwc6s8pnzar90bd8017wc3cnvx-libressl-4.2.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/jwdgv2x1a2a84v2jkj964xvm2s0kymps-libressl-4.2.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/sjiz0iphbc7dvddlrbrk8k7kkqi3swz3-libressl-4.2.1-dev" + }, + { + "name": "out", + "path": "/nix/store/rn10w2jbdkz3f7p1q1fl6aml9b0352ki-libressl-4.2.1" + } + ], + "store_path": "/nix/store/vmrm6nr9hfhw7x8ln3ms98gszq709bfa-libressl-4.2.1-nc" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "nc", + "path": "/nix/store/54hijwy3gpc728s3468rv3sdw78ksakh-libressl-4.2.1-nc", + "default": true + }, + { + "name": "bin", + "path": "/nix/store/l5vvbs0vl734mshifahals0054pimlx4-libressl-4.2.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/j26qyc85gk95bk06dq0fi0c9q8y7livx-libressl-4.2.1-man", + "default": true + }, + { + "name": "out", + "path": "/nix/store/4f21prki98shrvp29r88pnxhzw2y4qr2-libressl-4.2.1" + }, + { + "name": "dev", + "path": "/nix/store/0dndzgjzx25y7v1942c2rys20narddp8-libressl-4.2.1-dev" + } + ], + "store_path": "/nix/store/54hijwy3gpc728s3468rv3sdw78ksakh-libressl-4.2.1-nc" + } + } + }, "nixfmt@latest": { "last_modified": "2026-01-09T13:41:53Z", "resolved": "github:NixOS/nixpkgs/5f02c91314c8ba4afe83b256b023756412218535#nixfmt", diff --git a/scripts/act-ci.sh b/scripts/act-ci.sh index f67e136a2..60c7f85a9 100755 --- a/scripts/act-ci.sh +++ b/scripts/act-ci.sh @@ -2,10 +2,18 @@ set -euo pipefail # Run GitHub Actions workflows locally via act. -# Usage: scripts/act-ci.sh [--job JOB] [--platform ubuntu-latest=node:20-bullseye] +# Usage: scripts/act-ci.sh [--job JOB] [--platform ubuntu-latest=IMAGE] JOB="" -PLATFORM="ubuntu-latest=node:20-bullseye" +PLATFORMS=() + +host_arch="$(uname -m)" +if [[ $host_arch == "arm64" || $host_arch == "aarch64" ]]; then + PLATFORMS+=("ubuntu-24.04-arm=ghcr.io/catthehacker/ubuntu:act-24.04") +else + PLATFORMS+=("ubuntu-24.04=ghcr.io/catthehacker/ubuntu:act-24.04") +fi +PLATFORMS+=("ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-24.04") while [[ $# -gt 0 ]]; do case "$1" in @@ -14,7 +22,7 @@ while [[ $# -gt 0 ]]; do shift 2 ;; -p | --platform) - PLATFORM="$2" + PLATFORMS+=("$2") shift 2 ;; *) @@ -26,7 +34,9 @@ done CMD=(act) CMD+=(--pull=false) -CMD+=(--platform "${PLATFORM}") +for platform in "${PLATFORMS[@]}"; do + CMD+=(--platform "$platform") +done CMD+=(--input ACT=true) if [[ -n $JOB ]]; then CMD+=(--job "$JOB") diff --git a/scripts/android-env.sh b/scripts/android/env.sh similarity index 93% rename from scripts/android-env.sh rename to scripts/android/env.sh index 869c4a761..0ffe24379 100755 --- a/scripts/android-env.sh +++ b/scripts/android/env.sh @@ -1,12 +1,12 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash # Sets ANDROID_SDK_ROOT/ANDROID_HOME and PATH to the flake-pinned SDK if not already set. # Load shared platform versions if present. -script_dir="$(cd "$(dirname "$0")" && pwd)" -if [ -f "$script_dir/platform-versions.sh" ]; then - # shellcheck disable=SC1090 - . "$script_dir/platform-versions.sh" -fi +script_path="${BASH_SOURCE[0]:-$0}" +script_dir="$(cd "$(dirname "$script_path")" && pwd)" +# shellcheck disable=SC1090 +. "$script_dir/../shared/common.sh" +load_platform_versions "$script_dir" if [ -z "${ANDROID_MIN_API:-}" ] && [ -n "${PLATFORM_ANDROID_MIN_API:-}" ]; then ANDROID_MIN_API="$PLATFORM_ANDROID_MIN_API" diff --git a/scripts/android-manager.sh b/scripts/android/manager.sh similarity index 89% rename from scripts/android-manager.sh rename to scripts/android/manager.sh index 0c0f304f1..a1275adb6 100755 --- a/scripts/android-manager.sh +++ b/scripts/android/manager.sh @@ -4,10 +4,10 @@ set -euo pipefail action="${1:-}" shift || true -source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/android-env.sh" +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/env.sh" start_android() { - local flavor="${AVD_FLAVOR:-minsdk}" headless="${EMU_HEADLESS:-}" port="${EMU_PORT:-5554}" + local flavor="${AVD_FLAVOR:-latest}" headless="${EMU_HEADLESS:-}" port="${EMU_PORT:-5554}" local avd="${DETOX_AVD:-}" if [[ -z $avd ]]; then @@ -51,7 +51,7 @@ start) start_android ;; stop) stop_android ;; reset) reset_android ;; *) - echo "Usage: android-manager.sh {start|stop|reset}" >&2 + echo "Usage: manager.sh {start|stop|reset}" >&2 exit 1 ;; esac diff --git a/scripts/android-setup.sh b/scripts/android/setup.sh similarity index 93% rename from scripts/android-setup.sh rename to scripts/android/setup.sh index c7f43009c..ffc0b7cef 100755 --- a/scripts/android-setup.sh +++ b/scripts/android/setup.sh @@ -16,18 +16,10 @@ set -euo pipefail # AVD_SECONDARY_ABI (preferred ABI; optional) # AVD_SECONDARY_NAME (override final name) -require_tool() { - if ! command -v "$1" >/dev/null 2>&1; then - echo "Missing required tool: $1. Ensure devbox shell is active and required packages are installed." >&2 - exit 1 - fi -} - script_dir="$(cd "$(dirname "$0")" && pwd)" -if [ -f "$script_dir/platform-versions.sh" ]; then - # shellcheck disable=SC1090 - . "$script_dir/platform-versions.sh" -fi +# shellcheck disable=SC1090 +. "$script_dir/../shared/common.sh" +load_platform_versions "$script_dir" detect_sdk_root() { if [[ -n ${ANDROID_SDK_ROOT:-} ]]; then diff --git a/scripts/android/test.sh b/scripts/android/test.sh new file mode 100755 index 000000000..342a187a0 --- /dev/null +++ b/scripts/android/test.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1090 +. "$script_dir/../shared/common.sh" + +bash "$SCRIPTS_DIR/android/setup.sh" +yarn install +yarn e2e install +yarn build +yarn e2e build:android +yarn e2e test:android diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 000000000..d22efb734 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1090 +. "$script_dir/shared/common.sh" + +yarn install --immutable +yarn build +yarn lint diff --git a/scripts/ios/env.sh b/scripts/ios/env.sh new file mode 100755 index 000000000..c5ee09930 --- /dev/null +++ b/scripts/ios/env.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -euo pipefail + +devbox_omit_nix_env() { + if [ "${DEVBOX_OMIT_NIX_ENV_APPLIED:-}" = "1" ]; then + return 0 + fi + + export DEVBOX_OMIT_NIX_ENV_APPLIED=1 + + dump_env() { + echo "devbox omit-nix-env $1" + echo " PATH=$PATH" + echo " CC=${CC:-}" + echo " CXX=${CXX:-}" + echo " LD=${LD:-}" + echo " CPP=${CPP:-}" + echo " AR=${AR:-}" + echo " SDKROOT=${SDKROOT:-}" + echo " DEVELOPER_DIR=${DEVELOPER_DIR:-}" + } + + dump_env "before" + + eval "$(devbox shellenv --init-hook --install --no-refresh-alias --omit-nix-env=true)" + + if [ "$(uname -s)" = "Darwin" ]; then + PATH="$(printf '%s' "$PATH" | tr ':' '\n' | awk '!/^\/nix\/store\//{print}' | paste -sd ':' -)" + + for var in CC CXX LD CPP AR AS NM RANLIB STRIP OBJC OBJCXX SDKROOT DEVELOPER_DIR; do + value="${!var:-}" + if [ -n "$value" ] && [ "${value#/nix/store/}" != "$value" ]; then + unset "$var" + fi + done + + if [ -x /usr/bin/clang ]; then + export CC=/usr/bin/clang + export CXX=/usr/bin/clang++ + fi + + if command -v xcode-select >/dev/null 2>&1; then + dev_dir="$(xcode-select -p 2>/dev/null || true)" + if [ -n "$dev_dir" ]; then + export DEVELOPER_DIR="$dev_dir" + fi + fi + + unset SDKROOT + fi + + dump_env "after" +} + +devbox_omit_nix_env diff --git a/scripts/ios-manager.sh b/scripts/ios/manager.sh similarity index 88% rename from scripts/ios-manager.sh rename to scripts/ios/manager.sh index 96f1f0214..52359735f 100755 --- a/scripts/ios-manager.sh +++ b/scripts/ios/manager.sh @@ -2,10 +2,9 @@ set -euo pipefail script_dir="$(cd "$(dirname "$0")" && pwd)" -if [ -f "$script_dir/platform-versions.sh" ]; then - # shellcheck disable=SC1090 - . "$script_dir/platform-versions.sh" -fi +# shellcheck disable=SC1090 +. "$script_dir/../shared/common.sh" +load_platform_versions "$script_dir" action="${1:-}" shift || true @@ -46,7 +45,7 @@ start) start_ios ;; stop) stop_ios ;; reset) reset_ios ;; *) - echo "Usage: ios-manager.sh {start|stop|reset}" >&2 + echo "Usage: manager.sh {start|stop|reset}" >&2 exit 1 ;; esac diff --git a/scripts/ios/setup.sh b/scripts/ios/setup.sh new file mode 100755 index 000000000..de7827cad --- /dev/null +++ b/scripts/ios/setup.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1090 +. "$script_dir/../shared/common.sh" +# shellcheck disable=SC1090 +. "$script_dir/simctl.sh" +load_platform_versions "$script_dir" + +# Creates local iOS simulators for common targets. Requires Xcode command-line tools and jq. +# Env overrides: +# IOS_DEVICE_NAMES="iPhone 15,iPhone 17" (comma-separated) +# IOS_RUNTIME="26.1" (preferred runtime prefix; falls back to latest available) +# IOS_DOWNLOAD_RUNTIME=1 to attempt xcodebuild -downloadPlatform iOS when the preferred runtime is missing +# IOS_DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer" to override the Xcode path; defaults to xcode-select -p or the standard Xcode.app if found + +ensure_developer_dir() { + local desired="${IOS_DEVELOPER_DIR:-}" + if [[ -z $desired ]]; then + if xcode-select -p >/dev/null 2>&1; then + desired="$(xcode-select -p)" + elif [[ -d /Applications/Xcode.app/Contents/Developer ]]; then + desired="/Applications/Xcode.app/Contents/Developer" + fi + fi + + if [[ -n $desired && -d $desired ]]; then + export DEVELOPER_DIR="$desired" + export PATH="$DEVELOPER_DIR/usr/bin:$PATH" + return 0 + fi + + echo "Xcode developer directory not found. Install Xcode/CLI tools or set IOS_DEVELOPER_DIR to an Xcode path (e.g., /Applications/Xcode.app/Contents/Developer)." >&2 + exit 1 +} + +ensure_developer_dir + +require_tool xcrun "Missing required tool: xcrun. Install Xcode CLI tools before running (xcode-select --install or Xcode.app + xcode-select -s)." +require_tool jq + +ensure_simctl() { + if xcrun -f simctl >/dev/null 2>&1; then + return 0 + fi + cat >&2 <<'EOF' +Missing simctl. +- The standalone Command Line Tools do NOT include simctl; you need full Xcode. +- Install/locate Xcode.app, then select it: + sudo xcode-select -s /Applications/Xcode.app/Contents/Developer +- You can also set IOS_DEVELOPER_DIR to your Xcode path for this script. +EOF + exit 1 +} + +ensure_simctl + +main() { + ensure_core_sim_service || return 1 + IFS=',' read -r -a devices <<<"${IOS_DEVICE_NAMES:-${IOS_MIN_DEVICE:-${PLATFORM_IOS_MIN_DEVICE:-iPhone 13}},${IOS_MAX_DEVICE:-${PLATFORM_IOS_MAX_DEVICE:-iPhone 17}}}" + local runtime="${IOS_RUNTIME:-${IOS_MIN_RUNTIME:-${PLATFORM_IOS_MIN_RUNTIME:-15.0}}}" + for device in "${devices[@]}"; do + ensure_device "$(echo "$device" | xargs)" "$runtime" + done + echo "Done. Launch via Xcode > Devices or 'xcrun simctl boot \"\"' then 'open -a Simulator'." +} + +main "$@" diff --git a/scripts/ios-setup.sh b/scripts/ios/simctl.sh old mode 100755 new mode 100644 similarity index 64% rename from scripts/ios-setup.sh rename to scripts/ios/simctl.sh index 6da782438..ddfe7bf27 --- a/scripts/ios-setup.sh +++ b/scripts/ios/simctl.sh @@ -1,67 +1,6 @@ #!/usr/bin/env bash set -euo pipefail -script_dir="$(cd "$(dirname "$0")" && pwd)" -if [ -f "$script_dir/platform-versions.sh" ]; then - # shellcheck disable=SC1090 - . "$script_dir/platform-versions.sh" -fi - -# Creates local iOS simulators for common targets. Requires Xcode command-line tools and jq. -# Env overrides: -# IOS_DEVICE_NAMES="iPhone 15,iPhone 17" (comma-separated) -# IOS_RUNTIME="26.1" (preferred runtime prefix; falls back to latest available) -# IOS_DOWNLOAD_RUNTIME=1 to attempt xcodebuild -downloadPlatform iOS when the preferred runtime is missing -# IOS_DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer" to override the Xcode path; defaults to xcode-select -p or the standard Xcode.app if found - -require_tool() { - if ! command -v "$1" >/dev/null 2>&1; then - echo "Missing required tool: $1. Install Xcode CLI tools before running (xcode-select --install or Xcode.app + xcode-select -s)." >&2 - exit 1 - fi -} - -ensure_developer_dir() { - local desired="${IOS_DEVELOPER_DIR:-}" - if [[ -z $desired ]]; then - if xcode-select -p >/dev/null 2>&1; then - desired="$(xcode-select -p)" - elif [[ -d /Applications/Xcode.app/Contents/Developer ]]; then - desired="/Applications/Xcode.app/Contents/Developer" - fi - fi - - if [[ -n $desired && -d $desired ]]; then - export DEVELOPER_DIR="$desired" - export PATH="$DEVELOPER_DIR/usr/bin:$PATH" - return 0 - fi - - echo "Xcode developer directory not found. Install Xcode/CLI tools or set IOS_DEVELOPER_DIR to an Xcode path (e.g., /Applications/Xcode.app/Contents/Developer)." >&2 - exit 1 -} - -ensure_developer_dir - -require_tool xcrun -require_tool jq - -ensure_simctl() { - if xcrun -f simctl >/dev/null 2>&1; then - return 0 - fi - cat >&2 <<'EOF' -Missing simctl. -- The standalone Command Line Tools do NOT include simctl; you need full Xcode. -- Install/locate Xcode.app, then select it: - sudo xcode-select -s /Applications/Xcode.app/Contents/Developer -- You can also set IOS_DEVELOPER_DIR to your Xcode path for this script. -EOF - exit 1 -} - -ensure_simctl - ensure_core_sim_service() { local output status output="$(xcrun simctl list devices -j 2>&1)" || status=$? @@ -186,15 +125,3 @@ ensure_device() { xcrun simctl create "$display_name" "$device_type" "$runtime_id" echo "Created ${display_name}" } - -main() { - ensure_core_sim_service || return 1 - IFS=',' read -r -a devices <<<"${IOS_DEVICE_NAMES:-${IOS_MIN_DEVICE:-${PLATFORM_IOS_MIN_DEVICE:-iPhone 13}},${IOS_MAX_DEVICE:-${PLATFORM_IOS_MAX_DEVICE:-iPhone 17}}}" - local runtime="${IOS_RUNTIME:-${IOS_MIN_RUNTIME:-${PLATFORM_IOS_MIN_RUNTIME:-15.0}}}" - for device in "${devices[@]}"; do - ensure_device "$(echo "$device" | xargs)" "$runtime" - done - echo "Done. Launch via Xcode > Devices or 'xcrun simctl boot \"\"' then 'open -a Simulator'." -} - -main "$@" diff --git a/scripts/ios/test.sh b/scripts/ios/test.sh new file mode 100755 index 000000000..7bf5a4189 --- /dev/null +++ b/scripts/ios/test.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1090 +. "$script_dir/../shared/common.sh" + +if [ "$(uname -s)" = "Darwin" ]; then + . "$SCRIPTS_DIR/ios/env.sh" +fi + +echo "iOS test env" +echo " PATH=$PATH" +echo " CC=${CC:-}" +echo " CXX=${CXX:-}" +echo " SDKROOT=${SDKROOT:-}" +echo " DEVELOPER_DIR=${DEVELOPER_DIR:-}" + +bash "$SCRIPTS_DIR/ios/setup.sh" +yarn install +yarn e2e install +yarn e2e pods +yarn build +yarn e2e build:ios +yarn e2e test:ios diff --git a/scripts/shared/common.sh b/scripts/shared/common.sh new file mode 100644 index 000000000..a62edbf02 --- /dev/null +++ b/scripts/shared/common.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env sh + +require_tool() { + tool="$1" + message="${2:-Missing required tool: $tool. Ensure devbox shell is active and required packages are installed.}" + if ! command -v "$tool" >/dev/null 2>&1; then + echo "$message" >&2 + exit 1 + fi +} + +ensure_project_root() { + if [ -n "${PROJECT_ROOT:-}" ]; then + return 0 + fi + + base_dir="${1:-}" + if [ -z "$base_dir" ]; then + base_dir="$PWD" + fi + + git_root="" + if command -v git >/dev/null 2>&1; then + git_root="$(git -C "$base_dir" rev-parse --show-toplevel 2>/dev/null || true)" + fi + + if [ -n "$git_root" ]; then + PROJECT_ROOT="$git_root" + elif [ -f "$base_dir/../shared/common.sh" ] && [ -f "$base_dir/../build.sh" ]; then + PROJECT_ROOT="$(cd "$base_dir/.." && pwd)" + elif [ -f "$base_dir/shared/common.sh" ] && [ -f "$base_dir/build.sh" ]; then + PROJECT_ROOT="$(cd "$base_dir" && pwd)" + fi + + if [ -n "${PROJECT_ROOT:-}" ]; then + export PROJECT_ROOT + fi +} + +load_platform_versions() { + base_dir="$1" + platform_versions="${base_dir%/}/../platform-versions.sh" + if [ -f "$platform_versions" ]; then + # shellcheck disable=SC1090 + . "$platform_versions" + fi +} + +ensure_project_root "${SCRIPT_DIR:-${script_dir:-${PWD}}}" + +if [ -z "${SCRIPTS_DIR:-}" ] && [ -n "${PROJECT_ROOT:-}" ]; then + SCRIPTS_DIR="$PROJECT_ROOT/scripts" + export SCRIPTS_DIR +fi diff --git a/shells/devbox-android.json b/shells/devbox-android.json new file mode 100644 index 000000000..a1be49c55 --- /dev/null +++ b/shells/devbox-android.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.14.2/.schema/devbox.schema.json", + "packages": { + "yarn-berry": "latest", + "jdk17": "latest", + "gradle": "latest", + "jq": "latest", + "path:../nix#android-sdk": "" + }, + "shell": { + "init_hook": [ + ". $DEVBOX_PROJECT_ROOT/../scripts/shared/common.sh", + "echo 'Android SDK env configured (details: wiki/devbox.md#devbox-android).'", + ". $SCRIPTS_DIR/android/env.sh" + ], + "scripts": { + "setup-android": ["bash $SCRIPTS_DIR/android/setup.sh"], + "test-android": ["bash $SCRIPTS_DIR/android/test.sh"] + } + } +} diff --git a/shells/devbox-fast.json b/shells/devbox-fast.json new file mode 100644 index 000000000..0870e0cd9 --- /dev/null +++ b/shells/devbox-fast.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.14.2/.schema/devbox.schema.json", + "packages": { + "yarn-berry": "latest", + "jq": "latest", + "treefmt": "latest", + "nixfmt": "latest", + "shfmt": "latest" + }, + "shell": { + "init_hook": [". $DEVBOX_PROJECT_ROOT/../scripts/shared/common.sh"], + "scripts": { + "build": ["bash $SCRIPTS_DIR/build.sh"], + "format": ["treefmt"], + "lint": ["treefmt --fail-on-change"] + } + } +} diff --git a/shells/devbox-ios.json b/shells/devbox-ios.json new file mode 100644 index 000000000..ba43b7d33 --- /dev/null +++ b/shells/devbox-ios.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.14.2/.schema/devbox.schema.json", + "packages": { + "cocoapods": { + "version": "latest", + "platforms": ["x86_64-darwin", "aarch64-darwin"] + }, + "yarn-berry": "latest", + "jq": "latest" + }, + "shell": { + "init_hook": [ + ". $DEVBOX_PROJECT_ROOT/../scripts/shared/common.sh", + "if [ \"$(uname -s)\" = \"Darwin\" ]; then . $SCRIPTS_DIR/ios/env.sh; fi" + ], + "scripts": { + "setup-ios": ["bash $SCRIPTS_DIR/ios/setup.sh"], + "test-ios": ["bash $SCRIPTS_DIR/ios/test.sh"] + } + } +} diff --git a/shells/devbox.lock b/shells/devbox.lock new file mode 100644 index 000000000..ae82190ba --- /dev/null +++ b/shells/devbox.lock @@ -0,0 +1,201 @@ +{ + "lockfile_version": "1", + "packages": { + "cocoapods@latest": { + "last_modified": "2025-12-31T03:27:36Z", + "resolved": "github:NixOS/nixpkgs/f665af0cdb70ed27e1bd8f9fdfecaf451260fc55#cocoapods", + "source": "devbox-search", + "version": "1.16.2", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/av5g6hfp0yiir3iavg72js70ian8hxyf-cocoapods-1.16.2", + "default": true + } + ], + "store_path": "/nix/store/av5g6hfp0yiir3iavg72js70ian8hxyf-cocoapods-1.16.2" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/har71589bwmh6h6skisd20b3c6lrwmz7-cocoapods-1.16.2", + "default": true + } + ], + "store_path": "/nix/store/har71589bwmh6h6skisd20b3c6lrwmz7-cocoapods-1.16.2" + } + } + }, + "github:NixOS/nixpkgs/nixpkgs-unstable": { + "last_modified": "2026-01-27T15:18:14Z", + "resolved": "github:NixOS/nixpkgs/afce96367b2e37fc29afb5543573cd49db3357b7?lastModified=1769527094" + }, + "jq@latest": { + "last_modified": "2026-01-20T02:11:35Z", + "resolved": "github:NixOS/nixpkgs/ed142ab1b3a092c4d149245d0c4126a5d7ea00b0#jq", + "source": "devbox-search", + "version": "1.8.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/9rm6fm3zq1jq8rgsx528cw8wkmfya2gf-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/cv999saj62xhq7xv5i7q6944vljykfmw-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/5camppj4hz2mgkdbxs0kr6nvh6qa65wf-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/lak094rhhxlaj1qycadmxyfphgjadj5r-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/g371yvjasdr552v98p5kav7n35s1dfib-jq-1.8.1" + } + ], + "store_path": "/nix/store/9rm6fm3zq1jq8rgsx528cw8wkmfya2gf-jq-1.8.1-bin" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/m8qv4g54q3jmjb8i33v9lljcwhydx2vd-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/9x2457g76jikfy7xq4mjqwzl8iz3zvxj-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/5ykn83b3hhvnnq0p5vqgcrzihrl9wpsl-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/37ypy1595g6rj3cymh1mpk2b25fx40g7-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/16lg603jzppwjanlakcak1ais69mkd03-jq-1.8.1" + } + ], + "store_path": "/nix/store/m8qv4g54q3jmjb8i33v9lljcwhydx2vd-jq-1.8.1-bin" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/kkb17whpkdrmn9g3gk7y6l69vipxsw0i-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/iwr61wi83kflqvz8j5nf7ridaqq6nh2w-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/lypnqs272644l8ff6wfji9rg5jw10v7h-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/nyw97c4pywfcqqap5hyk9xjghczlbshl-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/ri930a557685c64bdh88a5031i7hx3vy-jq-1.8.1" + } + ], + "store_path": "/nix/store/kkb17whpkdrmn9g3gk7y6l69vipxsw0i-jq-1.8.1-bin" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/zssasryipb2x4gk2ahzacl4mvvcmk48j-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/7d4pv1iymyqk2lykwj1ydml3rjhc6gl3-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/rmxxm5jnxq93kvkhbr2b3hzj6v3ldp8z-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/mkhfvc69grlky3iblibkw9wcc12jcdqq-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/807g765zgpmp1c8fm5y40rw2gbr1k6dk-jq-1.8.1" + } + ], + "store_path": "/nix/store/zssasryipb2x4gk2ahzacl4mvvcmk48j-jq-1.8.1-bin" + } + } + }, + "yarn-berry@latest": { + "last_modified": "2025-12-31T03:27:36Z", + "resolved": "github:NixOS/nixpkgs/f665af0cdb70ed27e1bd8f9fdfecaf451260fc55#yarn-berry", + "source": "devbox-search", + "version": "4.12.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/2l7sbyyqardvrzr35zkrw67gbng5gb8y-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/2l7sbyyqardvrzr35zkrw67gbng5gb8y-yarn-berry-4.12.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/klx9ndw1djgx0zhhyrkcn9an094rmmwv-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/klx9ndw1djgx0zhhyrkcn9an094rmmwv-yarn-berry-4.12.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/m6cwiya6hrbwnlprh2cbnmz6c7mkylrf-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/m6cwiya6hrbwnlprh2cbnmz6c7mkylrf-yarn-berry-4.12.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/q1gys3zgijcciiafbh9nfawkx5wj8179-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/q1gys3zgijcciiafbh9nfawkx5wj8179-yarn-berry-4.12.0" + } + } + } + } +} diff --git a/shells/gradle.properties b/shells/gradle.properties new file mode 100644 index 000000000..a468eda9e --- /dev/null +++ b/shells/gradle.properties @@ -0,0 +1 @@ +org.gradle.java.home=/nix/store/hlm8a8cnp4hm8xkg0a2yy4kv7cq44jii-zulu-ca-jdk-17.0.12 diff --git a/wiki/devbox.md b/wiki/devbox.md index bc4cd06d1..76e710ed0 100644 --- a/wiki/devbox.md +++ b/wiki/devbox.md @@ -2,7 +2,7 @@ This repo ships a Devbox environment that preinstalls the Android SDK and common build tools like Gradle and Yarn. Devbox uses Nix under the hood to pin versions so everyone has the same setup. You don’t need to know Nix to use it. -Enter the environment with `devbox shell`. The init hook wires `ANDROID_SDK_ROOT`/`ANDROID_HOME` and PATH. Common scripts run via `devbox run`, for example `devbox run build`. For Devbox basics, see the official docs: https://www.jetify.com/devbox/docs/. +Enter the environment with `devbox shell`. The init hook wires `ANDROID_SDK_ROOT`/`ANDROID_HOME` and PATH. Common scripts run via `devbox run`, for example `devbox run build`. For Devbox basics, see the official docs: https://www.jetify.com/devbox/docs/. For script layout, see `wiki/scripts.md`; for Nix/SDK versions, see `wiki/nix.md`. ## Getting started @@ -13,13 +13,13 @@ Enter the environment with `devbox shell`. The init hook wires `ANDROID_SDK_ROOT ## Android -By default, Devbox uses the flake-pinned SDK (`path:./nix#android-sdk`). It sets `ANDROID_SDK_ROOT`/`ANDROID_HOME` and adds emulator/platform-tools/cmdline-tools to `PATH` via `scripts/android-env.sh`. Platform versions live in `nix/platform-versions.json` (single source of truth for min/max API and build tools; loaded by `scripts/platform-versions.sh`). To use a local SDK instead, launch with `ANDROID_HOME="$HOME/Library/Android/sdk" devbox shell` (or set `ANDROID_SDK_ROOT`). Clear both env vars to return to the Nix SDK. Inspect the active SDK with `echo "$ANDROID_SDK_ROOT"` and `which sdkmanager` inside the shell. Create/boot AVDs via `devbox run start-android*` (uses `scripts/android-setup.sh` + `scripts/android-manager.sh`). +By default, Devbox uses the flake-pinned SDK (`path:./nix#android-sdk`). It sets `ANDROID_SDK_ROOT`/`ANDROID_HOME` and adds emulator/platform-tools/cmdline-tools to `PATH` via `scripts/android/env.sh`. To use a local SDK instead, launch with `ANDROID_HOME="$HOME/Library/Android/sdk" devbox shell` (or set `ANDROID_SDK_ROOT`). Clear both env vars to return to the Nix SDK. Inspect the active SDK with `echo "$ANDROID_SDK_ROOT"` and `which sdkmanager` inside the shell. Create/boot AVDs via `devbox run start-android*` (uses `scripts/android/setup.sh` + `scripts/android/manager.sh`). Version sources are documented in `wiki/nix.md`. ### Emulator/AVD scripts - `devbox run start-android` launches the default “latest” AVD (API 33, Medium Phone). On arm64 hosts it uses the arm64-v8a image; on Intel it uses x86_64. Override with `AVD_FLAVOR=minsdk` to launch the API 21 Pixel AVD instead. You can also set `DETOX_AVD` to pick an exact AVD name. -- `devbox run start-android-latest` / `start-android-minsdk` explicitly launch the latest (API 33) or minsdk (API 21) AVDs. Both will create the AVD first via `scripts/android-setup.sh` if it does not exist. -- `scripts/android-setup.sh` now accepts env overrides: `AVD_API`, `AVD_DEVICE`, `AVD_TAG`, `AVD_ABI`, `AVD_NAME`. Defaults target API 21 for minsdk; CI passes API 33 for latest. The script auto-selects the best ABI for the host (arm64-v8a on arm, x86_64 on Intel) if `AVD_ABI` is unset. +- `devbox run start-android-latest` / `start-android-minsdk` explicitly launch the latest (API 33) or minsdk (API 21) AVDs. Both will create the AVD first via `scripts/android/setup.sh` if it does not exist. +- `scripts/android/setup.sh` accepts env overrides: `AVD_API`, `AVD_DEVICE`, `AVD_TAG`, `AVD_ABI`, `AVD_NAME`. Defaults target API 21 for minsdk; CI passes API 33 for latest. The script auto-selects the best ABI for the host (arm64-v8a on arm, x86_64 on Intel) if `AVD_ABI` is unset. - `devbox run reset-android` removes local AVDs/adb keys if you need a clean slate. - `EMU_HEADLESS=1 devbox run start-android*` to run the emulator headless (CI sets this); omit for a visible emulator locally. - `EMU_PORT=5554 devbox run start-android*` to set the emulator port/serial (defaults to 5554) and avoid adb conflicts. @@ -28,28 +28,27 @@ By default, Devbox uses the flake-pinned SDK (`path:./nix#android-sdk`). It sets ### Detox defaults - Android Detox defaults to the latest AVD (`medium_phone_API33_arm64_v8a` on arm hosts, x86*64 otherwise). Set `DETOX_AVD=pixel_API21*\*` to run against the minsdk AVD. -- CI Android E2E runs both API 21 (Pixel) and API 33 (Medium Phone) in parallel in the nightly workflow. Override the workflow matrix in `ci-e2e-nightly.yml` if needed. +- CI Android E2E runs both API 21 (Pixel) and API 33 (Medium Phone) in parallel in the full workflow. Override the workflow matrix in `ci-e2e-full.yml` if needed. ### Updating Android min/latest versions -- Bump pinned SDK versions in `nix/platform-versions.json` (platformVersions/buildToolsVersions/cmdLineToolsVersion). Rebuild devbox (`devbox shell --rebuild`) so everyone gets the new SDK. +- Bump pinned SDK versions in `nix/platform-versions.json` (platformVersions/buildToolsVersions/cmdLineToolsVersion). Refresh your devshell by running the `refresh` command while inside a devbox shell. - Update AVD defaults/names if you change API levels: - `devbox.json` (`start-android-*` scripts) for default AVD names. - `examples/E2E/.detoxrc.js` for the default `DETOX_AVD`. - - CI matrix in `.github/workflows/ci-e2e-nightly.yml` (`android-min`/`android-latest` targets). + - CI matrix in `.github/workflows/ci-e2e-full.yml` (`android-min`/`android-latest` targets). - Gradle uses `buildToolsVersion` from `examples/E2E/android/build.gradle`; Devbox exports `ANDROID_BUILD_TOOLS_VERSION` from `nix/platform-versions.json` (single source of truth) and you can override it if needed. ## iOS -iOS uses the host Xcode toolchain. There is no Nix-provisioned iOS SDK. Run `devbox run setup-ios` to install pods and bootstrap the iOS example/E2E apps. Full Xcode is required for `simctl` (Command Line Tools alone are not enough). Make sure Xcode command line tools are selected (`xcode-select --print-path` or `sudo xcode-select -s /Applications/Xcode.app/Contents/Developer`) and that you have agreed to the license if prompted. +iOS uses the host Xcode toolchain. There is no Nix-provisioned iOS SDK. Run `devbox run setup-ios` to provision simulators and validate Xcode tooling. Full Xcode is required for `simctl` (Command Line Tools alone are not enough). Make sure Xcode command line tools are selected (`xcode-select --print-path` or `sudo xcode-select -s /Applications/Xcode.app/Contents/Developer`) and that you have agreed to the license if prompted. -> Important: `devbox run` by itself uses the Nix compiler toolchain on macOS. For iOS work you must route commands through `devbox shell --omit-nix-env --command ""` (or run an interactive `devbox shell --omit-nix-env` first) so the Xcode toolchain is used. CI does this for setup/tests. +> Important: `devbox shell` injects Nix toolchain variables on macOS, which can break Xcode builds. The init hooks source `scripts/ios/env.sh` to undo that and re-select the system toolchain, and `scripts/ios/test.sh` re-applies it before running E2E. ### Simulators and Detox - `devbox run setup-ios` provisions simulators. Defaults are driven by `nix/platform-versions.json` (min/max device/runtime). Override via env vars to target a specific device/runtime. Set `IOS_DOWNLOAD_RUNTIME=0` to skip attempting `xcodebuild -downloadPlatform iOS` when the preferred runtime is missing. Set `IOS_DEVELOPER_DIR` (e.g., `/Applications/Xcode.app/Contents/Developer`) to point at a specific Xcode; otherwise it uses `xcode-select -p` or the default Xcode.app if found. -- On macOS, use `devbox shell --omit-nix-env --command ""` when invoking iOS builds/tests to ensure the Xcode toolchain is used instead of the Nix compiler toolchain. -- `devbox run start-ios` provisions simulators (via `setup-ios`), then boots the chosen device (`DETOX_IOS_DEVICE` or default `iPhone 17`) and opens Simulator. Set `IOS_FLAVOR=minsdk` to target the min sim (per `nix/platform-versions.json`) or leave default for latest. Internally uses `scripts/ios-manager.sh`. +- `devbox run start-ios` provisions simulators (via `setup-ios`), then boots the chosen device (`DETOX_IOS_DEVICE` or default `iPhone 17`) and opens Simulator. Set `IOS_FLAVOR=minsdk` to target the min sim (per `nix/platform-versions.json`) or leave default for latest. Internally uses `scripts/ios/manager.sh`. - `devbox run reset-ios` shuts down/erases and removes all local simulator devices. - `devbox run stop-android` / `stop-ios` / `stop` to shut down running emulators/simulators (handy for headless runs). - Detox defaults to `iPhone 17` for local runs; override with `DETOX_IOS_DEVICE`. CI runs a matrix: min sim (from `nix/platform-versions.json`) and latest (iPhone 17 @ latest runtime). @@ -68,17 +67,22 @@ iOS uses the host Xcode toolchain. There is no Nix-provisioned iOS SDK. Run `dev - Adjust platform defaults in `nix/platform-versions.json` and rebuild Devbox if you change Android SDK versions. - Update Detox default device in `examples/E2E/.detoxrc.js` if the default device changes. -- Update CI matrices in `.github/workflows/ci-e2e-nightly.yml` (ios-min/ios-latest rows) if you want to override the platform defaults in CI. +- Update CI matrices in `.github/workflows/ci-e2e-full.yml` (ios-min/ios-latest rows) if you want to override the platform defaults in CI. -### Platform versions source of truth +### CI devbox shells -`nix/platform-versions.json` is the single source of truth for min/max SDK targets and Android build tools. It feeds: +The root `devbox.json` is a full local dev environment. CI uses slim Devbox configs under `shells/` to avoid pulling unnecessary SDKs: -- `nix/flake.nix` (Android SDK packages). -- `scripts/platform-versions.sh` (exports env vars via `jq` for devbox scripts). -- CI workflows (`ci-e2e-optional.yml` and `ci-e2e-nightly.yml`) for iOS/Android target selection. +- `shells/devbox-fast.json`: build + lint only. +- `shells/devbox-android.json`: Android SDK + JDK/Gradle for Android E2E. +- `shells/devbox-ios.json`: CocoaPods + Yarn for iOS E2E (Xcode still required on macOS). -After updating `nix/platform-versions.json`: +Run them locally with: -- Run `devbox install` or `devbox shell --rebuild` to refresh the SDK. -- If you change iOS min/max, re-run the iOS E2E workflow to confirm the runtime/device exists on the runner. +```sh +devbox run --config=shells/devbox-fast.json build +devbox run --config=shells/devbox-android.json test-android +devbox run --config=shells/devbox-ios.json test-ios +``` + +Note: when you use `devbox run --config=shells/...`, Devbox treats `shells/` as the config root. The init hooks set `SCRIPTS_DIR` to point back at the repo-level `scripts/` folder. diff --git a/wiki/nix.md b/wiki/nix.md new file mode 100644 index 000000000..986eff98d --- /dev/null +++ b/wiki/nix.md @@ -0,0 +1,37 @@ +# Nix + Platform Versions + +This repo uses a Nix flake for the Android SDK, and a JSON file for single-source platform versioning. + +## Files + +- `nix/flake.nix` + + - Defines the pinned Android SDK (emulator, system images, build tools). + - Exposes an `android-sdk` output used by Devbox (`path:./nix#android-sdk`). + +- `nix/platform-versions.json` + + - Single source of truth for Android/iOS min and max targets. + - Contains Android build tools + cmdline tools versions. + +- `scripts/platform-versions.sh` + - Loads `nix/platform-versions.json` via `jq` and exports env vars for scripts and CI. + +## How versions flow + +1. `nix/platform-versions.json` is updated. +2. `nix/flake.nix` reads those values when building the Android SDK output. +3. `scripts/platform-versions.sh` exports the same values for: + - scripts under `scripts/android/` and `scripts/ios/` + - CI workflows that set min/max targets + +## Updating versions + +1. Edit `nix/platform-versions.json`. +2. In a devbox shell, run `refresh` to rebuild the SDK. +3. If iOS min/max changes, re-run the iOS E2E workflow to confirm the runtime/device exists on the runner. + +## CI targets + +- Latest E2E: `.github/workflows/ci-e2e-latest.yml` +- Full (min+max) E2E: `.github/workflows/ci-e2e-full.yml` diff --git a/wiki/scripts.md b/wiki/scripts.md new file mode 100644 index 000000000..483d447c4 --- /dev/null +++ b/wiki/scripts.md @@ -0,0 +1,90 @@ +# Scripts Overview + +This repo uses `scripts/` as the entry point for devbox commands and CI helpers. Scripts are organized by platform with a small shared helper layer. + +## Layout + +- `scripts/build.sh`: JS build + lint for fast CI. +- `scripts/platform-versions.sh`: loads `nix/platform-versions.json` and exports platform vars for scripts. +- `scripts/shared/common.sh`: shared helpers (tool checks, platform version loader). +- `scripts/android/`: Android SDK, AVD, and E2E helpers. +- `scripts/ios/`: iOS simulator setup, toolchain fixups, and E2E helpers. +- `scripts/act-ci.sh`: local CI runner helper for `act`. + +## Shared helpers + +- `scripts/shared/common.sh` + - `require_tool`: asserts a tool exists (with an optional custom message). + - `load_platform_versions`: sources `scripts/platform-versions.sh` if present. + - `PROJECT_ROOT`: auto-detected git root when unset. + - `SCRIPTS_DIR`: defaults to `$PROJECT_ROOT/scripts` when unset. + +## Android scripts + +- `scripts/android/env.sh` + + - Sets `ANDROID_SDK_ROOT`/`ANDROID_HOME` and PATH for the Nix SDK. + - Loads platform defaults via `scripts/platform-versions.sh`. + - Used by devbox init hooks in `devbox.json` and `shells/devbox-android.json`. + +- `scripts/android/setup.sh` + + - Creates/ensures AVDs for min and max API levels. + - Depends on `sdkmanager`, `avdmanager`, `emulator` in PATH (Devbox shell). + - Uses platform defaults from `scripts/platform-versions.sh`. + +- `scripts/android/manager.sh` + + - Starts/stops/resets AVDs and applies emulator defaults. + - Uses `devbox run setup-android` to ensure AVDs exist. + +- `scripts/android/test.sh` + - Runs setup + yarn build + Android E2E (Detox). + - Used by `devbox run test-android` and CI Android workflows. + +## iOS scripts + +- `scripts/ios/env.sh` + + - Workaround for Devbox macOS toolchain injection. + - Removes Nix toolchain variables and re-selects system clang/Xcode. + - Sourced by devbox init hooks and re-applied in `scripts/ios/test.sh`. + +- `scripts/ios/simctl.sh` + + - Helpers for runtime selection and simulator management. + - Used by `scripts/ios/setup.sh`. + +- `scripts/ios/setup.sh` + + - Ensures Xcode tools are selected and simulators exist. + - Uses `scripts/ios/simctl.sh` to choose runtimes/devices. + +- `scripts/ios/manager.sh` + + - Boots/shuts down simulators via `simctl`. + - Uses platform defaults from `scripts/platform-versions.sh`. + +- `scripts/ios/test.sh` + - Applies `scripts/ios/env.sh`, then runs setup + yarn build + iOS E2E. + - Used by `devbox run test-ios` and CI iOS workflows. + +## Devbox wiring + +Root devbox (`devbox.json`) exposes: + +- `build` -> `scripts/build.sh` +- `test-android` -> `scripts/android/test.sh` +- `test-ios` -> `scripts/ios/test.sh` +- `setup-android` -> `scripts/android/setup.sh` +- `setup-ios` -> `scripts/ios/setup.sh` +- `start-android*` -> `scripts/android/manager.sh` +- `start-ios` -> `scripts/ios/manager.sh` + +Slim CI shells: + +- `shells/devbox-fast.json` -> `scripts/build.sh` +- `shells/devbox-android.json` -> `scripts/android/test.sh` +- `shells/devbox-ios.json` -> `scripts/ios/test.sh` + +See `wiki/devbox.md` for usage and `wiki/nix.md` for platform version sources.