106 lines
4.9 KiB
Makefile
106 lines
4.9 KiB
Makefile
IMAGE_NAME := opencode-sandbox
|
|
IMAGE_TAG := local
|
|
IMAGE := $(IMAGE_NAME):$(IMAGE_TAG)
|
|
NETWORK_NAME := opencode-sandbox-net
|
|
WORKDIR_NAME := $(notdir $(patsubst %/,%,$(CURDIR)))
|
|
# OpenCode follows the XDG Base Directory spec with three distinct dirs:
|
|
# config (settings/plugins), data (auth.json, opencode.db, snapshots), and
|
|
# state (session/model history, locks) -- data and state are NOT the same
|
|
# directory, despite the similar names.
|
|
CONFIG_DIR := $(HOME)/.config/opencode
|
|
DATA_DIR := $(HOME)/.local/share/opencode
|
|
STATE_DIR := $(HOME)/.local/state/opencode
|
|
|
|
# No :Z/:z SELinux suffixes: on macOS the host side (APFS) has no SELinux
|
|
# concept, so the labeling flag would be a meaningless no-op here.
|
|
#
|
|
# Known virtiofs caveat (default podman-machine file share on macOS/applehv):
|
|
# inotify file-change events don't propagate into the container. Not a
|
|
# blocker for this sandbox, just a rough edge if a tool relies on watching
|
|
# /workspace for live changes.
|
|
#
|
|
# --memory/--memory-swap/--cpus: on macOS, `podman machine`'s Linux VM has a
|
|
# FIXED resource allocation (check with `podman machine inspect`; currently
|
|
# 4 GiB / 6 CPUs, bumped up from the 2 GiB default -- see `podman machine
|
|
# set --memory <MB> podman-machine-default`, which requires the machine to
|
|
# be stopped first). Without a cgroup cap here, a memory-hungry build tool
|
|
# (Gradle's daemon and its separate Kotlin-compiler daemon are the worst
|
|
# offenders -- both size their heap off "available" memory with no idea
|
|
# they're sharing a small VM) can consume the VM's *entire* RAM. That
|
|
# doesn't just OOM-kill the container -- it starves the VM's own guest
|
|
# kernel, which stalls the very processes Podman needs to stay responsive
|
|
# (its API socket, gvproxy, SSH), making the whole machine appear to hang.
|
|
# Capping the container forces a contained OOM kill instead. --memory-swap
|
|
# equal to --memory disables extra swap for the container specifically, so
|
|
# it fails fast rather than thrashing. Sized to leave ~1 GiB / 2 CPUs of
|
|
# headroom below the VM's own allocation for the VM/podman/gvproxy itself --
|
|
# adjust alongside `podman machine set` if you resize the machine again.
|
|
# Override any of these per-invocation by exporting OPENCODE_SANDBOX_MEMORY,
|
|
# OPENCODE_SANDBOX_MEMORY_SWAP, and/or OPENCODE_SANDBOX_CPUS before running
|
|
# `make opencode`/`make shell`.
|
|
OPENCODE_SANDBOX_MEMORY ?= 3072m
|
|
OPENCODE_SANDBOX_MEMORY_SWAP ?= 3072m
|
|
OPENCODE_SANDBOX_CPUS ?= 4
|
|
|
|
RUN_FLAGS := \
|
|
--rm -it \
|
|
--userns=keep-id \
|
|
--memory=$(OPENCODE_SANDBOX_MEMORY) \
|
|
--memory-swap=$(OPENCODE_SANDBOX_MEMORY_SWAP) \
|
|
--cpus=$(OPENCODE_SANDBOX_CPUS) \
|
|
--network $(NETWORK_NAME) \
|
|
--network-alias $(WORKDIR_NAME) \
|
|
-v $(CURDIR):/workspace \
|
|
-v $(CONFIG_DIR):/home/dev/.config/opencode \
|
|
-v $(DATA_DIR):/home/dev/.local/share/opencode \
|
|
-v $(STATE_DIR):/home/dev/.local/state/opencode \
|
|
-w /workspace
|
|
|
|
.PHONY: build opencode shell install
|
|
|
|
# Only relevant on macOS, where podman requires a running VM ("podman
|
|
# machine"). Handles "machine exists but stopped" -> start it. Does NOT
|
|
# handle "no machine exists at all" -- that needs a one-time manual
|
|
# `podman machine init`, left as a documented prerequisite since it's a
|
|
# heavier, resource-allocating operation.
|
|
#
|
|
# Written as a single shell line deliberately: macOS ships GNU Make 3.81
|
|
# (frozen pre-GPLv3), which predates `.ONESHELL:` (added in 3.82). Without
|
|
# it, Make runs a separate shell per newline in the *expanded* recipe text,
|
|
# which would split an if/then/fi block from a multi-line variable across
|
|
# unrelated shell invocations and break it. A single logical line sidesteps
|
|
# the issue entirely and works on any Make version.
|
|
define ENSURE_MACHINE
|
|
[ "$$(uname -s)" != "Darwin" ] || podman machine list --format '{{.Running}}' 2>/dev/null | grep -q '^true$$' || { echo "==> No running podman machine found; starting default machine..."; podman machine start; }
|
|
endef
|
|
|
|
# Idempotent: creates the shared cross-container network once, reused on
|
|
# every subsequent invocation. Any other Podman dev container joined to this
|
|
# same network name can resolve and reach this sandbox by its workdir alias.
|
|
define ENSURE_NETWORK
|
|
podman network exists $(NETWORK_NAME) || podman network create $(NETWORK_NAME)
|
|
endef
|
|
|
|
build:
|
|
@$(ENSURE_MACHINE)
|
|
podman build -t $(IMAGE) -f Containerfile .
|
|
|
|
opencode:
|
|
@$(ENSURE_MACHINE)
|
|
@$(ENSURE_NETWORK)
|
|
podman run $(RUN_FLAGS) $(IMAGE) opencode
|
|
|
|
shell:
|
|
@$(ENSURE_MACHINE)
|
|
@$(ENSURE_NETWORK)
|
|
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)"
|