bootstrap
Adom Hydrogen (platform base)
Public Made by Adomby adom
Layer Hydrogen's workspace onto a standard Adom workstation.
← Commit history
Publish 0.5.20
4 files changed
+151−3
code-server-versionadded+16@@ -0,0 +1,16 @@+# THE CODE-SERVER VERSION THE FLEET SHOULD BE ON.+#+# Moving the fleet is editing this line and publishing a new adom/hydrogen-bootstrap.+# Every container's 30-minute sweep runs this package's install.sh, which STAGES the+# upgrade when the installed version is below this one, and a systemd unit applies it at+# the next workspace start, when nobody has a live AI session (swapping the binary+# restarts code-server, which restarts the extension host and kills a turn in flight).+#+# Rules the installer enforces, so a careless edit here cannot hurt anyone:+# * upgrade only, never downgrade (v27 shipped 4.131.0 while the bake's own default+# still says 4.124.2; a downgrade path would have shipped that regression).+# * never below the highest engines.vscode among the installed AI extensions.+# * the .deb must download and verify before anything is staged.+#+# Leave EMPTY to disable the gate entirely (no staging, no upgrade).+CODE_SERVER_TARGET=4.131.0
install.sh+132@@ -340,3 +340,135 @@ bash "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/migrate-retired-slugs.sh" || # (retired "ad" shorthand — see the hydrogen-naming glossary). Drop the stale # old-name dir wherever an earlier version deployed it. rm -rf "$HOME/.claude/skills/hydrogen-embedded-ad" "$HOME/.codex/skills/hydrogen-embedded-ad" 2>/dev/null || true++# ─────────────────────────────────────────────────────────────────────────────+# 8. CODE-SERVER VERSION GATE (John 2026-09-03: "a mechanism ... that lets us change the+# version number we want everyone on at some point in the future and then it propagates+# out naturally via adom-wiki pkg update").+#+# code-server is installed from a .deb, from no apt repo, with its own update check+# disabled. Nothing upgraded it, so it only ever moved when a new golden image was+# baked, and a new image reaches existing users only through a migration nobody wants+# to repeat. That is how a fleet silently ends up behind what the Claude Code extension+# needs, with no one able to see it.+#+# This gate makes the pin data instead of a bake argument: `code-server-version` in this+# package names the target, publishing a new bootstrap moves the fleet, and the routine+# sweep carries it. The pin STAYS: nothing auto-updates to whatever upstream released.+#+# STAGE HERE, APPLY AT BOOT. The sweep runs from the 30-minute hook, in the middle of+# someone's work, and replacing the binary restarts code-server (extension host down,+# live AI turn dead). So this step only downloads and verifies; a systemd oneshot+# ordered before code-server.service installs it at the next workspace start.+# ─────────────────────────────────────────────────────────────────────────────+cs_gate() {+ local conf="$HERE/code-server-version"+ [ -f "$conf" ] || return 0+ local target; target="$(sed -n 's/^CODE_SERVER_TARGET=//p' "$conf" | tr -d '[:space:]')"+ [ -n "$target" ] || { echo " code-server gate: no target set, skipping"; return 0; }++ # READ THE VERSION FROM dpkg, NOT FROM --version. `code-server --version` prints a+ # DIFFERENT SHAPE per build: "4.131.0 <commit> with Code 1.131.0" on the v27 image, but a+ # bare "1.123.0" (the VS CODE version) on this cloud container. Parsing field 1 therefore+ # compared a Code version against a code-server target and called 1.123.0 "older than+ # 4.131.0", which is true as text and meaningless as fact. dpkg is authoritative for a+ # .deb install; the --version parse is a fallback that must still yield a 4.x string.+ local have+ have="$(dpkg-query -W -f='${Version}' code-server 2>/dev/null | sed 's/[-+].*//')"+ [ -n "$have" ] || have="$(code-server --version 2>/dev/null | tr ' ' '\n' | grep -m1 -E '^4\.[0-9]+\.[0-9]+$')"+ case "${have:-}" in+ 4.*) ;;+ *) echo " code-server gate: could not read a code-server version (got '${have:-none}'), skipping"; return 0 ;;+ esac++ # Upgrade only. `sort -V -C` succeeds when the list is already ordered, so this is true+ # exactly when have <= target; equal means nothing to do.+ if [ "$have" = "$target" ]; then echo " code-server $have is the target"; return 0; fi+ if ! printf '%s\n%s\n' "$have" "$target" | sort -V -C; then+ echo " code-server $have is NEWER than the target $target: refusing to downgrade"+ return 0+ fi++ # Never below what the installed AI extensions declare they need. An editor that cannot+ # run the Claude extension is worse than an old one that can.+ local floor+ floor="$(for pj in "$HOME"/.local/share/code-server/extensions/*/package.json; do+ [ -f "$pj" ] || continue+ python3 -c "import json,sys;print((json.load(open(sys.argv[1])).get('engines') or {}).get('vscode','') or '')" "$pj" 2>/dev/null+ done | tr -d '^><=~ ' | grep -E '^[0-9]+\.[0-9]+' | sort -V | tail -1)"+ if [ -n "$floor" ]; then+ # code-server's version tracks VS Code's minor: 4.131.0 ships Code 1.131.0.+ local code_ver="1.${target#*.}"+ if ! printf '%s\n%s\n' "$floor" "$code_ver" | sort -V -C; then+ echo " code-server gate: target $target (Code $code_ver) is BELOW what an installed extension needs ($floor): refusing"+ return 0+ fi+ fi++ local stage="$HOME/.adom/code-server"+ install -d -m 0755 "$stage"+ local deb="$stage/code-server_${target}_amd64.deb"+ if [ ! -s "$deb" ]; then+ echo " code-server $have -> $target: downloading"+ curl -fsSL --retry 3 --retry-delay 2 --connect-timeout 15 --max-time 600 \+ "https://github.com/coder/code-server/releases/download/v${target}/code-server_${target}_amd64.deb" \+ -o "$deb.part" || { echo " code-server gate: download failed, will retry next sweep"; rm -f "$deb.part"; return 0; }+ # A truncated or HTML error page is not a package: dpkg-deb reads the real archive.+ if ! dpkg-deb --info "$deb.part" >/dev/null 2>&1; then+ echo " code-server gate: downloaded file is not a valid .deb, discarding"; rm -f "$deb.part"; return 0+ fi+ mv "$deb.part" "$deb"+ fi+ printf '%s\n' "$target" > "$stage/target"+ echo " code-server $target staged; it installs at the next workspace start"++ # The applier: a boot-time oneshot, ordered before code-server, so the swap never lands+ # mid-session. Idempotent, and it deletes the staged .deb once the version matches.+ install -d -m 0755 "$HOME/.adom/bin"+ cat > "$HOME/.adom/bin/adom-code-server-apply" <<'APPLY'+#!/bin/bash+# Installs a code-server .deb staged by adom/hydrogen-bootstrap's version gate. Runs at+# workspace start, before code-server, so no live AI session is interrupted.+set -u+stage="$HOME/.adom/code-server"+[ -f "$stage/target" ] || exit 0+target="$(cat "$stage/target")"+have="$(code-server --version 2>/dev/null | head -1 | awk '{print $1}')"+if [ "$have" = "$target" ]; then rm -f "$stage"/code-server_*.deb "$stage/target"; exit 0; fi+deb="$stage/code-server_${target}_amd64.deb"+[ -s "$deb" ] || exit 0+echo "[code-server-apply] $have -> $target"+if sudo dpkg -i "$deb" >/dev/null 2>&1; then+ now="$(code-server --version 2>/dev/null | head -1 | awk '{print $1}')"+ echo "[code-server-apply] now $now"+ [ "$now" = "$target" ] && rm -f "$deb" "$stage/target"+else+ echo "[code-server-apply] dpkg failed; leaving the staged package for the next boot"+fi+APPLY+ chmod 0755 "$HOME/.adom/bin/adom-code-server-apply"++ install -d -m 0755 "$HOME/.config/systemd/user" 2>/dev/null || true+ if [ -w /etc/systemd/system ] || sudo -n true 2>/dev/null; then+ sudo tee /etc/systemd/system/adom-code-server-apply.service >/dev/null <<UNIT+[Unit]+Description=Apply a staged code-server upgrade before the editor starts+Before=code-server.service+ConditionPathExists=/home/adom/.adom/code-server/target++[Service]+Type=oneshot+User=adom+ExecStart=/home/adom/.adom/bin/adom-code-server-apply+RemainAfterExit=no++[Install]+WantedBy=multi-user.target+UNIT+ sudo systemctl daemon-reload >/dev/null 2>&1 || true+ sudo systemctl enable adom-code-server-apply.service >/dev/null 2>&1 || true+ else+ echo " code-server gate: no sudo to install the boot unit; the staged package waits"+ fi+}+cs_gate
package.json+2−2@@ -4,7 +4,7 @@ "type": "bootstrap", "title": "Adom Hydrogen (platform base)", "brief": "Layer Hydrogen's workspace onto a standard Adom workstation.",- "version": "0.5.19",+ "version": "0.5.20", "description": "Hydrogen's PLATFORM-GENERIC workspace layer on top of adom/core. Installs adom/core for the full Adom ecosystem, then adds everything common to running inside Hydrogen on ANY platform: the runtime-neutral hydrogen-* skills and the generic editor configuration (Claude Code CLI + extension, VS Code settings, trusted domains). Platform-specific layers depend on THIS: adom/hydrogen-windows-bootstrap (WSL2), adom/hydrogen-mac-bootstrap, adom/hydrogen-ubuntu-bootstrap. The reference design other orgs copy to build their own bootstrap on adom/core.", "org": "adom", "tags": [@@ -32,4 +32,4 @@ "hydrogen editor configuration", "where do hydrogen skills come from" ]-}+}
page.json+1−1@@ -4,7 +4,7 @@ "type": "bootstrap", "title": "Adom Hydrogen (platform base)", "brief": "Layer Hydrogen's workspace onto a standard Adom workstation.",- "version": "0.5.19",+ "version": "0.5.20", "description": "Hydrogen's PLATFORM-GENERIC workspace layer on top of adom/core. Installs adom/core for the full Adom ecosystem, then adds everything common to running inside Hydrogen on ANY platform: the runtime-neutral hydrogen-* skills and the generic editor configuration (Claude Code CLI + extension, VS Code settings, trusted domains). Platform-specific layers depend on THIS: adom/hydrogen-windows-bootstrap (WSL2), adom/hydrogen-mac-bootstrap, adom/hydrogen-ubuntu-bootstrap. The reference design other orgs copy to build their own bootstrap on adom/core.", "org": "adom", "tags": [