Files
platform/scripts/setup/officer-setup/lib/repo.sh
T
pastilhasandClaude Opus 5 8a0946ea39 officer-setup section 3: dependencies
bun install, as the account, in the checkout.

Two things stated because a failure here is otherwise opaque.

The lockfile is frozen — bunfig.toml sets [install] frozenLockfile = true — so
bun resolves from bun.lock and nothing else. A package.json that disagrees with
it is a hard failure rather than a quiet resolution, which is deliberate: the
friction exists so an unexplained lockfile change shows up in a diff. If the
install fails complaining about the lockfile, the section says that is the
frozen lockfile working and that it wants a human to read the diff, rather than
reporting a generic failure.

node-pty has no Linux prebuild, so this compiles it from source on every machine.
That is what build-essential and python3 are in machine-setup's core utils for,
and the section says so — the failure would otherwise surface much later as a
terminal that never starts.

Success is checked by the artefact rather than by the exit status: bun can
complete while the native module is not built, because it skips a dependency's
lifecycle scripts unless it trusts the package. So the section looks for
node_modules/node-pty/build/Release/*.node and, when it is missing, names the
consequence and the command that fixes it instead of reporting success.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 21:55:04 +00:00

83 lines
3.6 KiB
Bash

#!/bin/bash
# =============================================================================
# officer-setup — the repository
# =============================================================================
#
# Definitions only.
#
# ── Cloned as the owner, never as root ──
#
# A repository cloned by root is one the owner cannot pull, cannot commit in, and
# whose node_modules they cannot write. Every git operation here runs as the
# account, from a directory that account can stat.
[[ -n "${OFFICER_SETUP_REPO_LOADED:-}" ]] && return 0
OFFICER_SETUP_REPO_LOADED=1
OFFICER_REPO="${OFFICER_REPO:-https://gitea.officer.dev/officerdev/platform.git}"
platform_dir() { echo "${OFFICER_ROOT}/platform"; }
repo_exists() { [[ -d "$(platform_dir)/.git" ]]; }
repo_remote() { (cd "$(platform_dir)" 2>/dev/null && git remote get-url origin 2>/dev/null) || true; }
repo_branch() { (cd "$(platform_dir)" 2>/dev/null && git branch --show-current 2>/dev/null) || true; }
repo_is_dirty() { [[ -n "$(cd "$(platform_dir)" 2>/dev/null && git status --porcelain 2>/dev/null)" ]]; }
# Split an ssh:// URL into host and port, for the reachability check below.
repo_ssh_host() { sed -E 's|^ssh://[^@]*@([^:/]+).*|\1|' <<<"$1"; }
repo_ssh_port() { sed -nE 's|^ssh://[^@]*@[^:]+:([0-9]+)/.*|\1|p' <<<"$1"; }
# Can this account actually clone it?
#
# `git ls-remote` is the real question — not "does the host answer" but "can this
# account read this repository". Both prompts are disabled, because neither fails
# cleanly on its own: over https git asks for a username nobody is there to type,
# and over ssh it asks for a password or stops on host-key verification. With
# both off, an unreachable or unreadable repository is an immediate non-zero
# instead of a hang.
repo_reachable() {
as_owner "GIT_TERMINAL_PROMPT=0 \
GIT_SSH_COMMAND='ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=8' \
timeout 20 git ls-remote '$1' >/dev/null 2>&1" /
}
# The https form of the same repository, for a machine with no key.
repo_https_url() {
sed -E 's|^ssh://[^@]*@([^:/]+)(:[0-9]+)?/|https://\1/|' <<<"$1"
}
clone_repo() {
local url="$1" dest
dest="$(platform_dir)"
install -d -m 0755 -o "$USERNAME" -g "$(user_group)" "$OFFICER_ROOT"
as_owner "GIT_TERMINAL_PROMPT=0 git clone '${url}' '${dest}'" /
}
pull_repo() { as_owner "git -C '$(platform_dir)' pull --ff-only" /; }
# -----------------------------------------------------------------------------
# Dependencies
# -----------------------------------------------------------------------------
#
# ── The lockfile is frozen, and that is the point ──
#
# bunfig.toml sets [install] frozenLockfile = true, so `bun install` resolves from
# bun.lock and nothing else. A package.json that disagrees with the lockfile is a
# hard failure rather than a quiet resolution — which is deliberate: the friction
# exists so that an unexplained lockfile change shows up in a diff. See the
# supply-chain note in CLAUDE.md.
#
# So a failure here is usually one of two things, and they need different
# answers: the lockfile genuinely disagrees with package.json, or node-pty failed
# to build. Both are reported as such rather than as "install failed".
deps_installed() { [[ -d "$(platform_dir)/node_modules" ]]; }
# node-pty has no Linux prebuild, so `bun install` compiles it every time. This is
# the artefact that proves it worked, and its absence is why the terminal sidecar
# would not start.
node_pty_built() { compgen -G "$(platform_dir)/node_modules/node-pty/build/Release/*.node" >/dev/null 2>&1; }
install_deps() { as_owner "cd '$(platform_dir)' && bun install 2>&1"; }