# `mix mob.deploy`
[🔗](https://github.com/genericjam/mob_dev/blob/master/lib/mix/tasks/mob.deploy.ex#L1)

Compiles the project then pushes BEAM files to all connected
Android devices and iOS simulators.

## Modes

**Fast deploy** (default) — push BEAMs + restart. Use this for day-to-day
Elixir code changes. Requires the native app already installed on device.

    mix mob.deploy

**Full deploy** — build native binary + install APK/app + push BEAMs.
Use this the first time, or after changes to native C/Java/Swift code.

    mix mob.deploy --native

## Options

  * `--native`              — build native binaries before pushing BEAMs
  * `--no-restart`          — push BEAMs but don't restart the app
  * `--device <id>`         — target a specific device; use `mix mob.devices` to find IDs
  * `--dist-port <N>`       — pin the BEAM dist listen port (default: auto-allocated per
                            device, `9100 + index`). Use to resolve EPMD collisions when
                            multiple sims/emulators are running the same app concurrently
                            and the auto-allocated ports aren't what you want.
  * `--node-suffix <S>`     — append `_<S>` to the BEAM node name (default: auto-derived
                            from device serial on Android, SIMULATOR_UDID on iOS sim). Use
                            for scripted scenarios where you need a specific naming scheme.
  * `--schedulers <N>`      — set BEAM scheduler count (saved to mob.exs)
  * `--beam-flags "<flags>"` — arbitrary BEAM flags string (saved to mob.exs)
  * `--slim`                — strip OTP source/debug for size measurement on
                              a real device. OFF by default for dev iteration
                              (the strip pass adds ~5-10s per build); use this
                              to verify a slim build runs before
                              `mix mob.republish` round-trips through TestFlight.
                              The strip set is controlled by `MobDev.OtpAudit.Slim`;
                              per-app overrides live in `mob.exs`:

                                  config :mob_dev,
                                    slim: [
                                      drop_libs: ["my_unused_dep"],
                                      keep_libs: ["mnesia"],
                                      audit: true,                       # opt in
                                      # Single capture (a starting point):
                                      trace_json: "priv/mob_trace.json",
                                      # OR multiple captures unioned —
                                      # much safer for production
                                      # stripping. A lib is trace-
                                      # strippable only if NONE of the
                                      # captures observed any of its
                                      # modules.
                                      trace_jsons: [
                                        "priv/boot.json",
                                        "priv/ui.json",
                                        "priv/auth.json"
                                      ]
                                    ]

                              With `audit: true`, the slim pass runs
                              `MobDev.OtpAudit` against the bundle and
                              expands the strip set with foreign apps
                              + (when a trace is supplied) the
                              trace-augmented strip set. Trace JSON
                              comes from `mix mob.trace_otp --json`.

## BEAM scheduler tuning

The default native build uses `1:1` (single scheduler) for battery efficiency.
Override for the current deploy and all future deploys until changed:

    # Pin to 2 schedulers
    mix mob.deploy --schedulers 2

    # Let BEAM auto-detect — one scheduler per logical core
    mix mob.deploy --schedulers 0

    # Arbitrary flags (replaces --schedulers)
    mix mob.deploy --beam-flags "-S 4:4 -A 4"

The chosen value is written to `mob.exs` under `beam_flags:` and reused on
subsequent `mix mob.deploy` runs that don't pass either flag. The flags are
written alongside the BEAMs as a `mob_beam_flags` file that the native launcher
reads at startup — no APK/app rebuild required.

## Under the hood

A fast deploy is equivalent to:

    mix deps.get                                     # only with --native
    mix compile

    # Android
    adb push _build/prod/lib/*/ebin/*.beam /data/data/<pkg>/files/lib/*/ebin/
    adb shell am force-stop <package>               # restart

    # iOS simulator
    xcrun simctl spawn <udid> cp <beam_files> <app_bundle>/

When Erlang distribution is already reachable (app running, node connected),
`mix mob.deploy` skips `adb push` and hot-pushes via RPC instead — equivalent
to calling `nl(Module)` in IEx for every changed module:

    :rpc.call(node, :code, :load_binary, [Module, path, beam_binary])

With `--native`, it also runs the platform build before pushing BEAMs:

    # Android
    ./gradlew assembleDebug
    adb install -r app/build/outputs/apk/debug/app-debug.apk

    # iOS simulator
    xcodebuild -scheme <app> -destination 'platform=iOS Simulator,...' build
    xcrun simctl install booted <app>.app

# `format_summary`

```elixir
@spec format_summary([Device.t()], [Device.t()], [Device.t()], keyword()) :: [
  String.t()
]
```

Build the per-deploy summary lines from the three device buckets.

Returns an iolist of strings (one per line) that the task prints
verbatim. Public so the report shape can be pinned against fixture
device lists — keeps "Failed on N" from regressing back into
counting skipped-because-not-installed devices.

Opts:
  * `:restart` — boolean; controls the post-deploy IEx hint line

---

*Consult [api-reference.md](api-reference.md) for complete listing*
