feat(android): Add android.lock and hash override mechanism#23
Merged
feat(android): Add android.lock and hash override mechanism#23
Conversation
This was referenced Apr 21, 2026
d906e9c to
c07b73e
Compare
7eeccd4 to
c8ca69b
Compare
Automatically detects and fixes nixpkgs hash mismatches when Google updates files on their servers without changing version numbers. Changes: - New hash-fix.sh script for automatic hash mismatch detection - Hash override mechanism in flake.nix - Automatic fix on SDK evaluation failure - hash-overrides.json for preserving reproducibility - User-friendly error messages with fix instructions Before: Hash mismatches cause cryptic Nix errors, require manual fixes After: Automatically detects, fixes, and instructs user to commit overrides Example:⚠️ Android SDK hash mismatch detected Fixing automatically... ✅ Hash mismatch fixed! 1. Run 'devbox shell' again to rebuild with the fix 2. Commit hash-overrides.json to preserve reproducibility Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace bespoke hash-fix.sh script with integrated hash commands in devices.sh. Hash overrides now stored in android.lock (not separate file) and managed via: - android.sh hash show - display current overrides - android.sh hash update <url> <hash> - add/update override - android.sh hash clear - remove all overrides Key changes: - Hash overrides stored in android.lock (optional field, not set by default) - Flake reads from android.lock instead of android.json - android.sh devices sync preserves hash_overrides field - Removed hash-fix.sh and android:hash-fix script - Simplified error message in core.sh to suggest manual fix By default, hash overrides are not set - only use as temporary fix when Google updates files on their servers but nixpkgs hasn't caught up yet.
- Update config/README.md to reflect android.lock instead of hash-overrides.json
- Add hash override check to doctor.sh showing active overrides
- Fix misleading 'Fixing automatically...' message in core.sh
- Doctor now shows hash override status and provides clear commands
Doctor output example:
Hash Overrides:
⚠ 2 hash override(s) active
- platform-tools_r37.0.0-darwin.zip
- build-tools_r36.1.0-darwin.zip
View: android.sh hash show
Clear: android.sh hash clear (when nixpkgs is updated)
Split into two jq calls to avoid complex conditional merging: - One for standard lock file (no hash overrides) - One for lock file with hash overrides Fixes: jq compile error 'unexpected )' when running android:sync
Doctor now tests whether hash overrides are still needed
Android packages in nixpkgs use SHA1 hashes (via repo.json), not SHA256.
Updated hash override mechanism to match nixpkgs format:
- Flake overlay now overrides 'sha1' parameter instead of 'sha256'
- Hash commands expect SHA1 hex format (40 chars), e.g., 8c4c926d0ca192376b2a04b0318484724319e67c
- Documentation updated with correct format and examples
- Error messages show how to compute SHA1 with shasum/sha1sum
This matches the official nixpkgs update script format from #511856.
Example hash override in android.lock:
{
"hash_overrides": {
"https://dl.google.com/.../platform-tools_r37.0.0-darwin.zip": "8c4c926d0ca192376b2a04b0318484724319e67c"
}
}
Moves the Android SDK flake.nix from the ephemeral .devbox/virtenv
directory to devbox.d/<plugin-name>/ so the flake and its lock file
can be committed and version controlled per-project.
**Changes:**
- Copy flake.nix to `{{ .DevboxDir }}` instead of `{{ .Virtenv }}`
- Update core.sh to look for flake in ANDROID_CONFIG_DIR first
- Update README to clarify flake.lock location and purpose
- Add react-native example devbox.d configs to demonstrate structure
**Benefits:**
- Projects can version control their flake.lock for reproducible builds
- Each project can have different Android SDK versions/configurations
- Lock file updates via `devbox run devices.sh sync` or `nix flake update`
- Consistent with where device configs live (devbox.d/)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements a two-stage configuration model for reproducible Android builds: - **Stage 1**: Edit env vars in devbox.json (easy to change) - **Stage 2**: Run `android:sync` to generate lock files (commit to git) **Changes:** 1. **android.lock file** - Pins Android SDK configuration - Generated from env vars by `android:sync` command - Committed to git for team-wide reproducibility - Makes SDK changes reviewable in PRs 2. **Unified sync command** - `devbox run android:sync` - Generates android.lock from env vars - Regenerates devices.lock from device JSONs - Syncs AVDs to match device definitions - One command to sync all configuration 3. **Drift detection** - Warns on shell init if config is out of sync - Compares env vars with android.lock - Shows which values don't match - Provides clear instructions to fix 4. **Comprehensive documentation** - Explains env var → lock file model - Step-by-step update guide - Separates Android SDK updates from nixpkgs updates - Clarifies why reproducibility matters **Benefits:** - Reproducible: Lock files ensure identical builds across team - Reviewable: SDK changes visible in PRs - Explicit: Must run sync to apply changes (no accidents) - Detectable: Warns if env vars drift from lock file **Example workflow:** ```sh devbox run android:sync git add devbox.json devbox.d/ && git commit ``` Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
6c2d461 to
6ccebcf
Compare
**Root Cause:**
Device filtering logic appended newlines after each filtered device,
causing an empty line to be processed when only one device was selected.
This triggered "missing required fields" errors and caused emulator
startup to fail in strict mode.
**Example:**
```bash
ANDROID_DEVICES=max # Filter to single device
# Result: {"name":"max",...}\n
# Loop processes: 1) valid JSON ✓ 2) empty string ✗
```
**Fixes:**
1. Strip trailing newline after device filtering (line 395)
2. Add empty-line guard in processing loop (lines 436-442)
3. Add debug logging to show raw device JSON (lines 447-450)
4. Add device count logging before loop (lines 426-427)
**Testing:**
Added comprehensive unit tests in test-device-filtering.sh:
- Single device filter (the bug case)
- Multiple device filter
- Empty filter (all devices)
- Invalid filter (no matches)
- Device count logging
- Empty line guard behavior
All tests pass ✓
**Impact:**
- Fixes Android E2E CI failures on PR #23
- Fixes React Native Android E2E CI failures
- Improves debugging with device count and raw JSON logging
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
**Problem:**
The hash override mechanism was using `pkgs.appendOverlays` which doesn't
work correctly - it caused this error when hash_overrides were present:
```
error: expected a set but found a function: «lambda extendsWithExclusion @
«github:NixOS/nixpkgs/b86751b»/lib/customisation.nix:864:30»
```
**Root Cause:**
`pkgs.appendOverlays` doesn't properly apply overlays to an existing pkgs
instance. The correct approach is to re-import nixpkgs with overlays when
hash overrides are needed.
**Fix:**
Changed flake.nix to re-import nixpkgs with the fetchurl overlay when
hash_overrides are present in android.lock:
```nix
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;
```
**Testing:**
✅ Tested locally with hash override for platform-tools_r37.0.0-darwin.zip
✅ SDK built successfully with correct hash
✅ `devbox run doctor` confirms SDK working
✅ `devbox run android:sync` creates AVDs successfully
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The verify-emulator-ready step was using pgrep to detect if the emulator process started, but pgrep doesn't work reliably across process-compose boundaries in pure/isolated environments. This changes the verification to use 'android.sh emulator ready' which uses adb to check the emulator's boot status via the sys.boot_completed property. This is: - POSIX compliant (uses adb, not pgrep) - Works across process boundaries - Platform-independent (macOS, Linux, BSD, etc.) - More reliable (checks actual boot status, not just process existence) Bug introduced in: c5b0ce7 'fix(tests): Fix test timeouts and add early failure detection' Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace the second pgrep usage (crash detection during boot) with adb-based detection. This uses 'adb devices | grep emulator-' to check if the emulator is still visible to adb. This is: - POSIX compliant (uses adb + grep, no pgrep) - Uses native Android tooling (adb) - Platform-independent (macOS, Linux, BSD) - More reliable (checks adb connection, not process existence) Now all emulator verification uses Android-native tools (adb) instead of generic CLI tools (pgrep), improving portability and reliability. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ress messages Changes: 1. Split emulator verification into two stages: - Stage 1 (30s): Check if emulator appears in 'adb devices' (early failure detection) - Stage 2 (300s): Wait for emulator to be fully booted via 'android.sh emulator ready' 2. Remove broken progress message variables that weren't expanding correctly in process-compose (showed 's/s' instead of actual elapsed time) This fixes CI failures where the emulator was taking longer than 30s to fully boot. The first stage now just checks if adb can see the emulator (fast), then the second stage waits for full boot completion. Fixes #23 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements android.lock for reproducible Android SDK builds with optional hash override support. Consolidates flake relocation (from closed #18) with hash override mechanism.
Size: ~320 lines net (+350/-30 across 8 files)
What's Included
1. android.lock (from closed #18)
android.sh devices synccommand (generates android.lock + devices.lock + syncs AVDs)2. Hash Override Mechanism
hash_overridesfield in android.lock (SHA1 hex format)android.sh hash show/update/clearChanges
Flake improvements:
New commands:
android.sh devices sync- Generate locks and sync AVDsandroid.sh hash show- View current hash overridesandroid.sh hash update <url> <sha1>- Add hash overrideandroid.sh hash clear- Remove all overridesDoctor enhancements:
Impact
Before:
After:
Example android.lock with override:
{ "ANDROID_BUILD_TOOLS_VERSION": "36.1.0", "ANDROID_COMPILE_SDK": 36, "hash_overrides": { "https://dl.google.com/android/repository/platform-tools_r37.0.0-darwin.zip": "8c4c926d0ca192376b2a04b0318484724319e67c" } }Related PRs
Part of 6-PR split of #17:
feat(android): Move flake to devbox.d and add android.lock #18 - Core feature (closed, merged into this PR)🔗 Related nixpkgs PR: NixOS/nixpkgs#511856