Skip to content

feat: bundle static tmux in macOS/Linux installers as a private fallback#2488

Open
Lucifer4255 wants to merge 6 commits into
AgentWrapper:mainfrom
Lucifer4255:biley/bundle-tmux-installers
Open

feat: bundle static tmux in macOS/Linux installers as a private fallback#2488
Lucifer4255 wants to merge 6 commits into
AgentWrapper:mainfrom
Lucifer4255:biley/bundle-tmux-installers

Conversation

@Lucifer4255

Copy link
Copy Markdown

Closes #2443.

On macOS/Linux, session spawn is tmux-only, but the packaged app ships no tmux — a clean machine fails pre-spawn with RUNTIME_PREREQUISITE_MISSING (#2256/#2259 made that failure clear; this makes it go away). Implements the private bundled fallback discussed in the issue: never a system install, never mutating the user's machine.

Resolution order (re-evaluated on every command)

  1. AO_TMUX_BIN — explicit override; a set-but-broken value fails loudly instead of falling through
  2. System tmux from PATH — always wins when present. Existing users see zero behavior change, and installing tmux later takes effect on the next spawn
  3. App-bundled binary at Resources/tmux-dist/tmux (stamped into the daemon env as AO_BUNDLED_TMUX by the Electron supervisor; accepted only if an executable regular file)
  4. The existing clear RUNTIME_PREREQUISITE_MISSING error (wire code unchanged, no API contract regen)

What's in here

  • Backend: new tmux.ResolveBinary/NewResolver shared by all consumers — the tmux runtime adapter (per-command resolution), the spawn prerequisite gate (validateRuntimePrerequisites), and ao doctor (PASS output now shows provenance: via PATH | bundled | AO_TMUX_BIN) — so they can never disagree on which binary is used. AO_TMUX_BIN/AO_BUNDLED_TMUX are read centrally in config.Load().
  • Artifact pipeline: manual-dispatch .github/workflows/tmux-artifacts.yml builds tmux 3.5a + libevent 2.1.12 + ncurses 6.5 from sha256-verified source tarballs, natively per target (macOS arm64/x64: static deps, dynamic only against libSystem; Linux x64: musl fully-static on alpine with compiled-in terminfo fallbacks), and publishes a tmux-artifacts-v<ver>-<rev> release with checksums.txt + provenance/license notes (tmux ISC, libevent BSD-3, ncurses MIT-X11).
  • Packaging: fetch-tmux.mjs runs in prepackage/premake/publish (before signing, so the binary is sealed into the macOS signature), verifies the download against the release checksums.txt with optional in-repo pins as a cross-check, and ships tmux-dist as an extraResource. AO_SKIP_TMUX_FETCH=1 escape hatch for offline dev packaging. deb/rpm additionally declare a system tmux dependency as a cheap complement.
  • Docs: provenance + rollover procedure in frontend/docs/desktop-release.md; landing docs updated so "install tmux" is CLI-only guidance.

Testing

  • Backend: full go test ./... green — new table-driven resolver tests (override wins / system beats bundled / bundled accepted when PATH empty / non-executable rejected / sentinel errors), per-command re-resolution tests in the tmux adapter, spawn-gate tests (bundled accepted, resolver failure creates no session row), doctor provenance tests.
  • Frontend: 460 vitest tests green, incl. bundledTmuxPath and checksum-parsing/pin-resolution suites.
  • Live smoke with the compiled ao binary across all four states:
    WARN tmux: not found in PATH (the desktop app falls back to its bundled tmux; …)
    PASS tmux: …/faketmux/tmux (tmux 3.5a, via bundled)
    PASS tmux: /opt/homebrew/bin/tmux (tmux 3.7b, via PATH)
    PASS tmux: …/faketmux/tmux (tmux 3.5a, via AO_TMUX_BIN)

Follow-ups before release

  • Dispatch the tmux-artifacts workflow once to cut tmux-artifacts-v3.5a-1, then (optionally) copy the per-asset sha256 pins into fetch-tmux.mjs.
  • Prove macOS notarization of the bundled Mach-O on a test release; if osx-sign misses it under Resources/, the fallback is an explicit binaries list in osxSign.
  • Packaged smoke on a tmux-less machine per the checklist in desktop-release.md.

Resolve tmux in one place shared by the spawn prerequisite gate, the tmux
runtime adapter, and ao doctor: AO_TMUX_BIN override, then system PATH,
then the app-bundled binary (AO_BUNDLED_TMUX), then the existing
RUNTIME_PREREQUISITE_MISSING error. Resolution re-runs per command so a
tmux installed after daemon start wins on the next spawn. Nothing is ever
installed to the user's system.
Manual-dispatch workflow compiling tmux + libevent + ncurses from
checksum-verified source tarballs, natively per target (macOS arm64/x64,
Linux x64 musl-static), and publishing a tmux-artifacts-v<ver>-<rev>
release with checksums and provenance notes for the packaging fetch step.
…kage hook

fetch-tmux.mjs downloads the checksum-pinned artifact from the
tmux-artifacts release into frontend/tmux-dist before packaging/signing,
ships it as an extraResource, and deb/rpm additionally declare a system
tmux dependency. Checksums are placeholders until the first artifacts
release is cut.
bundledTmuxPath locates the tmux-dist extraResource for packaged
macOS/Linux builds; the daemon uses it only when no system tmux is on
PATH. Dev and Windows pass nothing.
Updated fetch-tmux.mjs to improve checksum verification with optional sha256 pins for provenance. Introduced functions to parse checksums and resolve expected hashes. Added a new test suite for these functionalities to ensure correctness.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bundle tmux in the macOS/Linux installers so sessions work on a clean install

1 participant