Skip to content

fix(update): refuse to initialize an install root instead of wiping it - #664

Merged
JeroenSoeters merged 1 commit into
mainfrom
naxty/formae-update-cli-crash
Sep 4, 2026
Merged

fix(update): refuse to initialize an install root instead of wiping it#664
JeroenSoeters merged 1 commit into
mainfrom
naxty/formae-update-cli-crash

Conversation

@naxty

@naxty naxty commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • formae update no longer initializes an orbital tree root. It refuses with the resolved path and the ways out; creating install roots stays with the installer, the only component that knows it owns the target directory.
  • Root derivation now resolves symlinks before taking the grandparent directory, so a shim on PATH resolves to the real install instead of making its own prefix the managed root.

Why

The tree root was derived from the running binary's location (dirname(dirname(os.Executable()))), and when that path carried no tree, update prompted to initialize it. mgr.Initialize() is a force-init — orbital removes the root recursively before recreating it. So a formae reached through a copy or symlink in a foreign prefix (a package manager's bin directory, /usr/local/bin, a build tree) resolved to that prefix, and confirming the prompt deleted it. The update then installed into the fresh tree and reported success, so nothing signalled the loss.

Two properties make it worse than a bad prompt: os.Executable() on darwin returns the invoked path with symlinks intact, so any shim silently retargets the root; and root-owned prefixes cause orbital to re-exec under sudo first, so the delete can run elevated.

Refusing is the only safe answer here — update cannot distinguish "user wants a new root" from "user is running the wrong binary", and one of those two readings is destructive. With symlink resolution in place, shims keep working, so the refusal only fires on genuinely rootless prefixes.

refresh and the plugin commands already refused in this situation; update was the outlier.

`formae update` derived the orbital tree root from the running binary's
location and, when that path carried no tree, offered to initialize one.
orbital's Initialize is a force-init: it removes the root recursively
before recreating it. A formae reached through a copy or symlink in a
foreign prefix (a package manager's bin, /usr/local/bin, a build tree)
resolved to that prefix, so confirming the prompt deleted an unrelated
directory — and the update then reported success.

update now refuses with the resolved path and the ways out; creating
install roots stays with the installer, which is the only component that
knows it owns the target. Root derivation also resolves symlinks first,
so a shim on PATH resolves to the real install rather than making its own
prefix the managed root.
@JeroenSoeters
JeroenSoeters merged commit da0192e into main Sep 4, 2026
31 checks passed
@JeroenSoeters
JeroenSoeters deleted the naxty/formae-update-cli-crash branch September 4, 2026 00:38
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.

2 participants