Skip to content

thirdshift update - #49

Merged
JacobStephens2 merged 3 commits into
mainfrom
issue-34
Sep 25, 2026
Merged

JacobStephens2 merged 3 commits into
mainfrom
issue-34

Conversation

@JacobStephens2

Copy link
Copy Markdown
Owner

Summary

thirdshift update now works. It uses dist's axoupdater with the install receipt the shell installer writes.

thirdshift update
  load ~/.config/thirdshift/thirdshift-receipt.json
    none                        -> refuse, name the right update command, exit 1
  receipt is for another copy   -> refuse, name the right update command, exit 1
  ask GitHub for the latest stable release
    unreachable / none          -> explain on stderr, exit 1
    same version                -> "thirdshift 0.1.0 is the latest release", exit 0
    newer                       -> run its installer (stdout captured)
                                   -> "updated thirdshift 0.1.0 to X", exit 0
 src/
 ├── main.rs        # "update" arm now calls update::update(); the Run path never does
+└── update.rs      # the only caller of axoupdater
 tests/
 ├── command_surface.rs   # the "not yet available" test is removed
+└── update.rs            # offline: receipt in the Scenario HOME, fake Releases API
  • stdout stays empty. The installer's stdout is captured, and it comes back inside the error if the install fails.
  • Safe during a Run. On Linux and macOS, dist 0.33's installer mvs the new binary into place. The rename leaves a running Run on the old inode.
  • Refuses a copy it didn't install. A copy the receipt isn't for gets refused (for example ~/.cargo/bin/thirdshift while an installer copy also exists). Without that check, axoupdater would quietly report "up to date".
  • No OpenSSL. HTTPS goes through reqwest with rustls: cargo tree -i openssl-sys and cargo tree -i native-tls match nothing.
  • README. The Updating section now leads with thirdshift update. The rest of the README is left to README for the release, and ADR-0003 #36.

Evidence

  • Before: thirdshift update printed thirdshift: update is not yet available and exited 1.
    After: with no receipt:

    $ thirdshift update; echo $?
    thirdshift: this thirdshift has no install receipt; update only replaces the copy the thirdshift installer put in place. To update this one instead:
      built from source:  git pull && cargo install --path .
      from crates.io:     cargo install thirdshift
      copied by hand:     reinstall with curl -LsSf https://github.com/JacobStephens2/thirdshift/releases/latest/download/thirdshift-installer.sh | sh
    1
    

    Against the real GitHub API, where no release exists yet: thirdshift: can't update from GitHub Releases: There are no stable releases available for thirdshift, exit 1, stdout empty.

  • tests/update.rs is all offline. GitHub is replaced by a python3 -m http.server fake, reached through axoupdater's THIRDSHIFT_INSTALLER_GHE_BASE_URL:

    HOME and XDG_CONFIG_HOME empty            -> exit 1, reinstall hints on stderr, stdout ""   (the acceptance test)
    receipt for another install prefix        -> exit 1, names that prefix, hints
    latest release == our version             -> exit 0, "thirdshift 0.1.0 is the latest release", installer not run
    newer release, installer echoes to stdout -> exit 0, "updated thirdshift 0.1.0 to 99.0.0", stdout ""
    installer exits 1                         -> exit 1, its output on stderr
    no release published                      -> exit 1, "can't update from GitHub Releases: ..."
    GitHub unreachable (127.0.0.1:1)          -> exit 1, same explanation
    

    I checked that the stdout test can fail: without disable_installer_stdout(), it fails with left: "installing thirdshift 99.0.0\n".

  • cargo fmt --check, cargo clippy --all-targets -D warnings and the full cargo test pass locally, after merging the current main.

  • Not verified locally: the musl build. aws-lc-sys (below) needs musl-gcc, which this machine doesn't have. This PR touches Cargo.toml and Cargo.lock, so the release build job and the Rocky and WSL smoke jobs run on it. They are the check for the fourth acceptance criterion.

Merge Danger

Door: two-way

Revert the commit to go back. Nothing is published by merging.

Blast Radius: release-build

reqwest's rustls uses the aws-lc-rs backend, which brings in aws-lc-sys (C). So the static musl build now compiles C, where before its only native crates were libc and signal-hook. If CI's musl build or the Rocky smoke job fails, that is where to look. The lockfile also grows by about 180 crates, including image, which axoasset's remote feature pulls in. The Run path is unchanged apart from the update arm in main.

Unaddressed findings

Standards

  • Duplicated failure preamble in tests/update.rs (code == 1, stdout empty): not fixed, because it matches how the other test files write their assertions.
  • VERSION repeats env!("CARGO_PKG_VERSION"): not fixed, because the repo already does the same in main.rs and plugin.rs.
  • update() returns a String rather than an outcome enum: not fixed, because main only prints it. An enum would add a type that nothing branches on.
  • run_with_env takes a list of variables though most calls pass one: not fixed, because the no-receipt test passes two, HOME and XDG_CONFIG_HOME.

Spec

  • aws-lc-sys is a new native C dependency, against Spec: release thirdshift 0.1.0 as installable binaries #30's "only native dependencies are libc and signal-hook": not fixed. axoasset pins reqwest's default-tls, which is rustls on aws-lc-rs, and Cargo features can't switch it to ring. The spec's hard rule, rustls and never OpenSSL, still holds. This PR's musl build and smoke jobs are the check.
  • Tests beyond the one integration test the spec asked for (the fake Releases API and six more tests): kept. They exercise acceptance criteria 1 and 3 offline, through the binary's stdout, stderr and exit code, not through axoupdater's internals.
  • The THIRDSHIFT_INSTALLER_GHE_BASE_URL override can point update at another server: not changed. It is axoupdater's (and dist's installer's) own documented override, and anyone who can set it can already change PATH.
  • XDG_CONFIG_HOME is now set for every Scenario: kept on purpose. It keeps every test away from a real receipt on the developer's machine.
  • The dependency weight (about 180 crates, including image): not fixed, because it comes with axoupdater's github_releases feature, which the spec's choice of axoupdater requires.

Closes #34

🤖 Generated with Claude Code

JacobStephens2 and others added 3 commits September 25, 2026 16:13
`thirdshift update` reads the install receipt the shell installer wrote and
replaces the binary with the latest stable GitHub Release, or says it is
already on it. It refuses a copy the receipt isn't for (built from source,
installed from crates.io or copied by hand) and names the command that
updates it. Messages go to stderr; stdout stays empty.

axoupdater's HTTPS goes through reqwest with rustls, so there is no OpenSSL
in the tree. The tests run offline, against a receipt in the scenario's
HOME and a local fake of the GitHub Releases API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Share the refusal text between the two refusals, drop the half-used app
name constant, route every test's GitHub override through one helper, and
run the no-receipt test with HOME and XDG_CONFIG_HOME on an empty
directory, as the issue describes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 merged commit 169e303 into main Sep 25, 2026
13 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-34 branch September 25, 2026 16:27
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.

thirdshift update

1 participant