Repository navigation
fix: clear duplicate copies of managed tools - #107
Merged
Merged
Conversation
A tool installed by hand next to the one the roles manage leaves two copies, and the one in ~/.local/bin, ~/go/bin or a cargo home wins on PATH while nothing updates it. Each copy now goes only where the managed copy exists and is a different file (device and inode after following links), so the only working copy is never the one removed.
The removals tag clears user-level copies of the tools the roles install system-wide, and purges a .deb or .rpm that duplicates a /usr/local/bin tool when no repository offers it and a simulated removal takes nothing else. developer-go removes the distro Go once /usr/local/go is in, kept with a message when another package needs it.
developer-rust deregisters cargo-home copies of sccache, sd and fnm with cargo uninstall --root, so cargo install-update cannot bring them back, and removes uv and uvx left by the old installer. cargo-tarpaulin is retired from every cargo home, including one cargo has no record of, which used to fail the uninstall. Where CARGO_HOME is relocated, binaries the effective home also holds are removed from the old ~/.cargo/bin, and its caches and anything installed only there stay.
Relocation is now decided by comparing the directories, not their paths. A ~/.cargo symlinked to the relocated home read as stale, so the live config.toml was renamed to config.toml.superseded on every run.
typos and alint install into the effective CARGO_HOME, and the soe PATH drop-in and the hyperi-update scripts use ${CARGO_HOME:-$HOME/.cargo}/bin. The Linux update script also puts /usr/local/go/bin on PATH, which a GUI launch otherwise lacks once the distro Go is gone.
Package removals now go through one task file, system_cleanup/tasks/purge_packages.yml, used by the distro Go purge, the hand-installed package purge and the Docker Desktop tombstone. It refuses a removal that would take any other package, and marks everything the removal would orphan as manually installed first. hyperi-update runs apt-get autoremove and dnf autoremove unattended, so without that gcc, binutils and libc6-dev went at the next update after the Go purge. The distro Go goes only when /usr/local/go/bin/go runs, resolves under /usr/local to a different file than /usr/bin/go, and hyperi-go.sh exists, and only golang* packages are candidates. A /usr/local/go linked into the distro tree had the only Go purged. go and gofmt are now linked into /usr/local/bin so cron, systemd units and non-login shells keep a Go. A hand-installed package is purged only when it is on an allowlist of upstream release packages (macbash, git-scrub, dive, golangci-lint, k9s), no repository offers it, and the apt lists exist. Repository absence alone purged Ubuntu's yq, sccache and gitleaks wherever the lists were empty. The user-level sweep drops yq, tea, sd and act, whose names other programs share, skips directories, dangling links and bin directories that are links or resolve outside the home. hyperi-update refetches a static binary only when the copy in /usr/local/bin is its own, so a user-level copy no longer makes it install a second one. ~/.cargo is swept only when the home the host exports resolves to the effective one. A rust_cargo_home the host does not declare deleted cargo, rustc and rustup from the only cargo directory on PATH. It now leaves ~/.cargo and its config.toml alone and records a warning. The exported homes are probed once in the developer role with printenv, so an unexported shell variable no longer counts, and contributor installs typos and alint into the same home. In the cargo homes, retired and duplicate tools are deregistered before cargo install-update runs, a ~/.cargo package whose binaries all go is deregistered too, and cargo uninstall fails the run unless cargo simply has no record of the package. hyperi-update reads CARGO_HOME and RUSTUP_HOME from the login shell when they are unset, its unit loads /etc/environment, a missing rustup or cargo-install-update beside a cargo home is a failure, and ~/go/bin is on its PATH.
The shared purge read dnf's translated table headers, so under a German locale it found no orphans, removed the distro Go and left gcc, binutils and glibc-devel for the next autoremove. Every parsed command now runs in the C locale, and the purge stops unless dnf's summary count matches the packages it parsed. It also stops when any simulation fails. It marks only the new orphans the purged packages depend on directly (requires and recommends), which keeps their own dependencies too, instead of every new orphan. Removing what the roles did not install now runs only on a removals or soe run, like the developer role's tombstones: the distro Go purge, the cargo-home duplicates and the stale ~/.cargo sweep. A --tags removals run reaches the cargo-home sweep through a new developer-rust removals.yml, and the cargo-home resolution moved to cargo_home.yml so both paths share it. cargo-tarpaulin is still retired on every run, as this role installed it. A CARGO_HOME the host declares but has not created yet counts as declared, so a first run no longer warns. The distro Go goes only when /usr/local/go/src/runtime also resolves under /usr/local, because a copy of Ubuntu's tree keeps its standard library as links into /usr/share. go and gofmt in /usr/local/bin are pointed at /usr/local/go only when missing, dangling or already pointing there, and anything else is reported. The distro Rust removal failed with a depsolve error on any Fedora host with rust-std-static. It now removes every package built from the distro's Rust sources (rust-defaults and rustc-N.NN on Ubuntu) through the same purge. hyperi-update reads CARGO_HOME and RUSTUP_HOME from sentinel lines the login shell prints into a temporary file, takes only absolute paths, and no longer waits on a child a profile left in the background. Both updaters exit 1 when any step failed, so the systemd unit shows the failure. The remediation fixture removes dpkg-dev and rpm-build before installing Go and asserts the Go removal would orphan gcc, copies the Go tree with links dereferenced, builds with the managed Go in verify, and adds a gosrc host whose /usr/local/go still links into /usr/share.
A host dnf config with clean_requirements_on_remove=False drops the orphan table from the removal plan. The count check then passed with nothing marked, so gcc was left for autoremove. The plan now forces the setting on. Packages the run keeps, because something depends on them or the plan could not be read, are reported at the end of the run, whose heading now covers both cases.
Contributor
|
Released in v2.24.13 -- https://github.com/hyperi-io/hyperi-developer/releases/tag/v2.24.13 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Copies of managed tools left behind by hand installs now get cleared, so each tool has one copy and it updates with the host. The cleanup only removes what it can prove is a duplicate. Anything it is unsure of stays and shows up in the end-of-run report.
--tags removals/ soe runs clear:cargo install-updatecannot bring them back.system_cleanup/tasks/purge_packages.yml. It marks the direct dependencies the purge would orphan as manual first. Without that, hyperi-update's unattended autoremove takes gcc, binutils and libc headers afterwards. It runs in the C locale, forces dnf to list orphans, and stops the purge if the plan does not add up.Tested in containers: the remediation scenario converges twice and verifies clean on ubuntu:26.04, ubuntu:24.04 and fedora:44, plus two 26.04 hosts with /usr/local/go linked into, or copied from, the distro tree. The fixture asserts the Go purge really would orphan gcc, and verify asserts it did not. Also covered:
Not run: a full developer-rust or developer-go install converge, the timer under systemd, and macOS.
Done when a converged box has one copy of each managed tool and autoremove has nothing of ours to take.