Skip to content

Releases: GenericJam/mob_dev

0.6.15

23 Jun 05:31
c709cf7

Choose a tag to compare

Security

  • Bumped req 0.5.18 → 0.6.2 (pulls finch 0.22.0 → 0.23.0), clearing
    EEF-CVE-2026-49755 (HIGH) and EEF-CVE-2026-49756 (LOW) flagged by
    mix mob.security_scan. req is a transitive dep (via igniter); the bump
    stays within igniter's ~> 0.5 requirement.

Fixed

  • mix mob.new_plugin no longer scaffolds plugins pinned to the abandoned
    mob ~> 0.6.
    MobDev.Plugin.Scaffold hard-coded {:mob, "~> 0.6"} in the
    generated mix.exs and mob_version: "~> 0.6" in every tier's manifest, so a
    freshly scaffolded plugin could not activate against the published mob 0.7.x
    (installed :mob 0.7.x does not satisfy mob_version "~> 0.6"). The requirement
    is now derived at scaffold time from the mob actually resolved in the project
    (Scaffold.detect_mob_requirement/0), falling back to a single
    @fallback_mob_requirement constant ("~> 0.7") when mob isn't loadable.
    mix.exs and the manifest always agree. A Scaffold test pins the default and
    asserts a generated manifest validates against a matching mob version, so the
    pin can't silently lag a future mob release. (#21)

0.6.14

23 Jun 03:53

Choose a tag to compare

