Skip to content

Latest commit

 

History

History
823 lines (646 loc) · 27.2 KB

File metadata and controls

823 lines (646 loc) · 27.2 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Repository Overview

This is a mobile development templates repository providing Devbox plugins and example projects for Android, iOS, and React Native. The plugins enable project-local, reproducible mobile development environments without touching global state (e.g., ~/.android).

Critical Development Rules

NEVER Modify .devbox/virtenv/ Directly

IMPORTANT: Never edit files in .devbox/virtenv/ directories. These are temporary runtime directories that are regenerated from plugin sources.

Correct workflow:

  1. Edit source files in plugins/{platform}/virtenv/scripts/
  2. Run devbox run sync to copy changes to example projects
  3. The .devbox/virtenv/ directories are automatically regenerated on devbox shell or devbox run

Why this matters:

  • Changes to .devbox/virtenv/ are lost when the virtenv is regenerated
  • Plugin sources in plugins/ are the source of truth
  • Example projects sync from plugin sources

Development workflow:

  • Full sync: devbox run sync - Reinstalls all example projects (slow, complete)
  • Quick sync: scripts/dev/sync-examples.sh - Copies plugin scripts only (fast, development-only)

Logging Guidelines

IMPORTANT: All logs must go to ${TEST_LOGS_DIR} (defaults to reports/logs/), never to /tmp/.

Standardized Logging Functions:

Use the built-in logging functions from lib.sh for consistent, identifiable output:

# Auto-detects script name from $0
android_log_info "Creating AVD: pixel_api30"
android_log_warn "Emulator already running"
android_log_error "APK not found"
android_log_debug "SDK path: /nix/store/..."

# Or explicitly provide script name
android_log_info "avd.sh" "Creating AVD: pixel_api30"
ios_log_warn "simulator.sh" "Simulator boot timeout"

Output format:

[INFO] [avd.sh] Creating AVD: pixel_api30
[WARN] [emulator.sh] Emulator already running
[ERROR] [deploy.sh] APK not found
[DEBUG] [core.sh] SDK path: /nix/store/...

Log levels:

  • android_log_debug / ios_log_debug - Only shown when DEBUG=1 or ANDROID_DEBUG=1 / IOS_DEBUG=1
  • android_log_info / ios_log_info - Always shown
  • android_log_warn / ios_log_warn - Always shown
  • android_log_error / ios_log_error - Always shown

File Logging Paths:

For writing log files, use project-local paths:

# Use environment variables (preferred)
echo "$data" > "${TEST_LOGS_DIR}/test-output.txt"
mkdir -p "${TEST_LOGS_DIR}"
log_file="${TEST_LOGS_DIR}/$(date +%Y%m%d-%H%M%S)-test.log"

# Or use hardcoded path if variables unavailable
echo "$data" > reports/logs/test-output.txt
mkdir -p reports/logs

Incorrect:

echo "$data" > /tmp/test-output.txt  # WRONG - /tmp not project-local
log_file="/tmp/test.log"              # WRONG - may be cleaned up by system

Environment Variables:

  • REPORTS_DIR: Base reports directory (default: reports)
  • TEST_LOGS_DIR: Test logs directory (default: reports/logs)
  • TEST_RESULTS_DIR: Test results directory (default: reports/results)

Why this matters:

  • /tmp/ files may be cleaned up by the system
  • reports/logs/ is gitignored and project-local
  • CI/CD systems expect logs in reports/
  • Consistent location makes debugging easier
  • Standardized logging makes scripts identifiable in process-compose output
  • Environment variables allow for configuration flexibility

Process Isolation and Safety

CRITICAL RULE: Only terminate processes that we explicitly started. Never interfere with external processes that may have been spawned by other projects.

This applies to ALL processes:

  • Metro bundlers
  • Android emulators
  • iOS simulators
  • Running apps
  • Development servers
  • Any other background processes

