Installer
This commit is contained in:
parent
12760ed095
commit
4cb1328ad6
3 changed files with 119 additions and 2 deletions
12
Makefile
12
Makefile
|
|
@ -29,7 +29,7 @@ RUN_FLAGS := \
|
||||||
-v $(STATE_DIR):/home/dev/.local/state/opencode \
|
-v $(STATE_DIR):/home/dev/.local/state/opencode \
|
||||||
-w /workspace
|
-w /workspace
|
||||||
|
|
||||||
.PHONY: build opencode shell
|
.PHONY: build opencode shell install
|
||||||
|
|
||||||
# Only relevant on macOS, where podman requires a running VM ("podman
|
# Only relevant on macOS, where podman requires a running VM ("podman
|
||||||
# machine"). Handles "machine exists but stopped" -> start it. Does NOT
|
# machine"). Handles "machine exists but stopped" -> start it. Does NOT
|
||||||
|
|
@ -67,3 +67,13 @@ shell:
|
||||||
@$(ENSURE_MACHINE)
|
@$(ENSURE_MACHINE)
|
||||||
@$(ENSURE_NETWORK)
|
@$(ENSURE_NETWORK)
|
||||||
podman run $(RUN_FLAGS) $(IMAGE) bash
|
podman run $(RUN_FLAGS) $(IMAGE) bash
|
||||||
|
|
||||||
|
# Builds the image, then installs a global `opencode-sandbox` shortcut on
|
||||||
|
# the host (see scripts/install-shortcut.sh for what "installs" means -- a
|
||||||
|
# shim in ~/.local/bin plus a PATH edit for the user's login shell). Kept out
|
||||||
|
# of the recipe itself: multi-line shell-with-conditionals in a Make recipe
|
||||||
|
# means doubling every $ as $$ and fighting Make's per-line shell splitting
|
||||||
|
# (see the ENSURE_MACHINE comment above) -- not worth it for something this
|
||||||
|
# size.
|
||||||
|
install: build
|
||||||
|
sh scripts/install-shortcut.sh "$(CURDIR)"
|
||||||
|
|
|
||||||
23
README.md
23
README.md
|
|
@ -25,10 +25,31 @@ A rootless Podman container for running [OpenCode](https://github.com/anomalyco/
|
||||||
make build # build (or rebuild) the container image
|
make build # build (or rebuild) the container image
|
||||||
make opencode # launch the OpenCode TUI in the current directory
|
make opencode # launch the OpenCode TUI in the current directory
|
||||||
make shell # open a bash shell in the container instead
|
make shell # open a bash shell in the container instead
|
||||||
|
make install # build, then install a global `opencode-sandbox` shortcut (see below)
|
||||||
```
|
```
|
||||||
|
|
||||||
Run `make opencode` or `make shell` from any project directory — the image is shared, so there's nothing per-project to set up.
|
Run `make opencode` or `make shell` from any project directory — the image is shared, so there's nothing per-project to set up.
|
||||||
|
|
||||||
|
### `make install`
|
||||||
|
|
||||||
|
Installs a shim at `~/.local/bin/opencode-sandbox` that runs `make opencode`
|
||||||
|
against this repo's Makefile, using whatever directory you're standing in —
|
||||||
|
not this repo — as `/workspace`. Once installed, `opencode-sandbox` works as
|
||||||
|
a global command from any directory, no `cd` into this repo required. (Only
|
||||||
|
`opencode` gets a shortcut; `make shell` still has to be run from here.)
|
||||||
|
|
||||||
|
It also makes sure `~/.local/bin` is on `PATH` for future shells, based on
|
||||||
|
your login shell (`$SHELL`):
|
||||||
|
|
||||||
|
- zsh → appended to `~/.zshrc` (or `~/.zshenv` if that's the only one of the two that already exists; `~/.zshrc` is created if neither does)
|
||||||
|
- bash → `~/.bash_profile` on macOS, `~/.bashrc` on Linux (created if missing)
|
||||||
|
- fish → added via `fish_add_path` (no config file edit needed)
|
||||||
|
- anything else → appended to `~/.profile`
|
||||||
|
|
||||||
|
The edit is wrapped in `# >>> opencode-sandbox >>>` markers so re-running
|
||||||
|
`make install` never duplicates it, and is skipped entirely if
|
||||||
|
`~/.local/bin` is already on `PATH`.
|
||||||
|
|
||||||
## How it's put together
|
## How it's put together
|
||||||
|
|
||||||
**Containerfile** — multi-stage build on `debian:trixie-slim`:
|
**Containerfile** — multi-stage build on `debian:trixie-slim`:
|
||||||
|
|
@ -37,7 +58,7 @@ Run `make opencode` or `make shell` from any project directory — the image is
|
||||||
- OpenCode is downloaded directly from its canonical GitHub releases (`github.com/anomalyco/opencode`) for the container's architecture, and its SHA256 is checked against a hash pinned in the Containerfile before it's installed — there's no maintainer-published signature for OpenCode releases, so this hash is captured by hand and is the actual root of trust here. Bump `OPENCODE_VERSION` and re-run the `sha256sum` steps documented in the Containerfile's comments whenever you upgrade it.
|
- OpenCode is downloaded directly from its canonical GitHub releases (`github.com/anomalyco/opencode`) for the container's architecture, and its SHA256 is checked against a hash pinned in the Containerfile before it's installed — there's no maintainer-published signature for OpenCode releases, so this hash is captured by hand and is the actual root of trust here. Bump `OPENCODE_VERSION` and re-run the `sha256sum` steps documented in the Containerfile's comments whenever you upgrade it.
|
||||||
- A few build-time smoke tests (`ldd`, `java -version`, `node --version`, etc.) fail the build immediately if anything is broken, rather than surfacing at first `make opencode`.
|
- A few build-time smoke tests (`ldd`, `java -version`, `node --version`, etc.) fail the build immediately if anything is broken, rather than surfacing at first `make opencode`.
|
||||||
|
|
||||||
**Makefile** — three targets only, as designed:
|
**Makefile** — four targets:
|
||||||
- `build` builds the image.
|
- `build` builds the image.
|
||||||
- `opencode` and `shell` both run the container with the same flags: `--userns=keep-id` (so files created in the container are owned by your real host user, not root — verified: rootless Podman's user-namespace remapping means `id` shows `root` *inside* the container's own namespace view, but the file lands on the host owned by your actual UID/GID), the current directory bind-mounted to `/workspace`, and OpenCode's three XDG directories bind-mounted from your home directory:
|
- `opencode` and `shell` both run the container with the same flags: `--userns=keep-id` (so files created in the container are owned by your real host user, not root — verified: rootless Podman's user-namespace remapping means `id` shows `root` *inside* the container's own namespace view, but the file lands on the host owned by your actual UID/GID), the current directory bind-mounted to `/workspace`, and OpenCode's three XDG directories bind-mounted from your home directory:
|
||||||
|
|
||||||
|
|
|
||||||
86
scripts/install-shortcut.sh
Executable file
86
scripts/install-shortcut.sh
Executable file
|
|
@ -0,0 +1,86 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# Installs a global `opencode-sandbox` shim so that command, run from any
|
||||||
|
# directory on the host, runs `make opencode` against *this* repo's
|
||||||
|
# Makefile, mounting whatever directory you're standing in (not this repo)
|
||||||
|
# as /workspace. Invoked from the Makefile's `install` target as:
|
||||||
|
# sh scripts/install-shortcut.sh "$(CURDIR)"
|
||||||
|
#
|
||||||
|
# POSIX sh only (no bashisms) -- this always runs under Make's own
|
||||||
|
# $(SHELL), normally /bin/sh, regardless of the user's interactive shell.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
REPO_DIR=${1:?"usage: $0 <repo-dir>"}
|
||||||
|
BIN_DIR="$HOME/.local/bin"
|
||||||
|
SHIM="$BIN_DIR/opencode-sandbox"
|
||||||
|
|
||||||
|
mkdir -p "$BIN_DIR"
|
||||||
|
|
||||||
|
# -f (not -C): -C would chdir make into the repo before $(CURDIR) is
|
||||||
|
# evaluated, breaking the "mount whatever directory you're standing in"
|
||||||
|
# behavior that -v $(CURDIR):/workspace in the Makefile depends on.
|
||||||
|
cat > "$SHIM" <<EOF
|
||||||
|
#!/bin/sh
|
||||||
|
exec make -f "$REPO_DIR/Makefile" opencode "\$@"
|
||||||
|
EOF
|
||||||
|
chmod +x "$SHIM"
|
||||||
|
echo "==> Installed shim: $SHIM"
|
||||||
|
|
||||||
|
# Already on PATH? Nothing left to do.
|
||||||
|
case ":$PATH:" in
|
||||||
|
*":$BIN_DIR:"*)
|
||||||
|
echo "==> $BIN_DIR is already on PATH."
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Resolve the user's *login* shell via $SHELL, not the shell running this
|
||||||
|
# script (always Make's $(SHELL)/sh) -- same reason rustup/nvm/pnpm's own
|
||||||
|
# installers all key off $SHELL instead of process-detecting the caller.
|
||||||
|
shell_name=$(basename "${SHELL:-}")
|
||||||
|
|
||||||
|
if [ "$shell_name" = fish ] && command -v fish >/dev/null 2>&1; then
|
||||||
|
# fish_add_path persists via a universal variable -- no config file to edit.
|
||||||
|
fish -c "fish_add_path $BIN_DIR"
|
||||||
|
echo "==> Added $BIN_DIR to PATH via fish_add_path."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$shell_name" in
|
||||||
|
zsh)
|
||||||
|
# Prefer .zshrc (the standard interactive-shell file); if it doesn't
|
||||||
|
# exist but .zshenv does, respect that existing setup instead of
|
||||||
|
# scattering PATH across two files. Only create .zshrc if neither exists.
|
||||||
|
if [ -f "$HOME/.zshrc" ]; then
|
||||||
|
rc="$HOME/.zshrc"
|
||||||
|
elif [ -f "$HOME/.zshenv" ]; then
|
||||||
|
rc="$HOME/.zshenv"
|
||||||
|
else
|
||||||
|
rc="$HOME/.zshrc"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
bash)
|
||||||
|
# macOS Terminal.app runs bash as a login shell, sourcing
|
||||||
|
# ~/.bash_profile (not ~/.bashrc) unless the user's profile chains to it.
|
||||||
|
case "$(uname -s)" in
|
||||||
|
Darwin) rc="$HOME/.bash_profile" ;;
|
||||||
|
*) rc="$HOME/.bashrc" ;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*) rc="$HOME/.profile" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
marker_begin="# >>> opencode-sandbox >>>"
|
||||||
|
marker_end="# <<< opencode-sandbox <<<"
|
||||||
|
|
||||||
|
if [ -f "$rc" ] && grep -qF "$marker_begin" "$rc"; then
|
||||||
|
echo "==> $rc already configures PATH for opencode-sandbox; skipping."
|
||||||
|
else
|
||||||
|
touch "$rc"
|
||||||
|
{
|
||||||
|
printf '\n%s\n' "$marker_begin"
|
||||||
|
printf 'export PATH="%s:$PATH"\n' "$BIN_DIR"
|
||||||
|
printf '%s\n' "$marker_end"
|
||||||
|
} >> "$rc"
|
||||||
|
echo "==> Added $BIN_DIR to PATH in $rc."
|
||||||
|
echo "==> Restart your shell (or run: . $rc) to pick it up."
|
||||||
|
fi
|
||||||
Reference in a new issue