← Commit history

Publish 0.5.20

John Lauer ·864fdf02a0 ·1mo ago ·parent e3168c6
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"   ]-}\ No newline at end of file+}
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": [