Implementation requirements:

  1. Track Process IDs: When starting a process, record its PID in a project-local file:

    # Example from Metro implementation
    rn_track_metro_pid() {
      suite_name="${1:-default}"
      metro_pid="$2"
      metro_dir="${DEVBOX_VIRTENV}/metro"
      pid_file="$metro_dir/pid-${suite_name}.txt"
    
      mkdir -p "$metro_dir"
      echo "$metro_pid" > "$pid_file"
    }
  2. Verify Before Killing: Before terminating a process, verify:

    • The PID file exists (we tracked it)
    • The process is still running
    • The process is actually what we expect (check command name)
    # Example from Metro implementation
    if [ ! -f "$pid_file" ]; then
      echo "No PID tracked - we didn't start it"
      return 0
    fi
    
    metro_pid=$(cat "$pid_file")
    
    if ps -p "$metro_pid" >/dev/null 2>&1; then
      process_cmd=$(ps -p "$metro_pid" -o command= 2>/dev/null || true)
      if echo "$process_cmd" | grep -q "react-native start"; then
        kill "$metro_pid" 2>/dev/null || true
      else
        echo "PID $metro_pid is not Metro, skipping"
      fi
    fi
  3. Let Process Managers Handle Lifecycle: When using process-compose, let it manage process termination. Cleanup scripts should only remove state files:

    cleanup:
      command: |
        # DON'T kill Metro - process-compose handles it
        # DO clean up state files
        rm -f ${DEVBOX_VIRTENV}/metro/port-android.txt
        rm -f ${DEVBOX_VIRTENV}/metro/env-android.sh

Why this matters:

  • Multiple projects may run simultaneously on the same machine
  • Developers may have long-running processes from other work
  • Killing external processes causes data loss and frustration
  • Process isolation ensures reproducible, conflict-free execution
  • Clean separation enables parallel test execution with --pure

Core Architecture

Plugin System

