Skip to content

Alternative CLIs: absolute shim paths, update notices from the embedded config, and an installer step - #215

Merged
pjcdawkins merged 8 commits into
mainfrom
feat/config-install-shim-paths
Oct 14, 2026
Merged

pjcdawkins merged 8 commits into
mainfrom
feat/config-install-shim-paths

Conversation

@pjcdawkins

@pjcdawkins pjcdawkins commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor
  • config:install shims now contain the absolute config path, so they work under cron and CI where HOME or XDG_CONFIG_HOME differ. They run the upsun found on PATH at install time, by its unresolved path (e.g. /opt/homebrew/bin/upsun), and fall back to a PATH lookup if it has gone. The Windows .bat shim does the same. --absolute now writes the path of the running executable instead. When run through another alt CLI, the shim still targets the binary's own executable (from the embedded config).
  • With CLI_CONFIG_FILE, update checks and notices take wrapper.* and the application name, executable and slug from the binary's embedded config, as the binary is unchanged. The env var prefix, updates settings and state file still come from the loaded config. The notice is only shown if there is an upgrade command for the install method: the binary may be a private copy (e.g. from an existing vendor installer), which the generic upgrade instructions would not update.
  • installer.sh: optional CONFIG_URL, installed with config:install -y after the CLI. CONFIG_URL and vendor_name are in one block at the top so a vendor build can set them.
  • A second shim in a directory outside PATH (e.g. a legacy location) can be written with config:install --force --bin-dir <dir>; the duplicate check is only about PATH precedence, so no change is needed.

🤖 Generated with Claude Code

pjcdawkins and others added 5 commits October 14, 2026 09:22
The shim referred to the config file via "${HOME}" or "${XDG_CONFIG_HOME}"
(or "%AppData%" on Windows). Under cron or CI, where those differ or are
unset, it pointed at the wrong file and the CLI failed with "failed to load
config". The shim now contains the absolute path.

The shim ran the CLI by name, or with --absolute by os.Executable(), which
resolves symlinks on Linux (e.g. to a versioned Homebrew Cellar path). It now
records the unresolved path found on the PATH at install time, falling back
to os.Executable() if the CLI is not on the PATH, and runs the CLI by name if
that file has gone. The --absolute option is removed, as the path is now
always recorded.

Paths are quoted for the shell when needed, and the Windows batch file gets
the same fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…otices

With CLI_CONFIG_FILE (e.g. an alternative CLI installed by config:install),
the update check and notice read wrapper.* and the application name,
executable and slug from the loaded config. That config describes the
alternative CLI, not the binary that runs it, so the notice could not
give the right upgrade command, or was not shown at all.

Config.Embedded() returns the binary's embedded config. It is now used for
the release repository, the install method override, the Homebrew formula,
the package marker slug, the Scoop command and the name in the notice.

The environment variable prefix, the updates settings and the state file
still come from the loaded config, so each alternative CLI keeps its own
check interval and notification throttle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When CONFIG_URL is set, the installer runs "upsun config:install -y" with
it after installing the CLI, as the installing user. This installs an
alternative CLI (a config file and a shim that runs upsun with it). A
failure stops the installer with an error.

CONFIG_URL and vendor_name are in one block at the top of the script, so
a vendor's installer build can set them by substitution. Without
CONFIG_URL the installer behaves as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@upsun-dispatch upsun-dispatch Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

Changes suggested — 🟡 1 warning · 🔵 2 minor points

🔍 Full review · 16 files reviewed

Verification
  • The Windows shim doubles % in paths inside set "VAR=..." and in the quoted target, and falls back to targetName through goto fallback when the target is missing.
  • POSIX shellQuote leaves safe paths unquoted and single-quotes anything else, escaping embedded ' as '\'', so $HOME in a path stays literal.
  • Embedded() returns the receiver when the raw bytes equal the embedded YAML, otherwise a cached parse of the embedded config, and falls back to the receiver if that parse fails.
  • IsAutoUpdating and newInstallProbe still read the env var prefix from the loaded config, so &lt;PREFIX>INSTALL_METHOD keeps working for the alt CLI.
  • installer.sh runs config:install -y with stdin from /dev/null, and passes the full dir_bin path for the raw method, so a dir_bin outside PATH still works.

