Skip to content

v2.0.0: remote install, live-only progress, and two correctness sweeps - #96

Merged
divijg19 merged 1 commit into
mainfrom
update
Sep 28, 2026
Merged

divijg19 merged 1 commit into
mainfrom
update

Conversation

@divijg19

@divijg19 divijg19 commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

Installable with go install, transparent install output, and two rounds of correctness fixes against the v1.9.9 baseline.

What changed

Module path. Renamed to github.com/divijg19/Helm so the published binary is installable with go install github.com/divijg19/Helm/cmd/helm@latest. The major version stays unversioned because cmd/helm is package main and the module ships no library import path.

Transparent install output. The terminal streams the toolchain's own output (go: downloading, go: extracting, and anything else it prints) live under each installing tool, and that live output is the record. A successful install no longer repeats it in a trailing block, so the terminal's notes branch is gone entirely. The report-level notes field carries the names of tools that were successfully updated and produced output — it never carried the text, and the docs now say so.

Fixes

Issue Problem
#97 install.sh could rm -rf the caller's $TMPDIR on an unsupported platform or architecture
#98 helm --list counted one uninspectable binary twice, reporting "2 issues found"
#99 The live install path had no test coverage because the tested function was dead since v1.8.0

Also fixed, with a regression test that fails before the fix:

  • --help and --version constructed the App, which shells out to go env, so a broken toolchain made them exit 3 and print nothing — failing in exactly the state they exist to diagnose. They now answer before any toolchain access.
  • An empty GOBIN serialized as "tools": null, violating the never-null array contract. Fixed at the builder and enforced at the JSON wire boundary.
  • A failed install reported only its name in --json/--ci/--quiet, while resolution failures already produced a diagnostic, so a script could not tell a network failure from a build failure. UpdateReport now carries failed_detail; both new keys are omitempty, so a clean run is byte-identical.
  • Failure messages did not agree in number with their count, so a single problem read as "1 issues found".

Consolidation

One Err() per report type replaces ten copies of three failure messages, so the exit code, stderr text, and JSON success flag cannot disagree. The update summary rows, the skipped-tool list, and the selection-plus-eligibility split shared by Plan and ResolveUpdateCandidates each have a single builder, so a plan and the update that follows it cannot disagree. Removed: the tool.Status enum and both carriers, a redundant installTool parameter, a variable that shadowed its own helper, a verification map made unnecessary by Verify's positional contract, and the Invocation type whose parameter cli.Run never read.

Verification

gofmt · go vet · staticcheck (CI check set) · golangci-lint (0 issues) · go test -count=1 ./... · go test -race -count=1 ./... · GOOS=windows and GOOS=darwin builds · go mod tidy -diff · sh -n install.sh · goreleaser check · golden regeneration audit (all 20 goldens byte-exact).

install.sh was additionally rehearsed end to end against a local release, running the unmodified script through both the pinned-VERSION and resolve-latest paths, confirming the binary and both aliases install and report the right version, and confirming a tampered artifact is rejected by the checksum check before anything is written.

Closes #97, closes #98, closes #99.

Install remotely via `go install`, stream toolchain output live, and fix the
defects found by auditing the v1.9.9 baseline twice.

Module rename to github.com/divijg19/Helm, so the published binary is
installable with `go install`. The major version stays unversioned because
cmd/helm is package main and the module ships no library import path.

Install transparency: the terminal streams the toolchain's own output as each
tool installs, and that live output is the record. A successful install no
longer repeats it in a trailing block, so the terminal's notes branch is gone
entirely. The report-level notes field names the tools that were successfully
updated and produced output; it never carried the text, and the docs now say so.