Three main plugins are located in plugins/:

  1. android - Android SDK + emulator management via Nix flake

    • SDK flake: devbox.d/<android-plugin-dir>/flake.nix
    • Device definitions: devbox.d/<android-plugin-dir>/devices/*.json
    • Scripts: .devbox/virtenv/android/scripts/
    • Configuration: Environment variables in plugin.json
    • Note: <android-plugin-dir> is android for local includes, but for GitHub includes it uses the full path (e.g., segment-integrations.devbox-plugins.android)
  2. ios - iOS toolchain + simulator management for macOS

    • Device definitions: devbox.d/<ios-plugin-dir>/devices/*.json
    • Scripts: .devbox/virtenv/ios/scripts/
    • Configuration: Environment variables in plugin.json
    • Note: <ios-plugin-dir> is ios for local includes, but for GitHub includes it uses the full path (e.g., segment-integrations.devbox-plugins.ios)
  3. react-native - Composition layer over Android + iOS plugins

    • Inherits both Android and iOS device management
    • Enables cross-platform React Native development

Key Concepts

Device Definitions: JSON files defining emulator/simulator configurations stored in devbox.d/<plugin-dir>/devices/. The actual directory name for <plugin-dir> depends on how the plugin is included: for local includes (e.g., plugin:../plugins/android) it matches the plugin name (e.g., android), but for GitHub includes it uses the full dotted path (e.g., segment-integrations.devbox-plugins.android).

  • Android: {name, api, device, tag, preferred_abi}
  • iOS: {name, runtime}
  • Default devices: min.json and max.json

Lock Files: devices/devices.lock optimizes CI by limiting which SDK versions are evaluated

  • Generated via {platform}.sh devices eval
  • Contains checksums of device definitions for validation

Caching: Nix handles flake evaluation caching internally

  • iOS: .xcode_dev_dir.cache, .shellenv.cache for expensive shell operations
  • No custom Android SDK caching needed - Nix manages this

Environment Scoping: All plugins follow naming patterns:

  • {PLATFORM}_CONFIG_DIR - Configuration directory
  • {PLATFORM}_DEVICES_DIR - Device definitions
  • {PLATFORM}_SCRIPTS_DIR - Runtime scripts
  • {PLATFORM}_DEFAULT_DEVICE - Default device selection
  • ANDROID_DEVICES - Android devices to evaluate (comma-separated, empty = all)
  • IOS_DEVICES - iOS devices to evaluate (comma-separated, empty = all)

Common Commands

Devbox CLI Usage

IMPORTANT:

  • devbox run executes commands/scripts (can run ANY binary in PATH, not just devbox.json scripts)
  • devbox shell starts interactive shell (NOT for running commands)
# Execute commands (preferred)
devbox run test                    # Run script from devbox.json
devbox run android.sh devices list # Run any binary in PATH
devbox run --pure pytest tests/    # Isolated environment
devbox run --list                  # List available scripts

# Interactive shell (for exploration only)
devbox shell                       # Enter shell with packages

# Package management
devbox add python@3.11             # Add package
devbox list                        # List packages
devbox init                        # Create devbox.json

Setup

# Install devbox dependencies
devbox shell

# Validate plugin installation
cd examples/{android|ios|react-native}
devbox shell

Device Management

Android

# List devices
devbox run --pure android.sh devices list

# Create/update/delete devices
devbox run --pure android.sh devices create pixel_api28 --api 28 --device pixel --tag google_apis
devbox run --pure android.sh devices update pixel_api28 --api 29
devbox run --pure android.sh devices delete pixel_api28

# Regenerate lock file (after creating/updating/deleting devices)
devbox run --pure android.sh devices eval

# Sync AVDs to match device definitions
devbox run --pure android.sh devices sync

# View configuration
devbox run --pure android.sh config show

# Override configuration (set in devbox.json)
# {
#   "include": ["plugin:android"],
#   "env": {
#     "ANDROID_DEFAULT_DEVICE": "max",
#     "ANDROID_DEVICES": "min,max"
#   }
# }

iOS

# List devices
devbox run --pure ios.sh devices list

# Create/update/delete devices
devbox run --pure ios.sh devices create iphone15 --runtime 17.5
devbox run --pure ios.sh devices update iphone15 --runtime 18.0
devbox run --pure ios.sh devices delete iphone15

# Regenerate lock file (after creating/updating/deleting devices)
devbox run --pure ios.sh devices eval

# Sync simulators to match device definitions
devbox run --pure ios.sh devices sync

# View configuration
devbox run --pure ios.sh config show

# Override configuration (set in devbox.json)
# {
#   "include": ["plugin:ios"],
#   "env": {
#     "IOS_DEFAULT_DEVICE": "max",
#     "IOS_DEVICES": "min,max"
#   }
# }

Building and Running

Android

cd examples/android

# Build the app (user-defined in example devbox.json)
devbox run --pure build:android

# Start emulator (plugin-provided)
devbox run --pure start:emu [device]  # Defaults to ANDROID_DEFAULT_DEVICE

# Build, install, and launch app on emulator (user-defined in example devbox.json)
devbox run --pure start:app [device]

# Stop emulator (plugin-provided)
devbox run --pure stop:emu

iOS

cd examples/ios

# Build the app (user-defined in example devbox.json)
devbox run --pure build:ios

# Start simulator (plugin-provided)
devbox run --pure start:sim [device]  # Defaults to IOS_DEFAULT_DEVICE

# Build, install, and launch app on simulator (plugin-provided via ios.sh run)
devbox run --pure start:app [device]

# Stop simulator (plugin-provided)
devbox run --pure stop:sim

React Native

cd examples/react-native

# Install dependencies
npm install

# Android workflow
devbox run --pure start:emu [device]   # plugin-provided
devbox run --pure start:app [device]   # user-defined
devbox run --pure stop:emu             # plugin-provided

# iOS workflow
devbox run --pure start:sim [device]   # plugin-provided
devbox run --pure start:app [device]   # user-defined (calls ios.sh run)
devbox run --pure stop:sim             # plugin-provided

# Build for all platforms (user-defined)
devbox run build  # Runs build:android, build:ios, build:web

Testing

Plugin Tests

# Run Android plugin tests
cd plugins/tests/android
./test-*.sh

# Run iOS plugin tests
cd plugins/tests/ios
./test-*.sh

CI Workflows

# Validate locally with act (requires Docker)
act -j android-plugin-tests
act -j ios-plugin-tests

Project Structure

.
├── plugins/
│   ├── android/          # Android plugin
│   │   ├── config/       # Default config templates
│   │   ├── scripts/      # Runtime scripts (android.sh, avd.sh, etc.)
│   │   ├── plugin.json   # Plugin manifest
│   │   └── REFERENCE.md  # Complete API reference
│   ├── ios/              # iOS plugin
│   │   ├── config/
│   │   ├── scripts/
│   │   ├── plugin.json
│   │   └── REFERENCE.md
│   ├── react-native/     # React Native plugin
│   │   ├── plugin.json
│   │   └── REFERENCE.md
│   ├── tests/            # Plugin unit tests
│   └── CONVENTIONS.md    # Plugin development patterns
├── examples/
│   ├── android/          # Minimal Android app
│   │   ├── devbox.d/     # Device definitions and config
│   │   └── devbox.json   # Includes android plugin
│   ├── ios/              # Swift package example
│   │   ├── devbox.d/
│   │   └── devbox.json   # Includes ios plugin
│   └── react-native/     # React Native app
│       ├── devbox.d/     # Both Android and iOS devices
│       └── devbox.json   # Includes react-native plugin
├── tests/                # E2E test scripts
│   ├── e2e-android.sh
│   ├── e2e-ios.sh
│   ├── e2e-react-native.sh
│   ├── e2e-sequential.sh
│   └── e2e-all.sh
├── wiki/                 # Documentation
│   ├── guides/           # User guides and cheatsheets
│   ├── reference/        # CLI, config, and env var references
│   └── project/          # Architecture and strategy docs
├── .github/workflows/
│   ├── pr-checks.yml     # Fast PR validation
│   └── e2e-full.yml      # Full E2E tests
└── devbox.json           # Root devbox config

Development Patterns

Working with Plugins

When modifying plugins:

  1. Plugin configuration is in plugin.json (init hooks, env vars, scripts)
  2. Runtime scripts go in scripts/ directory
  3. Follow conventions in plugins/CONVENTIONS.md:
    • Use {platform}_ prefixes for functions
    • set -euo pipefail for safety
    • Non-blocking validation (warn, don't fail)
    • Debug logging via {PLATFORM}_DEBUG=1

Script Layering Architecture

Plugin scripts are organized into strict layers to prevent circular dependencies. Critical rule: scripts can only source/depend on scripts from earlier layers, never from the same layer or later layers.

scripts/
├── lib/        # Layer 1: Pure utilities
├── platform/   # Layer 2: SDK/platform setup
├── domain/     # Layer 3: Domain operations (AVD, emulator, run)
├── user/       # Layer 4: User-facing CLI
└── init/       # Layer 5: Environment initialization

Key principles:

  • lib/: Pure utility functions, no platform-specific logic
  • platform/: SDK resolution, PATH setup, device configuration
  • domain/: Internal domain operations - atomic, independent, orchestrated by layer 4
  • user/: User-facing CLI commands (android.sh, devices.sh) - orchestrates domain operations
  • init/: Environment initialization run by devbox hooks

Critical: Domain layer scripts cannot call each other. If two domain scripts need the same functionality, that functionality must be moved to the platform or lib layer. The user layer orchestrates multiple domain operations.

See wiki/project/ARCHITECTURE.md for complete documentation.

Device Management Workflow

  1. Device definitions are JSON files in devbox.d/<plugin-dir>/devices/ (where <plugin-dir> depends on the include method; see Plugin System above)
  2. Modify devices using CLI commands (not manual editing)
  3. After changes, regenerate lock file: {platform}.sh devices eval
  4. Lock files should be committed to optimize CI

Adding New Devices

# Android - specify API level and device profile
devbox run --pure android.sh devices create pixel_api30 \
  --api 30 \
  --device pixel \
  --tag google_apis \
  --preferred_abi x86_64

# iOS - specify simulator runtime version
devbox run --pure ios.sh devices create iphone14 --runtime 16.4

# Regenerate lock file after adding
devbox run --pure {platform}.sh devices eval

Debugging

Enable debug logging:

# Platform-specific
ANDROID_DEBUG=1 devbox shell
IOS_DEBUG=1 devbox shell

# Global
DEBUG=1 devbox shell

Check cache validity:

# iOS - view cached Xcode path
cat .devbox/virtenv/ios/.xcode_dev_dir.cache

# Android SDK - Nix handles caching internally (no cache file to check)

Validate lock files:

devbox run --pure android.sh devices eval
devbox run --pure ios.sh devices eval

Contributing Guidelines

Code Philosophy

Simplicity and readability first. Code should be easy to understand at a glance. Prefer straightforward solutions over clever ones. If you need to explain what code does, the code is probably too complex.

DRY and single responsibility. Extract repeated logic into functions with clear names. Each function should do one thing well. Each file should have a focused purpose.

Keep files focused and manageable. Don't let files grow with unrelated functions. Split large files by concern:

  • lib.sh - Generic utilities (path manipulation, JSON parsing, logging)
  • devices.sh - Device management operations
  • avd.sh - AVD-specific operations
  • env.sh - Environment variable setup

When a file exceeds ~500 lines or contains unrelated functions, split it.

Minimal comments in code. Write self-documenting code with clear function and variable names. Use comments only for:

  • Why decisions were made (not what the code does)
  • Complex algorithms that can't be simplified
  • Workarounds for external tool bugs

Document public APIs exhaustively in REFERENCE.md files, not in code comments.

Fail loudly, avoid fallbacks. When something is wrong, the code should exit with a clear error message and non-zero status. Avoid silent fallbacks that hide problems.

Reduce edge cases and unexpected behavior. Design for the common path. When edge cases arise, validate assumptions early and fail fast rather than adding complex branching logic.

Scripts fail on error. Shell scripts use different strictness levels depending on context:

  • Sourced scripts (lib.sh, core.sh, setup.sh): set -e (no -u due to Node.js package compatibility issues with unset variables)
  • User-facing CLIs (android.sh, ios.sh, devices.sh): set -eu
  • Test scripts: set -euo pipefail

Functions return 0 on success, non-zero on failure. Avoid || true except in validation functions where warnings shouldn't block execution.

Validation warns but doesn't block. User-facing validation commands (like lock file checksum mismatches) should warn with actionable fix commands but never prevent the user from continuing. The validation philosophy is "inform, don't obstruct."

Documentation Style

Documentation is split into two types with different purposes:

1. Guides and Examples - Help users accomplish tasks

  • Use prose style with short, digestible paragraphs (2-4 sentences)
  • Only use bullet points and numbered lists for step-by-step instructions
  • Focus on practical workflows and common use cases
  • Include runnable code examples
  • No marketing language or superlatives
  • Examples: CLAUDE.md, CONVENTIONS.md, workflow README files

2. Reference Documentation - Exhaustive explanations of all options

  • Document every user-facing option, variable, method, and command
  • Organized by component (environment variables, CLI commands, config options)
  • Concise descriptions without fluff
  • Include valid values, defaults, and constraints
  • Examples: REFERENCE.md files for each plugin

General writing rules:

  • Write concisely. Remove unnecessary words.
  • No marketing language. Avoid terms like "powerful," "seamless," "robust," "flexible."
  • Use active voice. "The script validates" not "Validation is performed."
  • One concept per paragraph.
  • Code examples should be runnable and realistic.

Naming Standards

Environment Variables:

{PLATFORM}_{CATEGORY}_{DESCRIPTOR}

Examples:
- ANDROID_DEFAULT_DEVICE
- ANDROID_SDK_ROOT
- IOS_DEVELOPER_DIR
- ANDROID_BUILD_TOOLS_VERSION

Shell Scripts:

{platform}.sh       - Main CLI entry point (android.sh, ios.sh)
{feature}.sh        - Feature-specific scripts (devices.sh, avd.sh, env.sh)
lib.sh              - Shared utility functions
test-{feature}.sh   - Test scripts

Examples:
- android.sh
- devices.sh
- env.sh
- lib.sh
- test-devices.sh

Shell Functions:

{platform}_{category}_{action}

Examples:
- android_devices_list
- android_devices_create
- ios_get_developer_dir
- android_validate_lock_file

Device Files:

{descriptor}.json   - Device definition files

Examples:
- min.json          - Minimum supported version
- max.json          - Maximum/latest version
- pixel_api30.json  - Descriptive device name

Lock Files:

devices.lock        - Generated lock file (plain text, device:checksum format)

Cache Files:

.{feature}.cache    - Hidden cache files with descriptive names

Examples:
- .xcode_dev_dir.cache
- .shellenv.cache

Directory Structure Standards

Plugin Directory Layout:

plugins/{platform}/
├── config/              # Template files copied to user projects
│   ├── devices/         # Default device definitions
│   └── *.yaml           # Process-compose test suites
├── scripts/             # Runtime scripts
│   ├── {platform}.sh    # Main CLI
│   ├── lib.sh           # Shared utilities
│   ├── env.sh           # Environment setup
│   └── {feature}.sh     # Feature scripts
├── plugin.json          # Plugin manifest
└── REFERENCE.md         # Complete API reference

Example Project Layout:

examples/{platform}/
├── devbox.d/
│   └── <plugin-dir>/    # Directory name depends on include method
│       └── devices/     # User device definitions
│           ├── *.json
│           └── devices.lock
├── devbox.json          # Includes plugin via path: (local development)
└── README.md            # Usage guide
# Note: Examples use local path includes (path:../../plugins/{platform}/plugin.json)
# so PR checks test against the current plugin source. User-facing docs show the
# GitHub URL format (github:segment-integrations/devbox-plugins?dir=plugins/{platform}).
# <plugin-dir> is "{platform}" for local/path includes, but for GitHub includes it
# uses the full dotted path (e.g., "segment-integrations.devbox-plugins.android").

Test Directory Layout:

plugins/tests/
├── {platform}/
│   ├── test-lib.sh           # Unit tests for lib.sh
│   ├── test-devices.sh       # Unit tests for devices.sh
│   ├── test-device-mgmt.sh   # Integration tests
│   └── test-validation.sh    # Validation tests
└── test-framework.sh         # Shared test utilities

Process-Compose Standards

File naming:

{suite}.yaml

Examples:
- lint.yaml
- unit-tests.yaml
- e2e.yaml

Process naming:

{category}-{feature}

Examples:
- lint-android
- test-android-lib
- e2e-android
- summary

Log locations:

test-results/{suite-name}-logs

Examples:
- test-results/devbox-lint-logs
- test-results/android-repo-e2e-logs

Always include a summary process:

  • Depends on all other processes with process_completed (not process_completed_successfully)
  • Displays test results in clean, scannable format
  • Lists log file locations for debugging

Git Commit Standards

Commits should follow conventional commit format:

{type}({scope}): {description}

Examples:
- feat(android): add device sync command
- fix(ios): resolve Xcode path caching issue
- docs(contributing): add naming standards
- test(android): add device management tests
- refactor(react-native): simplify plugin composition

Types: feat, fix, docs, test, refactor, perf, chore

Scopes: android, ios, react-native, ci, docs, tests

CI/CD

Fast PR Checks (pr-checks.yml)

  • Runs automatically on every PR and push to main
  • Fast tests (lint + unit + integration) plus E2E tests with max devices only
  • Tests: Android max, iOS max, React Native (android-max, ios-max, web)

Full E2E Tests (e2e-full.yml)

  • Weekly schedule (Monday 00:00 UTC) or manual trigger
  • Tests both min and max platform versions:
    • Android: API 21 (min) to API 36 (max)
    • iOS: iOS 15.4 (min) to iOS 26.2 (max)
  • Matrix execution for parallel testing

Running CI Locally

# Requires act (GitHub Actions local runner)
# Install: devbox add act

# Run specific jobs
act -j android-plugin-tests
act -j ios-plugin-tests
act -j android-quick-smoke
act -j ios-quick-smoke

# Run full workflow
act -W .github/workflows/pr-checks.yml

Configuration

Configuration for both Android and iOS plugins is managed via environment variables defined in plugin.json. These env vars are converted to JSON at runtime for internal use.

Android Plugin Environment Variables

  • ANDROID_DEFAULT_DEVICE - Default emulator
  • ANDROID_DEVICES - Devices to evaluate (comma-separated, empty = all)
  • ANDROID_APP_APK - APK path/glob for installation
  • ANDROID_BUILD_TOOLS_VERSION - Build tools version
  • ANDROID_LOCAL_SDK - Use local SDK instead of Nix (0/1)

iOS Plugin Environment Variables

  • IOS_DEFAULT_DEVICE - Default simulator
  • IOS_DEVICES - Devices to evaluate (comma-separated, empty = all)
  • IOS_APP_ARTIFACT - Path or glob for .app bundle (empty = auto-detect via xcodebuild + search)
  • IOS_DOWNLOAD_RUNTIME - Auto-download runtimes (0/1, default: 1)

Important Implementation Notes

Android SDK via Nix Flake

  • The Android SDK is composed via Nix flake at devbox.d/<android-plugin-dir>/flake.nix (directory name depends on include method)
  • Flake outputs: android-sdk, android-sdk-full, android-sdk-preview
  • Nix handles flake evaluation caching internally (fast after first evaluation)
  • Lock file limits which API versions are evaluated (optimization for CI)

iOS Xcode Discovery

  • Multiple strategies: IOS_DEVELOPER_DIR env var → /Applications/Xcode*.app (latest by version) → xcode-select -p/Applications/Xcode.app fallback
  • Path cached in .xcode_dev_dir.cache (1-hour TTL)

Validation Philosophy

  • Validation warnings never block execution
  • Warn with actionable fix commands
  • Skip validation in CI or when tools are missing
  • Examples: lock file checksum mismatches, missing SDK paths

Script Safety

  • Sourced scripts: set -e (no -u due to Node.js package compatibility)
  • User-facing CLIs: set -eu
  • Test scripts: set -euo pipefail
  • Functions return 0 on success, non-zero on failure
  • Validation functions use || true to avoid blocking

References

For complete command and configuration references, see:

  • plugins/android/REFERENCE.md
  • plugins/ios/REFERENCE.md
  • plugins/react-native/REFERENCE.md
  • plugins/CONVENTIONS.md
  • wiki/project/ARCHITECTURE.md
  • wiki/project/ENVIRONMENT-SETUP-STRATEGY.md
  • .github/workflows/README.md