The PR adds unit tests: TestGenerateExecutable (shim_test.go), TestFindExecutableTarget, an updated TestConfigInstallCmd and TestAlt, TestEmbedded, TestPrintUpdateMessageWithAltConfig and TestNewInstallProbeWithAltConfig. CI runs them through the make test-go step in .github/workflows/ci.yml. No test covers the installer.sh install_config step, and none runs the Windows shim.

Review details
  • Commit: 28d6967
  • Model: claude-opus-5-5

Review 1 of 10 for this pull request · View the full run

Comment thread commands/config_install.go Resolved
Comment thread commands/config_install.go Resolved
Comment thread commands/root_test.go Resolved
…--absolute

When config:install ran through an existing alt CLI, the shim targeted
that alt CLI's executable (from the loaded config), so the new CLI ran
with the other alt CLI's config. The target name now comes from the
embedded config, and a config for that name is refused.

--absolute is back, to run the current executable rather than the one
found on the PATH (e.g. a build that is not on the PATH).

Tests take the expected embedded values from Config.Embedded(), so they
pass with other embedded configs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@upsun-dispatch

upsun-dispatch Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

📋 PR Summary

Makes alternative CLI configs sturdier. config:install shims now hold the absolute config path and the upsun path found at install time, falling back to a PATH lookup if that binary is gone, so they work under cron and CI. When CLI_CONFIG_FILE is set, update notices take the application identity and wrapper.* settings from the binary's embedded config, and appear only if the install method has an upgrade command. installer.sh gains an optional CONFIG_URL step. The latest push only renames a parameter in printUpdateMessage.

Changes
Layer / File(s) Summary
Shim installation
commands/config_install.go Shims now point to the absolute config path and the unresolved upsun path from install time. --absolute writes the path of the running executable instead.
commands/config_install_test.go Tests for the new shim contents.
internal/config/alt/alt.go Shim generation (Unix and .bat) uses the absolute paths, with a PATH fallback if the binary is gone.
internal/config/alt/alt_test.go Updates the existing alt config tests.
internal/config/alt/shim_test.go New tests that check how shims behave.
Update notices
commands/root.go printUpdateMessage uses the embedded config for the application name and upgrade command, and prints nothing under CLI_CONFIG_FILE if there is no upgrade command. The latest push renames its newRelease parameter to rel.
commands/root_test.go Tests for when the update notice is shown or skipped.
internal/config/config.go Gives access to the binary's embedded config.
internal/config/config_test.go Tests for the embedded config accessor.
internal/install.go Install-method detection and upgrade commands are based on the embedded config.
internal/install_test.go Tests for install detection.
internal/update.go Update checks take wrapper.* settings from the embedded config.
Installer
installer.sh Adds an optional CONFIG_URL, installed with config:install -y after the CLI. CONFIG_URL and vendor_name sit together at the top so a vendor build can set them.
Docs
docs/design/update-message-install-detection.md Explains where update notices get their settings when CLI_CONFIG_FILE is set.
README.md Adds one line about the change.
CLAUDE.md Adds one guidance line.

pjcdawkins and others added 2 commits October 14, 2026 17:49
With CLI_CONFIG_FILE, the binary may be a private copy installed by
another installer, in a location that install detection does not
recognize. The generic link to the upsun/cli upgrade instructions would
not update that copy, so the notice is now skipped when there is no
upgrade command for the install method. Without CLI_CONFIG_FILE, the
link is shown as before.

printUpdateMessage now takes the install method, so tests do not depend
on the memoized detection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pjcdawkins
pjcdawkins merged commit 63b2128 into main Oct 14, 2026
14 checks passed
@pjcdawkins
pjcdawkins deleted the feat/config-install-shim-paths branch October 14, 2026 18:14
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