diff --git a/.github/workflows/e2e-full.yml b/.github/workflows/e2e-full.yml index 090df6a6..33e33424 100644 --- a/.github/workflows/e2e-full.yml +++ b/.github/workflows/e2e-full.yml @@ -58,6 +58,9 @@ jobs: with: enable-cache: true + - name: Rewrite plugin URLs to local + run: bash scripts/dev/rewrite-plugin-urls.sh --to-local examples/ + - name: Run Android E2E test working-directory: examples/android env: @@ -131,6 +134,9 @@ jobs: with: enable-cache: true + - name: Rewrite plugin URLs to local + run: bash scripts/dev/rewrite-plugin-urls.sh --to-local examples/ + - name: Run iOS E2E test working-directory: examples/ios env: @@ -245,6 +251,9 @@ jobs: with: enable-cache: true + - name: Rewrite plugin URLs to local + run: bash scripts/dev/rewrite-plugin-urls.sh --to-local examples/ + - name: Run React Native E2E test working-directory: examples/react-native run: | diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 03f88c93..87b2d864 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -74,6 +74,9 @@ jobs: with: enable-cache: true + - name: Rewrite plugin URLs to local + run: bash scripts/dev/rewrite-plugin-urls.sh --to-local examples/ + - name: Run Android E2E test working-directory: examples/android env: @@ -139,6 +142,9 @@ jobs: with: enable-cache: true + - name: Rewrite plugin URLs to local + run: bash scripts/dev/rewrite-plugin-urls.sh --to-local examples/ + - name: Run iOS E2E test working-directory: examples/ios env: @@ -253,6 +259,9 @@ jobs: with: enable-cache: true + - name: Rewrite plugin URLs to local + run: bash scripts/dev/rewrite-plugin-urls.sh --to-local examples/ + - name: Run React Native E2E test working-directory: examples/react-native run: | diff --git a/devbox.json b/devbox.json index 385d8ce0..77ff9260 100644 --- a/devbox.json +++ b/devbox.json @@ -74,6 +74,9 @@ "sync": [ "echo 'Syncing example projects with latest plugins...'", "echo ''", + "echo 'Rewriting plugin URLs to local paths...'", + "bash scripts/dev/rewrite-plugin-urls.sh --to-local examples/", + "echo ''", "echo '1/3 Android...'", "(cd examples/android && rm -rf .devbox devbox.d devbox.lock && devbox install)", "echo ' Generating device lock file...'", @@ -194,6 +197,11 @@ "bump": [ "bash scripts/bump.sh \"${@}\"" ], + "restore-plugin-urls": [ + "echo 'Restoring plugin URLs to GitHub format...'", + "bash scripts/dev/rewrite-plugin-urls.sh --to-github examples/", + "echo '✓ Plugin URLs restored to GitHub format'" + ], "test:fast": [ "echo 'Running fast tests (lint + unit + integration in parallel)...'", "devbox run lint", diff --git a/devbox.lock b/devbox.lock index d566cb93..a04e81ce 100644 --- a/devbox.lock +++ b/devbox.lock @@ -278,6 +278,55 @@ } } }, + "rustup@latest": { + "last_modified": "2026-04-01T01:09:41Z", + "plugin_version": "0.0.1", + "resolved": "github:NixOS/nixpkgs/62e3050a29278c985725a86704faa1e99236b51a#rustup", + "source": "devbox-search", + "version": "1.29.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/vf4ymfv40jsq1r2bapps3ll8xxqng794-rustup-1.29.0", + "default": true + } + ], + "store_path": "/nix/store/vf4ymfv40jsq1r2bapps3ll8xxqng794-rustup-1.29.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/q335jx3dgagxcj79bgxaxa027bq3vf7a-rustup-1.29.0", + "default": true + } + ], + "store_path": "/nix/store/q335jx3dgagxcj79bgxaxa027bq3vf7a-rustup-1.29.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/psqiilmbpc0d5c12qpswamkhdgzq5dcv-rustup-1.29.0", + "default": true + } + ], + "store_path": "/nix/store/psqiilmbpc0d5c12qpswamkhdgzq5dcv-rustup-1.29.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/3jmhpvfwqag9jcxi21l2lrv79ha7h4p5-rustup-1.29.0", + "default": true + } + ], + "store_path": "/nix/store/3jmhpvfwqag9jcxi21l2lrv79ha7h4p5-rustup-1.29.0" + } + } + }, "shellcheck@latest": { "last_modified": "2026-01-23T17:20:52Z", "resolved": "github:NixOS/nixpkgs/a1bab9e494f5f4939442a57a58d0449a109593fe#shellcheck", diff --git a/examples/android/devbox.d/android/android.lock b/examples/android/devbox.d/android/android.lock new file mode 100644 index 00000000..b7c23576 --- /dev/null +++ b/examples/android/devbox.d/android/android.lock @@ -0,0 +1,14 @@ +{ + "ANDROID_BUILD_TOOLS_VERSION": "36.1.0", + "ANDROID_CMDLINE_TOOLS_VERSION": "19.0", + "ANDROID_COMPILE_SDK": 36, + "ANDROID_TARGET_SDK": 36, + "ANDROID_SYSTEM_IMAGE_TAG": "google_apis", + "ANDROID_INCLUDE_NDK": false, + "ANDROID_NDK_VERSION": "27.0.12077973", + "ANDROID_INCLUDE_CMAKE": false, + "ANDROID_CMAKE_VERSION": "3.22.1", + "hash_overrides": { + "https://dl.google.com/android/repository/platform-tools_r37.0.0-darwin.zip": "8c4c926d0ca192376b2a04b0318484724319e67c" + } +} diff --git a/examples/android/devbox.json b/examples/android/devbox.json index 3ee3a9b2..6c3efcdd 100644 --- a/examples/android/devbox.json +++ b/examples/android/devbox.json @@ -1,5 +1,5 @@ { - "include": ["path:../../plugins/android/plugin.json"], + "include": ["github:segment-integrations/mobile-devtools?dir=plugins/android&ref=main"], "packages": { "jdk17": "latest", "gradle": "latest" diff --git a/examples/ios/devbox.json b/examples/ios/devbox.json index c6b19027..f0d839a6 100644 --- a/examples/ios/devbox.json +++ b/examples/ios/devbox.json @@ -1,5 +1,5 @@ { - "include": ["path:../../plugins/ios/plugin.json"], + "include": ["github:segment-integrations/mobile-devtools?dir=plugins/ios&ref=main"], "packages": { "process-compose": "latest" }, diff --git a/examples/react-native/android/app/build.gradle b/examples/react-native/android/app/build.gradle index fa4de542..ac0d8a6d 100644 --- a/examples/react-native/android/app/build.gradle +++ b/examples/react-native/android/app/build.gradle @@ -25,7 +25,9 @@ react { /* Bundling */ // A list containing the node command and its flags. Default is just 'node'. - // nodeExecutableAndArgs = ["node"] + // Use NODE_BINARY env var if set (from devbox), otherwise default to 'node' + def nodeBinary = System.getenv("NODE_BINARY") ?: "node" + nodeExecutableAndArgs = [nodeBinary] // // The command to run when bundling. By default is 'bundle' // bundleCommand = "ram-bundle" diff --git a/examples/react-native/devbox.d/android/android.lock b/examples/react-native/devbox.d/android/android.lock new file mode 100644 index 00000000..04848db9 --- /dev/null +++ b/examples/react-native/devbox.d/android/android.lock @@ -0,0 +1,14 @@ +{ + "ANDROID_BUILD_TOOLS_VERSION": "35.0.0", + "ANDROID_CMDLINE_TOOLS_VERSION": "19.0", + "ANDROID_COMPILE_SDK": 35, + "ANDROID_TARGET_SDK": 35, + "ANDROID_SYSTEM_IMAGE_TAG": "google_apis", + "ANDROID_INCLUDE_NDK": true, + "ANDROID_NDK_VERSION": "29.0.14206865", + "ANDROID_INCLUDE_CMAKE": true, + "ANDROID_CMAKE_VERSION": "4.1.2", + "hash_overrides": { + "https://dl.google.com/android/repository/platform-tools_r37.0.0-darwin.zip": "8c4c926d0ca192376b2a04b0318484724319e67c" + } +} diff --git a/examples/react-native/devbox.d/android/devices/devices.lock b/examples/react-native/devbox.d/android/devices/devices.lock index 070a0bd6..62b95ea1 100644 --- a/examples/react-native/devbox.d/android/devices/devices.lock +++ b/examples/react-native/devbox.d/android/devices/devices.lock @@ -4,13 +4,15 @@ "name": "medium_phone_api35", "api": 35, "device": "medium_phone", - "tag": "google_apis" + "tag": "google_apis", + "filename": "max" }, { "name": "pixel_api21", "api": 21, "device": "pixel", - "tag": "google_apis" + "tag": "google_apis", + "filename": "min" } ], "checksum": "f5bfab3fdcbe8a23858954c18b1fa86d28a3316e801523aa6d4aa72ca9cf5ab7" diff --git a/examples/react-native/devbox.json b/examples/react-native/devbox.json index 54d1b252..043cfe87 100644 --- a/examples/react-native/devbox.json +++ b/examples/react-native/devbox.json @@ -1,5 +1,5 @@ { - "include": ["path:../../plugins/react-native/plugin.json"], + "include": ["github:segment-integrations/mobile-devtools?dir=plugins/react-native&ref=main"], "packages": [ "nodejs@20", "watchman@latest", diff --git a/plugins/android/plugin.json b/plugins/android/plugin.json index f6e78d93..0a95c938 100644 --- a/plugins/android/plugin.json +++ b/plugins/android/plugin.json @@ -1,6 +1,6 @@ { "name": "android", - "version": "0.0.3", + "version": "0.0.4", "description": "Sets Android home/AVD paths inside the Devbox virtenv for reproducible, project-local Android tooling.", "env": { "ANDROID_USER_HOME": "{{ .Virtenv }}/android", @@ -41,6 +41,7 @@ "create_files": { "{{ .Virtenv }}/android": "", "{{ .Virtenv }}/android/avd": "", + "{{ .Virtenv }}/flake.nix": "virtenv/flake.nix", "{{ .Virtenv }}/scripts/lib/lib.sh": "virtenv/scripts/lib/lib.sh", "{{ .Virtenv }}/scripts/platform/core.sh": "virtenv/scripts/platform/core.sh", "{{ .Virtenv }}/scripts/platform/device_config.sh": "virtenv/scripts/platform/device_config.sh", @@ -58,7 +59,6 @@ "{{ .Virtenv }}/scripts/init/init-hook.sh": "virtenv/scripts/init/init-hook.sh", "{{ .Virtenv }}/scripts/init/setup.sh": "virtenv/scripts/init/setup.sh", "{{ .Virtenv }}/scripts/init/doctor.sh": "virtenv/scripts/init/doctor.sh", - "{{ .DevboxDir }}/flake.nix": "virtenv/flake.nix", "{{ .DevboxDir }}/devices/min.json": "config/devices/min.json", "{{ .DevboxDir }}/devices/max.json": "config/devices/max.json" }, diff --git a/plugins/android/virtenv/flake.nix b/plugins/android/virtenv/flake.nix index 110b24cf..9ac33d0a 100644 --- a/plugins/android/virtenv/flake.nix +++ b/plugins/android/virtenv/flake.nix @@ -14,10 +14,13 @@ ]; # Read android.lock (generated by android.sh devices sync) + # The lock file is in devbox.d/android/, read via ANDROID_CONFIG_DIR env var # On first initialization, android.lock may not exist yet, so provide defaults - androidLockExists = builtins.pathExists ./android.lock; + configDir = builtins.getEnv "ANDROID_CONFIG_DIR"; + androidLockPath = if configDir != "" then "${configDir}/android.lock" else ./android.lock; + androidLockExists = builtins.pathExists androidLockPath; androidLockData = if androidLockExists - then builtins.fromJSON (builtins.readFile ./android.lock) + then builtins.fromJSON (builtins.readFile androidLockPath) else { # Default values for initial flake evaluation before sync runs ANDROID_BUILD_TOOLS_VERSION = "36.1.0"; diff --git a/plugins/android/virtenv/scripts/platform/core.sh b/plugins/android/virtenv/scripts/platform/core.sh index 558ec884..bfba6008 100644 --- a/plugins/android/virtenv/scripts/platform/core.sh +++ b/plugins/android/virtenv/scripts/platform/core.sh @@ -69,10 +69,8 @@ resolve_flake_sdk_root() { root="${ANDROID_SDK_FLAKE_PATH:-}" if [ -z "$root" ]; then - # Flake is in the config directory (devbox.d/) where device configs live - if [ -n "${ANDROID_CONFIG_DIR:-}" ] && [ -d "${ANDROID_CONFIG_DIR}" ]; then - root="${ANDROID_CONFIG_DIR}" - elif [ -n "${ANDROID_RUNTIME_DIR:-}" ] && [ -d "${ANDROID_RUNTIME_DIR}" ]; then + # Flake is in the runtime directory (.devbox/virtenv/android/) + if [ -n "${ANDROID_RUNTIME_DIR:-}" ] && [ -d "${ANDROID_RUNTIME_DIR}" ]; then root="${ANDROID_RUNTIME_DIR}" elif [ -n "${ANDROID_SCRIPTS_DIR:-}" ] && [ -d "${ANDROID_SCRIPTS_DIR}" ]; then # Fallback: flake in same directory as scripts (virtenv) - deprecated @@ -104,13 +102,14 @@ resolve_flake_sdk_root() { fi # Build the SDK to ensure it's in the Nix store + # Use --impure to allow flake to read ANDROID_CONFIG_DIR for android.lock location # Capture stderr so failures are visible instead of silently swallowed [ -n "${ANDROID_DEBUG_SETUP:-}" ] && echo "[CORE-$$] Building SDK: path:${root}#${output}" >&2 _nix_stderr="" _nix_stderr_file="$(mktemp "${TMPDIR:-/tmp}/android-nix-build-XXXXXX.stderr")" sdk_out=$( nix --extra-experimental-features 'nix-command flakes' \ - build "path:${root}#${output}" --no-link --print-out-paths 2>"$_nix_stderr_file" + build "path:${root}#${output}" --no-link --print-out-paths --impure 2>"$_nix_stderr_file" ) || true _nix_stderr="" if [ -f "$_nix_stderr_file" ]; then diff --git a/plugins/android/virtenv/scripts/user/setup.sh b/plugins/android/virtenv/scripts/user/setup.sh index 4115d8bc..e494b78a 100755 --- a/plugins/android/virtenv/scripts/user/setup.sh +++ b/plugins/android/virtenv/scripts/user/setup.sh @@ -37,7 +37,7 @@ else sdk_out=$( nix --extra-experimental-features 'nix-command flakes' \ - build "path:${flake_root}#${flake_output}" --no-link --print-out-paths 2>&1 + build "path:${flake_root}#${flake_output}" --no-link --print-out-paths --impure 2>&1 ) || true # Check if we got a valid store path @@ -90,6 +90,21 @@ else echo "✅ [OK] Android SDK: ${ANDROID_SDK_ROOT}" fi +# Set ANDROID_NDK_ROOT if NDK is included +if [ "${ANDROID_INCLUDE_NDK:-false}" = "true" ] || [ "${ANDROID_INCLUDE_NDK:-0}" = "1" ]; then + if [ -n "${ANDROID_NDK_VERSION:-}" ]; then + ndk_path="${ANDROID_SDK_ROOT}/ndk/${ANDROID_NDK_VERSION}" + if [ -d "$ndk_path" ]; then + export ANDROID_NDK_ROOT="$ndk_path" + else + # Fallback: check for ndk-bundle (older SDK layout) + if [ -d "${ANDROID_SDK_ROOT}/ndk-bundle" ]; then + export ANDROID_NDK_ROOT="${ANDROID_SDK_ROOT}/ndk-bundle" + fi + fi + fi +fi + # Write SDK root to shared state file (for process-compose sibling processes) if [ -n "${ANDROID_RUNTIME_DIR:-}" ]; then mkdir -p "${ANDROID_RUNTIME_DIR}/.state" diff --git a/plugins/ios/plugin.json b/plugins/ios/plugin.json index 7db7cbaf..6637b5ba 100644 --- a/plugins/ios/plugin.json +++ b/plugins/ios/plugin.json @@ -1,6 +1,6 @@ { "name": "ios", - "version": "0.0.3", + "version": "0.0.4", "description": "Configures iOS tooling inside Devbox and ensures Xcode toolchain usage.", "env": { "IOS_CONFIG_DIR": "{{ .DevboxDir }}", diff --git a/plugins/react-native/plugin.json b/plugins/react-native/plugin.json index 9161eb04..cadae37d 100644 --- a/plugins/react-native/plugin.json +++ b/plugins/react-native/plugin.json @@ -1,6 +1,6 @@ { "name": "react-native", - "version": "0.0.4", + "version": "0.0.5", "description": "Aggregates the Android and iOS Devbox plugins for React Native projects.", "include": [ "github:segment-integrations/mobile-devtools?dir=plugins/android&ref=main", diff --git a/scripts/dev/rewrite-plugin-urls.sh b/scripts/dev/rewrite-plugin-urls.sh new file mode 100755 index 00000000..49dcc22d --- /dev/null +++ b/scripts/dev/rewrite-plugin-urls.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# Rewrite plugin URLs in devbox.json files +# Usage: rewrite-plugin-urls.sh [--to-local|--to-github] [directory] +# +# --to-local: Rewrite github: URLs to path: (for local testing) +# --to-github: Rewrite path: URLs to github: (restore public format) +# directory: Directory to search (default: examples/) + +set -euo pipefail + +mode="${1:---to-local}" +search_dir="${2:-examples}" + +if [ ! -d "$search_dir" ]; then + echo "ERROR: Directory not found: $search_dir" >&2 + exit 1 +fi + +case "$mode" in + --to-local) + echo "Rewriting plugin URLs to local paths..." + + # Rewrite examples/ devbox.json files + if [ -d "$search_dir" ]; then + echo " Processing examples in $search_dir..." + find "$search_dir" -name "devbox.json" -type f | while read -r file; do + if grep -q "github:segment-integrations/mobile-devtools" "$file"; then + echo " Rewriting: $file" + + # Backup original + cp "$file" "$file.bak" + + # Rewrite URLs using sed (macOS compatible) + sed -i '' \ + -e 's|"github:segment-integrations/mobile-devtools?dir=plugins/android&ref=main"|"path:../../plugins/android/plugin.json"|g' \ + -e 's|"github:segment-integrations/mobile-devtools?dir=plugins/ios&ref=main"|"path:../../plugins/ios/plugin.json"|g' \ + -e 's|"github:segment-integrations/mobile-devtools?dir=plugins/react-native&ref=main"|"path:../../plugins/react-native/plugin.json"|g' \ + "$file" + fi + done + fi + + # Rewrite plugins/ plugin.json files (react-native includes android/ios) + if [ -f "plugins/react-native/plugin.json" ]; then + echo " Processing react-native plugin..." + if grep -q "github:segment-integrations/mobile-devtools" "plugins/react-native/plugin.json"; then + echo " Rewriting: plugins/react-native/plugin.json" + + # Backup original + cp "plugins/react-native/plugin.json" "plugins/react-native/plugin.json.bak" + + # Rewrite URLs to relative paths from react-native plugin + sed -i '' \ + -e 's|"github:segment-integrations/mobile-devtools?dir=plugins/android&ref=main"|"path:../android/plugin.json"|g' \ + -e 's|"github:segment-integrations/mobile-devtools?dir=plugins/ios&ref=main"|"path:../ios/plugin.json"|g' \ + "plugins/react-native/plugin.json" + fi + fi + + echo "✓ Rewrote plugin URLs to local paths" + ;; + + --to-github) + echo "Restoring plugin URLs to GitHub format..." + + # Restore examples/ devbox.json files + if [ -d "$search_dir" ]; then + echo " Processing examples in $search_dir..." + find "$search_dir" -name "devbox.json" -type f | while read -r file; do + if [ -f "$file.bak" ]; then + echo " Restoring from backup: $file" + mv "$file.bak" "$file" + elif grep -q "path:.*plugins/.*/plugin.json" "$file"; then + echo " Rewriting: $file" + + # Rewrite URLs using sed (macOS compatible) + sed -i '' \ + -e 's|"path:../../plugins/android/plugin.json"|"github:segment-integrations/mobile-devtools?dir=plugins/android\&ref=main"|g' \ + -e 's|"path:../../plugins/ios/plugin.json"|"github:segment-integrations/mobile-devtools?dir=plugins/ios\&ref=main"|g' \ + -e 's|"path:../../plugins/react-native/plugin.json"|"github:segment-integrations/mobile-devtools?dir=plugins/react-native\&ref=main"|g' \ + "$file" + fi + done + # Clean up any remaining backups + find "$search_dir" -name "devbox.json.bak" -type f -delete + fi + + # Restore plugins/ plugin.json files + if [ -f "plugins/react-native/plugin.json.bak" ]; then + echo " Processing react-native plugin..." + echo " Restoring from backup: plugins/react-native/plugin.json" + mv "plugins/react-native/plugin.json.bak" "plugins/react-native/plugin.json" + elif [ -f "plugins/react-native/plugin.json" ] && grep -q "path:../android/plugin.json\|path:../ios/plugin.json" "plugins/react-native/plugin.json"; then + echo " Processing react-native plugin..." + echo " Rewriting: plugins/react-native/plugin.json" + + # Rewrite URLs back to GitHub format + sed -i '' \ + -e 's|"path:../android/plugin.json"|"github:segment-integrations/mobile-devtools?dir=plugins/android\&ref=main"|g' \ + -e 's|"path:../ios/plugin.json"|"github:segment-integrations/mobile-devtools?dir=plugins/ios\&ref=main"|g' \ + "plugins/react-native/plugin.json" + fi + + echo "✓ Restored plugin URLs to GitHub format" + ;; + + *) + echo "ERROR: Unknown mode: $mode" >&2 + echo "Usage: $0 [--to-local|--to-github] [directory]" >&2 + exit 1 + ;; +esac