Skip to content

fix: realign hyperi-update with how tools install - #111

Merged
catinspace-au merged 2 commits into
mainfrom
fix/updater-realign
Oct 6, 2026
Merged

catinspace-au merged 2 commits into
mainfrom
fix/updater-realign

Conversation

@catinspace-au

Copy link
Copy Markdown
Contributor

hyperi-update now refreshes only what nothing at the OS level refreshes, and it checks every download before it touches /usr/local/bin.

  • One refetch helper replaces the three old ones. It handles per-arch asset names, Gitea and plain HTTPS hosts (tea, macbash), tar, nested tar and zip.
    • Each download is checked against the sha256 GitHub publishes for the asset, or the project's checksum file where that is missing. A mismatch keeps the old binary and fails the run.
    • The new binary is placed with a rename, so a running build never sees half a file.
    • It acts only when /usr/local/bin/ is ours, so a copy from a package or ~/.local/bin is left alone.
    • The release and ETag are cached, so an unchanged release is not downloaded again.
  • Newly refreshed:
    • lazygit, actionlint, osv-scanner, git-scrub, sccache, fnm, tea and macbash;
    • aws-vault, and kubectx on Fedora, both only taking releases at least 7 days old, as the roles do;
    • hadolint, gitleaks and act on Ubuntu, and sd on Fedora;
    • uv and uvx on Ubuntu, in ~/.local/bin as the user;
    • Go tools in ~/go/bin, rebuilt when the tool or the Go toolchain moves;
    • pnpm globals (--latest, because the role installs them unpinned);
    • uv's Python patch releases.
  • gron is the one release binary left out, because upstream has been dormant since 2022.
  • A replaced sccache stops its old server, so the next build starts the new one.
  • Removed: macOS claude update, which does nothing for the cask that brew upgrade --cask --greedy already covers.
  • Hardening:
    • curl timeouts;
    • the token is sent to api.github.com through a 0600 header file, and child processes no longer inherit it;
    • the exit cleanup ignores values inherited from the environment;
    • the macOS script's set -u and pipefail work again, since emulate -L zsh was resetting both.
  • Anonymous GitHub API calls are capped at 60/h per IP, and anonymous 304s count against that. When the API refuses, the last cached release is used and the run stays green.

Tested on ubuntu:24.04, ubuntu:26.04 and fedora:44 against real release assets:

  • every tool was replaced from a stand-in, and the second run reported each one current;
  • a tampered asset was rejected with the old binary kept;
  • a copy in ~/.local/bin and a symlink at /usr/local/bin/yq were left alone;
  • the uv user refresh went 0.11.0 to 0.12.23;
  • a Go toolchain bump rebuilt govulncheck;
  • an old sccache server was stopped after replacement;
  • test-hyperi-update.sh passed 18 of 18.

Not run: arm64, a real GitHub token, the systemd user unit restart, and macOS beyond zsh -n and --help.

Done when a weekly run on any supported box refreshes each tool exactly once, from a verified download, without adding a second copy.

The Linux updater now refreshes every release binary the roles put in /usr/local/bin and nothing at the OS level carries. One refetch helper replaces the three per-format ones. It takes the asset's own arch spelling, a forge other than GitHub (tea from Gitea, macbash from downloads.hyperi.io), tar, nested tar and zip assets, a published checksum, and a minimum release age that matches the role's 7-day cooldown for aws-vault and Fedora's kubectx and kubens. It only touches a binary that lives under /usr/local, and only on the distro whose role installs it there. A stamp in /var/lib/hyperi-update records the source and digest of each install, so a release that has not moved is not downloaded again. A GITHUB_TOKEN or GH_TOKEN in the environment is sent to api.github.com from a 0600 file.

Newly covered: lazygit, actionlint, osv-scanner, aws-vault, git-scrub, sccache, fnm, tea and macbash on both distros, hadolint, gitleaks and act on Ubuntu, and sd, kubectx and kubens on Fedora.

Both updaters now refresh pnpm globals with `pnpm update -g --latest`, which `npm update -g` never reached, and uv-managed Pythons with `uv python upgrade`, which installs no python or python3 shim. Go tools are refreshed only when their binary is in ~/go/bin and the module has a newer release, which adds gosec and flarectl on Linux without doubling a dnf copy. The macOS updater drops `claude update`, since the claude-code cask rides `brew upgrade --cask --greedy`, and finds brew's keg-only rustup.

developer-go appends ~/go/bin to the login PATH, where gopls, govulncheck, gosec and flarectl were installed but unreachable. Comments that claimed hyperi-update refreshed git-scrub on macOS and Apple container now say what does, and the install matrix lists the binaries the updater covers.
Every release binary the Linux updater installs is now checked against the sha256 GitHub publishes for the asset, read from the release document the updater already fetches. Where a release has no digest (dive v0.13.1) it falls back to the release's checksum file, and tea and macbash keep their .sha256 files. A mismatch fails the tool and leaves the installed binary alone. In a release list, the digest is read from the chosen release only, because an asset name without a version repeats in every release. A new binary is installed beside the old one and renamed over it, so it is never missing or half-written. A symlink member in an archive is rejected.

On Ubuntu, uv and uvx in the user's ~/.local/bin are refreshed from the same release as the user, since `uv self update` refuses a copy it did not install. When sccache is replaced, the hyperi-sccache user unit is restarted if it is running, otherwise a server still running the old binary is stopped.

Release lookups send the last ETag, and the response is cached per user. With a token, an unchanged release costs no rate limit. Anonymous 304s still count, and when the API refuses, the cached document stands in so a current tool is not reported as failed. Every curl call is bounded by connect and total timeouts.

The EXIT trap no longer acts on SUDO_KEEPALIVE_PID or GH_AUTH_HEADER inherited from the caller. The GitHub token is read once and stops being exported to child processes. The go tools section runs after the Go toolchain section and rebuilds a tool when the toolchain moved. On macOS, nounset and pipefail are set after `emulate`, which had reset them, so a failed softwareupdate is reported.

The comments on the timer unit, the astral role, removals and hyperi-go.sh now match what runs. The install matrix covers pnpm and uv Python refreshes in Tier 2, the verification, ETag and uv behaviour in Tier 3, gron as the one release binary left out, and the lazygit row.
@catinspace-au
catinspace-au merged commit 52eda5b into main Oct 6, 2026
18 checks passed
@catinspace-au
catinspace-au deleted the fix/updater-realign branch October 6, 2026 10:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant