Uh oh!
There was an error while loading. Please reload this page.
release-train: staging -> main - #473
Conversation
…roaming-profile machines (#409) (#437) PowerShell background jobs spawn their runspace in $HOME; on managed machines with roaming profiles that is a UNC share, and every cmd.exe a job starts there prints 'CMD.EXE was started with the above path as the current directory. UNC paths are not supported.' plus a RemoteException error record — 2-6 alarming red blocks on an otherwise healthy install. All five Start-Job sites (wsl --update, wsl --set-default-version, wsl --list, NCT install, NCT verify) now pass a shared -InitializationScript that pins the job to $env:SystemRoot (always local; no-op off-Windows). An AST-based Pester gate fails any future Start-Job that forgets it. Closes#409 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…lookups (re-land #410 onto develop) (#446) * fix(installer): pin k3d + helm versions — drop api.github.com latest lookups (#410) Ports the bash pins (#382) to the Windows installer: K3D_VERSION defaults to v5.9.0 and HELM_VERSION to v4.2.3 (lockstep with scripts/lib/common.sh until #435 single-sources them), env-overridable, validated against a release-tag shape before any URL is built (path-traversal gate mirroring the bootstrap and cli install.ps1). The unauthenticated releases/latest API allows 60 req/hour per IP — one shared corporate NAT exhausts it and fails installs (observed live on a customer install 2026-07-27). The literal value 'latest' still works but resolves API-free: the /releases/latest redirect Location for k3d, and get.helm.sh/helm-latest-version for helm — exactly like lib/setup-linux.sh. A Pester gate now fails the suite if any https://api.github.com fetch reappears in the installer. Closes#410 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(installer): restore retry parity on the 'latest' version resolvers (Bugbot #438) The new API-free 'latest' resolvers ran a single request — the old lookups and lib/setup-linux.sh retry 3x5s. Resolve-ToolVersion now drives the resolver through Invoke-WithRetry (resolvers throw on failure), so a one-off network blip on flaky corporate egress retries instead of aborting the install; a persistent failure still fails closed with the pin-a-tag remedy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(installer): bound the 'latest' lookup requests (-TimeoutSec 30, Bugbot #438) A host that accepts the TCP connect but never responds would hang the version resolvers indefinitely; the bash peers bound this with --connect-timeout 15 --max-time 30. Both resolver requests now carry -TimeoutSec 30; a timeout throws, so the retry ladder + fail-closed remedy from the previous commit take over. AST-based Pester gate keeps the timeouts in place. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Lukas Wuttke <lukas@tracebloc.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…447) * fix(installer): fail fast on k3d spawn failure; bound the create wait (#439) * fix(installer): fail fast on k3d spawn failure; bound the create wait (#412, #426 Windows half) Two fixes to New-K3dCluster's create path, both observed live: 1. Start-Process failure ('%1 is not a valid Win32 application' from a broken k3d.exe) is non-terminating by default, leaving $k3dProc null — and 'while (-not $null.HasExited)' is always true, so the installer spun 'Creating compute environment...' forever over a dead install. The spawn now runs under -ErrorAction Stop in a try/catch that cleans up the temp logs + proxy config and fails with the real exception, the log path, and a remedy. 2. k3d cluster create --wait has no timeout of its own, so a stalled image pull spun the spinner indefinitely. The wait is now bounded (15 min default, TB_CREATE_TIMEOUT_MIN override): on expiry the process is killed, the last stderr lines and the install-log path are printed, and the install fails loudly. Extracted as Wait-ProcessWithDeadline so the deadline/kill path is unit-tested. Closes#412. Windows half of #426 (the bash half — k3d create + helm timeouts in cluster.sh / install-client-helm.sh — follows separately). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(installer): remove the partial cluster when the create wait times out (Bugbot #439) Killing k3d mid --wait skips its own rollback, so the timeout path left a half-created cluster behind — and the next run's reuse path would see serversRunning > 0 and print 'Compute environment already running' over a broken environment. The timeout path now deletes the partial cluster (bounded at 2 min via Wait-ProcessWithDeadline) before failing, and tells the operator the exact manual command if the delete itself fails. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * fix(installer): Docker engine wait — 10 minutes, elapsed progress, named failure state (#440) * fix(installer): Docker engine wait — 10 minutes, elapsed progress, named failure state (#413) A first-ever Docker Desktop start on AV-heavy corporate machines routinely needs 5-10 minutes (WSL bootstrap, image unpack). The old 3-minute cap turned a normal cold start into a failed install plus a manual re-paste of the one-liner — observed as a recurring wait-then- re-run loop on hospital installs. - Wait bound: 3 min -> 10 min default, TB_DOCKER_WAIT_MIN override. - After the first minute the spinner shows elapsed minutes and the expected worst case, so the wait doesn't read as a hang. - On expiry the failure names the observed state: Docker Desktop process gone (start it / fix its error window) vs. running with the engine still down (tray-icon guidance) - instead of one generic line. Closes#413 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(installer): exited-Docker path drops the slow-start advice (Bugbot #440) When Docker Desktop's process has exited, 'a first start can be slow' and the TB_DOCKER_WAIT_MIN hint contradict the diagnosis and steer operators toward raising the wait instead of restarting/fixing the crash. The slow-start reassurance + override hint now print only on the engine-still-starting path; the exited path fails with its own start-and-fix remedy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
#441) * fix(installer): run the network-FS guard before the log dir is created (#432) setup_log_file mkdirs HOST_DATA_DIR and tees the whole session's output onto it BEFORE run_preflight fires — so on the exact machine the network-FS guard was built for (NFS home + sudo + root_squash), the unguarded mkdir failed with a bare error, or the log dir landed squashed/nobody-owned, before the friendly named failure could print. New early_data_dir_guard runs right after validate_config and before setup_log_file: same filesystem classification (extracted as the shared _pf_is_network_fstype), console-only, silent on local/undetermined filesystems, defers to the full check's warning under TRACEBLOC_ALLOW_NETWORK_FS. The call is declare -F-guarded so a stale bootstrap without the new helper proceeds as before. Closes#432 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(installer): early NFS guard names a followable remediation (Bugbot #441) The guard advised HOST_DATA_DIR=/local/path, but validate_config requires the data dir under $HOME (Bugbot #384 hardening) — so the exact audience this guard exists for (network home) was pointed at a fix that fails validation on re-run. The copy now states the real constraint and the two workable paths (local-home user, or the explicit TRACEBLOC_ALLOW_NETWORK_FS=1 override with its risk), and notes that datasets may stay on network storage via HOST_DATASET_DIR. Test asserts the impossible advice stays gone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(installer): early NFS guard skips an existing data dir (Bugbot #441 r2) The early guard's job is protecting the pre-log mkdir; an existing data dir has no at-risk mkdir, and a healthy machine's re-run must keep reaching the assess hand-off exactly as it did when the network-FS check lived only in run_preflight. Existing dir -> silent pass; the full preflight guard still classifies network storage for real (re)installs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…angs (bash half of #426) (#442) * fix(installer): bound k3d create and the helm calls — no indefinite hangs (#426, bash half) k3d cluster create --wait had no deadline of its own and its spinner none either: a stalled image pull (rate-limited registry, TLS-intercepting proxy) span the create forever. Both helm invocations (install + reconcile) ran under the deadline-less spin_cmd and could hang the same way against a wedged kube-apiserver. - create: --wait now always pairs with --timeout (default 15m, TB_CREATE_TIMEOUT_MIN override — the same env knob the Windows installer adopted); k3d aborts with a real error that the existing failure path dumps. spin() gained an optional deadline, used as a +5min backstop in case k3d itself wedges past its own timeout. - helm install + reconcile: new spin_cmd_bounded (spin_cmd + hard deadline, rc 124 with an explicit timeout note + log tail), default 10m via TB_HELM_TIMEOUT_MIN. Completes #426 (the Windows half shipped with the #439 stack). Closes#426 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(installer): spin's deadline kills the wrapper's children too (Bugbot #442) The create backstop passes the `( k3d … )` wrapper-subshell PID into spin, and the deadline path signalled only that PID — orphaning k3d, which kept creating the cluster after the installer had already exited as failed, racing any retry. The deadline path now TERMs the children while the parent is still alive (afterwards they reparent to init and pkill -P can't see them), then the wrapper, with a KILL sweep after the grace period. Harmless when bash exec-optimizes the wrapper away. Test proves the child dies with the wrapper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(installer): reparent-safe KILL sweep + explicit create-timeout UX (Bugbot #442 r2) 1. spin's deadline path captured no child PIDs, so once the TERM'd wrapper died, its children reparented to init and the pkill -KILL -P sweep found nothing — a TERM-immune k3d could survive the backstop. Child PIDs are now captured BEFORE any signal and the KILL sweep addresses them directly; test proves a trap-''-TERM child dies. 2. When the create backstop fires, the k3d log is often empty (hung daemon) — the operator saw a bare failure with no timeout hint. The 124 path now names the timeout, points at TB_CREATE_TIMEOUT_MIN, and deletes the partially created cluster (bounded) so a re-run cannot adopt it via the 'already exists' branch — parity with the Windows fix on #439. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(installer): spin's deadline path is set -e-proof (Bugbot #442 r3) Bare pkill (returns 1 with no children), kill on a reaped pid, and wait after a kill (reports the signal) could each abort the deadline path before 'return 124' under the installer's set -e — callers would see 143/1: no timeout copy, no partial-cluster cleanup, no bounded note. Every signal step is now failure-proofed, and a bash -c 'set -euo pipefail' end-to-end test pins the 124 contract for the childless (exec-optimized) case that reproduces the abort. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(installer): helm timeout names the pending-release unwedge (Bugbot #442 r4) A SIGKILLed helm (spin_cmd_bounded deadline) can leave the release wedged as pending-install/pending-upgrade — the next run then fails with Helm's 'another operation is in progress' and no guidance. Both call sites now capture the rc (if-! discarded it) and, on 124, print the exact unwedge commands (uninstall for a half-installed release, rollback for an upgrade) before the error. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(installer): fix over-broad grep assertions on the helm-bound tests Count invocation/hint lines, not comment mentions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(installer): rollback hint names the release, not the namespace (Bugbot #442 r5) The adopt path tracks release ($_rel) and namespace ($_ns) separately; the reconcile-timeout unwedge hint said 'helm rollback $_ns', which is a non-working command whenever they differ. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(installer): base-10-normalize the timeout knobs — octal trap defused (Bugbot #442 r6) TB_CREATE_TIMEOUT_MIN / TB_HELM_TIMEOUT_MIN sanitizers accepted 08/09, which bash arithmetic reads as invalid octal — aborting $(( … )) under set -e mid-create (k3d already backgrounded → partial cluster left behind), and 010 silently became 8. One shared, unit-tested helper (tb_minutes_or) now normalizes via 10# at all three call sites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* docs(rfc): least-privilege install (0001) — for discussion Draft RFC: the installer assumes blanket root/sudo, which excludes non-admin users (hospital/university/HPC researchers). Audit shows the ONLY privileged surface is the container runtime + two kernel modules; tools + the cluster are already user-space. Proposes a tiered model (zero-root when a runtime exists → rootless → one scoped privileged step) and ties in A2 (sudo/root) + B2 (PATH). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * RFC 0001: record decisions (Docker-only, kernel=Tier-2, build prepare-host + host audit, WSL2=Linux, detection yes) Resolves the five open questions from the first draft per Lukas 2026-07-22: Docker as the single runtime (rootless Docker at Tier 1); kernel modules a hard Tier-2 requirement with no userspace fallback; build a standalone prepare-host step plus a short host-audit report shared with doctor; WSL2 treated as Linux and native Windows preferring rootless Docker; and yes to side-effect-free detection. Rollout re-sequenced so detection + audit land first (also closing A2/B2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * RFC 0001: adopt @saadqbal review — rootless Docker as primary target Folds in Asad review of #369. Key flip: rootless Docker is the primary path, not a fallback, which dissolves the kernel-module question (fuse-overlayfs + slirp4netns remove overlay/br_netfilter from the privileged surface). Cant-modprobe now falls to rootless rather than failing; we probe cgroup v2 + unprivileged userns instead. prepare-host shipped as snippet + subcommand; WSL2 prefers rootless over Docker Desktop (licensing); detection uses docker-info + the id/sudo/sudo-n trio. Five open questions collapse to one hands-on spike: validate rootless Docker as the k3d backend across target hosts. Rollout re-sequenced so that spike leads the core build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(rfc): renumber to RFC-CLIENT-0002 and record the accepted status Two fixes to get this RFC out of limbo. **Renumber 0001 -> 0002.** `client` already has a `0001-rootless-spike.md` on develop, so this file collided with it inside its own repo. 0002 is the next free number in this repo. The header now carries the qualified ID `RFC-CLIENT-0002` — RFC numbers are per-repo, so a bare "RFC 0001" names four different documents across the org. **Status: Accepted (2026-07-25).** The design has plainly been accepted in practice: the tracking epic tracebloc/backend#1168 is 6-of-9 children merged, including the foundation, tier routing, sudo handling, Tier 0 and the rootless spike. Leaving the document as a permanent "do not merge" draft left the repo with no record of a decision the team had already acted on. Implementation status stays where it belongs — on the epic, not in this header. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…#415) (#449) The NVIDIA container toolkit setup ran entirely through Log (file-only), so the console sat blank through the 30s WSL probe, the multi-hundred-MB Ubuntu install, and the 180s toolkit install — and on failure printed a vague "set it up manually inside WSL later" with no actual commands. - Add Wait-JobWithProgress: spinner + elapsed/timeout heartbeat while a background job runs, bounded, stops the job on timeout. No GPU sub-step now goes silent for more than ~2s. - Add Show-GpuManualRemedy: copy-pastable install commands (same --tlsv1.2 + connect/max-time floor as the automated path) plus a `tracebloc doctor` follow-up, printed on every timeout/failure branch. - Ubuntu install is now a progress-tracked job instead of a silent `cmd /c ... | Out-Null`. - Visible Info intro (optional; CPU mode works either way) + Ok on success; drop the vague dead-end copy. - Extract shared spinner frames ($script:SpinnerFrames). - 11 Pester tests. Acceptance: no silent window >10s; timeout output carries runnable remedies. Kept in Step 1 (the --gpus flag must be decided before New-K3dCluster; moving it would force cluster recreation, #431). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…ge prod image (#454) * fix(chart): pin mysql-client by digest — keep fleets on the 5.7-lineage prod image (backend#723) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(e2e): era-aware --reuse-values pin assertion — the published release now carries the prod pin The path-1 assertion hardcoded the pre-#398 era: it expected NO ingestor pin after a --reuse-values upgrade because the published release's computed values predated images.ingestor.prodDigest. The #383 promotion (2026-07-27) published a release that includes the pin, so replayed computed values now carry it and the assertion fails on every chart-touching PR (first hit: #454). The expectation is now read from the baseline release itself (helm get values --all): pin absent => must not arrive (old behavior); pin present => the SAME digest must be replayed verbatim. Both eras keep asserting the actual limitation: --reuse-values never injects new chart defaults. Path-2's era-stale comment corrected, with a note on the replay-contamination signal a future pin bump will surface. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(e2e): fail fast when jq is missing (review: local runs, not just CI runners) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(e2e): one era boundary, stated once (the #383 promotion); track the path-2 tripwire in #459 (review) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(chart)+test: single-arch pin note + render assertion for the digest path (review) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ow carries the prod pin (#455) * fix(e2e): era-aware --reuse-values pin assertion — the published release now carries the prod pin The path-1 assertion hardcoded the pre-#398 era: it expected NO ingestor pin after a --reuse-values upgrade because the published release's computed values predated images.ingestor.prodDigest. The #383 promotion (2026-07-27) published a release that includes the pin, so replayed computed values now carry it and the assertion fails on every chart-touching PR (first hit: #454). The expectation is now read from the baseline release itself (helm get values --all): pin absent => must not arrive (old behavior); pin present => the SAME digest must be replayed verbatim. Both eras keep asserting the actual limitation: --reuse-values never injects new chart defaults. Path-2's era-stale comment corrected, with a note on the replay-contamination signal a future pin bump will surface. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(e2e): fail fast when jq is missing (review: local runs, not just CI runners) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(e2e): one era boundary, stated once (the #383 promotion); track the path-2 tripwire in #459 (review) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
#416) (#450) * feat(installer): preflight probes every download host, cross-OS + hard (#416) Preflight proved connectivity to registries + tracebloc endpoints, then Step 1 downloaded from hosts it never probed — so an all-green preflight was followed ~30s later by a blocked-download failure on TLS-intercepting / allowlist networks, misleading the IT contact who just watched preflight pass. - Probe every download host the default path fetches from, per-OS and HARD (a blocked one is now a named red preflight line, not a warn), but only when the fetch will actually happen (tool/app absent — a present tool is never re-downloaded, so its host isn't probed). * always: auth.docker.io (Docker Hub token host — allowed registry-1 but blocked token host used to fail only at in-cluster pull time) * Linux: get.docker.com, download.docker.com, github.com + objects.githubusercontent.com, dl.k8s.io, get.helm.sh * macOS: raw.githubusercontent.com (Homebrew), desktop.docker.com * Windows: desktop.docker.com, dl.k8s.io, get.helm.sh, github.com + objects.githubusercontent.com - objects.githubusercontent.com is probed explicitly: release assets 302 there and _pf_probe_url does not follow redirects, so github.com passing proved nothing about the asset host. - GPU hosts are deliberately excluded (GPU setup is optional; #415 handles its failure with runnable remedies — hard-failing preflight would contradict that). - New check-drift.sh parity check (_drift_preflight_hosts) locks the shared-core host set so the two installers can't drift apart. Fixes both halves in lockstep (preflight.sh + install-k8s.ps1). Tests: +5 bats (preflight), +2 bats (drift), +7 Pester; the one existing warn-only assertion (preflight.bats) is updated to the new hard behaviour. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(installer): Docker-engine host is warn, not hard; egress hint names all hosts (Bugbot #416) Bugbot found the download-host hard-probing over-generalized which host each path fetches Docker from, so a blocked but UNUSED host could abort a supported install: - macOS: desktop.docker.com hard-failed headless Macs that use Colima via brew. - Linux: get.docker.com / download.docker.com both hard, but pacman/zypper/Amazon use distro repos and RHEL clones use only download.docker.com. The Docker-engine install host is path/distro/environment-dependent, so it's now WARN-only (soft bucket) on Linux + macOS. The k8s tool binaries (dl.k8s.io, get.helm.sh, github.com + objects.githubusercontent.com) stay HARD — they're always direct-downloaded from the same host. Windows is unchanged: Docker Desktop is its sole path, so install-k8s.ps1 keeps desktop.docker.com hard. Also (Bugbot medium): the egress hint listed only the old always-critical hosts; it now names the tool-download hosts too, so a red line for a blocked download host has matching remediation. Mirrored in preflight.sh + install-k8s.ps1. Updated the two #416 bats tests that asserted the old hard behavior to assert warn-only for the Docker-engine host. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(installer): macOS also probes github.com for the Homebrew clone (Bugbot #416) install_homebrew fetches the install script from raw.githubusercontent.com and then git-clones Homebrew/brew + core from github.com. Preflight probed only the raw host, so a network that allows it but blocks github.com passed preflight then failed during Homebrew setup — the same multi-host gap already closed for k3d via objects.githubusercontent.com. Probe github.com too on macOS when brew is absent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(installer): probe formulae.brew.sh; drift check extracts probe URLs (review #416) Two reviewer findings (saadqbal): 1. formulae.brew.sh unprobed on macOS: `brew install` pulls formula METADATA from formulae.brew.sh (bottles come from ghcr.io, already probed), and it's hit even when brew is already installed. A blocked metadata host = green preflight then a failed `brew install`. Now hard-probed on macOS whenever a brew-installed tool (kubectl/k3d/helm/docker) is absent. 2. Drift check couldn't detect a deleted probe: it grepped the whole file, so each shared host also matched inside comments and the egress-hint strings — passing even if a real probe line were removed (the AC wasn't enforced; the bats cases only passed because the fixtures lacked that text). check-drift now extracts only the hosts in an actual PROBE URL (bash "…|https://host/…", ps1 url = "https://host/…") and diffs those. tracebloc.github.io drops from the shared set (ps1 probes it via $TRACEBLOC_HELM_REPO_URL, not a literal; Check 1 already pins the host map). Rewrote the drift bats fixtures to real probe entries + added a case proving a comment/hint-only host is still flagged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(installer): tighten drift host extraction + formulae.brew.sh trigger (Bugbot #416) Two Bugbot findings, both refining the earlier reviewer fixes (not reverting them): - Drift check (ps1 side) matched ANY `url = "https://…"`, including the winget bootstrap download line, so deleting the real k3d github.com probe still passed. Scope the extractor to hashtable probe entries (require `label =` on the line), so only genuine preflight probes count. Added a bats case with a stray $url= download line + a deleted probe -> now correctly flags drift. - formulae.brew.sh was hard-probed on `! has docker` too, but GUI Macs install Docker Desktop (desktop.docker.com), not brew — so docker-only-missing would hard-fail a host the install never uses. Trigger only on kubectl/k3d/helm (the tools that always install via brew). Added a bats case for docker-only-missing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(installer): probe formulae.brew.sh for headless (Colima) docker installs too (Bugbot #416) Round-3 dropped docker from the formulae.brew.sh trigger to avoid a GUI-Mac false-fail (Docker Desktop, not brew). But headless Macs install colima/docker via `brew install`, which DOES hit formulae.brew.sh — so docker-only-missing on a headless box went green in preflight then failed in Step 1. Make it path-aware: add a _pf_has_gui_session helper (mirrors setup-macos.sh) and probe formulae.brew.sh for a missing docker only when there's NO GUI session (the Colima/brew path). GUI Macs still skip it (Docker Desktop). Added bats cases for both GUI and headless. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(installer): consolidate macOS Docker-path egress probing (Bugbot #416) Fold the r3/r4/r5 point-fixes into one GUI-aware block keyed on _pf_has_gui_session, so each host is hard-probed only on the path that fetches it: - kubectl/k3d/helm absent -> formulae.brew.sh (always brew). - docker absent + GUI -> desktop.docker.com HARD (the actual Docker Desktop path; was warn-only -> a blocked CDN passed preflight then failed mid-download, r5). - docker absent + headless -> formulae.brew.sh (colima/docker via brew, r4). desktop.docker.com leaves the warn bucket entirely (it's hard on GUI, unprobed on headless). No behaviour is looser than before; the GUI Desktop CDN is now caught. Tests refreshed: GUI vs headless for both desktop.docker.com and formulae.brew.sh. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… Step 1 (#411) (#451) * fix(installer): execute-gate installed tools so a broken binary fails Step 1 (#411) The only post-install "verification" was a log-only, failure-masking interpolation, so a corrupt or wrong-architecture binary (winget shims / partial installs skip checksum verify; brew delivers with no checksum of ours) still printed "System tools" and only died at cluster-create in Step 2. All three OSes had the gap. - New shared execute-gate helpers: assert_tool_runs (common.sh, bash) and Assert-ToolRuns (install-k8s.ps1, PowerShell). Each runs the tool's self-check; on non-zero exit or an exception it removes the located binary and fails loudly with an arch-aware remedy, so the tool step fails instead of the cluster step. - Gate kubectl / k3d / helm after install on Linux (setup-linux.sh), macOS (setup-macos.sh), and Windows (install-k8s.ps1), on both the fresh-install and already-present paths. "System tools" success only prints once all gates pass. - kubectl is gated with `version --client` (NOT --short — removed in kubectl 1.28+, which would false-fail the gate). - New check-drift.sh parity check (_drift_execute_gates) so no installer can silently drop a gate for a tool. Tests: +2 bats (common — working tool passes / broken tool errors + removes the binary), +2 bats (drift parity), +7 Pester (exit-nonzero / exception / binary-removal / arch remedy / static gates). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(installer): make setup-linux tool mocks runnable for the execute-gate (#411) The execute-gate (#411) runs `<tool> version` after install, but the setup-linux.bats harness only marked tools "present" via has() — it never provided a RUNNABLE k3d/kubectl/helm. On the toolless CI runner the gate's probe found no binary and failed 4 install_k3d tests; locally it false-passed because a real k3d was on PATH. Add silent (non-recording) runnable stubs to setup() so the gate has something to execute and they shadow any real tool on a dev host. Test-only; no production change. * fix(installer): gate only removes binaries we placed; helm bare version; drift extracts calls (review #411) Three review findings on #411 (saadqbal + Bugbot): - Execute-gate deleted the wrong binary on the already-present path. Removal is now OPT-IN via `assert_tool_runs --rm <path>` / PS `-BinPath`, passed ONLY by the fresh-install callers that placed the binary. On the present / brew / winget path we don't pass it, so a broken pkg-managed binary is left in place (deleting a brew symlink just wedges the re-run, and the bad copy may be elsewhere on PATH). bash: --rm on Linux fresh-install, none on present or macOS/brew. ps1: -BinPath tracks the direct-download dest ($null for winget/present). - helm was gated with `version --short`; --short can be dropped like kubectl's was, which would false-fail the gate and (previously) delete a good binary. Use bare `helm version` on all three OSes. - _drift_execute_gates used a whole-file grep that could match comments. It now strips comment lines and matches the actual call (`assert_tool_runs … <t> version` / `Assert-ToolRuns -Name "<t>"`), handling the new --rm form. (no grep -q under pipefail — SIGPIPE would false-fail.) Tests: common.bats split into --rm-removes vs no-rm-leaves; check-drift.bats adds --rm-form + commented-out-gate cases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(installer): execute-gate removes a broken binary only if WE placed it AND it ran (Bugbot #411 r2) Round-1 made removal opt-in but only on the fresh-install path, so a broken installer-placed kubectl/k3d on the ALREADY-PRESENT path (left by a prior run) stayed → `has` true → re-run couldn't self-heal (Bugbot). helm already handled it. Unify all three: callers pass --rm "$TB_TOOLS_DIR/<tool>" (bash) / -BinPath "$TOOL_DIR\<tool>.exe" (ps1) on EVERY path, and the gate removes that path only when the binary that actually ran resolves to it — `command -v … -ef <path>` on bash, `(Get-Command).Source -eq $BinPath` on ps1. So a broken copy we own (fresh or prior-run) self-heals, while a brew/winget/pkg-manager copy elsewhere on PATH is never touched (satisfies the earlier reviewer guard too). Dropped the now-moot $k3dDest/$helmDest null-tracking and helm's -f branch. Tests: common.bats +decoy-copy case; install-k8s.Tests.ps1 removal split into ran-here (removed) vs resolved-elsewhere (left); check-drift already covers --rm. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… train) (#460) * ci(release): tag/chart-version guard + strict stability rule (backend#1301 Q5) The tag's base X.Y.Z must equal client/Chart.yaml's version (train-cut or manual), and any non-plain-semver tag must be a PRE-release -- otherwise it would become 'latest' (the installer bootstrap) and enter the helm index as stable. Tag passed via env per R8 (backend#889). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(release): guard gates BOTH jobs + self-healing demotion (Bugbot) The guard lived only inside the release job; sign-installer-manifest has no needs and kept stamping + attaching installers for a bad release -- which, left marked stable, already IS 'latest' (what the bootstrap resolves). Restructured: a dedicated verify job gates both jobs. And instead of fail-and-strand, an unmarked non-final tag is DEMOTED to prerelease ('latest' snaps back to the previous stable) and publishing continues as a proper pre-release. Chart-version mismatch stays a hard fail -- nothing to auto-fix without a bump. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(release): demote before hard-fail (Bugbot round 2) A release that is BOTH mis-marked stable AND chart-version-mismatched hit the exit 1 before the demotion ran, stranding it as 'latest' with no assets (404ing bootstrap). Demotion now runs first -- it is safe in isolation and must not be skippable by any other failure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
release-train: develop -> staging
LukasWodka
commented
Jul 29, 2026
bugbot run |
LukasWodka
commented
Jul 29, 2026
👋 Heads-up — Code review queue is at 42 / 30 Above the WIP limit. The team convention is to review existing PRs before opening new work. Open PRs currently in Code review (oldest first):
Pull from review before opening new work. (This is a nudge from the kanban WIP check, not a block.) |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 6267ee9. Configure here.
LukasWodka
commented
Jul 29, 2026
bugbot run |
Uh oh!
There was an error while loading. Please reload this page.
LukasWodka
commented
Jul 29, 2026
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 6267ee9. Configure here.
LukasWodka
commented
Jul 29, 2026
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 6267ee9. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
Automated promotion by the release train (RFC-0008 D14). Head is the train-managed
release-train/to-mainbranch (a mirror ofstaging), so it never collides with a human PR. Merged only when the fr-gate is green.Note
Medium Risk
Touches fleet-facing install/bootstrap paths, Helm release gating, and MySQL image pinning; risk is mitigated by extensive bats/Pester/drift coverage but mistakes could block installs or mis-publish releases.
Overview
Automated staging → main promotion packaging installer, chart, CI, and doc changes accumulated on the train.
Release & chart: Helm chart 1.9.7; release-helm-chart gains a shared
verifyjob (tag vsChart.yaml, demote mis-marked stable non-final tags) thatreleaseand sign-installer-manifest both depend on.images.mysqlClientis pinned to a 5.7-lineage digest (with a helm unittest asserting@sha256:rendering).Installers (bash + PowerShell):Execute-gates for kubectl/k3d/helm; bounded k3d create / helm install (
spindeadlines,TB_*_TIMEOUT_MIN); stricter preflight (e.g.auth.docker.io, conditional tool-download hosts;early_data_dir_guardbefore logging on NFS homes); Windows improvements (Docker wait, pinned k3d/helm without GitHub API, GPU job progress/remedies, UNC-safe jobs).scripts/manifest.sha256updated for touched libs.Docs & tests: New RFC-CLIENT-0002 (least-privilege install).
check-driftadds execute-gate and preflight-host parity checks; e2e auto-upgrade asserts--reuse-valuesingestor-pin behavior by baseline era.Reviewed by Cursor Bugbot for commit 6267ee9. Bugbot is set up for automated code reviews on this repo. Configure here.