Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .cursor/BUGBOT.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -125,6 +125,15 @@ Two things make this repo unusual and should shape every finding:
pinned standalone binaries — `errcheck`, `gofmt -s`, `goimports`, `ineffassign`, `misspell`,
`staticcheck`, plus `deadcode-check.sh`, `file-budget.sh`, `check-style.sh`. Don't infer
coverage from that file.
- **The two formatters run via `make fmt-check`, not inline in the workflow** (cli#549), and
they scope to `git ls-files '*.go'` rather than `.`. Both are deliberate: `.` walked untracked
scratch directories, and one definition of the file set is what stops local and CI
disagreeing. `scripts/format.sh` fails closed (exit 2) outside a work tree or on an empty file
list — do not "simplify" either guard away. `run_formatter` returns a status and
writes to a temp file rather than being captured in `$( )`: a function that
`exit`s inside a command substitution ends only the subshell, and the first cut
of this script shipped exactly that false green. `make fmt-selftest`
(`scripts/tests/format-verify.sh`) is the guard; it fails on the old shape.
- **`staticcheck` runs `-checks all,-ST1005` deliberately** — do not flag error-string
capitalisation or punctuation. It is a tracked, intentional exclusion (cli#279).
- `internal/submit/client.go:78` — `InsecureSkipVerify` is intentional for cluster-internal
Expand Down
70 changes: 46 additions & 24 deletions .github/workflows/build.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,15 +48,48 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: shellcheck + dash parse
# NO `apt-get` (cli#534). Both tools are already on `ubuntu-latest`:
# `shellcheck` is preinstalled -- tracebloc/.github's own `quality / shellcheck`
# job, a REQUIRED check in 16 repos, calls `shellcheck --version` with no
# install at all -- and `dash` IS Ubuntu's `/bin/sh`, an essential package.
#
# WHY IT HAD TO GO, and it is not tidiness. This step is the first thing in a
# REQUIRED check, and `apt-get` here had no retry and no time bound of its own,
# so a slow package mirror consumed the whole 10-minute job budget before any
# shell was parsed. Measured on cli#533 -- a workflow-only diff that cannot
# touch installer behaviour -- which failed FOUR consecutive times:
#
# job 96126585157 Installer (shell) failure 10m16s
# 15:34 shellcheck + dash parse <- 10 minutes here, then killed
#
# Nothing after the `apt-get` line ever ran, and the annotation said
# `Installer (shell)` exceeded 10m -- pointing whoever reads it at the
# installer rather than at package fetching.
#
# Removing the dependency beats hardening it: a step that installs nothing
# cannot stall on a mirror, and no retry/timeout wrapper can say that.
#
# THIS PR'S OWN RUN IS THE PROOF. If either tool were absent the step fails
# loudly on the first line, here, before merge -- which is a better check than
# any claim in this comment.
run: |
sudo apt-get update -qq && sudo apt-get install -y -qq shellcheck dash
shellcheck --version | head -2
Comment thread
saadqbal marked this conversation as resolved.
shellcheck --shell=sh --severity=error scripts/install.sh
shellcheck --shell=bash --severity=error scripts/check-style.sh
shellcheck --shell=bash --severity=error scripts/check-tool-pins.sh
shellcheck --shell=bash --severity=error scripts/format.sh
shellcheck --shell=bash --severity=error scripts/tests/format-verify.sh
dash -n scripts/install.sh
bash -n scripts/tests/install-verify.sh
shellcheck --shell=bash --severity=error scripts/tests/install-ps1-verify.sh
bash -n scripts/tests/install-ps1-verify.sh
# format.sh's own fail-closed properties. Formatters are stubbed, so this is
# hermetic and needs no Go toolchain — which is why it lives in this job
# rather than Lint. It exists because the first cut of format.sh reported
# "clean" on a formatter that never ran (#550 review).
- name: Formatter-gate harness (fail-closed / tracked-files scope)
run: bash scripts/tests/format-verify.sh

- name: Verification harness (mandatory cosign / fail-closed)
run: bash scripts/tests/install-verify.sh
# Same property on Windows (backend#2078). pwsh is preinstalled on the
Expand DownExpand Up@@ -128,29 +161,18 @@ jobs:
go install github.com/kisielk/errcheck@v1.20.0
errcheck ./...

- name: gofmt -s
run: |
drift="$(gofmt -s -l .)"
if [ -n "$drift" ]; then
echo "::error::gofmt -s drift in:"
echo "$drift" | sed 's/^/ /'
echo "::error::run \`make fmt\` to fix"
exit 1
fi

# goimports -local: enforce the stdlib / third-party / our-own import
# grouping that .golangci.yml's local-prefixes already declares. gofmt
# doesn't check grouping, so drift accumulated silently until now.
- name: goimports -local
run: |
go install golang.org/x/tools/cmd/goimports@v0.48.0
drift="$(goimports -local github.com/tracebloc/cli -l .)"
if [ -n "$drift" ]; then
echo "::error::goimports (import grouping) drift in:"
echo "$drift" | sed 's/^/ /'
echo "::error::run \`make fmt\` to fix"
exit 1
fi
# gofmt -s (simplification) + goimports -local (the stdlib / third-party /
# our-own import grouping that .golangci.yml's local-prefixes declares;
# gofmt does not check grouping).
#
# `make fmt-check`, not an inline copy: both formatters now scope to
# `git ls-files '*.go'` instead of `.` (cli#549), and a second inline copy
# of that scope here is how local and CI start disagreeing about which
# files are gated. It also drops the restated goimports pin — the version
# is declared once, by GOIMPORTS_VERSION in the Makefile, which is what
# check-tool-pins.sh now enforces for this tool too.
- name: gofmt -s + goimports -local
run: make fmt-check

- name: ineffassign
run: |
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/e2e.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -73,6 +73,13 @@ jobs:
# broke the copy assertions without touching delete.go. The glob
# internal/cli/delete*.go deliberately excludes data_delete*.go
# (`tracebloc data delete` is a different command, unit-tested).
#
# internal/cli/telemetry*.go is here for the same reason as
# internal/ui, and backend#2314 is the proof: the command-outcome
# event is emitted from main.go AFTER the offboard returns, and its
# spool lives inside the ~/.tracebloc the offboard just deleted — so
# a telemetry change re-created the wiped tree and broke the teardown
# suite's config-dir assertion without touching delete.go at all.
filters: |
e2e:
- '.github/workflows/e2e.yml'
Expand All@@ -82,6 +89,7 @@ jobs:
- 'cmd/**'
- 'test/integration/**'
- 'internal/cli/delete*.go'
- 'internal/cli/telemetry*.go'
- 'internal/nodeboot/**'
- 'internal/api/**'
- 'internal/config/**'
Expand Down
147 changes: 147 additions & 0 deletions .github/workflows/envelope-contract-drift.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
name: Envelope contract drift (cross-repo)

# internal/resources/envelope_contract.json is VENDORED from
# tracebloc/client-runtime (backend#2220, RFC-BACKEND-664 §P0). client-runtime
# owns the training-envelope arithmetic
# (node_sizing.envelope_from_allocatable); this repo, the bash installer and its
# PowerShell twin are readers of it. Before that consolidation the same policy
# was typed out in all three, none derived from the others — and they disagreed:
# set.go ranked candidate nodes (cpu, memory) while the bash installer ranked
# them (memory, cpu), so on a cluster of 8c/16Gi + 4c/32Gi `resources set` and
# the installer anchored on DIFFERENT nodes.
#
# Unlike the installers, Go needs no generator: the contract is embedded verbatim
# with go:embed, so the vendored artifact is byte-identical to upstream and this
# gate is a plain diff. internal/resources/contract_test.go replays the
# contract's golden vectors through MaxRunCores/MaxRunGiB on every PR; this job
# is the other half — it catches the contract itself going stale.
#
# Pin, don't float (scripts/.client-runtime-ref), exactly as this repo already
# does for tracebloc/client and tracebloc/data-ingestors: an unrelated upstream
# commit must not redden every open CLI PR, and the weekly run catches a pin gone
# stale enough to matter.
#
# FAIL-CLOSED. client-runtime is private, so this needs a token GITHUB_TOKEN
# cannot provide; when it cannot read upstream the job FAILS rather than warning
# and exiting 0. A check that never executed must not report as a passing one —
# the activation-phase fail-open cli#536 had to remove from the backend-fixtures
# gate for exactly this reason.

on:
schedule:
- cron: "0 6 * * 1" # weekly Monday, offset from chart-drift (05:00)
workflow_dispatch:
pull_request:
branches: [develop, main]
paths:
- "internal/resources/**"
- "scripts/.client-runtime-ref"
- ".github/workflows/envelope-contract-drift.yml"

permissions:
contents: read

jobs:
envelope-contract:
timeout-minutes: 10
name: Envelope contract gate (pinned client-runtime ref)
runs-on: ubuntu-latest
steps:
- name: Checkout this CLI ref
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: cli

- name: Resolve the pinned client-runtime ref
id: pin
# First non-comment, non-blank line of scripts/.client-runtime-ref — the
# same convention .client-ref and .data-ingestors-ref use. Shape is
# validated (SHA/branch/tag characters only, no "..") before it reaches
# the checkout action.
run: |
ref="$(grep -vE '^[[:space:]]*(#|$)' cli/scripts/.client-runtime-ref | head -1 | tr -d '[:space:]')"
if [ -z "$ref" ]; then
echo "::error file=scripts/.client-runtime-ref::no ref found — the first non-comment line must be a commit SHA"
exit 1
fi
if ! printf '%s' "$ref" | grep -qE '^[A-Za-z0-9][A-Za-z0-9._/-]*$' || printf '%s' "$ref" | grep -q '\.\.'; then
echo "::error file=scripts/.client-runtime-ref::invalid ref shape: $ref"
exit 1
fi
echo "ref=$ref" >> "$GITHUB_OUTPUT"

- name: Mint a read-only installation token for client-runtime
id: token
# Least privilege per the backend#2157 sweep: named `repositories`, not
# owner-wide, and contents:read only — this job reads two files.
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.RELEASE_TRAIN_APP_ID }}
private-key: ${{ secrets.RELEASE_TRAIN_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: client-runtime
permission-contents: read

- name: Checkout tracebloc/client-runtime @ pinned ref
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: tracebloc/client-runtime
ref: ${{ steps.pin.outputs.ref }}
token: ${{ steps.token.outputs.token }}
path: client-runtime
persist-credentials: false

- name: The vendored contract matches upstream, byte for byte
env:
PINNED_REF: ${{ steps.pin.outputs.ref }}
run: |
upstream="client-runtime/envelope_contract.json"
vendored="cli/internal/resources/envelope_contract.json"
if [ ! -f "$upstream" ]; then
echo "::error::$upstream is missing at $PINNED_REF — has the contract moved or been renamed?"
exit 1
fi
if ! diff -u "$vendored" "$upstream"; then
echo "::error file=internal/resources/envelope_contract.json::the vendored envelope contract has drifted from tracebloc/client-runtime@$PINNED_REF"
echo ""
echo "To adopt the upstream change:"
echo " 1. cp <client-runtime>/envelope_contract.json internal/resources/"
echo " 2. update the SHA in scripts/.client-runtime-ref"
echo " 3. go test ./internal/resources/... # the golden vectors WILL have moved"
echo ""
echo "If the overhead or the floors moved, that is a FLEET envelope change"
echo "(backend#2167, RFC-BACKEND-664 L0) — not a re-vendor. Say so on the PR."
exit 1
fi
echo "vendored contract matches client-runtime@$PINNED_REF"

- name: Upstream's own goldens are not stale against its own arithmetic
# A vendored contract can match upstream byte-for-byte while UPSTREAM's
# vectors have gone stale against upstream's code — in which case we are
# faithfully mirroring a lie. Re-derive them from client-runtime's own
# generator and require no diff. Pure-python, no cluster, no deps.
run: |
cd client-runtime
if [ ! -f scripts/gen_envelope_vectors.py ]; then
echo "::error::client-runtime@${{ steps.pin.outputs.ref }} has no scripts/gen_envelope_vectors.py — the contract's provenance cannot be verified"
exit 1
fi
python3 scripts/gen_envelope_vectors.py
if ! git diff --exit-code -- envelope_contract.json; then
echo "::error::client-runtime@${{ steps.pin.outputs.ref }} carries goldens that its own generator does not reproduce."
echo "The pinned ref is not self-consistent — fix it upstream, then re-vendor and re-pin here."
exit 1
fi
echo "upstream goldens reproduce from upstream code"

- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: cli/go.mod
cache: true
cache-dependency-path: cli/go.sum

- name: The CLI still agrees with the contract it vendored
run: |
cd cli
go test ./internal/resources/... -run 'Contract|Golden|DecisionA|Overhead|FloorText' -v
47 changes: 26 additions & 21 deletions Makefile
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,8 @@ help:
@echo " build build ./tracebloc"
@echo " install go install ./cmd/tracebloc"
@echo
@echo " individual: vet test lint lint-full fmt fmt-check schema-check"
@echo " individual: vet test lint lint-full fmt fmt-check fmt-selftest"
@echo " schema-check"
@echo " vulncheck deadcode file-budget check-style clean"
@echo " cover cover-integration cover-merge test-integration"

Expand All@@ -50,7 +51,7 @@ help:
# * schema-check — fetches data-ingestors at the pinned ref.
# * deadcode — another `go run tool@version` fetch.
.PHONY: check
check: vet test-fast fmt-check file-budget check-style check-tool-pins
check: vet test-fast fmt-check fmt-selftest file-budget check-style check-tool-pins
@echo "==> check: green (run 'make check-all' for the full CI set)"

# check-all: the full PR gate. `ci` is the original name and stays —
Expand DownExpand Up@@ -132,7 +133,7 @@ GOIMPORTS_VERSION ?= v0.48.0
# which fails on findings since #430. A green `make ci` must imply a green
# PR; lint-full's own guard tells you how to install the tool if missing.
.PHONY: ci
ci: vet test lint lint-full fmt-check schema-check vulncheck file-budget deadcode check-style check-tool-pins
ci: vet test lint lint-full fmt-check fmt-selftest schema-check vulncheck file-budget deadcode check-style check-tool-pins
@echo "==> ci: all green"

.PHONY: build
Expand DownExpand Up@@ -256,29 +257,33 @@ vulncheck:
lint-full:
$(GO) run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) run

# fmt / fmt-check: gofmt -s (simplification) + goimports -local (import
# grouping: stdlib / third-party / our own — matches .golangci.yml's
# local-prefixes).
#
# Both scope to `git ls-files '*.go'` rather than `.` (cli#549). `.` is the whole
# working TREE, so an untracked scratch directory holding Go files — a nested git
# worktree, a vendored copy, a build sandbox — failed `make check` while every
# tracked file was clean, and `make fmt` then rewrote content the repo does not
# track. build.yml's Lint job calls these same targets, so the file set has one
# definition; see scripts/format.sh for the fail-closed cases.
.PHONY: fmt
fmt:
gofmt -s -w .
$(GO) run golang.org/x/tools/cmd/goimports@$(GOIMPORTS_VERSION) -local github.com/tracebloc/cli -w .
@GO="$(GO)" GOIMPORTS_VERSION=$(GOIMPORTS_VERSION) ./scripts/format.sh --write

# fmt-check: gofmt -s (simplification) + goimports -local (import grouping:
# stdlib / third-party / our own — matches .golangci.yml's local-prefixes).
.PHONY: fmt-check
fmt-check:
@diff="$$(gofmt -s -l . 2>/dev/null)"; \
if [ -n "$$diff" ]; then \
echo "==> gofmt -s needed on:"; \
echo "$$diff" | sed 's/^/ /'; \
echo "==> run \`make fmt\` to fix"; \
exit 1; \
fi
@drift="$$($(GO) run golang.org/x/tools/cmd/goimports@$(GOIMPORTS_VERSION) -local github.com/tracebloc/cli -l .)"; \
if [ -n "$$drift" ]; then \
echo "==> goimports (import grouping) needed on:"; \
echo "$$drift" | sed 's/^/ /'; \
echo "==> run \`make fmt\` to fix"; \
exit 1; \
fi
@GO="$(GO)" GOIMPORTS_VERSION=$(GOIMPORTS_VERSION) ./scripts/format.sh --check

# fmt-selftest: the properties scripts/format.sh must not lose — the formatters
# are stubbed, so it is hermetic and ~6 s. It exists because the FIRST cut of
# format.sh shipped a false green: run_formatter `exit`ed from inside a command
# substitution, which ends only the subshell, so check mode read an empty capture
# and printed "clean" on a formatter that never ran (caught in review on #550).
# A comment cannot hold that shut; this can.
.PHONY: fmt-selftest
fmt-selftest:
@bash scripts/tests/format-verify.sh

.PHONY: schema-check
schema-check:
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
0.10.9
0.10.10
2 changes: 1 addition & 1 deletion docs/cli-navigation.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -138,7 +138,7 @@ flowchart TD

FIT{"fits the machine? (+ floors)"}
FIT -->|no| re2b(["exit 2 — too big / too small<br/>(macOS: raise Docker Desktop)"]):::fail
FIT -->|"no change"| re0n(["exit 0 — nothing to change"]):::ok
FIT -->|"no change"| re0n(["exit 0 — nothing to change<br/>(may still re-stamp provenance / clear a<br/>phantom GPU — never asks to confirm)"]):::ok
FIT -->|ok| CONF{"confirm? (--yes skips)"}
CONF -->|"declined / non-TTY, no --yes"| re0c(["exit 0 declined / exit 1 non-TTY"]):::fail
CONF -->|yes| PIN{"chart version pinned?"}
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,9 +42,10 @@ require (
github.com/santhosh-tekuri/jsonschema/v6 v6.0.3
github.com/schollz/progressbar/v3 v3.19.1
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.9
golang.org/x/sys v0.47.0
golang.org/x/term v0.45.0
golang.org/x/text v0.40.0
golang.org/x/text v0.41.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.36.3
k8s.io/apimachinery v0.36.3
Expand DownExpand Up@@ -76,7 +77,6 @@ require (
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
Expand Down
Loading
Loading