Files
Move/DISTRIBUTION-PLAN.md
T
nokeo08 83d91c5630 Initial commit: move CLI (milestone 2)
- src/move.ts entry point with CLI parsing, --help, --version
- src/cli.ts: parseCliArgs, printHelp, ParsedCliArgs, CliError, VERSION
- src/config.ts: Config type, DEFAULT_CONFIG, resolveConfig
- src/keeper.ts: synthetic-activity sweep + idle-watch loop
- package.json bin entry + shebang for 'bun link' global install
- install.sh: contributor bootstrap (will be repurposed; see
  DISTRIBUTION-PLAN.md for the end-user installer design)
- DISTRIBUTION-PLAN.md captures the tabled end-user distribution work
2026-06-15 14:31:31 -05:00

8.5 KiB

Distribution Plan for move

Status: Tabled 2026-06-15. Revisit after the initial Gitea push lands and the repo is publicly reachable at https://gitea.cahlen.com/nokeo08/Move.

This document captures the analysis and decisions for distributing the move CLI as an end-user-friendly tool, so we can resume without redoing the discussion.


Decisions locked in

Decision Choice
Primary hosting Self-hosted Gitea (https://gitea.cahlen.com/nokeo08/Move)
Default branch master
Audience Anyone who finds the repo (broader public-ish)
Effort budget for now Smallest viable curl-able install.sh
Bun handling Fail with a clear message when missing (no auto-install)
Reinstall behavior Idempotent via a version-marker file; MOVE_FORCE=1 overrides
Install location ~/.local/bin + ~/.local/share/move (XDG, no sudo), env-overridable
move shim Bash wrapper script, not symlink
PATH rc files Never modified; print instructions if PATH isn't right
Uninstaller In scope; same curl ... | sh pattern
npm publishing Out of scope
Standalone binaries (bun build --compile) Future work; depends on nut.js native-bundling testing
Homebrew tap Future work; cheap once releases exist

Distribution-option survey (from the discussion)

Four options were weighed:

  1. Compiled standalone binary (bun build --compile). Best UX (single binary, no Bun needed) but needs per-platform builds, a release pipeline, code signing on macOS, and depends on nut.js's prebuilt native .node files bundling correctly under --compileunverified.
  2. Curl-piped installerchosen. Single command, no clone, source-and-Bun model. Conventions used by bun, deno, rustup, nvm. Slight cold-start cost vs. compiled binary; tradeoff accepted.
  3. Package-manager distribution (bun add -g, brew). move is taken on npm; brew is macOS-centric; both add publishing overhead. Out of scope for first cut.
  4. Status quo (clone + ./install.sh + bun link). Developer flow only; will be renamed to dev-setup.sh.

Target end-user UX

curl -fsSL https://gitea.cahlen.com/nokeo08/Move/raw/branch/master/install.sh | sh
move --help

One command. No clone. No bun link. No chmod. After this the user has a global move on their PATH.


install.sh design (end-user, curl-able)

  • Shebang: #!/usr/bin/env sh (POSIX).
  • Strict mode: set -eu. Trap on errors with a clear "install failed at " message.

Top-to-bottom behavior

  1. Platform detection:
    • uname -sDarwin / Linux / else → "unsupported OS" + exit 1.
    • uname -marm64 / x86_64 (mapped to x64) / else → "unsupported arch" + exit 1.
    • nut.js supports darwin-arm64/x64, linux-x64, win32-x64. WSL users fall under Linux.
  2. Bun check:
    • command -v bun present → continue.
    • Absent → print Error: bun is not installed. Install from https://bun.sh (e.g. 'curl -fsSL https://bun.sh/install | bash'), then re-run. → exit 1.
  3. Resolve paths:
    • INSTALL_DIR=${MOVE_PREFIX:-$HOME/.local/share/move}
    • BIN_DIR=${MOVE_BIN_DIR:-$HOME/.local/bin}
    • mkdir -p both.
  4. Resolve version:
    • VERSION=${MOVE_VERSION:-master}.
  5. Idempotence check:
    • If $INSTALL_DIR/.installed-version exists AND its content equals $VERSION AND ${MOVE_FORCE:-0} is not 1:
      • Print move <version> already installed at $BIN_DIR/move. Set MOVE_FORCE=1 to reinstall, or pass a different MOVE_VERSION.
      • Exit 0.
  6. Clean install dir: rm -rf "$INSTALL_DIR"/* (preserving the dir itself so user-set permissions on it are kept).
  7. Download source:
    • TARBALL="https://gitea.cahlen.com/nokeo08/Move/archive/${VERSION}.tar.gz"
    • curl -fsSL "$TARBALL" -o "$TMP" (mktemp) → on failure, fail loudly with the offending URL.
    • tar -xzf "$TMP" -C "$INSTALL_DIR" --strip-components=1
    • rm -f "$TMP"
  8. Install deps: cd "$INSTALL_DIR" && bun install --production. --production skips devDependencies (TypeScript, @types/bun); saves tens of MB and seconds per install.
  9. Drop the wrapper:
    cat > "$BIN_DIR/move" <<EOF
    #!/usr/bin/env sh
    exec bun "$INSTALL_DIR/src/move.ts" "\$@"
    EOF
    chmod +x "$BIN_DIR/move"
    
    Wrapper, not a symlink, so the path is absolute and stable regardless of how the user's shell resolves things.
  10. Write version marker: printf '%s\n' "$VERSION" > "$INSTALL_DIR/.installed-version".
  11. PATH sanity check:
    • If $BIN_DIR is not on $PATH, print: Warning: $BIN_DIR is not on your PATH. Add this to your shell rc: export PATH="$BIN_DIR:$PATH"
    • Do not mutate the user's rc files — that breaks the "clean uninstall" contract.
  12. macOS Accessibility hint (carried over from current install.sh).
  13. Final message: Installed move $VERSION at $BIN_DIR/move. Run 'move --help' to get started.

Env vars

Var Default Purpose
MOVE_VERSION master Branch or tag to fetch from Gitea.
MOVE_PREFIX $HOME/.local/share/move Source install location.
MOVE_BIN_DIR $HOME/.local/bin Wrapper install location.
MOVE_FORCE unset Force reinstall even if version marker matches.

uninstall.sh design

curl -fsSL https://gitea.cahlen.com/nokeo08/Move/raw/branch/master/uninstall.sh | sh

Behavior:

  1. set -eu.
  2. Respect MOVE_PREFIX and MOVE_BIN_DIR env vars (same defaults as install).
  3. rm -f "$BIN_DIR/move".
  4. rm -rf "$INSTALL_DIR".
  5. Print Uninstalled. (Bun was not touched.).

Does not uninstall Bun — that's the user's runtime, not ours.


dev-setup.sh design (renamed from current install.sh)

The current install.sh is appropriate for contributors but inappropriate as the end-user installer. Rename to dev-setup.sh. Content stays nearly identical:

  • Verify Bun is installed (hard-error if missing).
  • bun install.
  • macOS Accessibility hint.
  • Trailing message updated to: Done. Dev workflow: 'bun run start' to run, or 'bun link' to install the global 'move' command. End-user installer is install.sh.

README changes (when we resume)

  • ## Install section becomes the curl one-liner targeting master, plus a short note about env-var overrides and a pointer to uninstall.
  • ## Run section collapses to move / move --help for the global install path.
  • New ## Uninstall section with the curl-pipe uninstall command.
  • New ## For contributors section absorbing the existing bun run start / bun run src/move.ts / bun link examples; points at dev-setup.sh.
  • ## Files table updates: add install.sh (end-user, curl-able), uninstall.sh (end-user, curl-able), dev-setup.sh (contributor bootstrap).

Verification plan (when we resume)

  1. sh -n install.sh and sh -n uninstall.sh — POSIX syntax check.
  2. Local end-to-end with overridden paths:
    MOVE_PREFIX=/tmp/movetest MOVE_BIN_DIR=/tmp/movetest/bin ./install.sh
    /tmp/movetest/bin/move --help
    ./install.sh                          # second run: already-installed message
    MOVE_FORCE=1 ./install.sh             # forced reinstall
    
  3. Uninstall against the test prefix; confirm nothing left under it.
  4. Re-run existing acceptance tests (tsc, --help, --version, --bogus) — should be unaffected; no source changes in this milestone.
  5. After committing/pushing, end-to-end against the actual Gitea URL.

Caveat: Gitea archive URL shape

The plan assumes https://gitea.cahlen.com/nokeo08/Move/archive/<ref>.tar.gz returns a tarball whose top-level directory contains the project files (peeled by --strip-components=1). This is the standard Gitea archive format, but if your Gitea version produces a different layout the installer will need a small tweak. The installer should error loudly with the offending URL if extraction fails.


Open future work (out of scope for the resume session)

  • Switch MOVE_VERSION default from master to "latest tag" via the Gitea API (/api/v1/repos/nokeo08/Move/tags) once tagged releases are routine.
  • Investigate bun build --compile + nut.js native-binary bundling for true zero-Bun installs. If it works, layer prebuilt binaries onto the curl installer so users without Bun also get one-command installs.
  • Homebrew tap for macOS users (cheap once a release pipeline exists).
  • Possible: PowerShell installer (install.ps1) for native Windows users without WSL.