diff --git a/.github/workflows/android-e2e.yml b/.github/workflows/android-e2e.yml new file mode 100644 index 000000000..deca04cc5 --- /dev/null +++ b/.github/workflows/android-e2e.yml @@ -0,0 +1,58 @@ +name: Android E2E (Reusable) + +on: + workflow_call: + inputs: + target: + description: "TARGET_SDK value (min/max/custom)" + required: true + type: string + runs_on: + description: "GitHub runner label" + required: false + type: string + default: "ubuntu-latest" + +jobs: + android-e2e: + runs-on: ${{ inputs.runs_on }} + env: + ANALYTICS_CI_DEBUG: "1" + EMU_HEADLESS: 1 + AVD_ABI: x86_64 + 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: Resolve devbox config + run: | + case "${{ inputs.target }}" in + min|max) + echo "DEVBOX_CONFIG=shells/android-${{ inputs.target }}/devbox.json" >> "$GITHUB_ENV" + ;; + *) + echo "Unsupported target '${{ inputs.target }}' for CI. Use min or max." >&2 + exit 1 + ;; + esac + - name: devbox installer + uses: jetify-com/devbox-install-action@v0.14.0 + with: + project-path: ${{ env.DEVBOX_CONFIG }} + enable-cache: 'false' + - name: Android E2E Tests + run: devbox run --pure --config=${{ env.DEVBOX_CONFIG }} test-android diff --git a/.github/workflows/ci-e2e-full.yml b/.github/workflows/ci-e2e-full.yml index 31d37f8b4..17cef47e5 100644 --- a/.github/workflows/ci-e2e-full.yml +++ b/.github/workflows/ci-e2e-full.yml @@ -4,6 +4,7 @@ on: schedule: - cron: '0 6 * * *' workflow_dispatch: + pull_request: concurrency: group: e2e-full-${{ github.ref }} @@ -11,56 +12,22 @@ concurrency: jobs: run-e2e-ios: - runs-on: macos-26 - env: - YARN_ENABLE_HARDENED_MODE: 0 - XCODE_VERSION: '26.2' strategy: matrix: include: - name: ios-min + target: min + runs_on: macos-14 - 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: - project-path: shells/devbox-ios.json - 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 + target: max + runs_on: macos-26 + uses: ./.github/workflows/ios-e2e.yml + with: + target: ${{ matrix.target }} + runs_on: ${{ matrix.runs_on }} + secrets: inherit run-e2e-android: - runs-on: ubuntu-24.04-arm - env: - EMU_HEADLESS: 1 strategy: matrix: include: @@ -68,40 +35,7 @@ jobs: 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: - project-path: shells/devbox-android.json - 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 + uses: ./.github/workflows/android-e2e.yml + with: + target: ${{ matrix.target }} + secrets: inherit diff --git a/.github/workflows/ci-e2e-latest.yml b/.github/workflows/ci-e2e-latest.yml index 98fd45c6d..3fa66bf64 100644 --- a/.github/workflows/ci-e2e-latest.yml +++ b/.github/workflows/ci-e2e-latest.yml @@ -9,75 +9,13 @@ concurrency: jobs: run-e2e-ios: - runs-on: macos-26 - env: - YARN_ENABLE_HARDENED_MODE: 0 - XCODE_VERSION: '26.2' - 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: - project-path: shells/devbox-ios.json - enable-cache: 'false' - - name: Resolve iOS targets - run: | - . scripts/platform-versions.sh - 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 run --config=shells/devbox-ios.json test-ios + uses: ./.github/workflows/ios-e2e.yml + with: + target: max + secrets: inherit run-e2e-android: - runs-on: ubuntu-24.04-arm - env: - EMU_HEADLESS: 1 - 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: - project-path: shells/devbox-android.json - enable-cache: 'false' - - name: Resolve Android targets - run: | - . scripts/platform-versions.sh - api="$PLATFORM_ANDROID_MAX_API" - device="$PLATFORM_ANDROID_MAX_DEVICE" - avd_name="${device}_API${api}_arm64_v8a" - echo "DETOX_AVD=${avd_name}" >> "$GITHUB_ENV" - - name: Android E2E Tests (latest) - run: devbox run --config=shells/devbox-android.json test-android + uses: ./.github/workflows/android-e2e.yml + with: + target: max + secrets: inherit diff --git a/.github/workflows/ci-fast.yml b/.github/workflows/ci-fast.yml index 6d110a051..a84dea6ce 100644 --- a/.github/workflows/ci-fast.yml +++ b/.github/workflows/ci-fast.yml @@ -18,7 +18,7 @@ jobs: - name: devbox installer uses: jetify-com/devbox-install-action@v0.14.0 with: - project-path: shells/devbox-fast.json + project-path: shells/minimal/devbox.json enable-cache: 'false' - name: build - run: devbox run --config=shells/devbox-fast.json build + run: devbox run --pure --config=shells/minimal/devbox.json build diff --git a/.github/workflows/ios-e2e.yml b/.github/workflows/ios-e2e.yml new file mode 100644 index 000000000..92c69b32e --- /dev/null +++ b/.github/workflows/ios-e2e.yml @@ -0,0 +1,139 @@ +name: iOS E2E (Reusable) + +on: + workflow_call: + inputs: + target: + description: "TARGET_SDK value (min/max/custom)" + required: true + type: string + runtime: + description: "Xcode version to install and iOS runtime to require (optional override)" + required: false + type: string + default: "" + devbox_config: + description: "Devbox config to use" + required: false + type: string + default: "shells/ios/devbox.json" + runs_on: + description: "GitHub runner label" + required: false + type: string + default: "macos-26" + +jobs: + ios-e2e: + runs-on: ${{ inputs.runs_on }} + env: + ANALYTICS_CI_DEBUG: "1" + YARN_ENABLE_HARDENED_MODE: 0 + steps: + - uses: actions/checkout@v4 + - name: Validate target + run: | + case "${{ inputs.target }}" in + min|max) + ;; + *) + echo "Unsupported target '${{ inputs.target }}' for CI. Use min or max." >&2 + exit 1 + ;; + esac + - name: Resolve iOS runtime + id: defaults + run: | + runtime="${{ inputs.runtime }}" + min="$(jq -r '.defaults.IOS_RUNTIME_MIN' nix/defaults.json)" + max="$(jq -r '.defaults.IOS_RUNTIME_MAX' nix/defaults.json)" + custom="$(jq -r '.defaults.IOS_RUNTIME_CUSTOM // empty' nix/defaults.json)" + if [ -z "$runtime" ]; then + case "${{ inputs.target }}" in + min) runtime="$min" ;; + max) runtime="$max" ;; + custom) runtime="$custom" ;; + *) echo "Unknown target: ${{ inputs.target }}" >&2; exit 1 ;; + esac + fi + if [ -z "$runtime" ] || [ "$runtime" = "null" ]; then + echo "Missing runtime for target ${{ inputs.target }}" >&2 + exit 1 + fi + if [ -z "$min" ] || [ "$min" = "null" ] || [ -z "$max" ] || [ "$max" = "null" ]; then + echo "Missing IOS_RUNTIME_MIN/IOS_RUNTIME_MAX in nix/defaults.json" >&2 + exit 1 + fi + echo "runtime=$runtime" >> "$GITHUB_OUTPUT" + echo "runtime_min=$min" >> "$GITHUB_OUTPUT" + echo "runtime_max=$max" >> "$GITHUB_OUTPUT" + - 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.6.0 + with: + xcode-version: ${{ steps.defaults.outputs.runtime }} + - name: Environment check + run: sh scripts/shared/env-check.sh + - name: Xcode diagnostics + run: | + set -euo pipefail + echo "Xcode path: $(xcode-select -p)" + xcodebuild -version + xcodebuild -showsdks + echo "Swift toolchain libs (simulator):" + ls -la "$(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphonesimulator" || true + echo "Swift runtime libs (SDK):" + ls -la "$(xcode-select -p)/Platforms/iPhoneSimulator.platform/Developer/SDKs" || true + sdk_path="$(xcrun --sdk iphonesimulator --show-sdk-path 2>/dev/null || true)" + if [ -n "$sdk_path" ]; then + ls -la "$sdk_path/usr/lib/swift" || true + fi + - name: Install Xcode components + run: | + if xcodebuild -help 2>&1 | grep -q "checkForNewerComponents"; then + sudo xcodebuild -runFirstLaunch -checkForNewerComponents + else + sudo xcodebuild -runFirstLaunch + fi + sudo xcodebuild -downloadPlatform iOS + - name: Verify Swift compatibility libs + run: | + set -euo pipefail + toolchain_swift_dir="$(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphonesimulator" + sdk_path="$(xcrun --sdk iphonesimulator --show-sdk-path 2>/dev/null || true)" + sdk_swift_dir="${sdk_path}/usr/lib/swift" + missing="" + for lib in swiftCompatibilityPacks swiftCompatibility56 swiftCompatibilityConcurrency; do + if [ ! -e "${toolchain_swift_dir}/lib${lib}.dylib" ] && [ ! -e "${sdk_swift_dir}/lib${lib}.dylib" ]; then + missing="${missing} ${lib}" + fi + done + if [ -n "$missing" ]; then + echo "Missing Swift compatibility libraries:${missing}" >&2 + echo "toolchain_swift_dir=${toolchain_swift_dir}" >&2 + echo "sdk_swift_dir=${sdk_swift_dir}" >&2 + exit 1 + fi + - name: devbox installer + uses: jetify-com/devbox-install-action@v0.14.0 + with: + project-path: ${{ inputs.devbox_config }} + enable-cache: 'false' + - name: iOS E2E Tests + run: devbox run --pure --config=${{ inputs.devbox_config }} test-ios + env: + TARGET_SDK: ${{ inputs.target }} + IOS_RUNTIME_MIN: ${{ steps.defaults.outputs.runtime_min }} + IOS_RUNTIME_MAX: ${{ steps.defaults.outputs.runtime_max }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 18f54d087..3255079bc 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -17,64 +17,30 @@ jobs: - name: devbox installer uses: jetify-com/devbox-install-action@v0.14.0 with: - project-path: shells/devbox-fast.json + project-path: shells/minimal/devbox.json enable-cache: 'false' - name: build - run: devbox run --config=shells/devbox-fast.json build + run: devbox run --pure --config=shells/minimal/devbox.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 + target: min + runs_on: macos-14 - 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: - project-path: shells/devbox-ios.json - 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 + target: max + runs_on: macos-26 + uses: ./.github/workflows/ios-e2e.yml + with: + target: ${{ matrix.target }} + runs_on: ${{ matrix.runs_on }} + secrets: inherit e2e-android: name: E2E Android (min/max) - runs-on: ubuntu-24.04-arm - env: - EMU_HEADLESS: 1 strategy: matrix: include: @@ -82,43 +48,10 @@ jobs: 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: - project-path: shells/devbox-android.json - 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 + uses: ./.github/workflows/android-e2e.yml + with: + target: ${{ matrix.target }} + secrets: inherit publish: name: Publish to npm @@ -138,11 +71,11 @@ jobs: - name: devbox installer uses: jetify-com/devbox-install-action@v0.14.0 with: - project-path: shells/devbox-fast.json + project-path: shells/minimal/devbox.json enable-cache: 'false' - name: Config, Build, Release - run: devbox run --config=shells/devbox-fast.json release + run: devbox run --pure --config=shells/minimal/devbox.json release env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} GH_TOKEN: ${{ secrets.GH_TOKEN }} @@ -150,4 +83,4 @@ jobs: - name: Update Apps run: | - devbox run update-apps + devbox run --pure update-apps diff --git a/.github/workflows/release-dry-run.yml b/.github/workflows/release-dry-run.yml new file mode 100644 index 000000000..0cc1aa6ac --- /dev/null +++ b/.github/workflows/release-dry-run.yml @@ -0,0 +1,25 @@ +name: Release Dry Run + +on: + workflow_dispatch: + pull_request: + branches: + - master + +jobs: + release-dry-run: + name: Release Dry Run + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + - name: devbox installer + uses: jetify-com/devbox-install-action@v0.14.0 + with: + project-path: shells/minimal/devbox.json + enable-cache: 'false' + - name: Release Dry Run + run: devbox run --pure --config=shells/minimal/devbox.json release-dry-run + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/devbox.json b/devbox.json index 6dd289314..6f4d41516 100644 --- a/devbox.json +++ b/devbox.json @@ -6,22 +6,37 @@ "platforms": ["x86_64-darwin", "aarch64-darwin"] }, "yarn-berry": "latest", + "git": "latest", + "bash": "latest", + "nodejs": "latest", + "coreutils": "latest", + "gnused": "latest", + "gnugrep": "latest", + "gawk": "latest", + "path:./nix#applesimutils": { + "version": "", + "platforms": ["aarch64-darwin", "x86_64-darwin"] + }, "treefmt": "latest", "nixfmt": "latest", "shfmt": "latest", "jdk17": "latest", - "gradle": "latest", + "gradle": "8.0.1", "jq": "latest", "netcat": "latest", + "act": "latest", "path:./nix#android-sdk": "" }, + "env": { + "ANDROID_CUSTOM_API": "29", + "ANDROID_CUSTOM_DEVICE": "pixel_6", + "IOS_CUSTOM_DEVICE": "iPhone 15" + }, "shell": { "init_hook": [ "echo 'Welcome to analytics-react-native devbox!' > /dev/null", - ". $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).'" + "export LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8", + "INIT_ANDROID=1 INIT_IOS=1 . $DEVBOX_PROJECT_ROOT/scripts/bootstrap/env.sh" ], "scripts": { "clean": [ @@ -31,23 +46,20 @@ "yarn cache clean", "find $DEVBOX_PROJECT_DIR -type d -name node_modules -exec rmdir {} \\;" ], - "build": ["bash $SCRIPTS_DIR/build.sh"], + "build": ["sh $DEVBOX_PROJECT_ROOT/scripts/run.sh build"], "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 $SCRIPTS_DIR/act-ci.sh --platform ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-24.04" + "test-android": ["sh $DEVBOX_PROJECT_ROOT/scripts/run.sh android test"], + "test-ios": ["sh $DEVBOX_PROJECT_ROOT/scripts/run.sh ios test"], + "setup-android": ["sh $DEVBOX_PROJECT_ROOT/scripts/run.sh android setup"], + "setup-ios": ["sh $DEVBOX_PROJECT_ROOT/scripts/run.sh ios setup"], + "start-emulator": ["sh $DEVBOX_PROJECT_ROOT/scripts/run.sh android start"], + "start-ios": ["sh $DEVBOX_PROJECT_ROOT/scripts/run.sh ios start"], + "start-android-min": ["TARGET_SDK=min sh $DEVBOX_PROJECT_ROOT/scripts/run.sh android start"], + "start-android-max": [ + "TARGET_SDK=max sh $DEVBOX_PROJECT_ROOT/scripts/run.sh android start" ], - "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 $SCRIPTS_DIR/android/manager.sh start" - ], - "start-android": ["bash $SCRIPTS_DIR/android/manager.sh start"], + "start-android": ["sh $DEVBOX_PROJECT_ROOT/scripts/run.sh android start"], "update-apps": [ "yarn install --no-immutable", "yarn e2e install --no-immutable", @@ -55,51 +67,15 @@ ], "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*", - "echo \"AVDs and adb keys removed. Recreate via devbox run start-android* as needed.\"" - ], - "reset-ios": [ - "xcrun simctl shutdown all || true", - "xcrun simctl erase all || true", - "xcrun simctl delete all || true", - "xcrun simctl delete unavailable || true", - "killall -9 com.apple.CoreSimulatorService 2>/dev/null || true", - "echo \"Simulators reset via simctl. Recreate via devbox run start-ios.\"" - ], - "stop-android": [ - "if command -v adb >/dev/null 2>&1; then", - " devices=$(adb devices -l 2>/dev/null | tail -n +2 | awk '{print $1}' | tr '\\n' ' ');", - " if [[ -n \"$devices\" ]]; then", - " echo \"Stopping Android emulators: $devices\";", - " for d in $devices; do adb -s \"$d\" emu kill >/dev/null 2>&1 || true; done;", - " else", - " echo \"No Android emulators detected via adb.\";", - " fi;", - "else", - " echo \"adb not found; skipping Android emulator shutdown.\";", - "fi", - "pkill -f \"emulator@\" >/dev/null 2>&1 || true", - "echo \"Android emulators stopped (if any were running).\"" - ], - "stop-ios": [ - "if command -v xcrun >/dev/null 2>&1 && xcrun -f simctl >/dev/null 2>&1; then", - " if xcrun simctl list devices booted | grep -q \"Booted\"; then", - " echo \"Shutting down booted iOS simulators...\";", - " xcrun simctl shutdown all >/dev/null 2>&1 || true;", - " else", - " echo \"No booted iOS simulators detected.\";", - " fi;", - "else", - " echo \"simctl not available; skipping iOS shutdown.\";", - "fi", - "echo \"iOS simulators shutdown (if any were running).\"" + "devbox update --config=shells/minimal/devbox.json", + "devbox update --config=shells/android-min/devbox.json", + "devbox update --config=shells/android-max/devbox.json", + "devbox update --config=shells/ios/devbox.json" ], + "reset-android": ["sh $DEVBOX_PROJECT_ROOT/scripts/run.sh android reset"], + "reset-ios": ["sh $DEVBOX_PROJECT_ROOT/scripts/run.sh ios reset"], + "stop-android": ["sh $DEVBOX_PROJECT_ROOT/scripts/run.sh android stop"], + "stop-ios": ["sh $DEVBOX_PROJECT_ROOT/scripts/run.sh ios stop"], "stop": ["devbox run stop-android", "devbox run stop-ios"], "test": ["devbox run test-android", "devbox run test-ios"] } diff --git a/devbox.lock b/devbox.lock index 81129d34f..aa64719ac 100644 --- a/devbox.lock +++ b/devbox.lock @@ -1,9 +1,181 @@ { "lockfile_version": "1", "packages": { + "act@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#act", + "source": "devbox-search", + "version": "0.2.84", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/60fx8ffpxxfl7chps435jz0yy40bgz7z-act-0.2.84", + "default": true + } + ], + "store_path": "/nix/store/60fx8ffpxxfl7chps435jz0yy40bgz7z-act-0.2.84" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/jrbdrlv46d8jglg30v078ncxmmc7rfrj-act-0.2.84", + "default": true + } + ], + "store_path": "/nix/store/jrbdrlv46d8jglg30v078ncxmmc7rfrj-act-0.2.84" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/j49qnbhrsi7qhw4jn9852f818nxmmj9r-act-0.2.84", + "default": true + } + ], + "store_path": "/nix/store/j49qnbhrsi7qhw4jn9852f818nxmmj9r-act-0.2.84" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/29dp2s13wyawgm1m5kvsl8n4mh107h8r-act-0.2.84", + "default": true + } + ], + "store_path": "/nix/store/29dp2s13wyawgm1m5kvsl8n4mh107h8r-act-0.2.84" + } + } + }, + "bash@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#bash", + "source": "devbox-search", + "version": "5.3p9", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/ngqf98amj0hv0jhzhz540p03wxjj0chj-bash-interactive-5.3p9", + "default": true + }, + { + "name": "man", + "path": "/nix/store/k23hpm86ymd7l92c7cg0a2wsjadr8mx6-bash-interactive-5.3p9-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/vhhwpi6h16bxbrvx1sdg5ag973dln1r9-bash-interactive-5.3p9-dev" + }, + { + "name": "doc", + "path": "/nix/store/dahmvcafcvsp553w8lhkqy2ppv7gd6m5-bash-interactive-5.3p9-doc" + }, + { + "name": "info", + "path": "/nix/store/lqd7rdyads0i42dhxj8zwzj0d01hbgqf-bash-interactive-5.3p9-info" + } + ], + "store_path": "/nix/store/ngqf98amj0hv0jhzhz540p03wxjj0chj-bash-interactive-5.3p9" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/62p6g8nz491c6z224wc6ci1m699y2jhn-bash-interactive-5.3p9", + "default": true + }, + { + "name": "man", + "path": "/nix/store/1ma79ibx4dn0hdbflsxyxccrxzjqqwr3-bash-interactive-5.3p9-man", + "default": true + }, + { + "name": "doc", + "path": "/nix/store/3a9xbr58hfgbj42rmsd9x2fwnir2aasy-bash-interactive-5.3p9-doc" + }, + { + "name": "info", + "path": "/nix/store/h87j74dh8b6lrj11720bda5qq1zfzac0-bash-interactive-5.3p9-info" + }, + { + "name": "debug", + "path": "/nix/store/j1i5n2snbiim8s63x9d41yiqv1anmsvi-bash-interactive-5.3p9-debug" + }, + { + "name": "dev", + "path": "/nix/store/n2i7ipwdbxiypxfballikvp8gx4jkivz-bash-interactive-5.3p9-dev" + } + ], + "store_path": "/nix/store/62p6g8nz491c6z224wc6ci1m699y2jhn-bash-interactive-5.3p9" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/n129ikd89z3didy0p2xw8hqgfaphyv11-bash-interactive-5.3p9", + "default": true + }, + { + "name": "man", + "path": "/nix/store/2m52h1lgaahqz6fag0aqw1499fjzq473-bash-interactive-5.3p9-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/7px2mpd1qj0106g64qp411p2y5cwlzbz-bash-interactive-5.3p9-dev" + }, + { + "name": "doc", + "path": "/nix/store/s9wwkzamvd36hwz94661rzg0s8bs86bc-bash-interactive-5.3p9-doc" + }, + { + "name": "info", + "path": "/nix/store/z9v40pvapyx3qd6liy9q4v6iwncwapl5-bash-interactive-5.3p9-info" + } + ], + "store_path": "/nix/store/n129ikd89z3didy0p2xw8hqgfaphyv11-bash-interactive-5.3p9" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/x12lw455sq6qy2wcya85d7rb88ybc3df-bash-interactive-5.3p9", + "default": true + }, + { + "name": "man", + "path": "/nix/store/1yg24id0csjk4nq1a3apimwf8dqisr9d-bash-interactive-5.3p9-man", + "default": true + }, + { + "name": "debug", + "path": "/nix/store/sihl8njk3077kr0bh1fnagdxy83hbyfb-bash-interactive-5.3p9-debug" + }, + { + "name": "dev", + "path": "/nix/store/a1phwny3n394ij9j7csxa51lvb7nf45d-bash-interactive-5.3p9-dev" + }, + { + "name": "doc", + "path": "/nix/store/64qrsa2hiz1ayjv0m655cqwzx54hib9w-bash-interactive-5.3p9-doc" + }, + { + "name": "info", + "path": "/nix/store/by59bhs57xx4i2nh01bsjm3gdprgrby1-bash-interactive-5.3p9-info" + } + ], + "store_path": "/nix/store/x12lw455sq6qy2wcya85d7rb88ybc3df-bash-interactive-5.3p9" + } + } + }, "cocoapods@latest": { - "last_modified": "2025-12-31T03:27:36Z", - "resolved": "github:NixOS/nixpkgs/f665af0cdb70ed27e1bd8f9fdfecaf451260fc55#cocoapods", + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#cocoapods", "source": "devbox-search", "version": "1.16.2", "systems": { @@ -11,21 +183,249 @@ "outputs": [ { "name": "out", - "path": "/nix/store/av5g6hfp0yiir3iavg72js70ian8hxyf-cocoapods-1.16.2", + "path": "/nix/store/xmpbzlm4h97izn0nwf5r3flxa3hqiawa-cocoapods-1.16.2", + "default": true + } + ], + "store_path": "/nix/store/xmpbzlm4h97izn0nwf5r3flxa3hqiawa-cocoapods-1.16.2" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/kk994ybb09jk38n2k2sp5mifgka5mixg-cocoapods-1.16.2", + "default": true + } + ], + "store_path": "/nix/store/kk994ybb09jk38n2k2sp5mifgka5mixg-cocoapods-1.16.2" + } + } + }, + "coreutils@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#coreutils", + "source": "devbox-search", + "version": "9.9", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/hf6y9njhfwvigr25kzrwmsvmv6jpni5n-coreutils-9.9", + "default": true + }, + { + "name": "info", + "path": "/nix/store/6bf622yq75zzhq5mdn187sk70sxs6fkh-coreutils-9.9-info" + } + ], + "store_path": "/nix/store/hf6y9njhfwvigr25kzrwmsvmv6jpni5n-coreutils-9.9" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/5pwllqskykz2by85b87kp1a8af587vcn-coreutils-9.9", + "default": true + }, + { + "name": "debug", + "path": "/nix/store/q3bqmaf9gi315ghy600wsyamzmnahkhk-coreutils-9.9-debug" + }, + { + "name": "info", + "path": "/nix/store/fxvw68h1qhpydph2l8j9p4hhs48va1fc-coreutils-9.9-info" + } + ], + "store_path": "/nix/store/5pwllqskykz2by85b87kp1a8af587vcn-coreutils-9.9" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/qgdq763j9ddmj7aq45x3s5108qvq4z7q-coreutils-9.9", + "default": true + }, + { + "name": "info", + "path": "/nix/store/fvsrfwspi4w7kkn2wvmhjv0f9jrzwqja-coreutils-9.9-info" + } + ], + "store_path": "/nix/store/qgdq763j9ddmj7aq45x3s5108qvq4z7q-coreutils-9.9" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/i2vmgx46q9hd3z6rigaiman3wl3i2gc4-coreutils-9.9", + "default": true + }, + { + "name": "info", + "path": "/nix/store/7c3i7919ys6jk8qkccspz3bkc1lv82d9-coreutils-9.9-info" + }, + { + "name": "debug", + "path": "/nix/store/fzcbb02abzvmyrvpa360abfbspnz9l1j-coreutils-9.9-debug" + } + ], + "store_path": "/nix/store/i2vmgx46q9hd3z6rigaiman3wl3i2gc4-coreutils-9.9" + } + } + }, + "gawk@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#gawk", + "source": "devbox-search", + "version": "5.3.2", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/78jn4adsl7zf2padciq7bfq15qykn5wf-gawk-5.3.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/1gj15nimzw33ik7l2cqs2ry52yxgiq2h-gawk-5.3.2-man", + "default": true + }, + { + "name": "info", + "path": "/nix/store/0a3x2fkdzkbkkqz4myjsr6r19n3mgiz4-gawk-5.3.2-info" + } + ], + "store_path": "/nix/store/78jn4adsl7zf2padciq7bfq15qykn5wf-gawk-5.3.2" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/p1ff2bvyyb0vzskfkls91nvpf8g7zwcc-gawk-5.3.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/9aqrvrdpz4viqmdw1fy6f8855ixhvfaq-gawk-5.3.2-man", + "default": true + }, + { + "name": "info", + "path": "/nix/store/sxpvs7nxblvg5fis84w67rz80ygvrcgw-gawk-5.3.2-info" + } + ], + "store_path": "/nix/store/p1ff2bvyyb0vzskfkls91nvpf8g7zwcc-gawk-5.3.2" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/mknqzwppq332dqs43fgcgmgar24dgayq-gawk-5.3.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/45llzyqcsqrx45rjz1dghs891s3xbny6-gawk-5.3.2-man", + "default": true + }, + { + "name": "info", + "path": "/nix/store/nhjsr3i830m044qglqalkbkqh9g7bwaq-gawk-5.3.2-info" + } + ], + "store_path": "/nix/store/mknqzwppq332dqs43fgcgmgar24dgayq-gawk-5.3.2" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/2xq9rayckw8zq26k274xxlikn77jn60j-gawk-5.3.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/44gbnv9kk7cy5grvpwnjjapq3fxgsh4y-gawk-5.3.2-man", + "default": true + }, + { + "name": "info", + "path": "/nix/store/miv2z2631wsjpp2vhism5bc4ipch490r-gawk-5.3.2-info" + } + ], + "store_path": "/nix/store/2xq9rayckw8zq26k274xxlikn77jn60j-gawk-5.3.2" + } + } + }, + "git@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#git", + "source": "devbox-search", + "version": "2.52.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/vnhprisb777byfjpp5mdd0mxwkpvhbc0-git-2.52.0", + "default": true + }, + { + "name": "doc", + "path": "/nix/store/p0dx3053175fpr3kjf0fqgs9x6gm3dri-git-2.52.0-doc" + } + ], + "store_path": "/nix/store/vnhprisb777byfjpp5mdd0mxwkpvhbc0-git-2.52.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/sd4bjblxfljbm13mpl50x8g336gw2dri-git-2.52.0", "default": true + }, + { + "name": "debug", + "path": "/nix/store/cx033x4nmr12d9dcn8hxjfrajc3983f0-git-2.52.0-debug" + }, + { + "name": "doc", + "path": "/nix/store/ylsmgdrnp78p8hn7n9lxpada78dka41v-git-2.52.0-doc" } ], - "store_path": "/nix/store/av5g6hfp0yiir3iavg72js70ian8hxyf-cocoapods-1.16.2" + "store_path": "/nix/store/sd4bjblxfljbm13mpl50x8g336gw2dri-git-2.52.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/har71589bwmh6h6skisd20b3c6lrwmz7-cocoapods-1.16.2", + "path": "/nix/store/sr42bfqa0pc2ysba678mrm49g78jdynp-git-2.52.0", + "default": true + }, + { + "name": "doc", + "path": "/nix/store/fky0ci2bhwgyh9klg5682pmqswqg7wk4-git-2.52.0-doc" + } + ], + "store_path": "/nix/store/sr42bfqa0pc2ysba678mrm49g78jdynp-git-2.52.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/ipwndaag56mm8g8gn8j98z0jvn8x4mk1-git-2.52.0", "default": true + }, + { + "name": "debug", + "path": "/nix/store/zd8di23fmzjwrhb5ij1bjnlfqkx9j7d6-git-2.52.0-debug" + }, + { + "name": "doc", + "path": "/nix/store/d8kc4kqzcrbcj92msxrpdsdjglh2q5gp-git-2.52.0-doc" } ], - "store_path": "/nix/store/har71589bwmh6h6skisd20b3c6lrwmz7-cocoapods-1.16.2" + "store_path": "/nix/store/ipwndaag56mm8g8gn8j98z0jvn8x4mk1-git-2.52.0" } } }, @@ -33,52 +433,180 @@ "last_modified": "2026-01-27T15:18:14Z", "resolved": "github:NixOS/nixpkgs/afce96367b2e37fc29afb5543573cd49db3357b7?lastModified=1769527094" }, - "gradle@latest": { - "last_modified": "2025-12-31T03:27:36Z", + "gnugrep@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#gnugrep", + "source": "devbox-search", + "version": "3.12", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/vh7z511kn1g6c4j4rrj2fgxnjsbny5mw-gnugrep-3.12", + "default": true + }, + { + "name": "info", + "path": "/nix/store/i484zygrqw554k0ddswv6k7lkn7i3za1-gnugrep-3.12-info" + } + ], + "store_path": "/nix/store/vh7z511kn1g6c4j4rrj2fgxnjsbny5mw-gnugrep-3.12" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/r21ffchrdgccar73w3skkah0aj15mj2b-gnugrep-3.12", + "default": true + }, + { + "name": "info", + "path": "/nix/store/2lj0xvg84lzn5bvap89grjzvrgx43kz9-gnugrep-3.12-info" + } + ], + "store_path": "/nix/store/r21ffchrdgccar73w3skkah0aj15mj2b-gnugrep-3.12" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/jblpqghdm8kbsk6lm2szsfz6qqy3ldfd-gnugrep-3.12", + "default": true + }, + { + "name": "info", + "path": "/nix/store/zvgc176fjzpnyhlp6y0f7pd1jl6zvv91-gnugrep-3.12-info" + } + ], + "store_path": "/nix/store/jblpqghdm8kbsk6lm2szsfz6qqy3ldfd-gnugrep-3.12" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/02vv0r262agf9j5n2y1gmbjvdf12zkl0-gnugrep-3.12", + "default": true + }, + { + "name": "info", + "path": "/nix/store/cgk1j37lw5vw7lxdlzhdhji3fii5b5id-gnugrep-3.12-info" + } + ], + "store_path": "/nix/store/02vv0r262agf9j5n2y1gmbjvdf12zkl0-gnugrep-3.12" + } + } + }, + "gnused@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#gnused", + "source": "devbox-search", + "version": "4.9", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/bdhywjmavg1hw3515cxsh8vxx8p42ixw-gnused-4.9", + "default": true + }, + { + "name": "info", + "path": "/nix/store/2q7ry9sap952dh21xda36g2gx44m9jvl-gnused-4.9-info" + } + ], + "store_path": "/nix/store/bdhywjmavg1hw3515cxsh8vxx8p42ixw-gnused-4.9" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/wl7cvwbv3p0ag201jry906ydpij3a9ij-gnused-4.9", + "default": true + }, + { + "name": "info", + "path": "/nix/store/srvxijm4zzcqp6krhxk8qhfcr52mh39a-gnused-4.9-info" + } + ], + "store_path": "/nix/store/wl7cvwbv3p0ag201jry906ydpij3a9ij-gnused-4.9" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/b882ldrvxjjkc130pqy53948y09hci8m-gnused-4.9", + "default": true + }, + { + "name": "info", + "path": "/nix/store/12wwcnqr14xnbjs9mf6l5igpqc167y61-gnused-4.9-info" + } + ], + "store_path": "/nix/store/b882ldrvxjjkc130pqy53948y09hci8m-gnused-4.9" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/ryz8kcrm2bxpccllfqlb7qldsfnqp5c2-gnused-4.9", + "default": true + }, + { + "name": "info", + "path": "/nix/store/rr44gnjkn0j0h67blxaf7c69w6y5xv03-gnused-4.9-info" + } + ], + "store_path": "/nix/store/ryz8kcrm2bxpccllfqlb7qldsfnqp5c2-gnused-4.9" + } + } + }, + "gradle@8.0.1": { + "last_modified": "2023-07-24T21:56:31Z", "plugin_version": "0.0.1", - "resolved": "github:NixOS/nixpkgs/f665af0cdb70ed27e1bd8f9fdfecaf451260fc55#gradle", + "resolved": "github:NixOS/nixpkgs/dfcffbd74fd6f0419370d8240e445252a39f4d10#gradle", "source": "devbox-search", - "version": "8.14.3", + "version": "8.0.1", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/v2xbkgrvn0b4g4qq7j5x60va0d4gf0kw-gradle-8.14.3", + "path": "/nix/store/lrh7pjhni25c21llri3v1ya6n3nylng4-gradle-8.0.1", "default": true } ], - "store_path": "/nix/store/v2xbkgrvn0b4g4qq7j5x60va0d4gf0kw-gradle-8.14.3" + "store_path": "/nix/store/lrh7pjhni25c21llri3v1ya6n3nylng4-gradle-8.0.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/mvs8d5c60yyx3sxpppg1r67yjvlrrhhh-gradle-8.14.3", + "path": "/nix/store/5h0z90r6r8c4s9x72ik0jp6sf782h4j0-gradle-8.0.1", "default": true } ], - "store_path": "/nix/store/mvs8d5c60yyx3sxpppg1r67yjvlrrhhh-gradle-8.14.3" + "store_path": "/nix/store/5h0z90r6r8c4s9x72ik0jp6sf782h4j0-gradle-8.0.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/jy3fpkvcymjaglzi9z2gbpzcyqypgfxh-gradle-8.14.3", + "path": "/nix/store/7k8494nxy4x2sh8d0rzpmmh6pmk1q1wf-gradle-8.0.1", "default": true } ], - "store_path": "/nix/store/jy3fpkvcymjaglzi9z2gbpzcyqypgfxh-gradle-8.14.3" + "store_path": "/nix/store/7k8494nxy4x2sh8d0rzpmmh6pmk1q1wf-gradle-8.0.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/80fgl9ffaxlxl9s4i1jb37krcljx669g-gradle-8.14.3", + "path": "/nix/store/83jn6p607rjb784jvypvx5r30pgq7kwa-gradle-8.0.1", "default": true } ], - "store_path": "/nix/store/80fgl9ffaxlxl9s4i1jb37krcljx669g-gradle-8.14.3" + "store_path": "/nix/store/83jn6p607rjb784jvypvx5r30pgq7kwa-gradle-8.0.1" } } }, @@ -111,8 +639,8 @@ } }, "jq@latest": { - "last_modified": "2026-01-12T00:44:08Z", - "resolved": "github:NixOS/nixpkgs/3fbab70c6e69c87ea2b6e48aa6629da2aa6a23b0#jq", + "last_modified": "2026-01-26T13:12:53Z", + "resolved": "github:NixOS/nixpkgs/13b0f9e6ac78abbbb736c635d87845c4f4bee51b#jq", "source": "devbox-search", "version": "1.8.1", "systems": { @@ -120,115 +648,115 @@ "outputs": [ { "name": "bin", - "path": "/nix/store/9rm6fm3zq1jq8rgsx528cw8wkmfya2gf-jq-1.8.1-bin", + "path": "/nix/store/qjs0qndyz1g97rsc1zp4cd692y5iph64-jq-1.8.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/cv999saj62xhq7xv5i7q6944vljykfmw-jq-1.8.1-man", + "path": "/nix/store/jlpyybc7pdh4gk17dc266d6a1szm7dk6-jq-1.8.1-man", "default": true }, { "name": "dev", - "path": "/nix/store/5camppj4hz2mgkdbxs0kr6nvh6qa65wf-jq-1.8.1-dev" + "path": "/nix/store/bhryp10d5w5h9rsav5k9m9jb55z26bsl-jq-1.8.1-dev" }, { "name": "doc", - "path": "/nix/store/lak094rhhxlaj1qycadmxyfphgjadj5r-jq-1.8.1-doc" + "path": "/nix/store/238sn0gg3i3i9v6kgx4g1k6b19frzy49-jq-1.8.1-doc" }, { "name": "out", - "path": "/nix/store/g371yvjasdr552v98p5kav7n35s1dfib-jq-1.8.1" + "path": "/nix/store/n64h0247s3674kry90l6kszx06zyrgfn-jq-1.8.1" } ], - "store_path": "/nix/store/9rm6fm3zq1jq8rgsx528cw8wkmfya2gf-jq-1.8.1-bin" + "store_path": "/nix/store/qjs0qndyz1g97rsc1zp4cd692y5iph64-jq-1.8.1-bin" }, "aarch64-linux": { "outputs": [ { "name": "bin", - "path": "/nix/store/m8qv4g54q3jmjb8i33v9lljcwhydx2vd-jq-1.8.1-bin", + "path": "/nix/store/c1qm5fsn6qbl09xdjx649vifabypyywd-jq-1.8.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/9x2457g76jikfy7xq4mjqwzl8iz3zvxj-jq-1.8.1-man", + "path": "/nix/store/2pjwv0ab8nilrg1lvjazf9y9w6g6pk5y-jq-1.8.1-man", "default": true }, { "name": "dev", - "path": "/nix/store/5ykn83b3hhvnnq0p5vqgcrzihrl9wpsl-jq-1.8.1-dev" + "path": "/nix/store/2sy4y09ddbi64pbg4is078110z70jsdw-jq-1.8.1-dev" }, { "name": "doc", - "path": "/nix/store/37ypy1595g6rj3cymh1mpk2b25fx40g7-jq-1.8.1-doc" + "path": "/nix/store/vx81xggapqwdd2l64mmxrkbafih461jc-jq-1.8.1-doc" }, { "name": "out", - "path": "/nix/store/16lg603jzppwjanlakcak1ais69mkd03-jq-1.8.1" + "path": "/nix/store/cfhajjz1k7gf31krbj18q9acb54xp5z9-jq-1.8.1" } ], - "store_path": "/nix/store/m8qv4g54q3jmjb8i33v9lljcwhydx2vd-jq-1.8.1-bin" + "store_path": "/nix/store/c1qm5fsn6qbl09xdjx649vifabypyywd-jq-1.8.1-bin" }, "x86_64-darwin": { "outputs": [ { "name": "bin", - "path": "/nix/store/kkb17whpkdrmn9g3gk7y6l69vipxsw0i-jq-1.8.1-bin", + "path": "/nix/store/51343hgchh7by4l8r1g244ma05ny3x0b-jq-1.8.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/iwr61wi83kflqvz8j5nf7ridaqq6nh2w-jq-1.8.1-man", + "path": "/nix/store/vx840ik1sj1h8fhqwa40aglvrgpa0r18-jq-1.8.1-man", "default": true }, { - "name": "dev", - "path": "/nix/store/lypnqs272644l8ff6wfji9rg5jw10v7h-jq-1.8.1-dev" + "name": "out", + "path": "/nix/store/bmg2xfw86wavg7fm062nyf6v48xzxh0j-jq-1.8.1" }, { - "name": "doc", - "path": "/nix/store/nyw97c4pywfcqqap5hyk9xjghczlbshl-jq-1.8.1-doc" + "name": "dev", + "path": "/nix/store/pp2hrljvalrrwyxh7is69nnlmxb2m7lk-jq-1.8.1-dev" }, { - "name": "out", - "path": "/nix/store/ri930a557685c64bdh88a5031i7hx3vy-jq-1.8.1" + "name": "doc", + "path": "/nix/store/h05xaf7fsasgp8cpyar2195cc8lbgih8-jq-1.8.1-doc" } ], - "store_path": "/nix/store/kkb17whpkdrmn9g3gk7y6l69vipxsw0i-jq-1.8.1-bin" + "store_path": "/nix/store/51343hgchh7by4l8r1g244ma05ny3x0b-jq-1.8.1-bin" }, "x86_64-linux": { "outputs": [ { "name": "bin", - "path": "/nix/store/zssasryipb2x4gk2ahzacl4mvvcmk48j-jq-1.8.1-bin", + "path": "/nix/store/qnaw7i777j52fpgbl5pgmzkq85znp083-jq-1.8.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/7d4pv1iymyqk2lykwj1ydml3rjhc6gl3-jq-1.8.1-man", + "path": "/nix/store/6mh88qsh57ivh31c5nqxc43n0hv9xhbk-jq-1.8.1-man", "default": true }, { "name": "dev", - "path": "/nix/store/rmxxm5jnxq93kvkhbr2b3hzj6v3ldp8z-jq-1.8.1-dev" + "path": "/nix/store/d73i1fvhrqms0sbfrvqaynsr8iva216v-jq-1.8.1-dev" }, { "name": "doc", - "path": "/nix/store/mkhfvc69grlky3iblibkw9wcc12jcdqq-jq-1.8.1-doc" + "path": "/nix/store/3ynrp4ypwv1g1jgsk638443p8lpd9g8f-jq-1.8.1-doc" }, { "name": "out", - "path": "/nix/store/807g765zgpmp1c8fm5y40rw2gbr1k6dk-jq-1.8.1" + "path": "/nix/store/fgsvqffyvcpjqs093wwf2d6dzxnmnqnv-jq-1.8.1" } ], - "store_path": "/nix/store/zssasryipb2x4gk2ahzacl4mvvcmk48j-jq-1.8.1-bin" + "store_path": "/nix/store/qnaw7i777j52fpgbl5pgmzkq85znp083-jq-1.8.1-bin" } } }, "netcat@latest": { - "last_modified": "2026-01-20T02:11:35Z", - "resolved": "github:NixOS/nixpkgs/ed142ab1b3a092c4d149245d0c4126a5d7ea00b0#netcat", + "last_modified": "2026-01-24T15:20:28Z", + "resolved": "github:NixOS/nixpkgs/ab9fbbcf4858bd6d40ba2bbec37ceb4ab6e1f562#netcat", "source": "devbox-search", "version": "4.2.1", "systems": { @@ -236,119 +764,119 @@ "outputs": [ { "name": "nc", - "path": "/nix/store/y2i4f3bwmgpxw4m6dl99dz9d7zp5axz2-libressl-4.2.1-nc", + "path": "/nix/store/gvknval1kw4gcwiihh61zck3qiz6qa5c-libressl-4.2.1-nc", "default": true }, { "name": "bin", - "path": "/nix/store/ycvwxl29jb6ajjzgkq2jgy1nqpahq5k4-libressl-4.2.1-bin", + "path": "/nix/store/27px9spniwk94lzkh3vbs0sxsbszlb55-libressl-4.2.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/2b61bckfnaiw3n5ppjg70avgd9rn60bp-libressl-4.2.1-man", + "path": "/nix/store/65l6sf3drz0j5zrjllwbf86lbpnh3206-libressl-4.2.1-man", "default": true }, { - "name": "out", - "path": "/nix/store/fm6zdqw6856i2snd4fikcsi9d1qagj5j-libressl-4.2.1" + "name": "dev", + "path": "/nix/store/9l41sz77w2am5svwv9bzkj3r77l7faa4-libressl-4.2.1-dev" }, { - "name": "dev", - "path": "/nix/store/bz5h2baa7bkpz8sgjc9ld8fr7cg5wapg-libressl-4.2.1-dev" + "name": "out", + "path": "/nix/store/209fqqlykqgvbmhqkxns8hxf661kvggr-libressl-4.2.1" } ], - "store_path": "/nix/store/y2i4f3bwmgpxw4m6dl99dz9d7zp5axz2-libressl-4.2.1-nc" + "store_path": "/nix/store/gvknval1kw4gcwiihh61zck3qiz6qa5c-libressl-4.2.1-nc" }, "aarch64-linux": { "outputs": [ { "name": "nc", - "path": "/nix/store/0dzxkwilv9lgd7j0429s2rmshy7p8gw7-libressl-4.2.1-nc", + "path": "/nix/store/5i8jpl10mgy8yy14ab3wcw96dknhcd52-libressl-4.2.1-nc", "default": true }, { "name": "bin", - "path": "/nix/store/5ama6wp3yi03hbixdcm5jy2ya9ikvzjz-libressl-4.2.1-bin", + "path": "/nix/store/xzmdycii4k6rxcb3pf6b7c6r0m8q6giq-libressl-4.2.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/vr4wlcvj5ba6wdmhj2aidalzqk33lrph-libressl-4.2.1-man", + "path": "/nix/store/nmngfbi6bxxcif5x450fqkixjrf9ajjz-libressl-4.2.1-man", "default": true }, { - "name": "dev", - "path": "/nix/store/hs5c0yvhf7n60xijr7bmpi592n45pb6i-libressl-4.2.1-dev" + "name": "out", + "path": "/nix/store/dmingcfvh3bqh1lz9f0b4wz0lyqg22p6-libressl-4.2.1" }, { - "name": "out", - "path": "/nix/store/fiy9987ph2kqr5drlr136w7hvm3v6rvg-libressl-4.2.1" + "name": "dev", + "path": "/nix/store/8jdkrf3bpb3v4pv6knnkfz9yny3pmsyp-libressl-4.2.1-dev" } ], - "store_path": "/nix/store/0dzxkwilv9lgd7j0429s2rmshy7p8gw7-libressl-4.2.1-nc" + "store_path": "/nix/store/5i8jpl10mgy8yy14ab3wcw96dknhcd52-libressl-4.2.1-nc" }, "x86_64-darwin": { "outputs": [ { "name": "nc", - "path": "/nix/store/vmrm6nr9hfhw7x8ln3ms98gszq709bfa-libressl-4.2.1-nc", + "path": "/nix/store/73gpj7z4j7xqvjbbsv3c9pnh37pxaysp-libressl-4.2.1-nc", "default": true }, { "name": "bin", - "path": "/nix/store/7kafgvpwc6s8pnzar90bd8017wc3cnvx-libressl-4.2.1-bin", + "path": "/nix/store/c41c038h81hma6y17xzm4ih70ayz0jw1-libressl-4.2.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/jwdgv2x1a2a84v2jkj964xvm2s0kymps-libressl-4.2.1-man", + "path": "/nix/store/dv4bd93ia2sd2ppydggh7s5jvng7w2ln-libressl-4.2.1-man", "default": true }, { "name": "dev", - "path": "/nix/store/sjiz0iphbc7dvddlrbrk8k7kkqi3swz3-libressl-4.2.1-dev" + "path": "/nix/store/a98pxfnpxmk8hdfxcfyhvd4lrj91s7zj-libressl-4.2.1-dev" }, { "name": "out", - "path": "/nix/store/rn10w2jbdkz3f7p1q1fl6aml9b0352ki-libressl-4.2.1" + "path": "/nix/store/klv7jfk255vbrs29znh3r88zb7fy7ywn-libressl-4.2.1" } ], - "store_path": "/nix/store/vmrm6nr9hfhw7x8ln3ms98gszq709bfa-libressl-4.2.1-nc" + "store_path": "/nix/store/73gpj7z4j7xqvjbbsv3c9pnh37pxaysp-libressl-4.2.1-nc" }, "x86_64-linux": { "outputs": [ { "name": "nc", - "path": "/nix/store/54hijwy3gpc728s3468rv3sdw78ksakh-libressl-4.2.1-nc", + "path": "/nix/store/n17al8mklxgzp888406yyfi2wqf49bam-libressl-4.2.1-nc", "default": true }, { "name": "bin", - "path": "/nix/store/l5vvbs0vl734mshifahals0054pimlx4-libressl-4.2.1-bin", + "path": "/nix/store/jhg0gcahxc6yqc156fc1hkqi0ps6f705-libressl-4.2.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/j26qyc85gk95bk06dq0fi0c9q8y7livx-libressl-4.2.1-man", + "path": "/nix/store/a4slaj05ph1m0vdb9vsw16mc24za8n4d-libressl-4.2.1-man", "default": true }, { - "name": "out", - "path": "/nix/store/4f21prki98shrvp29r88pnxhzw2y4qr2-libressl-4.2.1" + "name": "dev", + "path": "/nix/store/la1ilprnsj96dlaara2nk45lcd29f0as-libressl-4.2.1-dev" }, { - "name": "dev", - "path": "/nix/store/0dndzgjzx25y7v1942c2rys20narddp8-libressl-4.2.1-dev" + "name": "out", + "path": "/nix/store/x03afgp2cbxb6xmxs3i716wfq7zbgfdn-libressl-4.2.1" } ], - "store_path": "/nix/store/54hijwy3gpc728s3468rv3sdw78ksakh-libressl-4.2.1-nc" + "store_path": "/nix/store/n17al8mklxgzp888406yyfi2wqf49bam-libressl-4.2.1-nc" } } }, "nixfmt@latest": { - "last_modified": "2026-01-09T13:41:53Z", - "resolved": "github:NixOS/nixpkgs/5f02c91314c8ba4afe83b256b023756412218535#nixfmt", + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#nixfmt", "source": "devbox-search", "version": "1.2.0", "systems": { @@ -356,47 +884,128 @@ "outputs": [ { "name": "out", - "path": "/nix/store/4jzq73b6bax62245z5a5ag8xdazfw4fg-nixfmt-1.2.0", + "path": "/nix/store/lvb2z93xn3m0m2hw0w6cc0c3bsl2s8pp-nixfmt-1.2.0", "default": true } ], - "store_path": "/nix/store/4jzq73b6bax62245z5a5ag8xdazfw4fg-nixfmt-1.2.0" + "store_path": "/nix/store/lvb2z93xn3m0m2hw0w6cc0c3bsl2s8pp-nixfmt-1.2.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/as7f2yrlqgv130vdiw2xi7rhlgd1yk8v-nixfmt-1.2.0", + "path": "/nix/store/0qdx7ah7b1dwyxa03fblhcdjnpi29q25-nixfmt-1.2.0", "default": true } ], - "store_path": "/nix/store/as7f2yrlqgv130vdiw2xi7rhlgd1yk8v-nixfmt-1.2.0" + "store_path": "/nix/store/0qdx7ah7b1dwyxa03fblhcdjnpi29q25-nixfmt-1.2.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/qmas80hmzqbm7n5h9is2im9gjzxsl04a-nixfmt-1.2.0", + "path": "/nix/store/jicz2kx49mqif3hkl1m2wbwvx2lg457l-nixfmt-1.2.0", "default": true } ], - "store_path": "/nix/store/qmas80hmzqbm7n5h9is2im9gjzxsl04a-nixfmt-1.2.0" + "store_path": "/nix/store/jicz2kx49mqif3hkl1m2wbwvx2lg457l-nixfmt-1.2.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/yx338k689yp9hpnl6h5y22f7vbmi5pky-nixfmt-1.2.0", + "path": "/nix/store/b5m1h822ln0s493r30sgns77618ws59n-nixfmt-1.2.0", "default": true } ], - "store_path": "/nix/store/yx338k689yp9hpnl6h5y22f7vbmi5pky-nixfmt-1.2.0" + "store_path": "/nix/store/b5m1h822ln0s493r30sgns77618ws59n-nixfmt-1.2.0" + } + } + }, + "nodejs@latest": { + "last_modified": "2026-01-26T13:12:53Z", + "plugin_version": "0.0.2", + "resolved": "github:NixOS/nixpkgs/13b0f9e6ac78abbbb736c635d87845c4f4bee51b#nodejs_25", + "source": "devbox-search", + "version": "25.4.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/m5qq262d56ar77iggvla6d5swn8yw9y6-nodejs-25.4.0", + "default": true + }, + { + "name": "libv8", + "path": "/nix/store/3pkkak278knyi93bpv7f67r8ymaa75kl-nodejs-25.4.0-libv8" + }, + { + "name": "dev", + "path": "/nix/store/v63af7yli7i3caybdgv16fxgl2y5iz3k-nodejs-25.4.0-dev" + } + ], + "store_path": "/nix/store/m5qq262d56ar77iggvla6d5swn8yw9y6-nodejs-25.4.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/16jjxswcd6kr8391f7pa2vg0vhvwd74g-nodejs-25.4.0", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/nqxds9wybn5npisxpbhsyhbxmbhpayws-nodejs-25.4.0-dev" + }, + { + "name": "libv8", + "path": "/nix/store/rbdpra61acpg4aqs28wl1i4pvxybifsk-nodejs-25.4.0-libv8" + } + ], + "store_path": "/nix/store/16jjxswcd6kr8391f7pa2vg0vhvwd74g-nodejs-25.4.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/pig8ld1dg0pk15w38h1dy60j0fd909qm-nodejs-25.4.0", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/hqc95i644v03m54kra36jk1i962n1cyv-nodejs-25.4.0-dev" + }, + { + "name": "libv8", + "path": "/nix/store/qi6pchsq09am76hy7wb0sb15575gzmra-nodejs-25.4.0-libv8" + } + ], + "store_path": "/nix/store/pig8ld1dg0pk15w38h1dy60j0fd909qm-nodejs-25.4.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/pgcjcir9hkx39zfiya8hmrm7wxzb3sn5-nodejs-25.4.0", + "default": true + }, + { + "name": "libv8", + "path": "/nix/store/xc40d8b44b12ibi7ihczllczdymdvi7w-nodejs-25.4.0-libv8" + }, + { + "name": "dev", + "path": "/nix/store/s431j6qb3xhh1ws5hrc2c0lzjq0lh1k5-nodejs-25.4.0-dev" + } + ], + "store_path": "/nix/store/pgcjcir9hkx39zfiya8hmrm7wxzb3sn5-nodejs-25.4.0" } } }, "shfmt@latest": { - "last_modified": "2025-12-31T03:27:36Z", - "resolved": "github:NixOS/nixpkgs/f665af0cdb70ed27e1bd8f9fdfecaf451260fc55#shfmt", + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#shfmt", "source": "devbox-search", "version": "3.12.0", "systems": { @@ -404,47 +1013,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/dwz5z2wp95pkv6gsmz74w01qrihvhl1h-shfmt-3.12.0", + "path": "/nix/store/5ywb1qkbd829kazxxkcfxb356m7hljjw-shfmt-3.12.0", "default": true } ], - "store_path": "/nix/store/dwz5z2wp95pkv6gsmz74w01qrihvhl1h-shfmt-3.12.0" + "store_path": "/nix/store/5ywb1qkbd829kazxxkcfxb356m7hljjw-shfmt-3.12.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/6gqqharpvjfzm1wzny6a0zgf1v0aj53a-shfmt-3.12.0", + "path": "/nix/store/bii41pz9k7xf3z3f80s1ip29l42488ry-shfmt-3.12.0", "default": true } ], - "store_path": "/nix/store/6gqqharpvjfzm1wzny6a0zgf1v0aj53a-shfmt-3.12.0" + "store_path": "/nix/store/bii41pz9k7xf3z3f80s1ip29l42488ry-shfmt-3.12.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/98fwma9rxd04pxj9jrsgvf9xs76f9hlf-shfmt-3.12.0", + "path": "/nix/store/a8l3l8rdzs3agq7garh59m0xn8h3zgpq-shfmt-3.12.0", "default": true } ], - "store_path": "/nix/store/98fwma9rxd04pxj9jrsgvf9xs76f9hlf-shfmt-3.12.0" + "store_path": "/nix/store/a8l3l8rdzs3agq7garh59m0xn8h3zgpq-shfmt-3.12.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/c3bgrcwq2735ybl5zw68n9nqgwaa0yrj-shfmt-3.12.0", + "path": "/nix/store/r3pj36ll7v3ss2z679arpwkg7pnka3s3-shfmt-3.12.0", "default": true } ], - "store_path": "/nix/store/c3bgrcwq2735ybl5zw68n9nqgwaa0yrj-shfmt-3.12.0" + "store_path": "/nix/store/r3pj36ll7v3ss2z679arpwkg7pnka3s3-shfmt-3.12.0" } } }, "treefmt@latest": { - "last_modified": "2025-12-31T03:27:36Z", - "resolved": "github:NixOS/nixpkgs/f665af0cdb70ed27e1bd8f9fdfecaf451260fc55#treefmt", + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#treefmt", "source": "devbox-search", "version": "2.4.0", "systems": { @@ -452,47 +1061,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/48r10kj61pjhz8alfscs8vgrdmlfnqx9-treefmt-2.4.0", + "path": "/nix/store/37qcrdrfdf8b9pyli0j0vj9d69dykmpr-treefmt-2.4.0", "default": true } ], - "store_path": "/nix/store/48r10kj61pjhz8alfscs8vgrdmlfnqx9-treefmt-2.4.0" + "store_path": "/nix/store/37qcrdrfdf8b9pyli0j0vj9d69dykmpr-treefmt-2.4.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/d46fscf82k9ay6s8a3qxk3682ycqalds-treefmt-2.4.0", + "path": "/nix/store/ccv7dxwff3xpivsdhrjbq3mpk0br5v7d-treefmt-2.4.0", "default": true } ], - "store_path": "/nix/store/d46fscf82k9ay6s8a3qxk3682ycqalds-treefmt-2.4.0" + "store_path": "/nix/store/ccv7dxwff3xpivsdhrjbq3mpk0br5v7d-treefmt-2.4.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/yllw5b2js10ia8v0z8x3crbakhal0cs7-treefmt-2.4.0", + "path": "/nix/store/sbq2psv4q9hvfvalk00g0pkqjl2912m1-treefmt-2.4.0", "default": true } ], - "store_path": "/nix/store/yllw5b2js10ia8v0z8x3crbakhal0cs7-treefmt-2.4.0" + "store_path": "/nix/store/sbq2psv4q9hvfvalk00g0pkqjl2912m1-treefmt-2.4.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/1pdwlp9d1rrm3xp4s5rhhk7mkmx9cmv2-treefmt-2.4.0", + "path": "/nix/store/z8xrjfswqqpydc42lyas064pp8kxgnl0-treefmt-2.4.0", "default": true } ], - "store_path": "/nix/store/1pdwlp9d1rrm3xp4s5rhhk7mkmx9cmv2-treefmt-2.4.0" + "store_path": "/nix/store/z8xrjfswqqpydc42lyas064pp8kxgnl0-treefmt-2.4.0" } } }, "yarn-berry@latest": { - "last_modified": "2025-12-31T03:27:36Z", - "resolved": "github:NixOS/nixpkgs/f665af0cdb70ed27e1bd8f9fdfecaf451260fc55#yarn-berry", + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#yarn-berry", "source": "devbox-search", "version": "4.12.0", "systems": { @@ -500,41 +1109,41 @@ "outputs": [ { "name": "out", - "path": "/nix/store/2l7sbyyqardvrzr35zkrw67gbng5gb8y-yarn-berry-4.12.0", + "path": "/nix/store/k9bh72bpyqjnfq1nd3c6p1z2ijkx2yg6-yarn-berry-4.12.0", "default": true } ], - "store_path": "/nix/store/2l7sbyyqardvrzr35zkrw67gbng5gb8y-yarn-berry-4.12.0" + "store_path": "/nix/store/k9bh72bpyqjnfq1nd3c6p1z2ijkx2yg6-yarn-berry-4.12.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/klx9ndw1djgx0zhhyrkcn9an094rmmwv-yarn-berry-4.12.0", + "path": "/nix/store/mcvbqzb1kir87g5pm5624c5ysnfh89wp-yarn-berry-4.12.0", "default": true } ], - "store_path": "/nix/store/klx9ndw1djgx0zhhyrkcn9an094rmmwv-yarn-berry-4.12.0" + "store_path": "/nix/store/mcvbqzb1kir87g5pm5624c5ysnfh89wp-yarn-berry-4.12.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/m6cwiya6hrbwnlprh2cbnmz6c7mkylrf-yarn-berry-4.12.0", + "path": "/nix/store/hn3j954rlcc0gx8nm41sgfzbx0qwfq3c-yarn-berry-4.12.0", "default": true } ], - "store_path": "/nix/store/m6cwiya6hrbwnlprh2cbnmz6c7mkylrf-yarn-berry-4.12.0" + "store_path": "/nix/store/hn3j954rlcc0gx8nm41sgfzbx0qwfq3c-yarn-berry-4.12.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/q1gys3zgijcciiafbh9nfawkx5wj8179-yarn-berry-4.12.0", + "path": "/nix/store/2pmvaaggpi7ikx9xmhy3x2j0rpklaqrv-yarn-berry-4.12.0", "default": true } ], - "store_path": "/nix/store/q1gys3zgijcciiafbh9nfawkx5wj8179-yarn-berry-4.12.0" + "store_path": "/nix/store/2pmvaaggpi7ikx9xmhy3x2j0rpklaqrv-yarn-berry-4.12.0" } } } diff --git a/examples/AnalyticsReactNativeExample/.gitignore b/examples/AnalyticsReactNativeExample/.gitignore index 0cab2ac6f..f6ca9014e 100644 --- a/examples/AnalyticsReactNativeExample/.gitignore +++ b/examples/AnalyticsReactNativeExample/.gitignore @@ -21,6 +21,7 @@ DerivedData *.ipa *.xcuserstate ios/.xcode.env.local +ios/.xcode.env # Android/IntelliJ # diff --git a/examples/E2E-73/.detoxrc.js b/examples/E2E-73/.detoxrc.js index 1a19352a6..5536ce204 100644 --- a/examples/E2E-73/.detoxrc.js +++ b/examples/E2E-73/.detoxrc.js @@ -1,3 +1,19 @@ +const {execSync} = require('child_process'); + +const resolveGradleCmd = () => { + if (process.env.GRADLE_CMD) { + return process.env.GRADLE_CMD; + } + try { + execSync('command -v gradle', {stdio: 'ignore'}); + return 'gradle'; + } catch (_) { + return './gradlew'; + } +}; + +const gradleCmd = resolveGradleCmd(); + /** @type {Detox.DetoxConfig} */ module.exports = { testRunner: { @@ -41,14 +57,14 @@ module.exports = { type: 'android.apk', binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk', build: - 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug', + `cd android && ${gradleCmd} assembleDebug assembleAndroidTest -DtestBuildType=debug`, reversePorts: [8081], }, 'android.release': { type: 'android.apk', binaryPath: 'android/app/build/outputs/apk/release/app-release.apk', build: - 'cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release', + `cd android && ${gradleCmd} assembleRelease assembleAndroidTest -DtestBuildType=release`, }, }, devices: { diff --git a/examples/E2E-73/.gitignore b/examples/E2E-73/.gitignore index 0cab2ac6f..f6ca9014e 100644 --- a/examples/E2E-73/.gitignore +++ b/examples/E2E-73/.gitignore @@ -21,6 +21,7 @@ DerivedData *.ipa *.xcuserstate ios/.xcode.env.local +ios/.xcode.env # Android/IntelliJ # diff --git a/examples/E2E-73/ios/AnalyticsReactNativeE2E73.xcodeproj/project.pbxproj b/examples/E2E-73/ios/AnalyticsReactNativeE2E73.xcodeproj/project.pbxproj index 974b8d041..ccf2855be 100644 --- a/examples/E2E-73/ios/AnalyticsReactNativeE2E73.xcodeproj/project.pbxproj +++ b/examples/E2E-73/ios/AnalyticsReactNativeE2E73.xcodeproj/project.pbxproj @@ -582,8 +582,6 @@ ); OTHER_LDFLAGS = ( "$(inherited)", - "-Wl", - "-ld_classic", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; @@ -654,8 +652,6 @@ ); OTHER_LDFLAGS = ( "$(inherited)", - "-Wl", - "-ld_classic", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; diff --git a/examples/E2E/.detoxrc.js b/examples/E2E/.detoxrc.js index f944e52b0..7544da841 100644 --- a/examples/E2E/.detoxrc.js +++ b/examples/E2E/.detoxrc.js @@ -18,6 +18,20 @@ const safeParseJSON = cmd => { } }; +const resolveGradleCmd = () => { + if (process.env.GRADLE_CMD) { + return process.env.GRADLE_CMD; + } + try { + execSync('command -v gradle', {stdio: 'ignore'}); + return 'gradle'; + } catch (_) { + return './gradlew'; + } +}; + +const gradleCmd = resolveGradleCmd(); + const listAvailableDevices = () => { const parsed = safeParseJSON('xcrun simctl list devices -j'); if (!parsed || !parsed.devices) return []; @@ -119,14 +133,14 @@ module.exports = { type: 'android.apk', binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk', build: - 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug', + `cd android && ${gradleCmd} assembleDebug assembleAndroidTest -DtestBuildType=debug`, reversePorts: [8081], }, 'android.release': { type: 'android.apk', binaryPath: 'android/app/build/outputs/apk/release/app-release.apk', build: - 'cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release', + `cd android && ${gradleCmd} assembleRelease assembleAndroidTest -DtestBuildType=release`, }, }, devices: { diff --git a/examples/E2E/.gitignore b/examples/E2E/.gitignore index 0cab2ac6f..f6ca9014e 100644 --- a/examples/E2E/.gitignore +++ b/examples/E2E/.gitignore @@ -21,6 +21,7 @@ DerivedData *.ipa *.xcuserstate ios/.xcode.env.local +ios/.xcode.env # Android/IntelliJ # diff --git a/examples/E2E/ios/.xcode.env b/examples/E2E/ios/.xcode.env deleted file mode 100644 index 3d5782c71..000000000 --- a/examples/E2E/ios/.xcode.env +++ /dev/null @@ -1,11 +0,0 @@ -# This `.xcode.env` file is versioned and is used to source the environment -# used when running script phases inside Xcode. -# To customize your local environment, you can create an `.xcode.env.local` -# file that is not versioned. - -# NODE_BINARY variable contains the PATH to the node executable. -# -# Customize the NODE_BINARY variable here. -# For example, to use nvm with brew, add the following line -# . "$(brew --prefix nvm)/nvm.sh" --no-use -export NODE_BINARY=$(command -v node) diff --git a/examples/E2E/ios/AnalyticsReactNativeE2E.xcodeproj/project.pbxproj b/examples/E2E/ios/AnalyticsReactNativeE2E.xcodeproj/project.pbxproj index 9c478de2d..7fe7b261e 100644 --- a/examples/E2E/ios/AnalyticsReactNativeE2E.xcodeproj/project.pbxproj +++ b/examples/E2E/ios/AnalyticsReactNativeE2E.xcodeproj/project.pbxproj @@ -433,6 +433,7 @@ baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-AnalyticsReactNativeE2E-AnalyticsReactNativeE2ETests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 x86_64"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", @@ -461,6 +462,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; COPY_PHASE_STRIP = NO; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 x86_64"; INFOPLIST_FILE = AnalyticsReactNativeE2ETests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.4; LD_RUNPATH_SEARCH_PATHS = ( @@ -487,6 +489,7 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; ENABLE_BITCODE = NO; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 x86_64"; INFOPLIST_FILE = AnalyticsReactNativeE2E/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -513,6 +516,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 x86_64"; INFOPLIST_FILE = AnalyticsReactNativeE2E/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/examples/E2E/ios/Podfile b/examples/E2E/ios/Podfile index e8c21a1c7..f1cee2744 100644 --- a/examples/E2E/ios/Podfile +++ b/examples/E2E/ios/Podfile @@ -35,6 +35,23 @@ target 'AnalyticsReactNativeE2E' do end post_install do |installer| + is_apple_silicon = `uname -m`.strip == 'arm64' + if is_apple_silicon + excluded_archs = 'i386 x86_64' + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = excluded_archs + end + end + installer.aggregate_targets.each do |aggregate_target| + aggregate_target.user_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = excluded_archs + end + end + end + end + installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.4' diff --git a/examples/E2E/ios/Podfile.lock b/examples/E2E/ios/Podfile.lock index 0039daf46..a81ffcdf8 100644 --- a/examples/E2E/ios/Podfile.lock +++ b/examples/E2E/ios/Podfile.lock @@ -652,6 +652,6 @@ SPEC CHECKSUMS: sovran-react-native: eec37f82e4429f0e3661f46aaf4fcd85d1b54f60 Yoga: eddf2bbe4a896454c248a8f23b4355891eb720a6 -PODFILE CHECKSUM: a4c187e503408b85ffe8c89a4cb726ec541057ce +PODFILE CHECKSUM: a235bed286d6dd128990b8d39ccd155b6acdc28b COCOAPODS: 1.16.2 diff --git a/nix/applesimutils.nix b/nix/applesimutils.nix new file mode 100644 index 000000000..4af2945be --- /dev/null +++ b/nix/applesimutils.nix @@ -0,0 +1,34 @@ +{ lib +, stdenv +, fetchurl +}: + +stdenv.mkDerivation rec { + pname = "applesimutils"; + version = "0.9.12"; + + src = fetchurl { + url = + if stdenv.hostPlatform.system == "aarch64-darwin" then + "https://github.com/wix/AppleSimulatorUtils/releases/download/${version}/applesimutils-${version}.arm64_big_sur.bottle.tar.gz" + else + "https://github.com/wix/AppleSimulatorUtils/releases/download/${version}/applesimutils-${version}.big_sur.bottle.tar.gz"; + sha256 = "0cy1w8zcifdns0r95jf74qqkzmmmn079c8habf37f7h5lrgdhwrk"; + }; + + dontBuild = true; + + installPhase = '' + runHook preInstall + mkdir -p $out/bin + cp -v "0.9.12/bin/applesimutils" $out/bin/ + runHook postInstall + ''; + + meta = with lib; { + description = "Apple Simulator Utils (applesimutils) command line tool"; + homepage = "https://github.com/wix/AppleSimulatorUtils"; + license = licenses.mit; + platforms = platforms.darwin; + }; +} diff --git a/nix/defaults.json b/nix/defaults.json new file mode 100644 index 000000000..b67112e15 --- /dev/null +++ b/nix/defaults.json @@ -0,0 +1,19 @@ +{ + "defaults": { + "ANDROID_LOCAL_SDK": "0", + "ANDROID_MIN_API": "21", + "ANDROID_MAX_API": "33", + "ANDROID_CUSTOM_API": "29", + "ANDROID_SYSTEM_IMAGE_TAG": "google_apis", + "ANDROID_BUILD_TOOLS_VERSION": "30.0.3", + "ANDROID_CMDLINE_TOOLS_VERSION": "19.0", + "ANDROID_MIN_DEVICE": "pixel", + "ANDROID_MAX_DEVICE": "medium_phone", + "ANALYTICS_CI_DEBUG": "0", + "DEBUG": "0", + "IOS_RUNTIME_MIN": "15.4", + "IOS_RUNTIME_MAX": "26.2", + "IOS_MIN_DEVICE": "iPhone 13", + "IOS_MAX_DEVICE": "iPhone 17" + } +} diff --git a/nix/flake.nix b/nix/flake.nix index 06ff87380..95a02a329 100644 --- a/nix/flake.nix +++ b/nix/flake.nix @@ -13,19 +13,40 @@ "aarch64-darwin" ]; - versionData = builtins.fromJSON (builtins.readFile ./platform-versions.json); + versionData = builtins.fromJSON (builtins.readFile ./defaults.json); + defaultsData = if builtins.hasAttr "defaults" versionData then versionData.defaults else versionData; getVar = - name: default: - if builtins.hasAttr name versionData then toString (builtins.getAttr name versionData) else default; + name: + if builtins.hasAttr name defaultsData then toString (builtins.getAttr name defaultsData) + else builtins.throw "Missing required default in nix/defaults.json: ${name}"; + + unique = + list: + builtins.foldl' ( + acc: item: if builtins.elem item acc then acc else acc ++ [ item ] + ) [ ] list; androidSdkConfig = { - platformVersions = [ - (getVar "PLATFORM_ANDROID_MIN_API" "21") - (getVar "PLATFORM_ANDROID_MAX_API" "33") + platformVersions = unique [ + (getVar "ANDROID_MIN_API") + (getVar "ANDROID_MAX_API") + (getVar "ANDROID_CUSTOM_API") + ]; + buildToolsVersion = getVar "ANDROID_BUILD_TOOLS_VERSION"; + cmdLineToolsVersion = getVar "ANDROID_CMDLINE_TOOLS_VERSION"; + systemImageTypes = [ (getVar "ANDROID_SYSTEM_IMAGE_TAG") ]; + }; + androidSdkConfigMin = androidSdkConfig // { + platformVersions = unique [ + (getVar "ANDROID_MIN_API") + (getVar "ANDROID_MAX_API") ]; - buildToolsVersion = getVar "PLATFORM_ANDROID_BUILD_TOOLS_VERSION" "30.0.3"; - cmdLineToolsVersion = getVar "PLATFORM_ANDROID_CMDLINE_TOOLS_VERSION" "19.0"; - systemImageTypes = [ (getVar "PLATFORM_ANDROID_SYSTEM_IMAGE_TAG" "google_apis") ]; + }; + androidSdkConfigMax = androidSdkConfig // { + platformVersions = [ (getVar "ANDROID_MAX_API") ]; + }; + androidSdkConfigCustom = androidSdkConfig // { + platformVersions = [ (getVar "ANDROID_CUSTOM_API") ]; }; forAllSystems = @@ -49,23 +70,31 @@ }; }; + applesimutils = pkgs.callPackage ./applesimutils.nix { }; + abiVersions = if builtins.match "aarch64-.*" system != null then [ "arm64-v8a" ] else [ "x86_64" ]; - androidPkgs = pkgs.androidenv.composeAndroidPackages { - # Keep API 21 images for the AVD and add API 33 for React Native builds. - platformVersions = androidSdkConfig.platformVersions; - buildToolsVersions = [ androidSdkConfig.buildToolsVersion ]; - cmdLineToolsVersion = androidSdkConfig.cmdLineToolsVersion; - includeEmulator = true; - includeSystemImages = true; - includeNDK = false; - abiVersions = abiVersions; - systemImageTypes = androidSdkConfig.systemImageTypes; - }; + androidPkgs = + config: + pkgs.androidenv.composeAndroidPackages { + # Keep API 21 images for the AVD and add API 33 for React Native builds. + platformVersions = config.platformVersions; + buildToolsVersions = [ config.buildToolsVersion ]; + cmdLineToolsVersion = config.cmdLineToolsVersion; + includeEmulator = true; + includeSystemImages = true; + includeNDK = false; + abiVersions = abiVersions; + systemImageTypes = config.systemImageTypes; + }; in { - android-sdk = androidPkgs.androidsdk; - default = androidPkgs.androidsdk; + applesimutils = applesimutils; + android-sdk = (androidPkgs androidSdkConfig).androidsdk; + android-sdk-min = (androidPkgs androidSdkConfigMin).androidsdk; + android-sdk-max = (androidPkgs androidSdkConfigMax).androidsdk; + android-sdk-custom = (androidPkgs androidSdkConfigCustom).androidsdk; + default = (androidPkgs androidSdkConfig).androidsdk; } ); diff --git a/nix/platform-versions.json b/nix/platform-versions.json deleted file mode 100644 index 506669900..000000000 --- a/nix/platform-versions.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "PLATFORM_ANDROID_MIN_API": "21", - "PLATFORM_ANDROID_MAX_API": "33", - "PLATFORM_ANDROID_BUILD_TOOLS_VERSION": "30.0.3", - "PLATFORM_ANDROID_CMDLINE_TOOLS_VERSION": "19.0", - "PLATFORM_ANDROID_SYSTEM_IMAGE_TAG": "google_apis", - "PLATFORM_ANDROID_MIN_DEVICE": "pixel", - "PLATFORM_ANDROID_MAX_DEVICE": "medium_phone", - "PLATFORM_IOS_MIN_RUNTIME": "15.0", - "PLATFORM_IOS_MAX_RUNTIME": "", - "PLATFORM_IOS_MIN_DEVICE": "iPhone 13", - "PLATFORM_IOS_MAX_DEVICE": "iPhone 17" -} diff --git a/package.json b/package.json index d6232414c..34981f99b 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,5 @@ "semantic-release-yarn": "^3.0.2", "ts-jest": "^29.1.1", "typescript": "^5.2.2" - }, - "packageManager": "yarn@4.1.0" + } } diff --git a/scripts/act-ci.sh b/scripts/act-ci.sh deleted file mode 100755 index 60c7f85a9..000000000 --- a/scripts/act-ci.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Run GitHub Actions workflows locally via act. -# Usage: scripts/act-ci.sh [--job JOB] [--platform ubuntu-latest=IMAGE] - -JOB="" -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 - -j | --job) - JOB="$2" - shift 2 - ;; - -p | --platform) - PLATFORMS+=("$2") - shift 2 - ;; - *) - echo "Unknown option: $1" >&2 - exit 1 - ;; - esac -done - -CMD=(act) -CMD+=(--pull=false) -for platform in "${PLATFORMS[@]}"; do - CMD+=(--platform "$platform") -done -CMD+=(--input ACT=true) -if [[ -n $JOB ]]; then - CMD+=(--job "$JOB") -fi - -printf 'Running: %s\n' "${CMD[*]}" -exec "${CMD[@]}" diff --git a/scripts/android/env.sh b/scripts/android/env.sh deleted file mode 100755 index 0ffe24379..000000000 --- a/scripts/android/env.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/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_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" -fi -if [ -z "${ANDROID_MAX_API:-}" ] && [ -n "${PLATFORM_ANDROID_MAX_API:-}" ]; then - ANDROID_MAX_API="$PLATFORM_ANDROID_MAX_API" -fi -if [ -z "${ANDROID_BUILD_TOOLS_VERSION:-}" ] && [ -n "${PLATFORM_ANDROID_BUILD_TOOLS_VERSION:-}" ]; then - ANDROID_BUILD_TOOLS_VERSION="$PLATFORM_ANDROID_BUILD_TOOLS_VERSION" -fi -if [ -z "${ANDROID_CMDLINE_TOOLS_VERSION:-}" ] && [ -n "${PLATFORM_ANDROID_CMDLINE_TOOLS_VERSION:-}" ]; then - ANDROID_CMDLINE_TOOLS_VERSION="$PLATFORM_ANDROID_CMDLINE_TOOLS_VERSION" -fi -if [ -z "${ANDROID_SYSTEM_IMAGE_TAG:-}" ] && [ -n "${PLATFORM_ANDROID_SYSTEM_IMAGE_TAG:-}" ]; then - ANDROID_SYSTEM_IMAGE_TAG="$PLATFORM_ANDROID_SYSTEM_IMAGE_TAG" -fi - -# Only act if neither var is already provided. -if [ -z "${ANDROID_SDK_ROOT:-}" ] && [ -z "${ANDROID_HOME:-}" ]; then - DEVBOX_SDK_OUT=$( - nix --extra-experimental-features 'nix-command flakes' \ - eval --raw "path:${DEVBOX_PROJECT_ROOT}/nix#android-sdk.outPath" 2>/dev/null || true - ) - if [ -n "${DEVBOX_SDK_OUT:-}" ] && [ -d "$DEVBOX_SDK_OUT/libexec/android-sdk" ]; then - ANDROID_SDK_ROOT="$DEVBOX_SDK_OUT/libexec/android-sdk" - ANDROID_HOME="$ANDROID_SDK_ROOT" - fi -fi - -if [ -z "${ANDROID_SDK_ROOT:-}" ] && [ -n "${ANDROID_HOME:-}" ]; then - ANDROID_SDK_ROOT="$ANDROID_HOME" -fi - -if [ -n "${ANDROID_SDK_ROOT:-}" ] && [ -z "${ANDROID_HOME:-}" ]; then - ANDROID_HOME="$ANDROID_SDK_ROOT" -fi - -export ANDROID_SDK_ROOT ANDROID_HOME -export ANDROID_BUILD_TOOLS_VERSION - -if [ -n "${ANDROID_SDK_ROOT:-}" ]; then - # Prefer cmdline-tools;latest, or fall back to the highest numbered cmdline-tools folder. - cmdline_tools_bin="" - if [ -d "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin" ]; then - cmdline_tools_bin="$ANDROID_SDK_ROOT/cmdline-tools/latest/bin" - else - cmdline_tools_dir=$(find "$ANDROID_SDK_ROOT/cmdline-tools" -maxdepth 1 -mindepth 1 -type d -not -name latest 2>/dev/null | sort -V | tail -n 1) - if [ -n "${cmdline_tools_dir:-}" ] && [ -d "$cmdline_tools_dir/bin" ]; then - cmdline_tools_bin="$cmdline_tools_dir/bin" - fi - fi - - new_path="$ANDROID_SDK_ROOT/emulator:$ANDROID_SDK_ROOT/platform-tools" - - if [ -n "${cmdline_tools_bin:-}" ]; then - new_path="$new_path:$cmdline_tools_bin" - fi - - new_path="$new_path:$ANDROID_SDK_ROOT/tools/bin:$PATH" - PATH="$new_path" - export PATH - echo "Using Android SDK: $ANDROID_SDK_ROOT" - case "$ANDROID_SDK_ROOT" in - /nix/store/*) - echo "Source: Nix flake (reproducible, pinned). To use your local SDK instead, set ANDROID_HOME/ANDROID_SDK_ROOT before starting devbox shell." - ;; - *) - echo "Source: User/local SDK. To use the pinned Nix SDK, unset ANDROID_HOME/ANDROID_SDK_ROOT before starting devbox shell." - ;; - esac -else - echo "Android SDK not set; using system PATH" -fi diff --git a/scripts/android/manager.sh b/scripts/android/manager.sh deleted file mode 100755 index a1275adb6..000000000 --- a/scripts/android/manager.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -action="${1:-}" -shift || true - -source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/env.sh" - -start_android() { - local flavor="${AVD_FLAVOR:-latest}" headless="${EMU_HEADLESS:-}" port="${EMU_PORT:-5554}" - local avd="${DETOX_AVD:-}" - - if [[ -z $avd ]]; then - if [[ $flavor == "latest" ]]; then - local host_arch - host_arch="$(uname -m)" - avd="medium_phone_API33_$([[ $host_arch == "arm64" || $host_arch == "aarch64" ]] && echo arm64_v8a || echo x86_64)" - else - avd="pixel_API21_$(uname -m | grep -qi arm && echo arm64_v8a || echo x86_64)" - fi - fi - - devbox run setup-android - local target_serial="emulator-${port}" - if command -v adb >/dev/null 2>&1; then - adb devices | awk 'NR>1 && $2=="offline" {print $1}' | while read -r d; do adb -s "$d" emu kill >/dev/null 2>&1 || true; done - fi - echo "Starting Android emulator: ${avd} (flavor ${flavor}, port ${port}, headless=${headless:-0})" - emulator -avd "${avd}" ${headless:+-no-window} -port "${port}" -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -accel on -writable-system -no-snapshot-save & - adb -s "${target_serial}" wait-for-device - local boot_completed="" - until [ "$boot_completed" = "1" ]; do - boot_completed=$(adb -s "${target_serial}" shell getprop sys.boot_completed 2>/dev/null | tr -d "\r") - sleep 5 - done - adb -s "${target_serial}" shell settings put global window_animation_scale 0 - adb -s "${target_serial}" shell settings put global transition_animation_scale 0 - adb -s "${target_serial}" shell settings put global animator_duration_scale 0 -} - -stop_android() { - devbox run stop-android -} - -reset_android() { - devbox run reset-android -} - -case "$action" in -start) start_android ;; -stop) stop_android ;; -reset) reset_android ;; -*) - echo "Usage: manager.sh {start|stop|reset}" >&2 - exit 1 - ;; -esac diff --git a/scripts/android/setup.sh b/scripts/android/setup.sh deleted file mode 100755 index ffc0b7cef..000000000 --- a/scripts/android/setup.sh +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Creates AVDs using the Android SDK provided by devbox/flake (system images, emulator, NDK already installed). -# Run inside a devbox shell so SDK tools are available. -# Configurable via env: -# AVD_API (default 21) -# AVD_DEVICE (default "pixel") -# AVD_TAG (default "google_apis") -# AVD_ABI (preferred ABI; optional) -# AVD_NAME (override final AVD name; otherwise computed) -# Secondary AVD (created in addition to the primary): -# AVD_SECONDARY_API (default 33) -# AVD_SECONDARY_DEVICE (default "medium_phone") -# AVD_SECONDARY_TAG (default "google_apis") -# AVD_SECONDARY_ABI (preferred ABI; optional) -# AVD_SECONDARY_NAME (override final name) - -script_dir="$(cd "$(dirname "$0")" && pwd)" -# shellcheck disable=SC1090 -. "$script_dir/../shared/common.sh" -load_platform_versions "$script_dir" - -detect_sdk_root() { - if [[ -n ${ANDROID_SDK_ROOT:-} ]]; then - echo "$ANDROID_SDK_ROOT" - return - fi - - local sm - sm="$(command -v sdkmanager 2>/dev/null || true)" - if [[ -z $sm ]]; then - return - fi - sm="$(readlink -f "$sm")" - local candidates=( - "$(dirname "$sm")/.." - "$(dirname "$sm")/../share/android-sdk" - "$(dirname "$sm")/../libexec/android-sdk" - "$(dirname "$sm")/../.." - ) - for c in "${candidates[@]}"; do - if [[ -d "$c/platform-tools" || -d "$c/platforms" || -d "$c/system-images" ]]; then - echo "$c" - return - fi - done -} - -avd_exists() { - local name="$1" - avdmanager list avd | grep -q "Name: ${name}" -} - -pick_image() { - local api="$1" tag="$2" preferred_abi="$3" - local host_arch - host_arch="$(uname -m)" - - local candidates=() - if [[ -n ${preferred_abi:-} ]]; then - candidates=("$preferred_abi") - else - case "$host_arch" in - arm64 | aarch64) candidates=("arm64-v8a" "x86_64" "x86") ;; - *) candidates=("x86_64" "x86" "arm64-v8a") ;; - esac - fi - - for abi in "${candidates[@]}"; do - local image="system-images;android-${api};${tag};${abi}" - local path="${ANDROID_SDK_ROOT}/system-images/android-${api}/${tag}/${abi}" - if [[ -d $path ]]; then - echo "$image" - return 0 - fi - done - - return 1 -} - -create_avd() { - local name="$1" device="$2" image="$3" - local abi="${image##*;}" - - if avd_exists "$name"; then - echo "AVD ${name} already exists." - return 0 - fi - - echo "Creating AVD ${name} with ${image}..." - avdmanager create avd --force --name "$name" --package "$image" --device "$device" --abi "$abi" --sdcard 512M -} - -main() { - local detected_sdk_root - detected_sdk_root="$(detect_sdk_root)" - - if [[ -z ${ANDROID_SDK_ROOT:-} && -n $detected_sdk_root ]]; then - export ANDROID_SDK_ROOT="$detected_sdk_root" - fi - - if [[ -z ${ANDROID_SDK_ROOT:-} && -z ${ANDROID_HOME:-} ]]; then - echo "ANDROID_SDK_ROOT/ANDROID_HOME must be set. In a devbox shell, the flake-provided SDK should supply sdkmanager in PATH; if not, set ANDROID_SDK_ROOT to the flake's android-sdk path." >&2 - exit 1 - fi - - export ANDROID_HOME="${ANDROID_HOME:-$ANDROID_SDK_ROOT}" - - require_tool avdmanager - require_tool emulator - - local primary_api="${AVD_API:-${ANDROID_MIN_API:-${PLATFORM_ANDROID_MIN_API:-21}}}" - local primary_tag="${AVD_TAG:-${ANDROID_SYSTEM_IMAGE_TAG:-${PLATFORM_ANDROID_SYSTEM_IMAGE_TAG:-google_apis}}}" - local primary_device="${AVD_DEVICE:-pixel}" - local primary_preferred_abi="${AVD_ABI:-}" - - local secondary_api="${AVD_SECONDARY_API:-${ANDROID_MAX_API:-${PLATFORM_ANDROID_MAX_API:-33}}}" - local secondary_tag="${AVD_SECONDARY_TAG:-${ANDROID_SYSTEM_IMAGE_TAG:-${PLATFORM_ANDROID_SYSTEM_IMAGE_TAG:-google_apis}}}" - local secondary_device="${AVD_SECONDARY_DEVICE:-medium_phone}" - local secondary_preferred_abi="${AVD_SECONDARY_ABI:-}" - - local targets=( - "$primary_api|$primary_tag|$primary_device|$primary_preferred_abi|${AVD_NAME:-}" - "$secondary_api|$secondary_tag|$secondary_device|$secondary_preferred_abi|${AVD_SECONDARY_NAME:-}" - ) - - for target in "${targets[@]}"; do - IFS="|" read -r api tag device preferred_abi name_override <<<"$target" - - local api_image - if ! api_image="$(pick_image "$api" "$tag" "$preferred_abi")"; then - echo "Expected API ${api} system image (${tag}; preferred ABI ${preferred_abi:-auto}) not found under ${ANDROID_SDK_ROOT}/system-images/android-${api}." >&2 - echo "Re-enter the devbox shell (flake should provide images) or rebuild Devbox to fetch them." >&2 - continue - fi - - local abi="${api_image##*;}" - local avd_name="${name_override:-$(printf '%s_API%s_%s' "$device" "$api" "${abi//-/_}")}" - - create_avd "$avd_name" "$device" "$api_image" - if avd_exists "$avd_name"; then - echo "AVD ready: ${avd_name} (${api_image})" - fi - done - - echo "AVDs ready. Boot with: emulator -avd --netdelay none --netspeed full" -} - -main "$@" diff --git a/scripts/android/test.sh b/scripts/android/test.sh deleted file mode 100755 index 342a187a0..000000000 --- a/scripts/android/test.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/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/bootstrap/env.sh b/scripts/bootstrap/env.sh new file mode 100644 index 000000000..ef5295e01 --- /dev/null +++ b/scripts/bootstrap/env.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env sh + +if ! (return 0 2>/dev/null); then + echo "scripts/bootstrap/env.sh must be sourced." >&2 + exit 1 +fi + +if [ "${ENV_SH_LOADED:-}" = "1" ] && [ "${ENV_SH_LOADED_PID:-}" = "$$" ]; then + return 0 2>/dev/null || exit 0 +fi +ENV_SH_LOADED=1 +ENV_SH_LOADED_PID="$$" + +script_dir="$(cd "$(dirname "$0")" && pwd)" +repo_root="" +if [ -n "${DEVBOX_PROJECT_ROOT:-}" ] && [ -d "$DEVBOX_PROJECT_ROOT" ]; then + repo_root="$DEVBOX_PROJECT_ROOT" +elif [ -n "${DEVBOX_PROJECT_DIR:-}" ] && [ -d "$DEVBOX_PROJECT_DIR" ]; then + repo_root="$DEVBOX_PROJECT_DIR" +elif command -v git >/dev/null 2>&1; then + repo_root="$(git -C "$script_dir" rev-parse --show-toplevel 2>/dev/null || git -C "$PWD" rev-parse --show-toplevel 2>/dev/null || true)" +fi +if [ -z "$repo_root" ]; then + repo_root="$(cd "$script_dir/.." && pwd)" +fi + +if [ -z "${PROJECT_ROOT:-}" ]; then + PROJECT_ROOT="$repo_root" + export PROJECT_ROOT +fi + +if [ -z "${SCRIPTS_DIR:-}" ]; then + SCRIPTS_DIR="$repo_root/scripts" + export SCRIPTS_DIR +fi + +for shared_script in project.sh debug.sh tools.sh defaults.sh; do + if [ -f "$SCRIPTS_DIR/shared/$shared_script" ]; then + # shellcheck disable=SC1090 + . "$SCRIPTS_DIR/shared/$shared_script" + fi +done + +if [ -z "${IOS_NODE_BINARY:-}" ] && command -v node >/dev/null 2>&1; then + IOS_NODE_BINARY="$(command -v node)" + export IOS_NODE_BINARY +fi + +if [ -z "${IOS_XCODE_ENV_PATH:-}" ] && [ -n "${PROJECT_ROOT:-}" ]; then + xcode_env_path="$PROJECT_ROOT/examples/E2E/ios/.xcode.env" + if [ -d "$PROJECT_ROOT/examples/E2E/ios" ]; then + IOS_XCODE_ENV_PATH="$xcode_env_path" + export IOS_XCODE_ENV_PATH + fi +fi + +if [ "${INIT_ANDROID:-}" = "1" ]; then + # shellcheck disable=SC1090 + . "$SCRIPTS_DIR/platforms/android/env.sh" +fi + +if [ "${INIT_IOS:-}" = "1" ] && [ "$(uname -s)" = "Darwin" ]; then + # shellcheck disable=SC1090 + . "$SCRIPTS_DIR/platforms/ios/env.sh" +fi + +SHARED_LOADED=1 +SHARED_LOADED_PID="$$" diff --git a/scripts/bootstrap/init.sh b/scripts/bootstrap/init.sh new file mode 100644 index 000000000..b8911cd92 --- /dev/null +++ b/scripts/bootstrap/init.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env sh + +if ! (return 0 2>/dev/null); then + echo "scripts/bootstrap/init.sh must be sourced." >&2 + exit 1 +fi + +load_env() { + script_dir="$1" + init_path="$script_dir/bootstrap/env.sh" + if [ -n "${DEVBOX_PROJECT_ROOT:-}" ] && [ -f "${DEVBOX_PROJECT_ROOT}/scripts/bootstrap/env.sh" ]; then + init_path="${DEVBOX_PROJECT_ROOT}/scripts/bootstrap/env.sh" + elif [ -n "${DEVBOX_PROJECT_DIR:-}" ] && [ -f "${DEVBOX_PROJECT_DIR}/scripts/bootstrap/env.sh" ]; then + init_path="${DEVBOX_PROJECT_DIR}/scripts/bootstrap/env.sh" + fi + if [ ! -f "$init_path" ]; then + repo_root="" + if command -v git >/dev/null 2>&1; then + repo_root="$(git -C "$script_dir" rev-parse --show-toplevel 2>/dev/null || git -C "$PWD" rev-parse --show-toplevel 2>/dev/null || true)" + fi + if [ -n "$repo_root" ] && [ -f "$repo_root/scripts/bootstrap/env.sh" ]; then + init_path="$repo_root/scripts/bootstrap/env.sh" + fi + fi + # shellcheck disable=SC1090 + . "$init_path" +} diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index d22efb734..000000000 --- a/scripts/build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/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 deleted file mode 100755 index c5ee09930..000000000 --- a/scripts/ios/env.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/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 deleted file mode 100755 index 52359735f..000000000 --- a/scripts/ios/manager.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -script_dir="$(cd "$(dirname "$0")" && pwd)" -# shellcheck disable=SC1090 -. "$script_dir/../shared/common.sh" -load_platform_versions "$script_dir" - -action="${1:-}" -shift || true - -start_ios() { - local flavor="${IOS_FLAVOR:-latest}" - if [[ $flavor == "minsdk" ]]; then - export IOS_DEVICE_NAMES="${IOS_MIN_DEVICE:-${PLATFORM_IOS_MIN_DEVICE:-iPhone 13}}" - export IOS_RUNTIME="${IOS_MIN_RUNTIME:-${PLATFORM_IOS_MIN_RUNTIME:-15.0}}" - export DETOX_IOS_DEVICE="${DETOX_IOS_DEVICE:-${IOS_MIN_DEVICE:-${PLATFORM_IOS_MIN_DEVICE:-iPhone 13}}}" - else - export IOS_DEVICE_NAMES="${IOS_DEVICE_NAMES:-${IOS_MIN_DEVICE:-${PLATFORM_IOS_MIN_DEVICE:-iPhone 13}},${IOS_MAX_DEVICE:-${PLATFORM_IOS_MAX_DEVICE:-iPhone 17}}}" - export IOS_RUNTIME="${IOS_RUNTIME:-${IOS_MAX_RUNTIME:-${PLATFORM_IOS_MAX_RUNTIME:-}}}" - export DETOX_IOS_DEVICE="${DETOX_IOS_DEVICE:-iPhone 17}" - fi - - devbox run setup-ios - local sim_device="${DETOX_IOS_DEVICE}" - if ! xcrun simctl list devices | grep -q "${sim_device}"; then - echo "Simulator ${sim_device} not found; ensure setup-ios created it." >&2 - exit 1 - fi - echo "Starting iOS simulator: ${sim_device} (runtime ${IOS_RUNTIME})" - xcrun simctl boot "$sim_device" || true - open -a Simulator -} - -stop_ios() { - devbox run stop-ios -} - -reset_ios() { - devbox run reset-ios -} - -case "$action" in -start) start_ios ;; -stop) stop_ios ;; -reset) reset_ios ;; -*) - echo "Usage: manager.sh {start|stop|reset}" >&2 - exit 1 - ;; -esac diff --git a/scripts/ios/setup.sh b/scripts/ios/setup.sh deleted file mode 100755 index de7827cad..000000000 --- a/scripts/ios/setup.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/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/simctl.sh b/scripts/ios/simctl.sh deleted file mode 100644 index ddfe7bf27..000000000 --- a/scripts/ios/simctl.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ensure_core_sim_service() { - local output status - output="$(xcrun simctl list devices -j 2>&1)" || status=$? - if [[ -n ${status:-} ]]; then - echo "simctl failed while listing devices (status ${status}). CoreSimulatorService may be unhealthy." >&2 - echo "Try restarting it:" >&2 - echo " killall -9 com.apple.CoreSimulatorService 2>/dev/null || true" >&2 - echo " launchctl kickstart -k gui/$UID/com.apple.CoreSimulatorService" >&2 - echo "Then open Simulator once and rerun devbox run setup-ios." >&2 - echo "simctl error output:" >&2 - echo "$output" >&2 - return 1 - fi - - if echo "$output" | grep -q "CoreSimulatorService connection became invalid"; then - echo "CoreSimulatorService is not healthy. Try restarting it:" >&2 - echo " killall -9 com.apple.CoreSimulatorService 2>/dev/null || true" >&2 - echo " launchctl kickstart -k gui/$UID/com.apple.CoreSimulatorService" >&2 - echo "Then open Simulator once and rerun devbox run setup-ios." >&2 - echo "simctl error output:" >&2 - echo "$output" >&2 - return 1 - fi -} - -pick_runtime() { - local preferred="$1" - local json choice - json="$(xcrun simctl list runtimes -j)" - choice="$(echo "$json" | jq -r --arg v "$preferred" '.runtimes[] | select(.isAvailable and (.name|startswith("iOS \($v)"))) | "\(.identifier)|\(.name)"' | head -n1)" - if [[ -z $choice || $choice == "null" ]]; then - choice="$(echo "$json" | jq -r '.runtimes[] | select(.isAvailable and (.name|startswith("iOS "))) | "\(.version)|\(.identifier)|\(.name)"' | sort -Vr | head -n1 | cut -d"|" -f2-)" - fi - [[ -n $choice && $choice != "null" ]] || return 1 - echo "$choice" -} - -resolve_runtime() { - local preferred="$1" - if choice=$(pick_runtime "$preferred"); then - echo "$choice" - return 0 - fi - - if [[ ${IOS_DOWNLOAD_RUNTIME:-1} != "0" ]] && command -v xcodebuild >/dev/null 2>&1; then - echo "Preferred runtime iOS ${preferred} not found. Attempting to download via xcodebuild -downloadPlatform iOS..." >&2 - if xcodebuild -downloadPlatform iOS; then - if choice=$(pick_runtime "$preferred"); then - echo "$choice" - return 0 - fi - else - echo "xcodebuild -downloadPlatform iOS failed; continuing with available runtimes." >&2 - fi - fi - - pick_runtime "$preferred" -} - -existing_device_udid_any_runtime() { - local name="$1" - xcrun simctl list devices -j | jq -r --arg name "$name" '.devices[]?[]? | select(.name == $name) | .udid' | head -n1 -} - -device_data_dir_exists() { - local udid="${1:-}" - [[ -n $udid ]] || return 1 - local dir="$HOME/Library/Developer/CoreSimulator/Devices/$udid" - [[ -d $dir ]] -} - -devicetype_id_for_name() { - local name="$1" - xcrun simctl list devicetypes -j | jq -r --arg name "$name" '.devicetypes[] | select((.name|ascii_downcase) == ($name|ascii_downcase)) | .identifier' | head -n1 -} - -ensure_device() { - local base_name="$1" preferred_runtime="$2" - - # If a device with this name already exists anywhere, reuse it. - if - existing_udid=$(existing_device_udid_any_runtime "$base_name") - [[ -n ${existing_udid} ]] - then - if device_data_dir_exists "$existing_udid"; then - echo "Found existing ${base_name}: ${existing_udid}" - return 0 - fi - echo "Existing ${base_name} (${existing_udid}) is missing its data directory. Deleting stale simulator..." - xcrun simctl delete "$existing_udid" || true - fi - - local choice runtime_id runtime_name - if ! choice=$(resolve_runtime "$preferred_runtime"); then - echo "No available iOS simulator runtime found. Install one in Xcode (Settings > Platforms) and retry." >&2 - return 1 - fi - runtime_id="$(echo "$choice" | cut -d'|' -f1)" - runtime_name="$(echo "$choice" | cut -d'|' -f2)" - - local display_name="${base_name} (${runtime_name})" - - if ! device_type=$(devicetype_id_for_name "$base_name"); then - echo "Device type '${base_name}' is unavailable in this Xcode install. Skipping ${display_name}." >&2 - return 0 - fi - - # Also check for an existing device with the runtime-qualified display name. - if - existing_udid=$(existing_device_udid_any_runtime "$display_name") - [[ -n ${existing_udid} ]] - then - if device_data_dir_exists "$existing_udid"; then - echo "Found existing ${display_name}: ${existing_udid}" - return 0 - fi - echo "Existing ${display_name} (${existing_udid}) is missing its data directory. Deleting stale simulator..." - xcrun simctl delete "$existing_udid" || true - fi - - echo "Creating ${display_name}..." - xcrun simctl create "$display_name" "$device_type" "$runtime_id" - echo "Created ${display_name}" -} diff --git a/scripts/ios/test.sh b/scripts/ios/test.sh deleted file mode 100755 index 7bf5a4189..000000000 --- a/scripts/ios/test.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/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/platform-versions.sh b/scripts/platform-versions.sh deleted file mode 100644 index 0713ce9cb..000000000 --- a/scripts/platform-versions.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env sh -# Load shared platform version defaults from JSON for a single source of truth. - -script_dir="$(cd "$(dirname "$0")" && pwd)" -repo_root="$(cd "$script_dir/.." && pwd)" -versions_json="${PLATFORM_VERSIONS_JSON:-$repo_root/nix/platform-versions.json}" - -if [ -f "$versions_json" ] && command -v jq >/dev/null 2>&1; then - eval "$( - jq -r 'to_entries[] | "\(.key)=\(.value|@sh)"' "$versions_json" - )" -fi diff --git a/scripts/platforms/android/actions.sh b/scripts/platforms/android/actions.sh new file mode 100644 index 000000000..d6d6a5510 --- /dev/null +++ b/scripts/platforms/android/actions.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env sh + +if ! (return 0 2>/dev/null); then + echo "scripts/platforms/android/actions.sh must be sourced." >&2 + exit 1 +fi + +android_run() { + action="${1:-}" + shift 1 || true + + # shellcheck disable=SC1090 + . "$SCRIPTS_DIR/platforms/android/env.sh" + + case "$action" in + test) + # shellcheck disable=SC1090 + . "$SCRIPTS_DIR/platforms/android/avd.sh" + android_setup + yarn install --immutable + yarn e2e install + yarn build + yarn e2e build:android + yarn e2e test:android + ;; + setup) + # shellcheck disable=SC1090 + . "$SCRIPTS_DIR/platforms/android/avd.sh" + android_setup "$@" + ;; + start | stop | reset) + # shellcheck disable=SC1090 + . "$SCRIPTS_DIR/platforms/android/avd.sh" + case "$action" in + start) android_start "$@" ;; + stop) android_stop "$@" ;; + reset) android_reset "$@" ;; + esac + ;; + *) + echo "Usage: run.sh android {test|setup|start|stop|reset} [args]" >&2 + exit 1 + ;; + esac +} diff --git a/scripts/platforms/android/avd.sh b/scripts/platforms/android/avd.sh new file mode 100644 index 000000000..2468f2fb8 --- /dev/null +++ b/scripts/platforms/android/avd.sh @@ -0,0 +1,386 @@ +#!/usr/bin/env sh +set -eu + +if ! (return 0 2>/dev/null); then + echo "scripts/platforms/android/avd.sh must be sourced via scripts/run.sh." >&2 + exit 1 +fi + +scripts_root="${SCRIPTS_DIR:-$(cd "$(dirname "$0")" && pwd)}" +android_dir="$scripts_root/platforms/android" +if [ "${SHARED_LOADED:-}" != "1" ] || [ "${SHARED_LOADED_PID:-}" != "$$" ]; then + # shellcheck disable=SC1090 + . "$scripts_root/bootstrap/init.sh" + load_env "$scripts_root" +fi +debug_log_script "scripts/platforms/android/avd.sh" + +# shellcheck disable=SC1090 +. "$android_dir/lib.sh" + +detect_sdk_root() { + if [ -n "${ANDROID_SDK_ROOT:-}" ]; then + printf '%s\n' "$ANDROID_SDK_ROOT" + return 0 + fi + + sm=$(command -v sdkmanager 2>/dev/null || true) + if [ -z "$sm" ]; then + return 1 + fi + if command -v readlink >/dev/null 2>&1; then + sm="$(readlink "$sm" 2>/dev/null || printf '%s' "$sm")" + fi + sm_dir="$(cd "$(dirname "$sm")" && pwd)" + candidates="${sm_dir}/.. ${sm_dir}/../share/android-sdk ${sm_dir}/../libexec/android-sdk ${sm_dir}/../.." + for c in $candidates; do + if [ -d "$c/platform-tools" ] || [ -d "$c/platforms" ] || [ -d "$c/system-images" ]; then + printf '%s\n' "$c" + return 0 + fi + done + return 1 +} + +avd_exists() { + name="$1" + avdmanager list avd | grep -q "Name: ${name}" +} + +resolve_device() { + desired="$1" + if [ -z "$desired" ]; then + return 1 + fi + devices="$(avdmanager list device | awk -F': ' ' + /^id: /{ + id=$2 + if (index(id, "\"") > 0) { + q=index(id, "\"") + rest=substr(id, q + 1) + q2=index(rest, "\"") + if (q2 > 0) { id=substr(rest, 1, q2 - 1) } + } else { + split(id, parts, " ") + id=parts[1] + } + next + } + /^[[:space:]]*Name: /{ + name=$2 + if (id != "") { print id "\t" name; id="" } + } + ')" + if [ -z "$devices" ]; then + return 1 + fi + + desired_norm="$(android_normalize_name "$desired")" + desired_alt_norm="$(android_normalize_name "$(printf '%s' "$desired" | tr '_-' ' ')")" + + while IFS=$'\t' read -r id name; do + id_norm="$(android_normalize_name "$id")" + name_norm="$(android_normalize_name "$name")" + if [ "$id_norm" = "$desired_norm" ] || [ "$id_norm" = "$desired_alt_norm" ] || \ + [ "$name_norm" = "$desired_norm" ] || [ "$name_norm" = "$desired_alt_norm" ]; then + printf '%s\n' "$id" + return 0 + fi + done <&2 + else + echo "Debug: missing ABI path $path" >&2 + fi + fi + if [ -d "$path" ]; then + printf '%s\n' "$image" + IFS="$ifs_backup" + return 0 + fi + done + IFS="$ifs_backup" + + return 1 +} + +create_avd() { + name="$1" + device="$2" + image="$3" + abi="${image##*;}" + + if avd_exists "$name"; then + echo "AVD ${name} already exists." + return 0 + fi + + echo "Creating AVD ${name} with ${image}..." + avdmanager create avd --force --name "$name" --package "$image" --device "$device" --abi "$abi" --sdcard 512M +} + +add_target() { + target_line="$1" + if [ -z "${TARGETS:-}" ]; then + TARGETS="$target_line" + else + TARGETS="${TARGETS} +${target_line}" + fi +} + +android_setup() { + TARGETS="" + resolved_avd_name="" + detected_sdk_root="$(detect_sdk_root 2>/dev/null || true)" + target_sdk="${TARGET_SDK:-max}" + + if [ -z "${ANDROID_SDK_ROOT:-}" ] && [ -n "$detected_sdk_root" ]; then + ANDROID_SDK_ROOT="$detected_sdk_root" + export ANDROID_SDK_ROOT + fi + + if [ -z "${ANDROID_SDK_ROOT:-}" ] && [ -z "${ANDROID_HOME:-}" ]; then + echo "ANDROID_SDK_ROOT/ANDROID_HOME must be set. In a devbox shell, the flake-provided SDK should supply sdkmanager in PATH; if not, set ANDROID_SDK_ROOT to the flake's android-sdk path." >&2 + exit 1 + fi + + ANDROID_HOME="${ANDROID_HOME:-$ANDROID_SDK_ROOT}" + export ANDROID_HOME + + require_tool avdmanager + require_tool emulator + + IFS='|' read -r target_api target_device target_tag target_preferred_abi <&2 + echo "Re-enter the devbox shell (flake should provide images) or rebuild Devbox to fetch them." >&2 + exit 1 + fi + + + if [ -z "$TARGETS" ]; then + echo "No compatible Android system images found under ${ANDROID_SDK_ROOT}/system-images for configured APIs." >&2 + exit 1 + fi + + ifs_backup="$IFS" + IFS="$(printf '\n')" + for target in $TARGETS; do + IFS='|' read -r api tag device preferred_abi name_override </dev/null || true)" + fi + if [ -z "$api_image" ]; then + if [ -n "$preferred_abi" ]; then + require_dir_contains "$ANDROID_SDK_ROOT" "system-images/android-${api}/${tag}/${preferred_abi}" "Missing preferred ABI ${preferred_abi} for API ${api} (${tag}) under ${ANDROID_SDK_ROOT}." + fi + base_dir="${ANDROID_SDK_ROOT}/system-images/android-${api}/${tag}" + if [ -d "$base_dir" ]; then + available_abis="$(ls -1 "$base_dir" 2>/dev/null | tr '\n' ' ' | sed 's/[[:space:]]*$//')" + if [ -n "$available_abis" ]; then + host_arch="$(uname -m)" + if [ -n "$preferred_abi" ]; then + candidates="$preferred_abi" + else + case "$host_arch" in + arm64 | aarch64) candidates="arm64-v8a x86_64 x86" ;; + *) candidates="x86_64 x86 arm64-v8a" ;; + esac + fi + echo "Debug: host_arch=${host_arch} candidates=${candidates} base_dir=${base_dir}" >&2 + echo "API ${api} system image tag '${tag}' found, but no compatible ABI (preferred ${preferred_abi:-auto}). Available: ${available_abis}." >&2 + else + echo "API ${api} system image tag '${tag}' exists but has no ABI directories under ${base_dir}." >&2 + fi + else + echo "Expected API ${api} system image (${tag}; preferred ABI ${preferred_abi:-auto}) not found under ${ANDROID_SDK_ROOT}/system-images/android-${api}." >&2 + fi + echo "Re-enter the devbox shell (flake should provide images) or rebuild Devbox to fetch them." >&2 + continue + fi + + abi="${api_image##*;}" + abi_safe="$(printf '%s' "$abi" | tr '-' '_')" + if [ -n "$name_override" ]; then + avd_name="$name_override" + else + safe_device="$(android_sanitize_avd_name "$device" || true)" + if [ -z "$safe_device" ]; then + echo "Unable to derive a valid AVD name from device '${device}'." >&2 + exit 1 + fi + avd_name="$(printf '%s_API%s_%s' "$safe_device" "$api" "$abi_safe")" + fi + if [ -z "$resolved_avd_name" ]; then + resolved_avd_name="$avd_name" + fi + + create_avd "$avd_name" "$device" "$api_image" + if avd_exists "$avd_name"; then + echo "AVD ready: ${avd_name} (${api_image})" + fi + done + IFS="$ifs_backup" + + if [ -z "${DETOX_AVD:-}" ] && [ -n "$resolved_avd_name" ]; then + DETOX_AVD="$resolved_avd_name" + export DETOX_AVD + fi + echo "AVDs ready. Boot with: emulator -avd --netdelay none --netspeed full" +} + +android_start() { + target_sdk="${TARGET_SDK:-max}" + headless="${EMU_HEADLESS:-}" + port="${EMU_PORT:-5554}" + avd="${DETOX_AVD:-}" + + android_setup + + if [ -z "$avd" ] && [ -n "${AVD_NAME:-}" ]; then + avd="$AVD_NAME" + fi + + if [ -z "$avd" ]; then + IFS='|' read -r target_api target_device target_tag target_preferred_abi </dev/null || true)" + if [ -n "$api_image" ]; then + abi="${api_image##*;}" + abi_safe="$(printf '%s' "$abi" | tr '-' '_')" + safe_device="$(android_sanitize_avd_name "$target_device" || true)" + if [ -n "$safe_device" ]; then + avd="$(printf '%s_API%s_%s' "$safe_device" "$target_api" "$abi_safe")" + fi + fi + fi + + if [ -z "$avd" ]; then + echo "No AVD resolved; set DETOX_AVD or AVD_NAME explicitly." >&2 + exit 1 + fi + + target_serial="emulator-${port}" + if command -v adb >/dev/null 2>&1; then + adb devices | awk 'NR>1 && $2=="offline" {print $1}' | while read -r d; do adb -s "$d" emu kill >/dev/null 2>&1 || true; done + fi + echo "Starting Android emulator: ${avd} (target ${target_sdk}, port ${port}, headless=${headless:-0})" + if [ -n "$headless" ]; then + headless_flag="-no-window" + else + headless_flag="" + fi + emulator -avd "$avd" ${headless_flag:+$headless_flag} -port "$port" -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -accel on -writable-system -no-snapshot-save & + adb -s "$target_serial" wait-for-device + boot_completed="" + until [ "$boot_completed" = "1" ]; do + boot_completed=$(adb -s "$target_serial" shell getprop sys.boot_completed 2>/dev/null | tr -d "\r") + sleep 5 + done + adb -s "$target_serial" shell settings put global window_animation_scale 0 + adb -s "$target_serial" shell settings put global transition_animation_scale 0 + adb -s "$target_serial" shell settings put global animator_duration_scale 0 +} + +android_stop() { + if command -v adb >/dev/null 2>&1; then + adb devices | awk 'NR>1 && $2=="offline" {print $1}' | while read -r d; do adb -s "$d" emu kill >/dev/null 2>&1 || true; done + devices="$(adb devices -l 2>/dev/null | awk 'NR>1{print $1}' | tr '\n' ' ')" + if [ -n "$devices" ]; then + echo "Stopping Android emulators: $devices" + for d in $devices; do + adb -s "$d" emu kill >/dev/null 2>&1 || true + done + else + echo "No Android emulators detected via adb." + fi + else + echo "adb not found; skipping Android emulator shutdown." + fi + pkill -f "emulator@" >/dev/null 2>&1 || true + echo "Android emulators stopped (if any were running)." +} + +android_reset() { + rm_bin="rm" + if [ "$(uname -s)" = "Darwin" ] && [ -x /bin/rm ]; then + rm_bin="/bin/rm" + fi + avd_dir="$HOME/.android/avd" + if [ -d "$avd_dir" ]; then + if command -v chflags >/dev/null 2>&1; then + chflags -R nouchg "$avd_dir" >/dev/null 2>&1 || true + fi + chmod -R u+w "$avd_dir" >/dev/null 2>&1 || true + if ! "$rm_bin" -rf "$avd_dir"; then + echo "Failed to remove $avd_dir. Check permissions or Full Disk Access for your terminal." >&2 + return 1 + fi + fi + + if ! "$rm_bin" -f "$HOME/.android/adbkey" "$HOME/.android/adbkey.pub"; then + echo "Failed to remove adb keys under $HOME/.android. Check permissions." >&2 + return 1 + fi + echo "AVDs and adb keys removed. Recreate via start-android* as needed." +} diff --git a/scripts/platforms/android/env.sh b/scripts/platforms/android/env.sh new file mode 100755 index 000000000..2ac1aeaeb --- /dev/null +++ b/scripts/platforms/android/env.sh @@ -0,0 +1,302 @@ +#!/usr/bin/env sh + +if ! (return 0 2>/dev/null); then + echo "scripts/platforms/android/env.sh must be sourced via scripts/run.sh or scripts/bootstrap/env.sh." >&2 + exit 1 +fi +project_root="${PROJECT_ROOT:-}" +if [ -z "$project_root" ] && command -v git >/dev/null 2>&1; then + project_root="$(git -C "$(dirname "$0")" rev-parse --show-toplevel 2>/dev/null || git -C "$PWD" rev-parse --show-toplevel 2>/dev/null || true)" +fi +if [ -z "$project_root" ]; then + project_root="$(cd "$(dirname "$0")/../.." && pwd)" +fi +script_dir="$project_root/scripts/platforms/android" +if [ "${SHARED_LOADED:-}" != "1" ] || [ "${SHARED_LOADED_PID:-}" != "$$" ]; then + # shellcheck disable=SC1090 + . "$project_root/scripts/bootstrap/env.sh" +fi +debug_log_script "scripts/platforms/android/env.sh" + + + +resolve_flake_sdk_root() { + root="${PROJECT_ROOT:-}" + if [ -z "$root" ]; then + root="$(cd "$script_dir/../.." && pwd)" + fi + output="$1" + sdk_out=$( + nix --extra-experimental-features 'nix-command flakes' \ + eval --raw "path:${root}/nix#${output}.outPath" 2>/dev/null || true + ) + if [ -n "${sdk_out:-}" ] && [ -d "$sdk_out/libexec/android-sdk" ]; then + printf '%s\n' "$sdk_out/libexec/android-sdk" + return 0 + fi + return 1 +} + +detect_sdk_root_from_sdkmanager() { + sm=$(command -v sdkmanager 2>/dev/null || true) + if [ -z "$sm" ]; then + return 1 + fi + if command -v readlink >/dev/null 2>&1; then + sm="$(readlink "$sm" 2>/dev/null || printf '%s' "$sm")" + fi + sm_dir="$(cd "$(dirname "$sm")" && pwd)" + candidates="${sm_dir}/.. ${sm_dir}/../share/android-sdk ${sm_dir}/../libexec/android-sdk ${sm_dir}/../.." + for c in $candidates; do + if [ -d "$c/platform-tools" ] || [ -d "$c/platforms" ] || [ -d "$c/system-images" ]; then + printf '%s\n' "$c" + return 0 + fi + done + return 1 +} + +prefer_local="${ANDROID_LOCAL_SDK:-}" +case "$prefer_local" in + 1 | true | TRUE | yes | YES | on | ON) + prefer_local=1 + ;; + *) + prefer_local="" + ;; +esac +if [ -n "$prefer_local" ]; then + if [ -z "${ANDROID_SDK_ROOT:-}" ] && [ -n "${ANDROID_HOME:-}" ]; then + ANDROID_SDK_ROOT="$ANDROID_HOME" + fi + if [ -n "${ANDROID_SDK_ROOT:-}" ] && [ -z "${ANDROID_HOME:-}" ]; then + ANDROID_HOME="$ANDROID_SDK_ROOT" + fi +else + preferred_output="${ANDROID_SDK_FLAKE_OUTPUT:-}" + if [ -z "$preferred_output" ]; then + case "${TARGET_SDK:-max}" in + min) preferred_output="android-sdk-min" ;; + custom) preferred_output="android-sdk-custom" ;; + *) preferred_output="android-sdk-max" ;; + esac + fi + sdk_root_max="" + sdk_root_min="" + + if [ -n "$preferred_output" ]; then + preferred_root="$(resolve_flake_sdk_root "$preferred_output" 2>/dev/null || true)" + if [ -n "$preferred_root" ]; then + ANDROID_SDK_ROOT="$preferred_root" + ANDROID_HOME="$ANDROID_SDK_ROOT" + fi + fi + + sdk_root_max="$(resolve_flake_sdk_root "android-sdk-max" 2>/dev/null || true)" + sdk_root_min="$(resolve_flake_sdk_root "android-sdk" 2>/dev/null || true)" + + if [ -n "$sdk_root_max" ]; then + ANDROID_SDK_ROOT_MAX="$sdk_root_max" + ANDROID_HOME_MAX="$sdk_root_max" + fi + if [ -n "$sdk_root_min" ]; then + ANDROID_SDK_ROOT_MIN="$sdk_root_min" + ANDROID_HOME_MIN="$sdk_root_min" + fi + export ANDROID_SDK_ROOT_MAX ANDROID_HOME_MAX ANDROID_SDK_ROOT_MIN ANDROID_HOME_MIN + + if [ -n "$sdk_root_max" ]; then + ANDROID_SDK_ROOT="$sdk_root_max" + ANDROID_HOME="$ANDROID_SDK_ROOT" + elif [ -n "$sdk_root_min" ]; then + ANDROID_SDK_ROOT="$sdk_root_min" + ANDROID_HOME="$ANDROID_SDK_ROOT" + fi + + if [ -z "${ANDROID_SDK_ROOT:-}" ]; then + detected_root="$(detect_sdk_root_from_sdkmanager 2>/dev/null || true)" + if [ -n "$detected_root" ]; then + ANDROID_SDK_ROOT="$detected_root" + ANDROID_HOME="$ANDROID_SDK_ROOT" + fi + fi +fi + +if [ -z "${ANDROID_SDK_ROOT:-}" ] && [ -n "${ANDROID_HOME:-}" ]; then + ANDROID_SDK_ROOT="$ANDROID_HOME" +fi + +if [ -n "${ANDROID_SDK_ROOT:-}" ] && [ -z "${ANDROID_HOME:-}" ]; then + ANDROID_HOME="$ANDROID_SDK_ROOT" +fi + +export ANDROID_SDK_ROOT ANDROID_HOME +export ANDROID_BUILD_TOOLS_VERSION + +if [ -n "${HOME:-}" ]; then + if [ -z "${ANDROID_SDK_HOME:-}" ]; then + ANDROID_SDK_HOME="$HOME/.android" + export ANDROID_SDK_HOME + fi + if [ -z "${ANDROID_USER_HOME:-}" ]; then + ANDROID_USER_HOME="$ANDROID_SDK_HOME" + export ANDROID_USER_HOME + fi + if [ -z "${ANDROID_AVD_HOME:-}" ]; then + ANDROID_AVD_HOME="$ANDROID_SDK_HOME/avd" + export ANDROID_AVD_HOME + fi + mkdir -p "$ANDROID_SDK_HOME" "$ANDROID_AVD_HOME" 2>/dev/null || true +fi +ANDROID_ENV_LOADED=1 +ANDROID_ENV_LOADED_PID="$$" + + if [ -n "${ANDROID_SDK_ROOT:-}" ]; then + # Prefer cmdline-tools;latest, or fall back to the highest numbered cmdline-tools folder. + cmdline_tools_bin="" + if [ -d "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin" ]; then + cmdline_tools_bin="$ANDROID_SDK_ROOT/cmdline-tools/latest/bin" + else + cmdline_tools_dir=$(find "$ANDROID_SDK_ROOT/cmdline-tools" -maxdepth 1 -mindepth 1 -type d -not -name latest 2>/dev/null | sort -V | tail -n 1) + if [ -n "${cmdline_tools_dir:-}" ] && [ -d "$cmdline_tools_dir/bin" ]; then + cmdline_tools_bin="$cmdline_tools_dir/bin" + fi + fi + + new_path="$ANDROID_SDK_ROOT/emulator:$ANDROID_SDK_ROOT/platform-tools" + + if [ -n "${cmdline_tools_bin:-}" ]; then + new_path="$new_path:$cmdline_tools_bin" + fi + + new_path="$new_path:$ANDROID_SDK_ROOT/tools/bin:$PATH" + PATH="$new_path" + export PATH + if [ -n "${CI:-}" ] || [ -n "${GITHUB_ACTIONS:-}" ]; then + if [ "${ANALYTICS_CI_DEBUG:-}" = "1" ] || [ "${DEBUG:-}" = "1" ]; then + echo "Using Android SDK: $ANDROID_SDK_ROOT" + case "$ANDROID_SDK_ROOT" in + /nix/store/*) + echo "Source: Nix flake (reproducible, pinned). To use your local SDK instead, set ANDROID_LOCAL_SDK=1 before starting devbox shell." + ;; + *) + echo "Source: User/local SDK. To use the pinned Nix SDK, unset ANDROID_HOME/ANDROID_SDK_ROOT and ensure ANDROID_LOCAL_SDK is not set before starting devbox shell." + ;; + esac + fi + fi +if [ -n "${INIT_ANDROID:-}" ] && [ -z "${CI:-}" ] && [ -z "${GITHUB_ACTIONS:-}" ] && [ -z "${ANDROID_SDK_SUMMARY_PRINTED:-}" ]; then + ANDROID_SDK_SUMMARY_PRINTED=1 + export ANDROID_SDK_SUMMARY_PRINTED + + android_sdk_root="${ANDROID_SDK_ROOT:-${ANDROID_HOME:-}}" + android_sdk_version="${ANDROID_BUILD_TOOLS_VERSION:-${ANDROID_CMDLINE_TOOLS_VERSION:-30.0.3}}" + android_min_api="${ANDROID_MIN_API:-21}" + android_max_api="${ANDROID_MAX_API:-33}" + android_system_image_tag="${ANDROID_CUSTOM_SYSTEM_IMAGE_TAG:-${ANDROID_SYSTEM_IMAGE_TAG:-google_apis}}" + android_system_image_abi="" + android_target_api="${AVD_API:-${ANDROID_TARGET_API:-}}" + android_target_source="" + if [ -z "$android_target_api" ]; then + case "${TARGET_SDK:-max}" in + min) + android_target_api="$android_min_api" + android_target_source="min" + ;; + max) + android_target_api="$android_max_api" + android_target_source="max" + ;; + custom) + android_target_api="${ANDROID_CUSTOM_API:-}" + android_target_source="custom" + ;; + *) + android_target_api="$android_max_api" + android_target_source="max" + ;; + esac + elif [ -n "${AVD_API:-}" ]; then + android_target_source="avd" + elif [ -n "${ANDROID_TARGET_API:-}" ]; then + android_target_source="target" + fi + + android_target_device="${AVD_DEVICE:-}" + if [ -z "$android_target_device" ]; then + case "${TARGET_SDK:-max}" in + min) android_target_device="${ANDROID_MIN_DEVICE:-}" ;; + max) android_target_device="${ANDROID_MAX_DEVICE:-}" ;; + custom) android_target_device="${ANDROID_CUSTOM_DEVICE:-}" ;; + esac + if [ -z "$android_target_device" ]; then + if [ -n "$android_target_api" ] && [ "$android_target_api" = "$android_min_api" ]; then + android_target_device="${ANDROID_MIN_DEVICE:-}" + elif [ -n "$android_target_api" ] && [ "$android_target_api" = "$android_max_api" ]; then + android_target_device="${ANDROID_MAX_DEVICE:-}" + fi + fi + fi + + candidates="" + if [ -n "$android_sdk_root" ] && [ -n "$android_system_image_tag" ]; then + host_arch="$(uname -m)" + if [ "$host_arch" = "arm64" ] || [ "$host_arch" = "aarch64" ]; then + candidates="arm64-v8a x86_64 x86" + else + candidates="x86_64 x86 arm64-v8a" + fi + fi + + if [ -n "$android_sdk_root" ] && [ -n "$android_target_api" ] && [ -n "$android_system_image_tag" ]; then + for abi in $candidates; do + if [ -d "$android_sdk_root/system-images/android-${android_target_api}/${android_system_image_tag}/${abi}" ]; then + android_system_image_abi="$abi" + break + fi + done + fi + + if [ -n "$android_system_image_abi" ]; then + android_system_image_summary="${android_system_image_tag};${android_system_image_abi}" + else + android_system_image_summary="$android_system_image_tag" + fi + if [ -n "$android_target_device" ]; then + android_system_image_summary="${android_system_image_summary} (${android_target_device})" + fi + + if debug_enabled; then + if [ "${ANDROID_ENV_DEBUG_PRINTED:-}" != "1" ]; then + ANDROID_ENV_DEBUG_PRINTED=1 + export ANDROID_ENV_DEBUG_PRINTED + debug_dump_vars \ + ANDROID_SDK_ROOT \ + ANDROID_HOME \ + ANDROID_LOCAL_SDK \ + ANDROID_SDK_FLAKE_OUTPUT \ + ANDROID_SDK_ROOT_MIN \ + ANDROID_HOME_MIN \ + ANDROID_SDK_ROOT_MAX \ + ANDROID_HOME_MAX \ + ANDROID_MIN_API \ + ANDROID_MAX_API \ + TARGET_SDK \ + ANDROID_TARGET_API \ + ANDROID_SYSTEM_IMAGE_TAG \ + ANDROID_BUILD_TOOLS_VERSION \ + ANDROID_CMDLINE_TOOLS_VERSION + fi + fi + + echo "Resolved Android SDK" + echo " ANDROID_SDK_ROOT: ${android_sdk_root:-not set}" + echo " ANDROID_BUILD_TOOLS_VERSION: ${android_sdk_version:-30.0.3}" + echo " ANDROID_AVD_TARGET: api=${android_target_api:-not set} device=${android_target_device:-unknown} image=${android_system_image_summary:-google_apis}" + echo " Tip: use a local SDK with ANDROID_LOCAL_SDK=1 ANDROID_SDK_ROOT=/path/to/sdk (or ANDROID_HOME)." + fi +else + if [ -n "${CI:-}" ] || [ -n "${GITHUB_ACTIONS:-}" ]; then + echo "Android SDK not set; using system PATH" + fi +fi diff --git a/scripts/platforms/android/lib.sh b/scripts/platforms/android/lib.sh new file mode 100644 index 000000000..f19f32292 --- /dev/null +++ b/scripts/platforms/android/lib.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env sh + +if ! (return 0 2>/dev/null); then + echo "scripts/platforms/android/lib.sh must be sourced." >&2 + exit 1 +fi + +android_normalize_name() { + printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]' +} + +android_sanitize_avd_name() { + raw="$1" + if [ -z "$raw" ]; then + return 1 + fi + cleaned="$(printf '%s' "$raw" | tr ' ' '_' | tr -cd 'A-Za-z0-9._-')" + if [ -z "$cleaned" ]; then + return 1 + fi + printf '%s\n' "$cleaned" +} + +android_resolve_target() { + platform_min_api="${ANDROID_MIN_API:-21}" + platform_max_api="${ANDROID_MAX_API:-33}" + platform_min_device="${ANDROID_MIN_DEVICE:-pixel}" + platform_max_device="${ANDROID_MAX_DEVICE:-medium_phone}" + platform_image_tag="${ANDROID_SYSTEM_IMAGE_TAG:-google_apis}" + + target_sdk="${TARGET_SDK:-max}" + case "$target_sdk" in + min) + target_api="$platform_min_api" + target_device="$platform_min_device" + ;; + max) + target_api="$platform_max_api" + target_device="$platform_max_device" + ;; + custom) + target_api="${ANDROID_CUSTOM_API:-}" + target_device="${ANDROID_CUSTOM_DEVICE:-}" + if [ -z "$target_api" ]; then + echo "TARGET_SDK=custom requires ANDROID_CUSTOM_API to be set." >&2 + exit 1 + fi + if [ -z "$target_device" ]; then + echo "TARGET_SDK=custom requires ANDROID_CUSTOM_DEVICE to be set." >&2 + exit 1 + fi + ;; + *) + target_api="$platform_max_api" + target_device="$platform_max_device" + ;; + esac + + target_api="${AVD_API:-${ANDROID_TARGET_API:-$target_api}}" + target_tag="${AVD_TAG:-${ANDROID_CUSTOM_SYSTEM_IMAGE_TAG:-${ANDROID_SYSTEM_IMAGE_TAG:-$platform_image_tag}}}" + if [ -n "${AVD_DEVICE:-}" ]; then + target_device="$AVD_DEVICE" + fi + target_preferred_abi="${AVD_ABI:-}" + + printf '%s|%s|%s|%s\n' "$target_api" "$target_device" "$target_tag" "$target_preferred_abi" +} diff --git a/scripts/platforms/ios/actions.sh b/scripts/platforms/ios/actions.sh new file mode 100644 index 000000000..05efe2167 --- /dev/null +++ b/scripts/platforms/ios/actions.sh @@ -0,0 +1,192 @@ +#!/usr/bin/env sh + +if ! (return 0 2>/dev/null); then + echo "scripts/platforms/ios/actions.sh must be sourced." >&2 + exit 1 +fi + +ios_run() { + action="${1:-}" + shift 1 || true + + if [ "$(uname -s)" = "Darwin" ]; then + # shellcheck disable=SC1090 + . "$SCRIPTS_DIR/platforms/ios/env.sh" + fi + + case "$action" in + test) + # shellcheck disable=SC1090 + . "$SCRIPTS_DIR/platforms/ios/simctl.sh" + target_sdk="${TARGET_SDK:-max}" + runtime_version="" + device_name="" + case "$target_sdk" in + custom) + if [ -z "${IOS_CUSTOM_DEVICE:-}" ]; then + echo "TARGET_SDK=custom requires IOS_CUSTOM_DEVICE to be set." >&2 + exit 1 + fi + if [ -z "${IOS_RUNTIME_CUSTOM:-}" ]; then + echo "TARGET_SDK=custom requires IOS_RUNTIME_CUSTOM to be set." >&2 + exit 1 + fi + runtime_version="${IOS_RUNTIME_CUSTOM}" + device_name="${IOS_CUSTOM_DEVICE}" + ;; + min) + if [ -z "${IOS_RUNTIME_MIN:-}" ]; then + echo "TARGET_SDK=min requires IOS_RUNTIME_MIN to be set." >&2 + exit 1 + fi + runtime_version="${IOS_RUNTIME_MIN}" + device_name="${IOS_MIN_DEVICE:-iPhone 13}" + ;; + max) + if [ -z "${IOS_RUNTIME_MAX:-}" ]; then + echo "TARGET_SDK=max requires IOS_RUNTIME_MAX to be set." >&2 + exit 1 + fi + runtime_version="${IOS_RUNTIME_MAX}" + device_name="${IOS_MAX_DEVICE:-iPhone 17}" + ;; + *) + echo "Unsupported TARGET_SDK '${target_sdk}'. Use min, max, or custom." >&2 + exit 1 + ;; + esac + + IOS_RUNTIME="$runtime_version" + IOS_DEVICE_NAMES="$device_name" + DETOX_IOS_DEVICE="${DETOX_IOS_DEVICE:-$device_name}" + export IOS_RUNTIME IOS_DEVICE_NAMES DETOX_IOS_DEVICE + + ensure_developer_dir + require_tool jq + ensure_simctl + if ! resolve_runtime_name_strict "$runtime_version"; then + exit 1 + fi + ios_setup + yarn install --immutable + yarn e2e install + yarn e2e pods + yarn build + yarn e2e build:ios + yarn e2e test:ios + ;; + setup) + # shellcheck disable=SC1090 + . "$SCRIPTS_DIR/platforms/ios/simctl.sh" + ios_setup "$@" + ;; + start | stop | reset) + # shellcheck disable=SC1090 + . "$SCRIPTS_DIR/platforms/ios/simctl.sh" + case "$action" in + start) + ensure_developer_dir + require_tool jq + ensure_simctl + target_sdk="${TARGET_SDK:-max}" + runtime_version="" + case "$target_sdk" in + custom) + if [ -z "${IOS_CUSTOM_DEVICE:-}" ]; then + echo "TARGET_SDK=custom requires IOS_CUSTOM_DEVICE to be set." >&2 + exit 1 + fi + if [ -z "${IOS_RUNTIME_CUSTOM:-}" ]; then + echo "TARGET_SDK=custom requires IOS_RUNTIME_CUSTOM to be set." >&2 + exit 1 + fi + runtime_version="${IOS_RUNTIME_CUSTOM}" + IOS_DEVICE_NAMES="${IOS_CUSTOM_DEVICE}" + DETOX_IOS_DEVICE="${DETOX_IOS_DEVICE:-${IOS_CUSTOM_DEVICE}}" + ;; + min) + if [ -z "${IOS_RUNTIME_MIN:-}" ]; then + echo "TARGET_SDK=min requires IOS_RUNTIME_MIN to be set." >&2 + exit 1 + fi + runtime_version="${IOS_RUNTIME_MIN}" + IOS_DEVICE_NAMES="${IOS_MIN_DEVICE:-iPhone 13}" + DETOX_IOS_DEVICE="${DETOX_IOS_DEVICE:-${IOS_MIN_DEVICE:-iPhone 13}}" + ;; + max) + if [ -z "${IOS_RUNTIME_MAX:-}" ]; then + echo "TARGET_SDK=max requires IOS_RUNTIME_MAX to be set." >&2 + exit 1 + fi + runtime_version="${IOS_RUNTIME_MAX}" + IOS_DEVICE_NAMES="${IOS_MAX_DEVICE:-iPhone 17}" + DETOX_IOS_DEVICE="${DETOX_IOS_DEVICE:-${IOS_MAX_DEVICE:-iPhone 17}}" + ;; + *) + echo "Unsupported TARGET_SDK '${target_sdk}'. Use min, max, or custom." >&2 + exit 1 + ;; + esac + export IOS_DEVICE_NAMES DETOX_IOS_DEVICE + if [ -n "$runtime_version" ]; then + if ! resolve_runtime_name_strict "$runtime_version"; then + exit 1 + fi + IOS_RUNTIME="$runtime_version" + export IOS_RUNTIME + fi + ios_setup + sim_device="${DETOX_IOS_DEVICE}" + runtime_name="$(resolve_runtime_name "${runtime_version:-}" || true)" + display_name="$sim_device" + if [ -n "$runtime_name" ]; then + display_name="${sim_device} (${runtime_name})" + fi + + sim_udid="$(existing_device_udid_any_runtime "$display_name")" + if [ -z "$sim_udid" ]; then + sim_udid="$(existing_device_udid_any_runtime "$sim_device")" + fi + if [ -z "$sim_udid" ]; then + ensure_device "$sim_device" "${runtime_version:-}" + sim_udid="$(existing_device_udid_any_runtime "$display_name")" + fi + if [ -z "$sim_udid" ]; then + echo "Simulator ${sim_device} not found; ensure setup-ios created it." >&2 + exit 1 + fi + echo "Starting iOS simulator: ${sim_device} (runtime ${runtime_version:-})" + xcrun simctl boot "$sim_udid" || true + if [ -z "${CI:-}" ] && [ -z "${GITHUB_ACTIONS:-}" ]; then + open -a Simulator + fi + ;; + stop) + if command -v xcrun >/dev/null 2>&1 && xcrun -f simctl >/dev/null 2>&1; then + if xcrun simctl list devices booted | grep -q "Booted"; then + echo "Shutting down booted iOS simulators..." + xcrun simctl shutdown all >/dev/null 2>&1 || true + else + echo "No booted iOS simulators detected." + fi + else + echo "simctl not available; skipping iOS shutdown." + fi + echo "iOS simulators shutdown (if any were running)." + ;; + reset) + xcrun simctl shutdown all || true + xcrun simctl erase all || true + xcrun simctl delete all || true + xcrun simctl delete unavailable || true + killall -9 com.apple.CoreSimulatorService 2>/dev/null || true + echo "Simulators reset via simctl. Recreate via start-ios." + ;; + esac + ;; + *) + echo "Usage: run.sh ios {test|setup|start|stop|reset} [args]" >&2 + exit 1 + ;; + esac +} diff --git a/scripts/platforms/ios/env.sh b/scripts/platforms/ios/env.sh new file mode 100755 index 000000000..dd79cba18 --- /dev/null +++ b/scripts/platforms/ios/env.sh @@ -0,0 +1,191 @@ +#!/usr/bin/env sh + +if ! (return 0 2>/dev/null); then + echo "scripts/platforms/ios/env.sh must be sourced via scripts/run.sh or scripts/bootstrap/env.sh." >&2 + exit 1 +fi +set -eu + +script_dir="$(cd "$(dirname "$0")" && pwd)" +if [ "${SHARED_LOADED:-}" != "1" ] || [ "${SHARED_LOADED_PID:-}" != "$$" ]; then + init_path="$script_dir/../../bootstrap/env.sh" + if [ -n "${DEVBOX_PROJECT_ROOT:-}" ] && [ -f "${DEVBOX_PROJECT_ROOT}/scripts/bootstrap/env.sh" ]; then + init_path="${DEVBOX_PROJECT_ROOT}/scripts/bootstrap/env.sh" + elif [ -n "${DEVBOX_PROJECT_DIR:-}" ] && [ -f "${DEVBOX_PROJECT_DIR}/scripts/bootstrap/env.sh" ]; then + init_path="${DEVBOX_PROJECT_DIR}/scripts/bootstrap/env.sh" + fi + if [ ! -f "$init_path" ]; then + repo_root="" + if command -v git >/dev/null 2>&1; then + repo_root="$(git -C "$script_dir" rev-parse --show-toplevel 2>/dev/null || git -C "$PWD" rev-parse --show-toplevel 2>/dev/null || true)" + fi + if [ -n "$repo_root" ] && [ -f "$repo_root/scripts/bootstrap/env.sh" ]; then + init_path="$repo_root/scripts/bootstrap/env.sh" + fi + fi + # shellcheck disable=SC1090 + . "$init_path" +fi +debug_log_script "scripts/platforms/ios/env.sh" + +devbox_omit_nix_env() { + if [ "${DEVBOX_OMIT_NIX_ENV_APPLIED:-}" = "1" ]; then + return 0 + fi + + export DEVBOX_OMIT_NIX_ENV_APPLIED=1 + require_tool devbox "devbox is required to configure the macOS toolchain. Run this script inside a devbox shell." + + dump_env() { + if [ -n "${CI:-}" ] || [ -n "${GITHUB_ACTIONS:-}" ]; then + if ! debug_enabled; then + return 0 + fi + 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:-}" + fi + } + + dump_env "before" + + devbox_config_path="" + if [ -n "${DEVBOX_CONFIG:-}" ] && [ -f "$DEVBOX_CONFIG" ]; then + devbox_config_path="$DEVBOX_CONFIG" + elif [ -n "${DEVBOX_CONFIG_PATH:-}" ] && [ -f "$DEVBOX_CONFIG_PATH" ]; then + devbox_config_path="$DEVBOX_CONFIG_PATH" + elif [ -n "${DEVBOX_CONFIG_DIR:-}" ] && [ -f "${DEVBOX_CONFIG_DIR%/}/devbox.json" ]; then + devbox_config_path="${DEVBOX_CONFIG_DIR%/}/devbox.json" + fi + + if [ -n "$devbox_config_path" ]; then + eval "$(devbox --config "$devbox_config_path" shellenv --install --no-refresh-alias --omit-nix-env=true)" + else + eval "$(devbox shellenv --install --no-refresh-alias --omit-nix-env=true)" + fi + + 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="$(eval "printf '%s' \"\${$var-}\"")" + if [ -n "$value" ] && [ "${value#/nix/store/}" != "$value" ]; then + eval "unset $var" + fi + done + + if [ -x /usr/bin/clang ]; then + CC=/usr/bin/clang + CXX=/usr/bin/clang++ + export CC CXX + 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 + DEVELOPER_DIR="$dev_dir" + export DEVELOPER_DIR + fi + fi + + unset SDKROOT + fi + + dump_env "after" + +} + +devbox_omit_nix_env + +IOS_ENV_LOADED=1 +IOS_ENV_LOADED_PID="$$" + +if debug_enabled; then + if [ "${IOS_ENV_DEBUG_PRINTED:-}" != "1" ]; then + IOS_ENV_DEBUG_PRINTED=1 + export IOS_ENV_DEBUG_PRINTED + debug_dump_vars \ + IOS_RUNTIME \ + IOS_RUNTIME_MIN \ + IOS_RUNTIME_MAX \ + IOS_RUNTIME_CUSTOM \ + IOS_DEVICE_NAMES \ + DETOX_IOS_DEVICE \ + IOS_DEVELOPER_DIR \ + IOS_DOWNLOAD_RUNTIME \ + DEVELOPER_DIR \ + SDKROOT \ + CC \ + CXX + if command -v sw_vers >/dev/null 2>&1; then + sw_vers + fi + if command -v xcode-select >/dev/null 2>&1; then + echo "xcode-select: $(xcode-select -p 2>/dev/null || true)" + fi + if command -v xcodebuild >/dev/null 2>&1; then + xcodebuild -version 2>/dev/null || true + fi + if command -v swiftc >/dev/null 2>&1; then + swiftc --version 2>/dev/null || true + fi + if command -v clang >/dev/null 2>&1; then + clang --version 2>/dev/null | head -n 1 || true + fi + fi +fi + +if [ -n "${INIT_IOS:-}" ] && [ -z "${CI:-}" ] && [ -z "${GITHUB_ACTIONS:-}" ] && [ -z "${IOS_SDK_SUMMARY_PRINTED:-}" ]; then + IOS_SDK_SUMMARY_PRINTED=1 + export IOS_SDK_SUMMARY_PRINTED + + repo_root="${PROJECT_ROOT:-${DEVBOX_PROJECT_ROOT:-}}" + if [ -z "$repo_root" ] && [ -n "${SCRIPTS_DIR:-}" ]; then + repo_root="$(cd "$SCRIPTS_DIR/.." && pwd)" + fi + if [ -z "$repo_root" ]; then + repo_root="$(cd "$(dirname "$0")/../.." && pwd)" + fi + + ios_runtime="${IOS_RUNTIME_MAX:-}" + if [ -z "$ios_runtime" ] && command -v xcrun >/dev/null 2>&1; then + ios_runtime="$(xcrun --sdk iphonesimulator --show-sdk-version 2>/dev/null || true)" + fi + + xcode_dir="${DEVELOPER_DIR:-}" + if [ -z "$xcode_dir" ] && command -v xcode-select >/dev/null 2>&1; then + xcode_dir="$(xcode-select -p 2>/dev/null || true)" + fi + + xcode_version="unknown" + if command -v xcodebuild >/dev/null 2>&1; then + xcode_version="$(xcodebuild -version 2>/dev/null | awk 'NR==1{print $2}')" + fi + + ios_target_device="${DETOX_IOS_DEVICE:-}" + if [ -z "$ios_target_device" ]; then + if [ -n "${IOS_DEVICE_NAMES:-}" ]; then + ios_target_device="$(printf '%s' "$IOS_DEVICE_NAMES" | cut -d',' -f1 | xargs)" + else + case "${TARGET_SDK:-max}" in + min) ios_target_device="${IOS_MIN_DEVICE:-}" ;; + max) ios_target_device="${IOS_MAX_DEVICE:-}" ;; + custom) ios_target_device="${IOS_CUSTOM_DEVICE:-}" ;; + *) ios_target_device="${IOS_MAX_DEVICE:-}" ;; + esac + fi + fi + ios_target_runtime="${IOS_RUNTIME:-$ios_runtime}" + + echo "Resolved iOS SDK" + echo " DEVELOPER_DIR: ${xcode_dir:-not set}" + echo " XCODE_VERSION: ${xcode_version:-unknown}" + echo " IOS_RUNTIME: ${ios_runtime:-not set}" + echo " IOS_SIM_TARGET: device=${ios_target_device:-unknown} runtime=${ios_target_runtime:-not set}" +fi diff --git a/scripts/platforms/ios/simctl.sh b/scripts/platforms/ios/simctl.sh new file mode 100644 index 000000000..5c524009d --- /dev/null +++ b/scripts/platforms/ios/simctl.sh @@ -0,0 +1,274 @@ +#!/usr/bin/env sh +set -eu + +if ! (return 0 2>/dev/null); then + echo "scripts/platforms/ios/simctl.sh must be sourced via scripts/run.sh." >&2 + exit 1 +fi + +script_dir="$(cd "$(dirname "$0")" && pwd)" +if [ "${SHARED_LOADED:-}" != "1" ] || [ "${SHARED_LOADED_PID:-}" != "$$" ]; then + init_path="$script_dir/../../bootstrap/env.sh" + if [ -n "${DEVBOX_PROJECT_ROOT:-}" ] && [ -f "${DEVBOX_PROJECT_ROOT}/scripts/bootstrap/env.sh" ]; then + init_path="${DEVBOX_PROJECT_ROOT}/scripts/bootstrap/env.sh" + elif [ -n "${DEVBOX_PROJECT_DIR:-}" ] && [ -f "${DEVBOX_PROJECT_DIR}/scripts/bootstrap/env.sh" ]; then + init_path="${DEVBOX_PROJECT_DIR}/scripts/bootstrap/env.sh" + fi + if [ ! -f "$init_path" ]; then + repo_root="" + if command -v git >/dev/null 2>&1; then + repo_root="$(git -C "$script_dir" rev-parse --show-toplevel 2>/dev/null || git -C "$PWD" rev-parse --show-toplevel 2>/dev/null || true)" + fi + if [ -n "$repo_root" ] && [ -f "$repo_root/scripts/bootstrap/env.sh" ]; then + init_path="$repo_root/scripts/bootstrap/env.sh" + fi + fi + # shellcheck disable=SC1090 + . "$init_path" +fi +debug_log_script "scripts/platforms/ios/simctl.sh" + +ensure_core_sim_service() { + status=0 + output="$(xcrun simctl list devices -j 2>&1)" || status=$? + if [ "$status" -ne 0 ]; then + echo "simctl failed while listing devices (status ${status}). CoreSimulatorService may be unhealthy." >&2 + echo "Try restarting it:" >&2 + echo " killall -9 com.apple.CoreSimulatorService 2>/dev/null || true" >&2 + echo " launchctl kickstart -k gui/$UID/com.apple.CoreSimulatorService" >&2 + echo "Then open Simulator once and rerun devbox run setup-ios." >&2 + echo "simctl error output:" >&2 + echo "$output" >&2 + return 1 + fi + + if echo "$output" | grep -q "CoreSimulatorService connection became invalid"; then + echo "CoreSimulatorService is not healthy. Try restarting it:" >&2 + echo " killall -9 com.apple.CoreSimulatorService 2>/dev/null || true" >&2 + echo " launchctl kickstart -k gui/$UID/com.apple.CoreSimulatorService" >&2 + echo "Then open Simulator once and rerun devbox run setup-ios." >&2 + echo "simctl error output:" >&2 + echo "$output" >&2 + return 1 + fi +} + +pick_runtime() { + preferred="$1" + json="$(xcrun simctl list runtimes -j)" + choice="$(echo "$json" | jq -r --arg v "$preferred" '.runtimes[] | select(.isAvailable and (.name|startswith("iOS \($v)"))) | "\(.identifier)|\(.name)"' | head -n1)" + if [ -z "$choice" ] || [ "$choice" = "null" ]; then + choice="$(echo "$json" | jq -r '.runtimes[] | select(.isAvailable and (.name|startswith("iOS "))) | "\(.version)|\(.identifier)|\(.name)"' | sort -Vr | head -n1 | cut -d"|" -f2-)" + fi + if [ -n "$choice" ] && [ "$choice" != "null" ]; then + printf '%s\n' "$choice" + return 0 + fi + return 1 +} + +resolve_runtime() { + preferred="$1" + if choice="$(pick_runtime "$preferred")"; then + printf '%s\n' "$choice" + return 0 + fi + + if [ "${IOS_DOWNLOAD_RUNTIME:-1}" != "0" ] && command -v xcodebuild >/dev/null 2>&1; then + echo "Preferred runtime iOS ${preferred} not found. Attempting to download via xcodebuild -downloadPlatform iOS..." >&2 + if xcodebuild -downloadPlatform iOS; then + if choice="$(pick_runtime "$preferred")"; then + printf '%s\n' "$choice" + return 0 + fi + else + echo "xcodebuild -downloadPlatform iOS failed; continuing with available runtimes." >&2 + fi + fi + + pick_runtime "$preferred" +} + +resolve_runtime_strict() { + preferred="$1" + if choice="$(pick_runtime "$preferred")"; then + printf '%s\n' "$choice" + return 0 + fi + + if [ "${IOS_DOWNLOAD_RUNTIME:-1}" != "0" ] && command -v xcodebuild >/dev/null 2>&1; then + echo "Preferred runtime iOS ${preferred} not found. Attempting to download via xcodebuild -downloadPlatform iOS..." >&2 + if xcodebuild -downloadPlatform iOS; then + if choice="$(pick_runtime "$preferred")"; then + printf '%s\n' "$choice" + return 0 + fi + else + echo "xcodebuild -downloadPlatform iOS failed." >&2 + fi + fi + + echo "Preferred runtime iOS ${preferred} not found." >&2 + return 1 +} + +resolve_runtime_name() { + preferred="$1" + choice="$(resolve_runtime "$preferred" || true)" + if [ -n "$choice" ]; then + printf '%s\n' "$choice" | cut -d'|' -f2 + return 0 + fi + return 1 +} + +resolve_runtime_name_strict() { + preferred="$1" + choice="$(resolve_runtime_strict "$preferred" || true)" + if [ -n "$choice" ]; then + printf '%s\n' "$choice" | cut -d'|' -f2 + return 0 + fi + return 1 +} + +existing_device_udid_any_runtime() { + name="$1" + xcrun simctl list devices -j | jq -r --arg name "$name" '.devices[]?[]? | select(.name == $name) | .udid' | head -n1 +} + +device_data_dir_exists() { + udid="${1:-}" + if [ -z "$udid" ]; then + return 1 + fi + dir="$HOME/Library/Developer/CoreSimulator/Devices/$udid" + [ -d "$dir" ] +} + +devicetype_id_for_name() { + name="$1" + xcrun simctl list devicetypes -j | jq -r --arg name "$name" '.devicetypes[] | select((.name|ascii_downcase) == ($name|ascii_downcase)) | .identifier' | head -n1 +} + +ensure_device() { + base_name="$1" + preferred_runtime="$2" + + # If a device with this name already exists anywhere, reuse it. + existing_udid="$(existing_device_udid_any_runtime "$base_name")" + if [ -n "$existing_udid" ]; then + if device_data_dir_exists "$existing_udid"; then + echo "Found existing ${base_name}: ${existing_udid}" + return 0 + fi + echo "Existing ${base_name} (${existing_udid}) is missing its data directory. Deleting stale simulator..." + xcrun simctl delete "$existing_udid" || true + fi + + choice="$(resolve_runtime "$preferred_runtime" || true)" + if [ -z "$choice" ]; then + echo "No available iOS simulator runtime found. Install one in Xcode (Settings > Platforms) and retry." >&2 + return 1 + fi + runtime_id="$(printf '%s' "$choice" | cut -d'|' -f1)" + runtime_name="$(printf '%s' "$choice" | cut -d'|' -f2)" + + display_name="${base_name} (${runtime_name})" + + device_type="$(devicetype_id_for_name "$base_name" || true)" + if [ -z "$device_type" ]; then + echo "Device type '${base_name}' is unavailable in this Xcode install. Skipping ${display_name}." >&2 + return 0 + fi + + # Also check for an existing device with the runtime-qualified display name. + existing_udid="$(existing_device_udid_any_runtime "$display_name")" + if [ -n "$existing_udid" ]; then + if device_data_dir_exists "$existing_udid"; then + echo "Found existing ${display_name}: ${existing_udid}" + return 0 + fi + echo "Existing ${display_name} (${existing_udid}) is missing its data directory. Deleting stale simulator..." + xcrun simctl delete "$existing_udid" || true + fi + + echo "Creating ${display_name}..." + xcrun simctl create "$display_name" "$device_type" "$runtime_id" + echo "Created ${display_name}" +} + +ensure_developer_dir() { + desired="${IOS_DEVELOPER_DIR:-}" + PATH="/usr/bin:/bin:/usr/sbin:/sbin:${PATH}" + export PATH + 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 + + require_dir "$desired" "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)." + require_dir_contains "$desired" "Toolchains/XcodeDefault.xctoolchain" "Xcode toolchain missing under ${desired}." + require_dir_contains "$desired" "Platforms/iPhoneSimulator.platform" "iPhoneSimulator platform missing under ${desired}." + + DEVELOPER_DIR="$desired" + PATH="$DEVELOPER_DIR/usr/bin:$PATH" + export DEVELOPER_DIR PATH + return 0 +} + +ensure_simctl() { + if xcrun -f simctl >/dev/null 2>&1; then + return 0 + fi + cat >&2 <<'EOM' +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. +EOM + exit 1 +} + +ios_setup() { + if [ -n "${IOS_XCODE_ENV_PATH:-}" ]; then + node_binary="${IOS_NODE_BINARY:-${NODE_BINARY:-}}" + if [ -z "$node_binary" ]; then + echo "IOS_XCODE_ENV_PATH is set but IOS_NODE_BINARY/NODE_BINARY is empty." >&2 + return 1 + fi + env_dir="$(dirname "$IOS_XCODE_ENV_PATH")" + if [ ! -d "$env_dir" ]; then + echo "IOS_XCODE_ENV_PATH directory does not exist: ${env_dir}" >&2 + return 1 + fi + printf 'export NODE_BINARY=%s\n' "$node_binary" >"$IOS_XCODE_ENV_PATH" + fi + 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 ! ensure_core_sim_service; then + return 1 + fi + devices_list="${IOS_DEVICE_NAMES:-${IOS_MIN_DEVICE:-iPhone 13},${IOS_MAX_DEVICE:-iPhone 17}}" + runtime="${IOS_RUNTIME:-${IOS_RUNTIME_MAX:-}}" + if [ -z "$runtime" ]; then + echo "IOS_RUNTIME (or IOS_RUNTIME_MAX) must be set to create simulators." >&2 + return 1 + fi + + ifs_backup="$IFS" + IFS=',' + for device in $devices_list; do + device_trimmed="$(printf '%s' "$device" | xargs)" + ensure_device "$device_trimmed" "$runtime" + done + IFS="$ifs_backup" + echo "Done. Launch via Xcode > Devices or 'xcrun simctl boot \"\"' then 'open -a Simulator'." +} diff --git a/scripts/run.sh b/scripts/run.sh new file mode 100644 index 000000000..77f8665c8 --- /dev/null +++ b/scripts/run.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env sh +set -eu + +script_dir="$(cd "$(dirname "$0")" && pwd)" +bootstrap_dir="$script_dir/bootstrap" + +if [ "${RUN_SH_ACTIVE:-}" = "1" ]; then + echo "scripts/run.sh is already running." >&2 + exit 1 +fi +RUN_SH_ACTIVE=1 +export RUN_SH_ACTIVE + +# shellcheck disable=SC1090 +. "$bootstrap_dir/init.sh" +load_env "$script_dir" +debug_log_script "scripts/run.sh" + +scripts_root="${SCRIPTS_DIR:-$script_dir}" + +platform="${1:-}" +action="${2:-}" +shift 2 || true + +run_build() { + yarn install --immutable + yarn build + yarn lint +} + + +case "$platform" in + android) + # shellcheck disable=SC1090 + . "$scripts_root/platforms/android/actions.sh" + android_run "$action" "$@" + ;; + ios) + # shellcheck disable=SC1090 + . "$scripts_root/platforms/ios/actions.sh" + ios_run "$action" "$@" + ;; + build) + run_build "$@" + ;; + *) + echo "Usage: run.sh {android|ios} [args] | run.sh build" >&2 + exit 1 + ;; +esac diff --git a/scripts/shared/debug.sh b/scripts/shared/debug.sh new file mode 100644 index 000000000..253ed5db8 --- /dev/null +++ b/scripts/shared/debug.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env sh + +if ! (return 0 2>/dev/null); then + echo "scripts/shared/debug.sh must be sourced." >&2 + exit 1 +fi + +if [ "${DEBUG_SH_LOADED:-}" = "1" ] && [ "${DEBUG_SH_LOADED_PID:-}" = "$$" ]; then + return 0 2>/dev/null || exit 0 +fi +DEBUG_SH_LOADED=1 +DEBUG_SH_LOADED_PID="$$" + +debug_enabled() { + [ "${ANALYTICS_CI_DEBUG:-}" = "1" ] || [ "${DEBUG:-}" = "1" ] +} + +debug_log() { + if debug_enabled; then + printf '%s\n' "DEBUG: $*" + fi +} + +debug_log_script() { + if debug_enabled; then + if (return 0 2>/dev/null); then + context="sourced" + else + context="run" + fi + debug_log "$1 ($context)" + fi +} + +debug_dump_vars() { + if debug_enabled; then + for var in "$@"; do + value="$(eval "printf '%s' \"\${$var-}\"")" + printf 'DEBUG: %s=%s\n' "$var" "$value" + done + fi +} diff --git a/scripts/shared/defaults.sh b/scripts/shared/defaults.sh new file mode 100644 index 000000000..e737fd07d --- /dev/null +++ b/scripts/shared/defaults.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env sh + +if ! (return 0 2>/dev/null); then + echo "scripts/shared/defaults.sh must be sourced." >&2 + exit 1 +fi + +if [ "${DEFAULTS_SH_LOADED:-}" = "1" ] && [ "${DEFAULTS_SH_LOADED_PID:-}" = "$$" ]; then + return 0 2>/dev/null || exit 0 +fi +DEFAULTS_SH_LOADED=1 +DEFAULTS_SH_LOADED_PID="$$" + +if [ "${ENV_DEFAULTS_LOADED:-}" = "1" ] && [ "${ENV_DEFAULTS_LOADED_PID:-}" = "$$" ]; then + return 0 2>/dev/null || exit 0 +fi + +if [ -n "${PROJECT_ROOT:-}" ]; then + defaults_json="${ENV_DEFAULTS_JSON:-$PROJECT_ROOT/nix/defaults.json}" + jq_cmd="" + if command -v jq >/dev/null 2>&1; then + jq_cmd="jq" + fi + + if [ -f "$defaults_json" ] && [ -n "$jq_cmd" ]; then + tab="$(printf '\t')" + while IFS="$tab" read -r key value; do + if [ -z "$key" ]; then + continue + fi + current="$(eval "printf '%s' \"\${$key-}\"")" + if [ -z "$current" ]; then + eval "$key=\"\$value\"" + export "$key" + fi + done </dev/null 2>&1; then + sw_vers +fi +echo + +if command -v xcode-select >/dev/null 2>&1; then + echo "xcode-select: $(xcode-select -p 2>/dev/null || true)" +fi + +if command -v xcodebuild >/dev/null 2>&1; then + xcodebuild -version + xcodebuild -showsdks +fi +echo + +if command -v xcrun >/dev/null 2>&1; then + sdk_path="$(xcrun --sdk iphonesimulator --show-sdk-path 2>/dev/null || true)" + platform_path="$(xcrun --sdk iphonesimulator --show-sdk-platform-path 2>/dev/null || true)" + swift_toolchain_path="$(xcrun --sdk iphonesimulator --show-sdk-toolchain-path 2>/dev/null || true)" + echo "iphonesimulator sdk path: ${sdk_path:-unknown}" + echo "iphonesimulator platform path: ${platform_path:-unknown}" + echo "iphonesimulator toolchain path: ${swift_toolchain_path:-unknown}" +fi +echo + +toolchain_swift_dir="$(xcode-select -p 2>/dev/null)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphonesimulator" +sdk_swift_dir="${sdk_path:-}/usr/lib/swift" + +echo "Swift toolchain libs (simulator): ${toolchain_swift_dir}" +ls -la "$toolchain_swift_dir" 2>/dev/null || true +echo + +echo "Swift runtime libs (SDK): ${sdk_swift_dir}" +ls -la "$sdk_swift_dir" 2>/dev/null || true +echo + +for lib in swiftCompatibilityPacks swiftCompatibility56 swiftCompatibilityConcurrency; do + toolchain_dylib="${toolchain_swift_dir}/lib${lib}.dylib" + toolchain_static="${toolchain_swift_dir}/lib${lib}.a" + sdk_dylib="${sdk_swift_dir}/lib${lib}.dylib" + sdk_static="${sdk_swift_dir}/lib${lib}.a" + if [ -e "$toolchain_dylib" ]; then + echo "FOUND toolchain ${lib}: ${toolchain_dylib}" + elif [ -e "$toolchain_static" ]; then + echo "FOUND toolchain ${lib}: ${toolchain_static}" + elif [ -e "$sdk_dylib" ]; then + echo "FOUND sdk ${lib}: ${sdk_dylib}" + elif [ -e "$sdk_static" ]; then + echo "FOUND sdk ${lib}: ${sdk_static}" + else + echo "MISSING ${lib}" + fi +done diff --git a/scripts/shared/common.sh b/scripts/shared/project.sh similarity index 54% rename from scripts/shared/common.sh rename to scripts/shared/project.sh index a62edbf02..546641ad2 100644 --- a/scripts/shared/common.sh +++ b/scripts/shared/project.sh @@ -1,13 +1,15 @@ #!/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 -} +if ! (return 0 2>/dev/null); then + echo "scripts/shared/project.sh must be sourced." >&2 + exit 1 +fi + +if [ "${PROJECT_SH_LOADED:-}" = "1" ] && [ "${PROJECT_SH_LOADED_PID:-}" = "$$" ]; then + return 0 2>/dev/null || exit 0 +fi +PROJECT_SH_LOADED=1 +PROJECT_SH_LOADED_PID="$$" ensure_project_root() { if [ -n "${PROJECT_ROOT:-}" ]; then @@ -26,9 +28,9 @@ ensure_project_root() { if [ -n "$git_root" ]; then PROJECT_ROOT="$git_root" - elif [ -f "$base_dir/../shared/common.sh" ] && [ -f "$base_dir/../build.sh" ]; then + elif [ -f "$base_dir/../shared/project.sh" ] && [ -f "$base_dir/../run.sh" ]; then PROJECT_ROOT="$(cd "$base_dir/.." && pwd)" - elif [ -f "$base_dir/shared/common.sh" ] && [ -f "$base_dir/build.sh" ]; then + elif [ -f "$base_dir/shared/project.sh" ] && [ -f "$base_dir/run.sh" ]; then PROJECT_ROOT="$(cd "$base_dir" && pwd)" fi @@ -37,15 +39,6 @@ ensure_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 diff --git a/scripts/shared/tools.sh b/scripts/shared/tools.sh new file mode 100644 index 000000000..56c0eae78 --- /dev/null +++ b/scripts/shared/tools.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env sh + +if ! (return 0 2>/dev/null); then + echo "scripts/shared/tools.sh must be sourced." >&2 + exit 1 +fi + +if [ "${TOOLS_SH_LOADED:-}" = "1" ] && [ "${TOOLS_SH_LOADED_PID:-}" = "$$" ]; then + return 0 2>/dev/null || exit 0 +fi +TOOLS_SH_LOADED=1 +TOOLS_SH_LOADED_PID="$$" + +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 +} + +require_file() { + path="$1" + message="${2:-Missing required file: $path.}" + if [ ! -f "$path" ]; then + echo "$message" >&2 + exit 1 + fi +} + +require_dir() { + path="$1" + message="${2:-Missing required directory: $path.}" + if [ ! -d "$path" ]; then + echo "$message" >&2 + exit 1 + fi +} + +require_dir_contains() { + base="$1" + subpath="$2" + message="${3:-Missing required path: $base/$subpath.}" + if [ ! -e "$base/$subpath" ]; then + echo "$message" >&2 + exit 1 + fi +} + +require_var() { + var_name="$1" + message="${2:-Missing required environment variable: $var_name.}" + value="$(eval "printf '%s' \"\${$var_name-}\"")" + if [ -z "$value" ]; then + echo "$message" >&2 + exit 1 + fi +} diff --git a/shells/android-max/devbox.json b/shells/android-max/devbox.json new file mode 100644 index 000000000..ceeae43e6 --- /dev/null +++ b/shells/android-max/devbox.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.14.2/.schema/devbox.schema.json", + "packages": { + "yarn-berry": "latest", + "git": "latest", + "bash": "latest", + "nodejs": "latest", + "coreutils": "latest", + "gnused": "latest", + "gnugrep": "latest", + "gawk": "latest", + "jdk17": "latest", + "gradle": "8.0.1", + "jq": "latest", + "path:../../nix#android-sdk-max": "" + }, + "shell": { + "init_hook": [ + "export TARGET_SDK=max", + "export ANDROID_SDK_FLAKE_OUTPUT=android-sdk-max", + "INIT_ANDROID=1 . $DEVBOX_PROJECT_ROOT/../../scripts/bootstrap/env.sh" + ], + "scripts": { + "setup-android": ["sh $DEVBOX_PROJECT_ROOT/../../scripts/run.sh android setup"], + "test-android": ["sh $DEVBOX_PROJECT_ROOT/../../scripts/run.sh android test"] + } + } +} diff --git a/shells/android-max/devbox.lock b/shells/android-max/devbox.lock new file mode 100644 index 000000000..f15292572 --- /dev/null +++ b/shells/android-max/devbox.lock @@ -0,0 +1,250 @@ +{ + "lockfile_version": "1", + "packages": { + "github:NixOS/nixpkgs/nixpkgs-unstable": { + "last_modified": "2026-01-27T15:18:14Z", + "resolved": "github:NixOS/nixpkgs/afce96367b2e37fc29afb5543573cd49db3357b7?lastModified=1769527094" + }, + "gradle@8.0.1": { + "last_modified": "2023-07-24T21:56:31Z", + "plugin_version": "0.0.1", + "resolved": "github:NixOS/nixpkgs/dfcffbd74fd6f0419370d8240e445252a39f4d10#gradle", + "source": "devbox-search", + "version": "8.0.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/lrh7pjhni25c21llri3v1ya6n3nylng4-gradle-8.0.1", + "default": true + } + ], + "store_path": "/nix/store/lrh7pjhni25c21llri3v1ya6n3nylng4-gradle-8.0.1" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/5h0z90r6r8c4s9x72ik0jp6sf782h4j0-gradle-8.0.1", + "default": true + } + ], + "store_path": "/nix/store/5h0z90r6r8c4s9x72ik0jp6sf782h4j0-gradle-8.0.1" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/7k8494nxy4x2sh8d0rzpmmh6pmk1q1wf-gradle-8.0.1", + "default": true + } + ], + "store_path": "/nix/store/7k8494nxy4x2sh8d0rzpmmh6pmk1q1wf-gradle-8.0.1" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/83jn6p607rjb784jvypvx5r30pgq7kwa-gradle-8.0.1", + "default": true + } + ], + "store_path": "/nix/store/83jn6p607rjb784jvypvx5r30pgq7kwa-gradle-8.0.1" + } + } + }, + "jdk17@latest": { + "last_modified": "2025-10-22T20:59:19Z", + "resolved": "github:NixOS/nixpkgs/01b6809f7f9d1183a2b3e081f0a1e6f8f415cb09#jdk17", + "source": "devbox-search", + "version": "17.0.12", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/hlm8a8cnp4hm8xkg0a2yy4kv7cq44jii-zulu-ca-jdk-17.0.12", + "default": true + } + ], + "store_path": "/nix/store/hlm8a8cnp4hm8xkg0a2yy4kv7cq44jii-zulu-ca-jdk-17.0.12" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/i4zgq9685y6284hbf5a7ac9ysb88dvlz-zulu-ca-jdk-17.0.12", + "default": true + } + ], + "store_path": "/nix/store/i4zgq9685y6284hbf5a7ac9ysb88dvlz-zulu-ca-jdk-17.0.12" + } + } + }, + "jq@latest": { + "last_modified": "2026-01-26T13:12:53Z", + "resolved": "github:NixOS/nixpkgs/13b0f9e6ac78abbbb736c635d87845c4f4bee51b#jq", + "source": "devbox-search", + "version": "1.8.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/qjs0qndyz1g97rsc1zp4cd692y5iph64-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/jlpyybc7pdh4gk17dc266d6a1szm7dk6-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/bhryp10d5w5h9rsav5k9m9jb55z26bsl-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/238sn0gg3i3i9v6kgx4g1k6b19frzy49-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/n64h0247s3674kry90l6kszx06zyrgfn-jq-1.8.1" + } + ], + "store_path": "/nix/store/qjs0qndyz1g97rsc1zp4cd692y5iph64-jq-1.8.1-bin" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/c1qm5fsn6qbl09xdjx649vifabypyywd-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/2pjwv0ab8nilrg1lvjazf9y9w6g6pk5y-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/2sy4y09ddbi64pbg4is078110z70jsdw-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/vx81xggapqwdd2l64mmxrkbafih461jc-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/cfhajjz1k7gf31krbj18q9acb54xp5z9-jq-1.8.1" + } + ], + "store_path": "/nix/store/c1qm5fsn6qbl09xdjx649vifabypyywd-jq-1.8.1-bin" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/51343hgchh7by4l8r1g244ma05ny3x0b-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/vx840ik1sj1h8fhqwa40aglvrgpa0r18-jq-1.8.1-man", + "default": true + }, + { + "name": "out", + "path": "/nix/store/bmg2xfw86wavg7fm062nyf6v48xzxh0j-jq-1.8.1" + }, + { + "name": "dev", + "path": "/nix/store/pp2hrljvalrrwyxh7is69nnlmxb2m7lk-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/h05xaf7fsasgp8cpyar2195cc8lbgih8-jq-1.8.1-doc" + } + ], + "store_path": "/nix/store/51343hgchh7by4l8r1g244ma05ny3x0b-jq-1.8.1-bin" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/qnaw7i777j52fpgbl5pgmzkq85znp083-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/6mh88qsh57ivh31c5nqxc43n0hv9xhbk-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/d73i1fvhrqms0sbfrvqaynsr8iva216v-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/3ynrp4ypwv1g1jgsk638443p8lpd9g8f-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/fgsvqffyvcpjqs093wwf2d6dzxnmnqnv-jq-1.8.1" + } + ], + "store_path": "/nix/store/qnaw7i777j52fpgbl5pgmzkq85znp083-jq-1.8.1-bin" + } + } + }, + "yarn-berry@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#yarn-berry", + "source": "devbox-search", + "version": "4.12.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/k9bh72bpyqjnfq1nd3c6p1z2ijkx2yg6-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/k9bh72bpyqjnfq1nd3c6p1z2ijkx2yg6-yarn-berry-4.12.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/mcvbqzb1kir87g5pm5624c5ysnfh89wp-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/mcvbqzb1kir87g5pm5624c5ysnfh89wp-yarn-berry-4.12.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/hn3j954rlcc0gx8nm41sgfzbx0qwfq3c-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/hn3j954rlcc0gx8nm41sgfzbx0qwfq3c-yarn-berry-4.12.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/2pmvaaggpi7ikx9xmhy3x2j0rpklaqrv-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/2pmvaaggpi7ikx9xmhy3x2j0rpklaqrv-yarn-berry-4.12.0" + } + } + } + } +} diff --git a/shells/android-max/gradle.properties b/shells/android-max/gradle.properties new file mode 100644 index 000000000..a468eda9e --- /dev/null +++ b/shells/android-max/gradle.properties @@ -0,0 +1 @@ +org.gradle.java.home=/nix/store/hlm8a8cnp4hm8xkg0a2yy4kv7cq44jii-zulu-ca-jdk-17.0.12 diff --git a/shells/android-min/devbox.json b/shells/android-min/devbox.json new file mode 100644 index 000000000..7ef9735c2 --- /dev/null +++ b/shells/android-min/devbox.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.14.2/.schema/devbox.schema.json", + "packages": { + "yarn-berry": "latest", + "git": "latest", + "bash": "latest", + "nodejs": "latest", + "coreutils": "latest", + "gnused": "latest", + "gnugrep": "latest", + "gawk": "latest", + "jdk17": "latest", + "gradle": "8.0.1", + "jq": "latest", + "path:../../nix#android-sdk-min": "" + }, + "shell": { + "init_hook": [ + "export TARGET_SDK=min", + "export ANDROID_SDK_FLAKE_OUTPUT=android-sdk-min", + "INIT_ANDROID=1 . $DEVBOX_PROJECT_ROOT/../../scripts/bootstrap/env.sh" + ], + "scripts": { + "setup-android": ["sh $DEVBOX_PROJECT_ROOT/../../scripts/run.sh android setup"], + "test-android": ["sh $DEVBOX_PROJECT_ROOT/../../scripts/run.sh android test"] + } + } +} diff --git a/shells/android-min/devbox.lock b/shells/android-min/devbox.lock new file mode 100644 index 000000000..f15292572 --- /dev/null +++ b/shells/android-min/devbox.lock @@ -0,0 +1,250 @@ +{ + "lockfile_version": "1", + "packages": { + "github:NixOS/nixpkgs/nixpkgs-unstable": { + "last_modified": "2026-01-27T15:18:14Z", + "resolved": "github:NixOS/nixpkgs/afce96367b2e37fc29afb5543573cd49db3357b7?lastModified=1769527094" + }, + "gradle@8.0.1": { + "last_modified": "2023-07-24T21:56:31Z", + "plugin_version": "0.0.1", + "resolved": "github:NixOS/nixpkgs/dfcffbd74fd6f0419370d8240e445252a39f4d10#gradle", + "source": "devbox-search", + "version": "8.0.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/lrh7pjhni25c21llri3v1ya6n3nylng4-gradle-8.0.1", + "default": true + } + ], + "store_path": "/nix/store/lrh7pjhni25c21llri3v1ya6n3nylng4-gradle-8.0.1" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/5h0z90r6r8c4s9x72ik0jp6sf782h4j0-gradle-8.0.1", + "default": true + } + ], + "store_path": "/nix/store/5h0z90r6r8c4s9x72ik0jp6sf782h4j0-gradle-8.0.1" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/7k8494nxy4x2sh8d0rzpmmh6pmk1q1wf-gradle-8.0.1", + "default": true + } + ], + "store_path": "/nix/store/7k8494nxy4x2sh8d0rzpmmh6pmk1q1wf-gradle-8.0.1" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/83jn6p607rjb784jvypvx5r30pgq7kwa-gradle-8.0.1", + "default": true + } + ], + "store_path": "/nix/store/83jn6p607rjb784jvypvx5r30pgq7kwa-gradle-8.0.1" + } + } + }, + "jdk17@latest": { + "last_modified": "2025-10-22T20:59:19Z", + "resolved": "github:NixOS/nixpkgs/01b6809f7f9d1183a2b3e081f0a1e6f8f415cb09#jdk17", + "source": "devbox-search", + "version": "17.0.12", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/hlm8a8cnp4hm8xkg0a2yy4kv7cq44jii-zulu-ca-jdk-17.0.12", + "default": true + } + ], + "store_path": "/nix/store/hlm8a8cnp4hm8xkg0a2yy4kv7cq44jii-zulu-ca-jdk-17.0.12" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/i4zgq9685y6284hbf5a7ac9ysb88dvlz-zulu-ca-jdk-17.0.12", + "default": true + } + ], + "store_path": "/nix/store/i4zgq9685y6284hbf5a7ac9ysb88dvlz-zulu-ca-jdk-17.0.12" + } + } + }, + "jq@latest": { + "last_modified": "2026-01-26T13:12:53Z", + "resolved": "github:NixOS/nixpkgs/13b0f9e6ac78abbbb736c635d87845c4f4bee51b#jq", + "source": "devbox-search", + "version": "1.8.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/qjs0qndyz1g97rsc1zp4cd692y5iph64-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/jlpyybc7pdh4gk17dc266d6a1szm7dk6-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/bhryp10d5w5h9rsav5k9m9jb55z26bsl-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/238sn0gg3i3i9v6kgx4g1k6b19frzy49-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/n64h0247s3674kry90l6kszx06zyrgfn-jq-1.8.1" + } + ], + "store_path": "/nix/store/qjs0qndyz1g97rsc1zp4cd692y5iph64-jq-1.8.1-bin" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/c1qm5fsn6qbl09xdjx649vifabypyywd-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/2pjwv0ab8nilrg1lvjazf9y9w6g6pk5y-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/2sy4y09ddbi64pbg4is078110z70jsdw-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/vx81xggapqwdd2l64mmxrkbafih461jc-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/cfhajjz1k7gf31krbj18q9acb54xp5z9-jq-1.8.1" + } + ], + "store_path": "/nix/store/c1qm5fsn6qbl09xdjx649vifabypyywd-jq-1.8.1-bin" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/51343hgchh7by4l8r1g244ma05ny3x0b-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/vx840ik1sj1h8fhqwa40aglvrgpa0r18-jq-1.8.1-man", + "default": true + }, + { + "name": "out", + "path": "/nix/store/bmg2xfw86wavg7fm062nyf6v48xzxh0j-jq-1.8.1" + }, + { + "name": "dev", + "path": "/nix/store/pp2hrljvalrrwyxh7is69nnlmxb2m7lk-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/h05xaf7fsasgp8cpyar2195cc8lbgih8-jq-1.8.1-doc" + } + ], + "store_path": "/nix/store/51343hgchh7by4l8r1g244ma05ny3x0b-jq-1.8.1-bin" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/qnaw7i777j52fpgbl5pgmzkq85znp083-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/6mh88qsh57ivh31c5nqxc43n0hv9xhbk-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/d73i1fvhrqms0sbfrvqaynsr8iva216v-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/3ynrp4ypwv1g1jgsk638443p8lpd9g8f-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/fgsvqffyvcpjqs093wwf2d6dzxnmnqnv-jq-1.8.1" + } + ], + "store_path": "/nix/store/qnaw7i777j52fpgbl5pgmzkq85znp083-jq-1.8.1-bin" + } + } + }, + "yarn-berry@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#yarn-berry", + "source": "devbox-search", + "version": "4.12.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/k9bh72bpyqjnfq1nd3c6p1z2ijkx2yg6-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/k9bh72bpyqjnfq1nd3c6p1z2ijkx2yg6-yarn-berry-4.12.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/mcvbqzb1kir87g5pm5624c5ysnfh89wp-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/mcvbqzb1kir87g5pm5624c5ysnfh89wp-yarn-berry-4.12.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/hn3j954rlcc0gx8nm41sgfzbx0qwfq3c-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/hn3j954rlcc0gx8nm41sgfzbx0qwfq3c-yarn-berry-4.12.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/2pmvaaggpi7ikx9xmhy3x2j0rpklaqrv-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/2pmvaaggpi7ikx9xmhy3x2j0rpklaqrv-yarn-berry-4.12.0" + } + } + } + } +} diff --git a/shells/android-min/gradle.properties b/shells/android-min/gradle.properties new file mode 100644 index 000000000..a468eda9e --- /dev/null +++ b/shells/android-min/gradle.properties @@ -0,0 +1 @@ +org.gradle.java.home=/nix/store/hlm8a8cnp4hm8xkg0a2yy4kv7cq44jii-zulu-ca-jdk-17.0.12 diff --git a/shells/devbox-android.json b/shells/devbox-android.json deleted file mode 100644 index a1be49c55..000000000 --- a/shells/devbox-android.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$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 deleted file mode 100644 index 2413eb4e5..000000000 --- a/shells/devbox-fast.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "$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"], - "release": [ - "npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}", - "yarn install --immutable", - "yarn build", - "yarn release" - ], - "format": ["treefmt"], - "lint": ["treefmt --fail-on-change"] - } - } -} diff --git a/shells/devbox-ios.json b/shells/devbox-ios.json deleted file mode 100644 index ba43b7d33..000000000 --- a/shells/devbox-ios.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$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 deleted file mode 100644 index ae82190ba..000000000 --- a/shells/devbox.lock +++ /dev/null @@ -1,201 +0,0 @@ -{ - "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/ios/devbox.json b/shells/ios/devbox.json new file mode 100644 index 000000000..681ca8886 --- /dev/null +++ b/shells/ios/devbox.json @@ -0,0 +1,32 @@ +{ + "$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", + "git": "latest", + "bash": "latest", + "nodejs": "latest", + "path:../../nix#applesimutils": { + "version": "", + "platforms": ["aarch64-darwin", "x86_64-darwin"] + }, + "coreutils": "latest", + "gnused": "latest", + "gnugrep": "latest", + "gawk": "latest", + "jq": "latest" + }, + "shell": { + "init_hook": [ + "export LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8", + "INIT_IOS=1 . $DEVBOX_PROJECT_ROOT/../../scripts/bootstrap/env.sh" + ], + "scripts": { + "setup-ios": ["sh $DEVBOX_PROJECT_ROOT/../../scripts/run.sh ios setup"], + "test-ios": ["sh $DEVBOX_PROJECT_ROOT/../../scripts/run.sh ios test"] + } + } +} diff --git a/shells/ios/devbox.lock b/shells/ios/devbox.lock new file mode 100644 index 000000000..62575eddb --- /dev/null +++ b/shells/ios/devbox.lock @@ -0,0 +1,201 @@ +{ + "lockfile_version": "1", + "packages": { + "cocoapods@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#cocoapods", + "source": "devbox-search", + "version": "1.16.2", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/xmpbzlm4h97izn0nwf5r3flxa3hqiawa-cocoapods-1.16.2", + "default": true + } + ], + "store_path": "/nix/store/xmpbzlm4h97izn0nwf5r3flxa3hqiawa-cocoapods-1.16.2" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/kk994ybb09jk38n2k2sp5mifgka5mixg-cocoapods-1.16.2", + "default": true + } + ], + "store_path": "/nix/store/kk994ybb09jk38n2k2sp5mifgka5mixg-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-26T13:12:53Z", + "resolved": "github:NixOS/nixpkgs/13b0f9e6ac78abbbb736c635d87845c4f4bee51b#jq", + "source": "devbox-search", + "version": "1.8.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/qjs0qndyz1g97rsc1zp4cd692y5iph64-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/jlpyybc7pdh4gk17dc266d6a1szm7dk6-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/bhryp10d5w5h9rsav5k9m9jb55z26bsl-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/238sn0gg3i3i9v6kgx4g1k6b19frzy49-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/n64h0247s3674kry90l6kszx06zyrgfn-jq-1.8.1" + } + ], + "store_path": "/nix/store/qjs0qndyz1g97rsc1zp4cd692y5iph64-jq-1.8.1-bin" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/c1qm5fsn6qbl09xdjx649vifabypyywd-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/2pjwv0ab8nilrg1lvjazf9y9w6g6pk5y-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/2sy4y09ddbi64pbg4is078110z70jsdw-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/vx81xggapqwdd2l64mmxrkbafih461jc-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/cfhajjz1k7gf31krbj18q9acb54xp5z9-jq-1.8.1" + } + ], + "store_path": "/nix/store/c1qm5fsn6qbl09xdjx649vifabypyywd-jq-1.8.1-bin" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/51343hgchh7by4l8r1g244ma05ny3x0b-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/vx840ik1sj1h8fhqwa40aglvrgpa0r18-jq-1.8.1-man", + "default": true + }, + { + "name": "out", + "path": "/nix/store/bmg2xfw86wavg7fm062nyf6v48xzxh0j-jq-1.8.1" + }, + { + "name": "dev", + "path": "/nix/store/pp2hrljvalrrwyxh7is69nnlmxb2m7lk-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/h05xaf7fsasgp8cpyar2195cc8lbgih8-jq-1.8.1-doc" + } + ], + "store_path": "/nix/store/51343hgchh7by4l8r1g244ma05ny3x0b-jq-1.8.1-bin" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/qnaw7i777j52fpgbl5pgmzkq85znp083-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/6mh88qsh57ivh31c5nqxc43n0hv9xhbk-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/d73i1fvhrqms0sbfrvqaynsr8iva216v-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/3ynrp4ypwv1g1jgsk638443p8lpd9g8f-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/fgsvqffyvcpjqs093wwf2d6dzxnmnqnv-jq-1.8.1" + } + ], + "store_path": "/nix/store/qnaw7i777j52fpgbl5pgmzkq85znp083-jq-1.8.1-bin" + } + } + }, + "yarn-berry@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#yarn-berry", + "source": "devbox-search", + "version": "4.12.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/k9bh72bpyqjnfq1nd3c6p1z2ijkx2yg6-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/k9bh72bpyqjnfq1nd3c6p1z2ijkx2yg6-yarn-berry-4.12.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/mcvbqzb1kir87g5pm5624c5ysnfh89wp-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/mcvbqzb1kir87g5pm5624c5ysnfh89wp-yarn-berry-4.12.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/hn3j954rlcc0gx8nm41sgfzbx0qwfq3c-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/hn3j954rlcc0gx8nm41sgfzbx0qwfq3c-yarn-berry-4.12.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/2pmvaaggpi7ikx9xmhy3x2j0rpklaqrv-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/2pmvaaggpi7ikx9xmhy3x2j0rpklaqrv-yarn-berry-4.12.0" + } + } + } + } +} diff --git a/shells/minimal/devbox.json b/shells/minimal/devbox.json new file mode 100644 index 000000000..c072ef165 --- /dev/null +++ b/shells/minimal/devbox.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.14.2/.schema/devbox.schema.json", + "packages": { + "yarn-berry": "latest", + "git": "latest", + "bash": "latest", + "nodejs": "latest", + "coreutils": "latest", + "gnused": "latest", + "gnugrep": "latest", + "gawk": "latest", + "jq": "latest", + "treefmt": "latest", + "nixfmt": "latest", + "shfmt": "latest" + }, + "shell": { + "init_hook": [ + ". $DEVBOX_PROJECT_ROOT/../../scripts/bootstrap/env.sh" + ], + "scripts": { + "build": ["sh $DEVBOX_PROJECT_ROOT/../../scripts/run.sh build"], + "release": [ + "cd $DEVBOX_PROJECT_ROOT/../..", + "npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}", + "yarn install --immutable", + "yarn build", + "yarn release" + ], + "release-dry-run": [ + "cd $DEVBOX_PROJECT_ROOT/../..", + "yarn install --immutable", + "yarn build", + "yarn multi-semantic-release --dry-run" + ], + "format": ["treefmt"], + "lint": ["treefmt --fail-on-change"] + } + } +} diff --git a/shells/minimal/devbox.lock b/shells/minimal/devbox.lock new file mode 100644 index 000000000..9e6335872 --- /dev/null +++ b/shells/minimal/devbox.lock @@ -0,0 +1,317 @@ +{ + "lockfile_version": "1", + "packages": { + "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-26T13:12:53Z", + "resolved": "github:NixOS/nixpkgs/13b0f9e6ac78abbbb736c635d87845c4f4bee51b#jq", + "source": "devbox-search", + "version": "1.8.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/qjs0qndyz1g97rsc1zp4cd692y5iph64-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/jlpyybc7pdh4gk17dc266d6a1szm7dk6-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/bhryp10d5w5h9rsav5k9m9jb55z26bsl-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/238sn0gg3i3i9v6kgx4g1k6b19frzy49-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/n64h0247s3674kry90l6kszx06zyrgfn-jq-1.8.1" + } + ], + "store_path": "/nix/store/qjs0qndyz1g97rsc1zp4cd692y5iph64-jq-1.8.1-bin" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/c1qm5fsn6qbl09xdjx649vifabypyywd-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/2pjwv0ab8nilrg1lvjazf9y9w6g6pk5y-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/2sy4y09ddbi64pbg4is078110z70jsdw-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/vx81xggapqwdd2l64mmxrkbafih461jc-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/cfhajjz1k7gf31krbj18q9acb54xp5z9-jq-1.8.1" + } + ], + "store_path": "/nix/store/c1qm5fsn6qbl09xdjx649vifabypyywd-jq-1.8.1-bin" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/51343hgchh7by4l8r1g244ma05ny3x0b-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/vx840ik1sj1h8fhqwa40aglvrgpa0r18-jq-1.8.1-man", + "default": true + }, + { + "name": "out", + "path": "/nix/store/bmg2xfw86wavg7fm062nyf6v48xzxh0j-jq-1.8.1" + }, + { + "name": "dev", + "path": "/nix/store/pp2hrljvalrrwyxh7is69nnlmxb2m7lk-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/h05xaf7fsasgp8cpyar2195cc8lbgih8-jq-1.8.1-doc" + } + ], + "store_path": "/nix/store/51343hgchh7by4l8r1g244ma05ny3x0b-jq-1.8.1-bin" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/qnaw7i777j52fpgbl5pgmzkq85znp083-jq-1.8.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/6mh88qsh57ivh31c5nqxc43n0hv9xhbk-jq-1.8.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/d73i1fvhrqms0sbfrvqaynsr8iva216v-jq-1.8.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/3ynrp4ypwv1g1jgsk638443p8lpd9g8f-jq-1.8.1-doc" + }, + { + "name": "out", + "path": "/nix/store/fgsvqffyvcpjqs093wwf2d6dzxnmnqnv-jq-1.8.1" + } + ], + "store_path": "/nix/store/qnaw7i777j52fpgbl5pgmzkq85znp083-jq-1.8.1-bin" + } + } + }, + "nixfmt@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#nixfmt", + "source": "devbox-search", + "version": "1.2.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/lvb2z93xn3m0m2hw0w6cc0c3bsl2s8pp-nixfmt-1.2.0", + "default": true + } + ], + "store_path": "/nix/store/lvb2z93xn3m0m2hw0w6cc0c3bsl2s8pp-nixfmt-1.2.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/0qdx7ah7b1dwyxa03fblhcdjnpi29q25-nixfmt-1.2.0", + "default": true + } + ], + "store_path": "/nix/store/0qdx7ah7b1dwyxa03fblhcdjnpi29q25-nixfmt-1.2.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/jicz2kx49mqif3hkl1m2wbwvx2lg457l-nixfmt-1.2.0", + "default": true + } + ], + "store_path": "/nix/store/jicz2kx49mqif3hkl1m2wbwvx2lg457l-nixfmt-1.2.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/b5m1h822ln0s493r30sgns77618ws59n-nixfmt-1.2.0", + "default": true + } + ], + "store_path": "/nix/store/b5m1h822ln0s493r30sgns77618ws59n-nixfmt-1.2.0" + } + } + }, + "shfmt@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#shfmt", + "source": "devbox-search", + "version": "3.12.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/5ywb1qkbd829kazxxkcfxb356m7hljjw-shfmt-3.12.0", + "default": true + } + ], + "store_path": "/nix/store/5ywb1qkbd829kazxxkcfxb356m7hljjw-shfmt-3.12.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/bii41pz9k7xf3z3f80s1ip29l42488ry-shfmt-3.12.0", + "default": true + } + ], + "store_path": "/nix/store/bii41pz9k7xf3z3f80s1ip29l42488ry-shfmt-3.12.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/a8l3l8rdzs3agq7garh59m0xn8h3zgpq-shfmt-3.12.0", + "default": true + } + ], + "store_path": "/nix/store/a8l3l8rdzs3agq7garh59m0xn8h3zgpq-shfmt-3.12.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/r3pj36ll7v3ss2z679arpwkg7pnka3s3-shfmt-3.12.0", + "default": true + } + ], + "store_path": "/nix/store/r3pj36ll7v3ss2z679arpwkg7pnka3s3-shfmt-3.12.0" + } + } + }, + "treefmt@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#treefmt", + "source": "devbox-search", + "version": "2.4.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/37qcrdrfdf8b9pyli0j0vj9d69dykmpr-treefmt-2.4.0", + "default": true + } + ], + "store_path": "/nix/store/37qcrdrfdf8b9pyli0j0vj9d69dykmpr-treefmt-2.4.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/ccv7dxwff3xpivsdhrjbq3mpk0br5v7d-treefmt-2.4.0", + "default": true + } + ], + "store_path": "/nix/store/ccv7dxwff3xpivsdhrjbq3mpk0br5v7d-treefmt-2.4.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/sbq2psv4q9hvfvalk00g0pkqjl2912m1-treefmt-2.4.0", + "default": true + } + ], + "store_path": "/nix/store/sbq2psv4q9hvfvalk00g0pkqjl2912m1-treefmt-2.4.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/z8xrjfswqqpydc42lyas064pp8kxgnl0-treefmt-2.4.0", + "default": true + } + ], + "store_path": "/nix/store/z8xrjfswqqpydc42lyas064pp8kxgnl0-treefmt-2.4.0" + } + } + }, + "yarn-berry@latest": { + "last_modified": "2026-01-23T17:20:52Z", + "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#yarn-berry", + "source": "devbox-search", + "version": "4.12.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/k9bh72bpyqjnfq1nd3c6p1z2ijkx2yg6-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/k9bh72bpyqjnfq1nd3c6p1z2ijkx2yg6-yarn-berry-4.12.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/mcvbqzb1kir87g5pm5624c5ysnfh89wp-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/mcvbqzb1kir87g5pm5624c5ysnfh89wp-yarn-berry-4.12.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/hn3j954rlcc0gx8nm41sgfzbx0qwfq3c-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/hn3j954rlcc0gx8nm41sgfzbx0qwfq3c-yarn-berry-4.12.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/2pmvaaggpi7ikx9xmhy3x2j0rpklaqrv-yarn-berry-4.12.0", + "default": true + } + ], + "store_path": "/nix/store/2pmvaaggpi7ikx9xmhy3x2j0rpklaqrv-yarn-berry-4.12.0" + } + } + } + } +} diff --git a/wiki/devbox.md b/wiki/devbox.md index 76e710ed0..e70120610 100644 --- a/wiki/devbox.md +++ b/wiki/devbox.md @@ -2,28 +2,31 @@ 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/. For script layout, see `wiki/scripts.md`; for Nix/SDK versions, see `wiki/nix.md`. +Enter the environment with `devbox shell --pure`. The init hook wires `ANDROID_SDK_ROOT`/`ANDROID_HOME` and PATH. Common scripts run via `devbox run --pure`, for example `devbox run --pure 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 - Install Devbox (https://www.jetify.com/devbox/docs/install/). - From the repo root: `devbox install`. -- Enter the shell: `devbox shell`. -- Build/test: `devbox run build`, `devbox run test-android`, `devbox run test-ios`. +- Enter the shell: `devbox shell --pure`. +- Build/test: `devbox run --pure build`, `devbox run --pure test-android`, `devbox run --pure test-ios`. ## 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`. 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`. +By default, Devbox uses the flake-pinned SDKs and prefers the latest (`android-sdk-max`) when available. It sets `ANDROID_SDK_ROOT`/`ANDROID_HOME` and adds emulator/platform-tools/cmdline-tools to `PATH` via `scripts/platforms/android/env.sh`. To use a local SDK instead, launch with `ANDROID_LOCAL_SDK=1 ANDROID_HOME="$HOME/Library/Android/sdk" devbox shell --pure` (or set `ANDROID_SDK_ROOT`). Unset `ANDROID_LOCAL_SDK` (and `ANDROID_HOME`/`ANDROID_SDK_ROOT` if you set them) 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 --pure start-android*` (uses `scripts/platforms/android/avd.sh`). Version sources are documented in `wiki/nix.md`. + +Example custom targets (root `devbox.json` sets these via `shell.env` for testing): +`ANDROID_CUSTOM_API=29` `ANDROID_CUSTOM_DEVICE=pixel_6` `IOS_CUSTOM_DEVICE="iPhone 15"` ### 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` 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. -- `devbox run test-android` runs `setup-android` first to ensure AVDs exist, then delegates startup to Detox. It will not boot an emulator itself; use `start-android*` if you want it running beforehand. +- `devbox run --pure start-android` launches the default “max” AVD (from `nix/defaults.json`). Override with `TARGET_SDK=min` to launch the min AVD instead. You can also set `DETOX_AVD` or `AVD_NAME` to pick an exact AVD name. Internally uses `scripts/platforms/android/avd.sh`. +- `devbox run --pure start-android-max` / `start-android-min` explicitly launch the max (API 33) or min (API 21) AVDs. Both will create the AVD first via `scripts/platforms/android/avd.sh` if it does not exist. +- `scripts/platforms/android/avd.sh` accepts env overrides: `AVD_API`, `AVD_DEVICE`, `AVD_TAG`, `AVD_ABI`, `AVD_NAME`, `ANDROID_TARGET_API`. Defaults target the latest API (`ANDROID_MAX_API`) when available. 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 --pure reset-android` removes local AVDs/adb keys if you need a clean slate. +- `EMU_HEADLESS=1 devbox run --pure start-android*` to run the emulator headless (CI sets this); omit for a visible emulator locally. +- `EMU_PORT=5554 devbox run --pure start-android*` to set the emulator port/serial (defaults to 5554) and avoid adb conflicts. +- `devbox run --pure test-android` runs `setup-android` first to ensure AVDs exist, then delegates startup to Detox. It will not boot an emulator itself; use `start-android*` if you want it running beforehand. ### Detox defaults @@ -32,40 +35,40 @@ By default, Devbox uses the flake-pinned SDK (`path:./nix#android-sdk`). It sets ### Updating Android min/latest versions -- 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. +- Bump pinned SDK versions in `nix/defaults.json`. Refresh your devshell by running the `refresh` command while inside a devbox shell (`devbox shell --pure`). - 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-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. +- Gradle uses `buildToolsVersion` from `examples/E2E/android/build.gradle`; Devbox exports `ANDROID_BUILD_TOOLS_VERSION` from `nix/defaults.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 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. +iOS uses the host Xcode toolchain. There is no Nix-provisioned iOS SDK. Run `devbox run --pure 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 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. +> Important: `devbox shell --pure` injects Nix toolchain variables on macOS, which can break Xcode builds. The init hooks source `scripts/platforms/ios/env.sh` to undo that and re-select the system toolchain, and `scripts/run.sh` re-applies it before running iOS 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. -- `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). -- `devbox run test-ios` runs `setup-ios` first to ensure simulators exist; Detox handles booting. Use `start-ios` if you want to pre-boot. +- `devbox run --pure setup-ios` provisions simulators. Defaults are driven by `nix/defaults.json` (min/max device and iOS version). 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. Internally uses `scripts/platforms/ios/simctl.sh`. +- `devbox run --pure start-ios` provisions simulators (via `setup-ios`), then boots the chosen device (`DETOX_IOS_DEVICE` or default `iPhone 17`) and opens Simulator. Set `TARGET_SDK=min` to target the min sim (per `nix/defaults.json`) or leave default for latest. Internally uses `scripts/run.sh ios start`. +- `devbox run --pure reset-ios` shuts down/erases and removes all local simulator devices. +- `devbox run --pure 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/defaults.json`) and latest (iPhone 17). +- `devbox run --pure test-ios` runs `setup-ios` first to ensure simulators exist; Detox handles booting. Use `start-ios` if you want to pre-boot. ### Common env knobs -- Android: `AVD_FLAVOR` (minsdk/latest), `DETOX_AVD` (explicit AVD name), `EMU_HEADLESS` (1 for headless), `EMU_PORT` (emulator port/serial), `ANDROID_BUILD_TOOLS_VERSION` (override build-tools). -- iOS: `IOS_FLAVOR` (minsdk/latest), `DETOX_IOS_DEVICE` (explicit sim device), `IOS_RUNTIME` (preferred runtime), `IOS_DEVICE_NAMES` (comma list to create), `IOS_DEVELOPER_DIR` (Xcode path), `IOS_DOWNLOAD_RUNTIME` (0 to skip runtime download attempt). +- Android: `TARGET_SDK` (min/max), `DETOX_AVD` (explicit AVD name), `AVD_NAME` (explicit AVD name for create + start), `EMU_HEADLESS` (1 for headless), `EMU_PORT` (emulator port/serial), `ANDROID_BUILD_TOOLS_VERSION` (override build-tools). +- iOS: `TARGET_SDK` (min/max/custom), `DETOX_IOS_DEVICE` (explicit sim device), `IOS_DEVICE_NAMES` (comma list to create), `IOS_RUNTIME_MIN`/`IOS_RUNTIME_MAX` (runtime versions for min/max), `IOS_RUNTIME_CUSTOM` (runtime for custom), `IOS_DEVELOPER_DIR` (Xcode path), `IOS_DOWNLOAD_RUNTIME` (0 to skip runtime download attempt). The simulator runtime must exist in the active Xcode install. ### Releases -- `devbox run release` runs npm auth, yarn install/build, and `yarn release`. Requires `NPM_TOKEN` (and `GH_TOKEN`/`YARN_NPM_AUTH_TOKEN` for publishing). Used by the publish workflow. +- `devbox run --pure release` runs npm auth, yarn install/build, and `yarn release`. Requires `NPM_TOKEN` (and `GH_TOKEN`/`YARN_NPM_AUTH_TOKEN` for publishing). Used by the publish workflow. ### Updating iOS min/latest versions -- Adjust platform defaults in `nix/platform-versions.json` and rebuild Devbox if you change Android SDK versions. +- Adjust platform defaults in `nix/defaults.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-full.yml` (ios-min/ios-latest rows) if you want to override the platform defaults in CI. @@ -73,16 +76,18 @@ iOS uses the host Xcode toolchain. There is no Nix-provisioned iOS SDK. Run `dev The root `devbox.json` is a full local dev environment. CI uses slim Devbox configs under `shells/` to avoid pulling unnecessary SDKs: -- `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). +- `shells/minimal/devbox.json`: build + lint only. +- `shells/android-min/devbox.json`: Android SDK (min + max platforms) + JDK/Gradle for Android E2E. +- `shells/android-max/devbox.json`: Android SDK (max API) + JDK/Gradle for Android E2E. +- `shells/ios/devbox.json`: CocoaPods + Yarn for iOS E2E (Xcode still required on macOS). Run them locally with: ```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 +devbox run --pure --config=shells/minimal/devbox.json build +devbox run --pure --config=shells/android-min/devbox.json test-android +devbox run --pure --config=shells/android-max/devbox.json test-android +devbox run --pure --config=shells/ios/devbox.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. +Note: when you use `devbox run --pure --config=shells//devbox.json`, 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 index 986eff98d..321a95149 100644 --- a/wiki/nix.md +++ b/wiki/nix.md @@ -9,27 +9,28 @@ This repo uses a Nix flake for the Android SDK, and a JSON file for single-sourc - 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` +- `nix/defaults.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. +- `scripts/bootstrap/env.sh` + - Establishes `PROJECT_ROOT` and `SCRIPTS_DIR` for scripts and CI. ## How versions flow -1. `nix/platform-versions.json` is updated. +1. `nix/defaults.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/` +3. `scripts/shared/defaults.sh` loads defaults (via `jq`) and establishes script root context for: + - scripts under `scripts/platforms/android/` and `scripts/platforms/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. +1. Edit `nix/defaults.json`. +2. In a devbox shell (`devbox shell --pure`), run `refresh` to rebuild the SDK. +3. If iOS min/max versions change, re-run the iOS E2E workflow to confirm the runtime/device exists on the runner. +4. `nix/defaults.json` exports concrete defaults via the `defaults` section. ## CI targets diff --git a/wiki/release.md b/wiki/release.md index edc5d9f80..62365b5e8 100644 --- a/wiki/release.md +++ b/wiki/release.md @@ -17,13 +17,13 @@ This repo uses semantic-release with multi-semantic-release to version and publi ### CI/CD path (recommended) 1. Ensure `master`/`beta` are green. Merges must use conventional commits. -2. Trigger `Publish` workflow in Actions. Inputs are tokens only; workflow fetches full history, installs Devbox, then runs `devbox run release`. +2. Trigger `Publish` workflow in Actions. Inputs are tokens only; workflow fetches full history, installs Devbox, then runs `devbox run --pure release`. 3. Outputs: package tags (`${name}-vX.Y.Z`), npm publishes, GitHub releases, and updated changelog commits pushed back via the workflow token. ### Local dry run 1. `GH_TOKEN= NPM_TOKEN= YARN_NPM_AUTH_TOKEN=` (GH token needs `contents` write; npm token can be automation/classic publish). -2. `devbox run release -- --dry-run` to see what would publish. Omit `--dry-run` to actually publish (only do this if you intend to release from your machine). +2. `devbox run --pure release -- --dry-run` to see what would publish. Omit `--dry-run` to actually publish (only do this if you intend to release from your machine). ### Tips and gotchas diff --git a/wiki/scripts.md b/wiki/scripts.md index 483d447c4..1c831dbd7 100644 --- a/wiki/scripts.md +++ b/wiki/scripts.md @@ -1,90 +1,115 @@ # 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. +This repo uses `scripts/` as the entry point for devbox commands and tasks. Scripts are organized by platform with a small shared helper layer. Devbox runs are expected to use `--pure` for reproducible environments. ## 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`. +- `scripts/run.sh`: entrypoint for tasks (build/test). +- `scripts/bootstrap/env.sh`: establishes `PROJECT_ROOT`/`SCRIPTS_DIR`, loads `scripts/shared/*`, and optionally initializes platforms when `INIT_ANDROID`/`INIT_IOS` are set. +- `scripts/bootstrap/init.sh`: bootstraps `scripts/bootstrap/env.sh` when invoked from `scripts/run.sh`. +- `scripts/shared/project.sh`: project root + scripts path helpers. +- `scripts/shared/tools.sh`: shared tool checks. +- `scripts/shared/defaults.sh`: loads `nix/defaults.json` via `jq`. +- `scripts/platforms/android/`: Android SDK, AVD, and E2E helpers. +- `scripts/platforms/ios/`: iOS simulator setup, toolchain fixups, and E2E helpers. +- `scripts/platforms/android/actions.sh` + `scripts/platforms/ios/actions.sh`: platform task dispatchers called by `scripts/run.sh`. ## 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/shared/project.sh` + - `ensure_project_root`: resolves `PROJECT_ROOT`. - `SCRIPTS_DIR`: defaults to `$PROJECT_ROOT/scripts` when unset. +- `scripts/shared/tools.sh` + - `require_tool`: asserts a tool exists (with an optional custom message). + - `require_file`: asserts a file exists (with an optional custom message). + - `require_dir`: asserts a directory exists (with an optional custom message). + - `require_dir_contains`: asserts a directory contains a specific subpath (with an optional custom message). + - `require_var`: asserts an env var is set (with an optional custom message). +- `scripts/shared/defaults.sh` + - Loads `nix/defaults.json` (via `jq`) to export default env vars when available. ## Android scripts -- `scripts/android/env.sh` +- `scripts/platforms/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`. + - Sets `ANDROID_SDK_ROOT`/`ANDROID_HOME` and PATH for the Nix SDK (prefers `android-sdk-max` when available). + - Set `ANDROID_LOCAL_SDK=1` to keep a pre-set local SDK instead. + - Loads platform defaults via `scripts/shared/defaults.sh` (from `nix/defaults.json`). + - Used by devbox init hooks in `devbox.json` and `shells/android-min/devbox.json` + `shells/android-max/devbox.json`. -- `scripts/android/setup.sh` +- `scripts/platforms/android/avd.sh` - - Creates/ensures AVDs for min and max API levels. + - Creates/ensures AVDs for the target API level, then starts/stops/resets emulators. - 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. + - Uses platform defaults from `scripts/shared/defaults.sh`. -- `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` +- `scripts/platforms/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`. + - Sourced by devbox init hooks and re-applied in `scripts/run.sh` for iOS tasks. -- `scripts/ios/simctl.sh` +- `scripts/platforms/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`. +## User overrides + +These env vars can be set by users to override defaults or behavior. + +### Android + +- `ENV_DEFAULTS_JSON`: path to an alternate `nix/defaults.json` (advanced). +- `ANDROID_SDK_ROOT`, `ANDROID_HOME`: explicit SDK location (used with `ANDROID_LOCAL_SDK=1`). +- `ANDROID_LOCAL_SDK`: use a local Android SDK instead of the Nix SDK. +- `ANDROID_SDK_FLAKE_OUTPUT`: force a specific flake output (e.g., `android-sdk-max`). +- `TARGET_SDK`: `min`, `max`, or `custom` (selects which API/device pairing to use). +- `ANDROID_MIN_API`, `ANDROID_MAX_API`: override the min/max API levels. +- `ANDROID_MIN_DEVICE`, `ANDROID_MAX_DEVICE`: override the min/max AVD device names. +- `ANDROID_SYSTEM_IMAGE_TAG`: override the system image tag (default `google_apis`). +- `ANDROID_BUILD_TOOLS_VERSION`, `ANDROID_CMDLINE_TOOLS_VERSION`: override build tools/cmdline tools versions. +- `ANDROID_CUSTOM_API`, `ANDROID_CUSTOM_DEVICE`, `ANDROID_CUSTOM_SYSTEM_IMAGE_TAG`: required/optional when `TARGET_SDK=custom`. +- `ANDROID_TARGET_API`: force a specific API, bypassing `TARGET_SDK`. +- `AVD_API`, `AVD_DEVICE`, `AVD_TAG`, `AVD_ABI`, `AVD_NAME`: override AVD creation/selection. +- `DETOX_AVD`: force a specific AVD name for Detox. +- `EMU_HEADLESS`: `1` to launch the emulator without a window. +- `EMU_PORT`: emulator port/serial (default `5554`). +- `DEBUG` / `ANALYTICS_CI_DEBUG`: enables verbose logging for script helpers. + +### iOS + +- `IOS_MIN_DEVICE`, `IOS_MAX_DEVICE`: override min/max device names. +- `IOS_RUNTIME_MIN`, `IOS_RUNTIME_MAX`: required iOS simulator runtimes for min/max. +- `IOS_RUNTIME_CUSTOM`: required runtime when `TARGET_SDK=custom`. +- `IOS_DEVICE_NAMES`: comma-separated list of devices to create. +- `IOS_DEVELOPER_DIR`: override the Xcode path. +- `IOS_DOWNLOAD_RUNTIME`: set to `0` to skip `xcodebuild -downloadPlatform iOS`. +- `TARGET_SDK`: `min`, `max`, or `custom` (controls which device/runtime to boot for iOS). +- `IOS_CUSTOM_DEVICE`: used when `TARGET_SDK=custom`. +- `DETOX_IOS_DEVICE`: force a specific simulator name for Detox. -- `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` +- `build` -> `scripts/run.sh build` +- `test-android` -> `scripts/run.sh android test` +- `test-ios` -> `scripts/run.sh ios test` +- `setup-android` -> `scripts/run.sh android setup` +- `setup-ios` -> `scripts/run.sh ios setup` +- `start-android*` -> `scripts/run.sh android start` (uses `scripts/platforms/android/avd.sh`) +- `start-ios` -> `scripts/run.sh ios start` 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` +- `shells/minimal/devbox.json` -> `scripts/run.sh build` +- `shells/android-min/devbox.json` -> `scripts/run.sh android test` +- `shells/android-max/devbox.json` -> `scripts/run.sh android test` +- `shells/ios/devbox.json` -> `scripts/run.sh ios test` See `wiki/devbox.md` for usage and `wiki/nix.md` for platform version sources.