Repository navigation
Conversation
…member name go-selfupdate picks the executable out of the release archive by the target path's basename, and the archives always ship it as dci — so a standalone install renamed by its owner (dci-standalone, dci.bak) looked for itself in the tar and failed with 'executable not found in tar' (#91). Such installs now stage the download under the member name in a unique .dci-update-* directory next to the binary, and the final swap onto the running path runs through update.Apply, keeping the atomic replace, rollback, and Windows rename dance in the library. Installs named dci/dci.exe take the direct path exactly as before. Fixes #91
There was a problem hiding this comment.
I reviewed this PR and didn't find any bugs. It changes how the standalone updater replaces the running executable, and CODEOWNERS assigns these files to the maintainers, so a maintainer should still look at it.
What was reviewed:
stageReleaseTarget: installs nameddciordci.exestill update in place as before. Renamed installs stage into a.dci-update-*temp directory next to the binary, and the deferred cleanup removes it on every return path.selfUpdateBinary: checksum validation still runs insideUpdateTobefore anything is swapped. The staged file goes onto the real path only after that succeeds, and a rollback failure is reported as its own error.- Tests: a fake HTTPSource drives the full DetectVersion, UpdateTo and Apply path. They cover the renamed install, the
dciinstall and the bad-checksum case, and check that the original file is untouched and no staging leftovers remain.
Extended reasoning...
The change touches self_update.go, the standalone path that replaces the installed binary, plus new offline tests and one UPDATE-SPEC.md bullet. The security-sensitive part is the executable swap. Checksum verification still runs before the swap, and the code never tries to escalate privileges. I deferred instead of approving because .github/CODEOWNERS assigns every path to the maintainer group. The new Apply-based swap also relies on library behaviour I couldn't inspect here, and Windows has no test coverage.
Unnamed results on stageReleaseTarget, a shared writeOldExe fixture and file-scope aimExe seam for the tests, the two in-place staging cases as one table loop, and a readable staging-path assertion.
The production OpenAPI spec now requires scopes on the create-budget request body, so the two flag-style examples stopped validating (TestCommandDocsAgainstSpec fails on main as well — the spec drifted after the v2.9.0 release CI ran). Both examples now carry the same spec-valid cloud_provider scope as the file-based example.
|
The red Pushed two follow-up commits to this branch:
If a |
Problem
Standalone installs whose binary was renamed by its owner fail
dci update(#91, reproduced by the reporter against the live v2.5.2 release):Root cause
selfUpdateBinarypasses the resolved executable path toupdater.UpdateTo. go-selfupdate derives the archive-member name from that path's basename (decompressAndUpdate→filepath.Split, matched bymatchExecutableNameindecompress.go), and GoReleaser always ships the tar member asdci(binary: dci) — so a renamed install looks for itself in the archive. Asset detection is basename-independent, which is why the failure only surfaces at extraction.Checked upstream first: go-selfupdate has no executable-name config on any release (v1.6.0 pinned, v1.7.0 latest) or on
main, so the member name can't be pinned in the library config.Fix (18aa0ed)
An install whose basename isn't
dci/dci.exestages the download under the archive member name in a unique.dci-update-*directory next to the binary; the library unpacks and checksum-verifies there; the final swap onto the running path goes through the library's own exportedupdate.Apply(atomic replace, rollback, Windows rename dance), withupdate.RollbackErrorsurfacing a failed rollback as its own error. Installs nameddci/dci.exetake the direct path exactly as before. The staged file only needs to exist —update.Applyrenames it aside and overwrites it, never reads it. Error contract unchanged (UPDATE_FAILEDwrapping, manual-update hint).Verification
dci-standalonehit the exact dci update fails on standalone binaries not named 'dci': executable not found in tar #91 error before the change; after,{"updated":true}→ real v2.9.0 asset (checksum-verified), filename preserved,--versionanswering 2.9.0, no.dci-update-*/.oldjunk left.dci.exe, staging path/cleanup, unwritable-dir failure leaves the original untouched) and an end-to-end test through the realDetectVersion → UpdateTo → update.Applypath against a fakeHTTPSourcerelease server (renamed install, tar memberdci, checksum rejection leaves the original and no junk).go test .green, including the pty e2e battery.Follow-up commits on this branch
chore:simplification pass over the new helpers/tests (no behavior change): unnamed results onstageReleaseTarget, sharedwriteOldExefixture + file-scopeaimExeseam, the two in-place staging cases as one loop, a readable staging-path assertion.fix:repairscommand-docs/create-budget.yamlfor CI: the production OpenAPI spec now requiresscopeson the create-budget body, soTestCommandDocsAgainstSpecfailed on pristinemainas well (verified in a worktree — spec drift after main's last green run on 2026-10-16, not caused by this PR). Both flag-style examples now carry the spec-validcloud_providerscope from the file-based example; validated against the live spec locally and Prettier-clean. A different teaching value (e.g. atagdimension) is curation on top if preferred.Notes
dci update).Fixes #91