Skip to content

Mirror: auto-detect project toolchain and install remote tooling #3

Description

@degoke

Summary

outpost mirror run executes commands on a minimal remote host (Docker + Outpost bootstrap only). Projects that expect local dev tooling (make, go, node, etc.) fail with errors like make: command not found or go: command not found.

Mirror should recognize the project environment and lazily install the required tooling on the remote host when needed.

Problem

Today Outpost only auto-provisions:

  • Host level: Docker, Compose, inspection tools (bootstrap.Ensure)
  • Feature level: k3d/kind/kubectl, tmux, Incus (lazy Ensure* on first use)
  • Project level: Python venv only (mirror setup-python + venv rewrite on mirror run)

There is no equivalent for Go, Make, Node, or other common build tools. Users must SSH in and apt install manually, or avoid Make entirely.

Example failure:

outpost mirror run --no-sync -- make docker-build-prebuilt IMG=myapp:dev
# make: go: No such file or directory
# bash: go: command not found

Proposed solution

1. Detection

Merge two sources:

Explicit config in .outpost/project.yaml:

toolchain:
  packages: [make, git]   # apt/yum system packages
  go: "1.22.5"            # optional pinned runtime
  node: "20"              # optional
  auto: true              # install on mirror run (default true)

Auto-detect from repo markers after sync:

Marker Infer
go.mod Go (from go / toolchain directive)
Makefile make
package.json Node
requirements.txt Python (existing setup-python path)

Explicit config overrides auto-detect.

2. Ensure (lazy install)

Follow existing patterns (EnsureTmux, EnsureKubernetesTools):

  • System packages (make, git): apt/yum via passwordless sudo
  • Versioned runtimes (go, node): install under /var/lib/outpost/toolchains/<name>/<version>/ from official tarballs; prepend to PATH for the command — avoid distro packages with stale versions
  • Do not run arbitrary install scripts from the repo

3. Hook in mirror run

sync (if needed)
→ EnsureToolchain (NEW)
→ EnsureTmux (if detached)
→ RewritePythonCommand (existing)
→ run command with augmented PATH

Cache installed state (e.g. remote .outpost/toolchain.json or sync-state) to avoid re-checking every run. Re-plan when go.mod or project.yaml toolchain section changes.

4. CLI

outpost mirror run -- make build              # auto-ensure when auto: true
outpost mirror setup-toolchain                # explicit one-time setup (like setup-python)
outpost mirror toolchain plan                 # dry-run: show what would be installed

Design constraints

  • Keep host bootstrap (Docker, k3d) separate from project toolchain (make, go)
  • Prefer Docker-first when Makefile only needs docker build — detect/nudge where possible
  • Only allowlisted packages/runtimes; no curl | bash from project files
  • Idempotent installs; safe on shared hosts

MVP scope

  1. toolchain section in project.yaml (packages, go, auto)
  2. Auto-detect go.mod + Makefilemake + Go version
  3. EnsurePackages (apt/yum) + EnsureGo (tarball to /var/lib/outpost/toolchains/)
  4. Call from mirror run when auto: true
  5. mirror setup-toolchain command

References

  • Existing patterns: internal/mirror/python.go, internal/mirror/bootstrap.go (EnsureTmux), internal/bootstrap/bootstrap.go (EnsureKubernetesTools)
  • Python project config: Project.Python in internal/config/config.go

Out of scope (follow-ups)

  • Universal runtime manager (mise/asdf) on host
  • Running mirror commands inside a dev container with docker socket mount
  • Parsing Makefile targets to skip compiler install when only docker build is needed

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions