#!/usr/bin/make -f

# Debian rules for opencode.
#
# opencode is a Bun-native TypeScript monorepo. There is no Node.js-runnable
# build: the CLI is produced by `bun build --compile` as a self-contained
# binary that embeds the Bun runtime and all JS dependencies. Bun is not
# packaged in Debian, so this build:
#
#   1. downloads the Bun toolchain (pinned to upstream's packageManager
#      version) from the network,
#   2. installs the npm dependencies with `bun install` (pinned by the
#      committed bun.lock), and
#   3. builds the CLI for the build host's architecture only with
#      `bun run script/build.ts --single --skip-install` in packages/cli
#      (upstream 2.x monorepo layout; the web UI is embedded by default).
#
# The Bun toolchain, the npm dependencies and the models.dev provider catalog
# are fetched from the network at build time: this package REQUIRES a
# network-enabled build worker and will not build on an offline buildd.
#
# Upstream 2.x ships the models.dev catalog as a committed snapshot
# (packages/core/src/models-dev/snapshot.txt) that may lag behind the live
# catalog by weeks. The build refreshes it best-effort with upstream's own
# update-models-snapshot.ts script (OPENCODE_MODELS_URL can override the
# source) so offline/first-run users start from a current model list.
# The binary also refreshes the catalog live at runtime, so a failed
# refresh only means an older floor, never a stale user experience.
#
# Build and embed the web UI so the desktop launcher can start `opencode serve`
# (the `web` command was replaced by `serve` in upstream 2.x).
# The terminal TUI and all regular CLI commands remain available through
# /usr/bin/opencode.
#
# The web UI build (a large vite build, then a synchronous brotli-11
# compression of the archive) and Bun's bytecode compilation are silent for
# long stretches, which looks like a hung build to inactivity watchdogs on
# slow (arm64) workers. override_dh_auto_build therefore emits a heartbeat
# line (with load and memory readings) every 10 minutes while it runs:
# it keeps the log alive for the watchdog and distinguishes CPU-bound
# builds from memory-starved ones.

export DH_VERBOSE = 1
# sbuild sets HOME=/sbuild-nonexistent (unwritable); bun needs a writable HOME
# (install cache, tmp). Redirect HOME into the build tree.
export HOME := $(CURDIR)/debian/fakehome

# Upstream version and the Bun version upstream requires. Both are derived
# (from the Debian changelog and the root package.json "packageManager"
# field) so a new upstream release no longer needs manual rules edits.
#
# OPENCODE_VERSION must stay set: upstream's build scripts fall back to
# `git branch --show-current` without it, which fails in a build tree
# unpacked from the orig tarball (no .git).
UPSTREAM_VERSION := $(shell dpkg-parsechangelog -SVersion | sed -n 's/-[^-]*$$//p')
BUN_VERSION := $(shell sed -n 's/.*"packageManager": *"bun@\([^"]*\)".*/\1/p' package.json)
ifeq ($(strip $(BUN_VERSION)),)
$(error opencode: cannot read the required Bun version from package.json "packageManager")
endif

# Debian arch -> Bun build target name.
ifeq ($(DEB_HOST_ARCH),amd64)
BUN_TARGET_ARCH := x64
BUN_DL_ARCH := x64
else ifeq ($(DEB_HOST_ARCH),arm64)
BUN_TARGET_ARCH := arm64
BUN_DL_ARCH := aarch64
else
$(error opencode: unsupported DEB_HOST_ARCH $(DEB_HOST_ARCH))
endif

BUN_HOME := $(CURDIR)/debian/.bun-toolchain
BUN := $(BUN_HOME)/bun/bun

# Where the built binary lands. Upstream 2.x builds from packages/cli and
# names the per-target output directory cli-linux-<arch>
# (was opencode-linux-<arch> in the packages/opencode 1.x layout).
DIST_NAME := cli-linux-$(BUN_TARGET_ARCH)
DIST_DIR := packages/cli/dist/$(DIST_NAME)

%:
	dh $@

# ----------------------------------------------------------------------------
# Configure: fetch the Bun toolchain (network).
# ----------------------------------------------------------------------------
override_dh_auto_configure:
	set -e; \
	echo "==> fetching bun $(BUN_VERSION) for linux-$(BUN_DL_ARCH)"; \
	mkdir -p "$(BUN_HOME)"; \
	curl -fSL -o "$(BUN_HOME)/bun.zip" \
		"https://github.com/oven-sh/bun/releases/download/bun-v$(BUN_VERSION)/bun-linux-$(BUN_DL_ARCH).zip"; \
	unzip -q -o "$(BUN_HOME)/bun.zip" -d "$(BUN_HOME)"; \
	mv "$(BUN_HOME)/bun-linux-$(BUN_DL_ARCH)" "$(BUN_HOME)/bun"; \
	rm -f "$(BUN_HOME)/bun.zip"; \
	"$(BUN)" --version

# ----------------------------------------------------------------------------
# Build: install deps and compile the CLI for the build host's arch.
# ----------------------------------------------------------------------------
override_dh_auto_build:
	set -e; \
	mkdir -p $(CURDIR)/debian/fakehome; \
	export PATH="$(BUN_HOME)/bun:$$PATH"; \
	( while :; do sleep 600; \
		echo "==> [heartbeat] still building (load $$(cut -d' ' -f1-3 /proc/loadavg), mem avail $$(awk '/^MemAvailable/ {printf \"%dMB\", $$2/1024}' /proc/meminfo))"; \
	  done ) & \
	HEARTBEAT_PID=$$!; \
	trap 'kill $$HEARTBEAT_PID 2>/dev/null || true' EXIT; \
	echo "==> refreshing the bundled models.dev catalog snapshot (best-effort)"; \
	bun run --cwd packages/core script/update-models-snapshot.ts \
		|| echo "==> WARNING: models.dev refresh failed; keeping the upstream snapshot"; \
	echo "==> bun install --frozen-lockfile (deps pinned by bun.lock; fail"; \
	echo "    loudly if the lockfile ever drifts; lifecycle scripts skipped:"; \
	echo "    native modules ship prebuilt binaries and need no node-gyp)"; \
	bun install --frozen-lockfile --ignore-scripts; \
	echo "==> building $(DIST_NAME) (single target with embedded web UI)"; \
	cd packages/cli; \
	OPENCODE_VERSION=$(UPSTREAM_VERSION) \
		bun run script/build.ts --single --skip-install; \
	kill $$HEARTBEAT_PID 2>/dev/null || true

# ----------------------------------------------------------------------------
# Test: the staged executable is smoke-tested in the dh_strip sequence
# below. The full upstream test suite needs LLM provider credentials;
# skip it.
# ----------------------------------------------------------------------------
override_dh_auto_test:
	@echo "Skipping upstream test suite (requires LLM provider credentials)."
	@echo "The staged executable is smoke-tested in the dh_strip sequence."

# ----------------------------------------------------------------------------
# Install the compiled binary at a stable, architecture-independent path,
# provide the terminal command, and add separate Web and CLI desktop entries.
# ----------------------------------------------------------------------------
override_dh_auto_install:
	install -D -m 0755 "$(DIST_DIR)/bin/opencode" \
		debian/opencode/usr/lib/opencode/opencode
	install -D -m 0755 debian/opencode-wrapper \
		debian/opencode/usr/bin/opencode
	install -D -m 0644 debian/opencode-web.desktop \
		debian/opencode/usr/share/applications/opencode-web.desktop
	install -D -m 0644 debian/opencode-cli.desktop \
		debian/opencode/usr/share/applications/opencode-cli.desktop
	install -D -m 0644 packages/desktop/icons/prod/icon.png \
		debian/opencode/usr/share/pixmaps/opencode.png

# Bun standalone executables append their compiled module graph to the ELF.
# strip/dwz remove or rewrite that payload, leaving a plain Bun executable.
override_dh_strip:
	@echo "Skipping dh_strip: it corrupts Bun standalone executables."
	env -u BUN_BE_BUN debian/opencode/usr/lib/opencode/opencode --version

override_dh_dwz:
	@echo "Skipping dh_dwz: it is unsafe for Bun standalone executables."

# ----------------------------------------------------------------------------
# Clean.
# ----------------------------------------------------------------------------
override_dh_auto_clean:
	rm -rf "$(BUN_HOME)" node_modules packages/cli/dist packages/app/dist debian/fakehome
	dh_auto_clean