Added

  • :extra_static_libs hook on :static_nifs entries — a Mob app can now
    link external per-ABI static archives alongside its project NIF archives.
    Some project NIFs intentionally declare extern symbols and don't host-link
    their backing archive (avoiding a host/device archive mismatch during mix compile); this lets the native app link resolve those symbols against the
    correct per-ABI .a. Entries require concrete per-ABI keys (:ios_sim,
    :ios_device, :android_arm64, :android_arm32, :android_x86_64), and the
    matching archive paths are appended to the existing -Dproject_rust_libs=
    link argument. -D<module>_static=true is emitted only on the ABIs where the
    entry applies, and iOS project-NIF filtering is now per-ABI so a device-only
    guarded entry doesn't leak into simulator args. Also passes Zigler 0.16's
    required generated-build flags when re-driving staged Zig NIF builds and
    resolves Zigler dotfile sources with match_dot: true. Verified on a physical
    SM-T577U (arm64-v8a) tablet via a Ghostty VT NIF. (#24)

Changed

  • mix mob.deploy --native now preserves on-device app data when the signing
    key matches.
    The Android install path previously ran an unconditional
    adb uninstall before adb install, which wiped MOB_DATA_DIR (on-device
    identity, screen stores) on every native deploy — even an in-place update
    signed with the same (e.g. committed) debug keystore. It now attempts
    adb install -r first and only falls back to uninstall + install when the
    in-place update is genuinely rejected (INSTALL_FAILED_UPDATE_INCOMPATIBLE
    from a signature mismatch, INSTALL_FAILED_VERSION_DOWNGRADE, etc.). Apps that
    pin a committed debug keystore now keep their identity across --native
    redeploys. Decision logic extracted to NativeBuild.needs_clean_reinstall?/2
    and unit-tested.

Fixed

  • mix mob.deploy --native --android now fails fast with the real cause when
    zig is missing
    (landed in code before 0.6.13; previously undocumented).
    The Android JNI build is driven by build.zig; with zig off PATH it used to
    print a yellow "skipping build.zig step" warning and fall through to a CMake
    fallback that references C sources mob 0.7+ no longer ships, dying ~150 lines
    later with a misleading Cannot find source file: .../mob_nif.c. It now aborts
    before Gradle with an actionable message (install zig 0.15.x, verify with
    mix mob.doctor) when build.zig is present, zig is absent, and the legacy
    C sources are gone. Decision extracted to NativeBuild.zig_build_plan/3. (#20)

0.6.13

20 Jun 20:09

Choose a tag to compare

Changed

  • mix mob.deploy --native now preserves on-device app data when the signing
    key matches.
    The Android install path previously ran an unconditional
    adb uninstall before adb install, which wiped MOB_DATA_DIR (on-device
    identity, screen stores) on every native deploy — even an in-place update
    signed with the same (e.g. committed) debug keystore. It now attempts
    adb install -r first and only falls back to uninstall + install when the
    in-place update is genuinely rejected (INSTALL_FAILED_UPDATE_INCOMPATIBLE
    from a signature mismatch, INSTALL_FAILED_VERSION_DOWNGRADE, etc.). Apps that
    pin a committed debug keystore now keep their identity across --native
    redeploys. Decision logic extracted to NativeBuild.needs_clean_reinstall?/2
    and unit-tested.

Fixed

  • mix mob.deploy --native --android now fails fast with the real cause when
    zig is missing
    (landed in code before 0.6.13; previously undocumented).
    The Android JNI build is driven by build.zig; with zig off PATH it used to
    print a yellow "skipping build.zig step" warning and fall through to a CMake
    fallback that references C sources mob 0.7+ no longer ships, dying ~150 lines
    later with a misleading Cannot find source file: .../mob_nif.c. It now aborts
    before Gradle with an actionable message (install zig 0.15.x, verify with
    mix mob.doctor) when build.zig is present, zig is absent, and the legacy
    C sources are gone. Decision extracted to NativeBuild.zig_build_plan/3. (#20)

0.6.12

19 Jun 22:15

Choose a tag to compare

Fixed

  • 16 KB page-size alignment enforced at build time for every app. Android
    15+ devices use 16 KB memory pages and Google Play requires every bundled
    .so to have 16 KB-aligned LOAD segments. The -Wl,-z,max-page-size=16384
    link flag lives in the app's build.zig, which is copied once at mix mob.new
    and never regenerated — so apps generated before the template carried the flag
    kept linking 4 KB-aligned .so and failed Play. mix mob.deploy --native now
    reads the app's build.zig and, if its -shared link lacks the flag, injects
    it before linking (idempotent — a no-op when already present, e.g. the current
    mob_new template or a hand-fixed app). Pure core in inject_page_size_flag/1.

0.6.11

19 Jun 19:12

Choose a tag to compare

Added

  • mix mob.adopt — installs Mob into an existing Phoenix project,
    the Igniter-based install-into-existing counterpart to mix mob.new
    (which generates from scratch). Composable: the orchestrator runs the
    sub-installers mob.adopt.{deps,bridge,screen,mob_app,mob_exs,native,finalize},
    each invokable independently. Default LV-bridge mode wires window.mob
    through a LiveView phx-hook and generates a mob_app.ex that boots the
    host Phoenix endpoint on-device (SQLite Repo assumed); --no-live-view
    generates a thin-client shell whose WebView opens a deployed server.
    Pre-1.0: refuses loudly (via Igniter issues) on umbrella / non-Phoenix /
    heavily-customised app.js or root layout / non-SQLite LV hosts rather
    than risk breaking the app. The native Android/iOS trees (--android /
    --ios) render from mob_new's templates and require the mob_new archive
    installed
    (mix archive.install hex mob_new) — mob_new stays the single
    source of native templates; the Elixir-side adoption needs no archive.
    Contributed as mob_new#8
    by @ken-kost and relocated here — adopt is
    an Igniter task that mutates an existing project (like mob.add_nif /
    mob.enable), so it belongs in mob_dev (a Hex dep), not in mob_new (a
    self-contained Mix archive that can't carry Igniter). See
    decisions/2026-06-19-mob-adopt-lives-in-mob_dev.md. The shared
    patcher/generator helpers are duplicated from mob_new into
    MobDev.Adopt.{Patcher,Generator} pending the Phase-5 Igniter
    reunification.

0.6.10

19 Jun 07:01

Choose a tag to compare

Added

  • Plugin :cpp_archive NIFs — ship a C++ static library (e.g. an Nx
    backend) from a plugin. Manifest-driven cross-compile + --whole-archive
    static link (rule #11), with <module>_nif_init symbol verification and
    duplicate-init cross-validation. (#18)

Fixed

  • cpp_archive builds now fail fast with a named error on an unsupported
    Android ABI (e.g. the x86_64 emulator) instead of silently skipping — which
    previously deferred an unresolved <module>_nif_init to an on-device link
    failure.
  • The plugin manifest now requires a lowercase :module atom for
    :cpp_archive entries (fail-loud instead of fail-open / libnil.a).

0.6.9

19 Jun 04:47

Choose a tag to compare

Fixed

  • mix mob.publish --android now commits when Google requires
    changesNotSentForReview=true.
    Uploading a release while the app is under
    policy review (or otherwise can't auto-send for review) made the Play Edits
    :commit fail with HTTP 400 ("Please set the query parameter
    changesNotSentForReview to true"), discarding the whole edit so nothing
    reached the track. commit_edit/3 now detects that 400 and retries the commit
    with ?changesNotSentForReview=true; the changes land on the track and are
    sent for review from the Play Console UI. Verified uploading Io v13 to the
    internal track.

0.6.8

19 Jun 01:55

Choose a tag to compare

Added

  • mix mob.connect --only <serial> (alias --device/-d, repeatable).
    Restricts the run to devices whose serial/udid contains the given substring.
    Without it, connect attaches to every running device, so one slow or locked
    device (typically a plugged-in physical iPhone whose app restart blocks) could
    stall the whole session before any node connected. Verified end-to-end against
    a single Android phone: mix mob.connect --only ZY22CRLMWK tunnels, restarts,
    and connects livebook_mob_android_zy22crlmwk@127.0.0.1 on its serial-derived
    port 9633, then RPC into the device BEAM works (read live state, eval code).

0.6.7

18 Jun 20:59

Choose a tag to compare

Fixed

  • mix mob.connect reliability — dist ports keyed by device serial, not run
    index.
    The Mac runs one shared EPMD; assigning ports as 9100 + index meant
    every project's first device claimed 9100, so two phones (or two projects'
    device-0) registered the same port and adb forward tcp:9100 could only reach
    one — the other silently timed out. Ports are now derived from the device
    serial (Tunnel.serial_base_port/1, a crc32 hash into 9100..9899) and bumped
    past any port another live node/forward already holds (assign_dist_port/2).
    A given phone always gets the same unique port across runs and projects, and
    deploy and connect agree on it. Tunnel.setup/2setup/1 (port is now
    serial-derived, not index-passed).
  • Stale-tunnel cleanup. Tunnel.setup removes the device's own old forwards
    first (scoped to that serial), so prior runs no longer leave duplicate/wrong
    forwards that poison the next connect.
  • Real diagnostics on connect failure. A timed-out node now reports why
    app not running / Standby-killed, dist never registered in EPMD, registered at
    a different port, no forward, or cookie mismatch — instead of a black-box
    "timed out".

0.6.6

18 Jun 05:45

Choose a tag to compare

Fixed

  • Android native build skips ABIs the app's build.zig doesn't handle
    instead of hard-failing. mob_dev builds arm64-v8a/armeabi-v7a/x86_64 by
    default, but an app's app-owned build.zig (copied at mix mob.new time)
    may predate x86_64 support (mob_new < 0.4.5) and reject -Dabi=x86_64. That
    used to fail the whole native build — aborting before the
    io.mob.plugin.MobPluginBootstrap regen, so the next gradle build then failed
    on an unresolved bootstrap. Now each ABI is pre-flighted against the build.zig
    (build_zig_supports_abi?/2) and unsupported ones are skipped with a warning
    (gradle abiFilters wouldn't ship them anyway). Real failures of a SUPPORTED
    ABI still halt the build.