Correctness fixes, each with a test that fails before the fix:

  * Inventory reported invalid binaries in both Invalid and Unhealthy, so every
    renderer summed them twice and the user saw "2 issues found" for one
    problem, breaking the documented conservation invariant. Unhealthy now
    counts only unhealthy tools; three goldens regenerated.
  * --help and --version constructed the App, which shells out to `go env`, so a
    broken toolchain made them exit 3 and print nothing, failing in exactly the
    state they exist to diagnose. They now answer before any toolchain access.
  * An empty GOBIN serialized as "tools": null, violating the never-null array
    contract. Fixed at the builder and enforced at the JSON wire boundary.
  * tool.Update was displaced by ResolveUpdateCandidates in v1.8.0 and was
    reachable only from its own tests, so ~70 lines were dead while the
    streaming and notes contract it covered had no live-path coverage. Deleted,
    and its tests now drive UpdateCandidates with a real progress sink, which
    also pins the exact-version install reference.
  * install.sh armed its cleanup trap before assigning TMPDIR. TMPDIR is a
    standard variable that macOS exports in every interactive shell, so exiting
    early on an unsupported platform or architecture ran `rm -rf` against the
    caller's own temporary directory. The trap is now armed only after
    mktemp, against a private WORKDIR.
  * A failed install reported only its name in --json, --ci and --quiet, while
    the terminal kept the reason and resolution failures already produced a
    diagnostic. The two failure classes were treated asymmetrically, so a script
    could not tell a network failure from a build failure. UpdateReport now
    carries failed_detail, printed as `failed-reason:` in CI, a combined line on
    quiet's stderr, and an array in JSON. The terminal deliberately does not
    repeat it. Both new keys are omitempty, so a clean run is byte-identical.
  * Failure messages did not agree in number with their count, so a single
    problem was reported as "1 issues found". All three operation messages now
    use a pluralizing helper; the singular form is the only text that changes,
    so no golden moved.

Consolidation: one Err() per report type replaces ten copies of three failure
messages, and the terminal's empty-inventory path no longer bypasses it. The
update summary rows, the skipped-tool list, and the selection-plus-eligibility
split shared by Plan and ResolveUpdateCandidates each have one builder, so a
plan and the update that follows it cannot disagree. The declared ProgressSink
is now the type the App probes for. Removed the tool.Status enum and both of
its carriers, the redundant installTool ref parameter, a name that shadowed its
own helper, and a verification map that Verify's 1:1 positional contract makes
unnecessary (now asserted, not assumed).

Removed the Invocation type and cli.Run's unused inv parameter: the CLI never
read it, so it was production-dead, and main.go paid for filepath.Base for
nothing. The three alias tests remain as guards against reintroducing per-name
dispatch.

Test suite: fixed a TestMain that leaked three binaries per run (its defer was
unreachable before os.Exit), a duration mask that could not match the minute
form formatDuration emits past 60s, and an os.Stdout swap that was not restored
on panic. Collapsed four fixture builders into one shared helper, replaced
assertion-only tests with ones that pin the branch they name, and added
coverage for the concurrency clamp (whose removal hangs the suite rather than
failing it), filtered-update scope end to end, terminal failure rendering, the
CI outdated and info reports, NewRenderer's mode mapping, the empty module-path
fallback, semver v-prefix normalization, and App.load memoization. The
deterministic-ordering guard no longer depends on a 50ms scheduling margin; a
channel barrier makes the completion order structural. BenchmarkVerify measures
the healthy path against real executables rather than missing files.

Docs: corrected the notes contract, the --info and --help contracts, the
staticcheck configuration, the CI job list, the golden-regeneration procedure,
and the JSON omissions. Documented the failure-reason fields, --verbose, and
the validation that precedes the discovery header.

Verified: gofmt, go vet, staticcheck (CI check set), golangci-lint,
go test -count=1 ./..., go test -race -count=1 ./..., GOOS=windows and
GOOS=darwin builds, go mod tidy -diff, git diff --check, sh -n install.sh,
goreleaser check, and an end-to-end rehearsal of install.sh against a local
release (both the pinned-VERSION and resolve-latest paths, including that a
tampered artifact is rejected by the checksum check before anything installs).
@divijg19
divijg19 merged commit 7b5c118 into main Sep 28, 2026
4 checks passed

@divijg19 divijg19 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v2.0.0: remote install, live-only progress, and two correctness sweeps

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant