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.d/android/flake.lock b/examples/android/devbox.d/android/flake.lock new file mode 100644 index 00000000..4b8e88ed --- /dev/null +++ b/examples/android/devbox.d/android/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1776329215, + "narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b86751bc4085f48661017fa226dee99fab6c651b", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/examples/android/devbox.d/android/flake.nix b/examples/android/devbox.d/android/flake.nix new file mode 100644 index 00000000..110b24cf --- /dev/null +++ b/examples/android/devbox.d/android/flake.nix @@ -0,0 +1,147 @@ +{ + description = "Android SDK tools for Devbox (plugin local flake)"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + outputs = + { self, nixpkgs }: + let + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + + # Read android.lock (generated by android.sh devices sync) + # On first initialization, android.lock may not exist yet, so provide defaults + androidLockExists = builtins.pathExists ./android.lock; + androidLockData = if androidLockExists + then builtins.fromJSON (builtins.readFile ./android.lock) + else { + # Default values for initial flake evaluation before sync runs + ANDROID_BUILD_TOOLS_VERSION = "36.1.0"; + ANDROID_CMDLINE_TOOLS_VERSION = "19.0"; + 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"; + }; + defaultsData = androidLockData; + getVar = + name: + if builtins.hasAttr name defaultsData then toString (builtins.getAttr name defaultsData) + else builtins.throw "Missing required value in android.json: ${name}"; + + unique = + list: + builtins.foldl' ( + acc: item: if builtins.elem item acc then acc else acc ++ [ item ] + ) [ ] list; + + lockData = + if builtins.pathExists ./devices.lock + then builtins.fromJSON (builtins.readFile ./devices.lock) + else { devices = [ ]; }; + + # Extract API versions from lock file devices array, default to latest if empty + deviceApis = + if builtins.hasAttr "devices" lockData && (builtins.length lockData.devices) > 0 + then map (device: device.api) lockData.devices + else [ 36 ]; # Default to latest stable API + + # Include ANDROID_COMPILE_SDK in platform versions if set (for projects + # that compile against a different API than the emulator/device targets) + compileSdkApis = + if builtins.hasAttr "ANDROID_COMPILE_SDK" defaultsData + then [ (toString defaultsData.ANDROID_COMPILE_SDK) ] + else []; + + androidSdkConfig = { + platformVersions = unique ((map toString deviceApis) ++ compileSdkApis); + buildToolsVersion = getVar "ANDROID_BUILD_TOOLS_VERSION"; + cmdLineToolsVersion = getVar "ANDROID_CMDLINE_TOOLS_VERSION"; + systemImageTypes = [ (getVar "ANDROID_SYSTEM_IMAGE_TAG") ]; + includeNDK = + if builtins.hasAttr "ANDROID_INCLUDE_NDK" defaultsData then defaultsData.ANDROID_INCLUDE_NDK else false; + ndkVersion = getVar "ANDROID_NDK_VERSION"; + includeCMake = + if builtins.hasAttr "ANDROID_INCLUDE_CMAKE" defaultsData then defaultsData.ANDROID_INCLUDE_CMAKE else false; + cmakeVersion = getVar "ANDROID_CMAKE_VERSION"; + }; + + # Hash overrides for when Google updates files on their servers + # These can be set in android.lock to work around nixpkgs hash mismatches + # By default this field is not set - only set via `android.sh hash update` when upstream is broken + hashOverrides = if builtins.hasAttr "hash_overrides" androidLockData + then androidLockData.hash_overrides + else {}; + + forAllSystems = + f: + builtins.listToAttrs ( + map (system: { + name = system; + value = f system; + }) systems + ); + in + { + packages = forAllSystems ( + system: + let + pkgs = import nixpkgs { + inherit system; + config = { + allowUnfree = true; + android_sdk.accept_license = true; + }; + }; + + abiVersions = if builtins.match "aarch64-.*" system != null then [ "arm64-v8a" ] else [ "x86_64" ]; + + # Apply hash overrides to nixpkgs if any are specified + # Android packages use SHA1 hashes, not SHA256 + # We need to re-import nixpkgs with overlays when overrides are present + pkgsWithOverrides = if (builtins.length (builtins.attrNames hashOverrides)) > 0 + then import nixpkgs { + inherit system; + config = { + allowUnfree = true; + android_sdk.accept_license = true; + }; + overlays = [(final: prev: { + fetchurl = args: + if builtins.isAttrs args && builtins.hasAttr "url" args && builtins.hasAttr args.url hashOverrides + then prev.fetchurl (args // { sha1 = hashOverrides.${args.url}; }) + else prev.fetchurl args; + })]; + } + else pkgs; + + androidPkgs = + config: + pkgsWithOverrides.androidenv.composeAndroidPackages { + platformVersions = config.platformVersions; + buildToolsVersions = [ config.buildToolsVersion ]; + cmdLineToolsVersion = config.cmdLineToolsVersion; + includeEmulator = true; + includeSystemImages = true; + includeNDK = config.includeNDK; + ndkVersions = if config.includeNDK && config.ndkVersion != "" then [ config.ndkVersion ] else [ ]; + includeCmake = config.includeCMake; + cmakeVersions = if config.includeCMake && config.cmakeVersion != "" then [ config.cmakeVersion ] else [ ]; + abiVersions = abiVersions; + systemImageTypes = config.systemImageTypes; + }; + in + { + android-sdk = (androidPkgs androidSdkConfig).androidsdk; + default = (androidPkgs androidSdkConfig).androidsdk; + } + ); + + androidSdkConfig = androidSdkConfig; + }; +} 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/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/flake.lock b/examples/react-native/devbox.d/android/flake.lock new file mode 100644 index 00000000..4b8e88ed --- /dev/null +++ b/examples/react-native/devbox.d/android/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1776329215, + "narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b86751bc4085f48661017fa226dee99fab6c651b", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/examples/react-native/devbox.d/android/flake.nix b/examples/react-native/devbox.d/android/flake.nix new file mode 100644 index 00000000..110b24cf --- /dev/null +++ b/examples/react-native/devbox.d/android/flake.nix @@ -0,0 +1,147 @@ +{ + description = "Android SDK tools for Devbox (plugin local flake)"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + outputs = + { self, nixpkgs }: + let + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + + # Read android.lock (generated by android.sh devices sync) + # On first initialization, android.lock may not exist yet, so provide defaults + androidLockExists = builtins.pathExists ./android.lock; + androidLockData = if androidLockExists + then builtins.fromJSON (builtins.readFile ./android.lock) + else { + # Default values for initial flake evaluation before sync runs + ANDROID_BUILD_TOOLS_VERSION = "36.1.0"; + ANDROID_CMDLINE_TOOLS_VERSION = "19.0"; + 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"; + }; + defaultsData = androidLockData; + getVar = + name: + if builtins.hasAttr name defaultsData then toString (builtins.getAttr name defaultsData) + else builtins.throw "Missing required value in android.json: ${name}"; + + unique = + list: + builtins.foldl' ( + acc: item: if builtins.elem item acc then acc else acc ++ [ item ] + ) [ ] list; + + lockData = + if builtins.pathExists ./devices.lock + then builtins.fromJSON (builtins.readFile ./devices.lock) + else { devices = [ ]; }; + + # Extract API versions from lock file devices array, default to latest if empty + deviceApis = + if builtins.hasAttr "devices" lockData && (builtins.length lockData.devices) > 0 + then map (device: device.api) lockData.devices + else [ 36 ]; # Default to latest stable API + + # Include ANDROID_COMPILE_SDK in platform versions if set (for projects + # that compile against a different API than the emulator/device targets) + compileSdkApis = + if builtins.hasAttr "ANDROID_COMPILE_SDK" defaultsData + then [ (toString defaultsData.ANDROID_COMPILE_SDK) ] + else []; + + androidSdkConfig = { + platformVersions = unique ((map toString deviceApis) ++ compileSdkApis); + buildToolsVersion = getVar "ANDROID_BUILD_TOOLS_VERSION"; + cmdLineToolsVersion = getVar "ANDROID_CMDLINE_TOOLS_VERSION"; + systemImageTypes = [ (getVar "ANDROID_SYSTEM_IMAGE_TAG") ]; + includeNDK = + if builtins.hasAttr "ANDROID_INCLUDE_NDK" defaultsData then defaultsData.ANDROID_INCLUDE_NDK else false; + ndkVersion = getVar "ANDROID_NDK_VERSION"; + includeCMake = + if builtins.hasAttr "ANDROID_INCLUDE_CMAKE" defaultsData then defaultsData.ANDROID_INCLUDE_CMAKE else false; + cmakeVersion = getVar "ANDROID_CMAKE_VERSION"; + }; + + # Hash overrides for when Google updates files on their servers + # These can be set in android.lock to work around nixpkgs hash mismatches + # By default this field is not set - only set via `android.sh hash update` when upstream is broken + hashOverrides = if builtins.hasAttr "hash_overrides" androidLockData + then androidLockData.hash_overrides + else {}; + + forAllSystems = + f: + builtins.listToAttrs ( + map (system: { + name = system; + value = f system; + }) systems + ); + in + { + packages = forAllSystems ( + system: + let + pkgs = import nixpkgs { + inherit system; + config = { + allowUnfree = true; + android_sdk.accept_license = true; + }; + }; + + abiVersions = if builtins.match "aarch64-.*" system != null then [ "arm64-v8a" ] else [ "x86_64" ]; + + # Apply hash overrides to nixpkgs if any are specified + # Android packages use SHA1 hashes, not SHA256 + # We need to re-import nixpkgs with overlays when overrides are present + pkgsWithOverrides = if (builtins.length (builtins.attrNames hashOverrides)) > 0 + then import nixpkgs { + inherit system; + config = { + allowUnfree = true; + android_sdk.accept_license = true; + }; + overlays = [(final: prev: { + fetchurl = args: + if builtins.isAttrs args && builtins.hasAttr "url" args && builtins.hasAttr args.url hashOverrides + then prev.fetchurl (args // { sha1 = hashOverrides.${args.url}; }) + else prev.fetchurl args; + })]; + } + else pkgs; + + androidPkgs = + config: + pkgsWithOverrides.androidenv.composeAndroidPackages { + platformVersions = config.platformVersions; + buildToolsVersions = [ config.buildToolsVersion ]; + cmdLineToolsVersion = config.cmdLineToolsVersion; + includeEmulator = true; + includeSystemImages = true; + includeNDK = config.includeNDK; + ndkVersions = if config.includeNDK && config.ndkVersion != "" then [ config.ndkVersion ] else [ ]; + includeCmake = config.includeCMake; + cmakeVersions = if config.includeCMake && config.cmakeVersion != "" then [ config.cmakeVersion ] else [ ]; + abiVersions = abiVersions; + systemImageTypes = config.systemImageTypes; + }; + in + { + android-sdk = (androidPkgs androidSdkConfig).androidsdk; + default = (androidPkgs androidSdkConfig).androidsdk; + } + ); + + androidSdkConfig = androidSdkConfig; + }; +} diff --git a/examples/react-native/devbox.d/segment-integrations.mobile-devtools.android/flake.nix b/examples/react-native/devbox.d/segment-integrations.mobile-devtools.android/flake.nix new file mode 100644 index 00000000..110b24cf --- /dev/null +++ b/examples/react-native/devbox.d/segment-integrations.mobile-devtools.android/flake.nix @@ -0,0 +1,147 @@ +{ + description = "Android SDK tools for Devbox (plugin local flake)"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + outputs = + { self, nixpkgs }: + let + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + + # Read android.lock (generated by android.sh devices sync) + # On first initialization, android.lock may not exist yet, so provide defaults + androidLockExists = builtins.pathExists ./android.lock; + androidLockData = if androidLockExists + then builtins.fromJSON (builtins.readFile ./android.lock) + else { + # Default values for initial flake evaluation before sync runs + ANDROID_BUILD_TOOLS_VERSION = "36.1.0"; + ANDROID_CMDLINE_TOOLS_VERSION = "19.0"; + 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"; + }; + defaultsData = androidLockData; + getVar = + name: + if builtins.hasAttr name defaultsData then toString (builtins.getAttr name defaultsData) + else builtins.throw "Missing required value in android.json: ${name}"; + + unique = + list: + builtins.foldl' ( + acc: item: if builtins.elem item acc then acc else acc ++ [ item ] + ) [ ] list; + + lockData = + if builtins.pathExists ./devices.lock + then builtins.fromJSON (builtins.readFile ./devices.lock) + else { devices = [ ]; }; + + # Extract API versions from lock file devices array, default to latest if empty + deviceApis = + if builtins.hasAttr "devices" lockData && (builtins.length lockData.devices) > 0 + then map (device: device.api) lockData.devices + else [ 36 ]; # Default to latest stable API + + # Include ANDROID_COMPILE_SDK in platform versions if set (for projects + # that compile against a different API than the emulator/device targets) + compileSdkApis = + if builtins.hasAttr "ANDROID_COMPILE_SDK" defaultsData + then [ (toString defaultsData.ANDROID_COMPILE_SDK) ] + else []; + + androidSdkConfig = { + platformVersions = unique ((map toString deviceApis) ++ compileSdkApis); + buildToolsVersion = getVar "ANDROID_BUILD_TOOLS_VERSION"; + cmdLineToolsVersion = getVar "ANDROID_CMDLINE_TOOLS_VERSION"; + systemImageTypes = [ (getVar "ANDROID_SYSTEM_IMAGE_TAG") ]; + includeNDK = + if builtins.hasAttr "ANDROID_INCLUDE_NDK" defaultsData then defaultsData.ANDROID_INCLUDE_NDK else false; + ndkVersion = getVar "ANDROID_NDK_VERSION"; + includeCMake = + if builtins.hasAttr "ANDROID_INCLUDE_CMAKE" defaultsData then defaultsData.ANDROID_INCLUDE_CMAKE else false; + cmakeVersion = getVar "ANDROID_CMAKE_VERSION"; + }; + + # Hash overrides for when Google updates files on their servers + # These can be set in android.lock to work around nixpkgs hash mismatches + # By default this field is not set - only set via `android.sh hash update` when upstream is broken + hashOverrides = if builtins.hasAttr "hash_overrides" androidLockData + then androidLockData.hash_overrides + else {}; + + forAllSystems = + f: + builtins.listToAttrs ( + map (system: { + name = system; + value = f system; + }) systems + ); + in + { + packages = forAllSystems ( + system: + let + pkgs = import nixpkgs { + inherit system; + config = { + allowUnfree = true; + android_sdk.accept_license = true; + }; + }; + + abiVersions = if builtins.match "aarch64-.*" system != null then [ "arm64-v8a" ] else [ "x86_64" ]; + + # Apply hash overrides to nixpkgs if any are specified + # Android packages use SHA1 hashes, not SHA256 + # We need to re-import nixpkgs with overlays when overrides are present + pkgsWithOverrides = if (builtins.length (builtins.attrNames hashOverrides)) > 0 + then import nixpkgs { + inherit system; + config = { + allowUnfree = true; + android_sdk.accept_license = true; + }; + overlays = [(final: prev: { + fetchurl = args: + if builtins.isAttrs args && builtins.hasAttr "url" args && builtins.hasAttr args.url hashOverrides + then prev.fetchurl (args // { sha1 = hashOverrides.${args.url}; }) + else prev.fetchurl args; + })]; + } + else pkgs; + + androidPkgs = + config: + pkgsWithOverrides.androidenv.composeAndroidPackages { + platformVersions = config.platformVersions; + buildToolsVersions = [ config.buildToolsVersion ]; + cmdLineToolsVersion = config.cmdLineToolsVersion; + includeEmulator = true; + includeSystemImages = true; + includeNDK = config.includeNDK; + ndkVersions = if config.includeNDK && config.ndkVersion != "" then [ config.ndkVersion ] else [ ]; + includeCmake = config.includeCMake; + cmakeVersions = if config.includeCMake && config.cmakeVersion != "" then [ config.cmakeVersion ] else [ ]; + abiVersions = abiVersions; + systemImageTypes = config.systemImageTypes; + }; + in + { + android-sdk = (androidPkgs androidSdkConfig).androidsdk; + default = (androidPkgs androidSdkConfig).androidsdk; + } + ); + + androidSdkConfig = androidSdkConfig; + }; +} 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..bafb6f48 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", diff --git a/plugins/android/virtenv/scripts/user/setup.sh b/plugins/android/virtenv/scripts/user/setup.sh index 4115d8bc..0994a262 100755 --- a/plugins/android/virtenv/scripts/user/setup.sh +++ b/plugins/android/virtenv/scripts/user/setup.sh @@ -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