Documentation
¶
Overview ¶
Package selfupdate implements `logos update`: check GitHub for a newer release, verify it actually runs on this machine, then replace the running binary.
This is the one place in the codebase that makes a network call on its own initiative rather than because a user typed a command that needs one — and even here, only when the user typed `logos update` specifically. It never runs on a schedule, from doctor, or in the background, and the request it makes carries no vault path, no machine identifier, no telemetry: a bare GET with a User-Agent naming this binary and nothing else. See Update's doc comment for the verification chain that runs before anything is replaced.
Index ¶
- Constants
- func AssetName(version, goos, goarch string) string
- func ExtractBinary(archive []byte, goos string) ([]byte, error)
- func HomebrewStablePath(path string) string
- func IsDevBuild(version string) bool
- func Newer(latest, current string) bool
- func VerifyChecksum(sums []byte, name string, data []byte) error
- type Asset
- type Client
- type Error
- type InstallKind
- type Options
- type Release
- type Result
- type Step
Constants ¶
const HomebrewFormula = "logos-mcp"
HomebrewFormula is the tap formula's name. Homebrew's own collection already has a logos (a Bible study app), so the formula is logos-mcp while the command it installs is still logos, and brew's directories use the formula name.
Variables ¶
This section is empty.
Functions ¶
func AssetName ¶
AssetName is what scripts/release.sh names one platform's archive: logos_<version>_<goos>_<goarch>.tar.gz, or .zip on Windows. Go's own GOOS/GOARCH spellings are what the script uses directly — no npm-style x64 translation is needed here.
func ExtractBinary ¶
ExtractBinary pulls the logos executable out of a release archive. The archive holds it one directory down (logos_<version>_<goos>_<goarch>/logos), so this matches on suffix rather than an exact path.
func HomebrewStablePath ¶
HomebrewStablePath is <prefix>/opt/logos-mcp/bin/logos for a binary under <prefix>/Cellar/logos-mcp/<version>/, or "" when path is not a Homebrew install or the opt link does not exist. Homebrew points the opt link at the current version, so that path keeps working after `brew upgrade` removes this one.
func IsDevBuild ¶
IsDevBuild reports whether version is the unstamped default a plain `go build` produces. Updating one would silently replace a local build with a release binary — a footgun this refuses outright rather than warns about.
func Newer ¶
Newer reports whether latest is a later release than current. Before this, update treated any difference as an update, so a build stamped with a tag GitHub had not published yet was "updated" back to the previous release. A pre-release or Go pseudo-version sorts below the release it names, and build metadata after + is ignored, as in semver. A version this cannot read counts as newer only if it differs, which is what update did before.
Types ¶
type Client ¶
type Client struct {
HTTP *http.Client
APIBase string
Repo string
// Agent is the User-Agent sent on every request. It names the binary and
// nothing else — no hostname, no vault path, no user identifier.
Agent string
}
Client talks to GitHub. Every field is overridable so tests point this at an httptest.Server instead of the real API — nothing in this package should ever need real network access to be exercised.
func NewClient ¶
NewClient is what `logos update` uses. version is stamped into the User-Agent so a look at GitHub's own request logs identifies which release is asking, nothing more.
type Error ¶
Error names the step that failed. Every path out of Update that is not a clean success or "already current" returns one of these — invariant 4: a failure is reported, never swallowed into a success-shaped result.
type InstallKind ¶
type InstallKind int
InstallKind is how this binary got onto the machine, which decides whether `logos update` has anything to replace.
const ( // Standalone is a binary in an ordinary place — go install, a release // tarball extracted by hand, a plain reinstall. Replacing it in place is // exactly what the user wants. Standalone InstallKind = iota // NPX resolves a fresh copy from the registry on every invocation. There // is nothing durable here to replace. NPX // NPMManaged is installed through npm's per-platform package mechanism // (global or local). Replacing the binary works, but npm's own version // bookkeeping will not reflect it. NPMManaged // Homebrew lives in a versioned directory under the Cellar that the next // `brew upgrade` deletes, and brew's receipt records which version it put // there. Replacing the file would be undone by brew, so brew updates it. Homebrew )
func DetectInstall ¶
func DetectInstall(path string) InstallKind
DetectInstall classifies a resolved executable path. Callers should run it through filepath.EvalSymlinks first.
type Options ¶
type Options struct {
Client *Client
Verify func(path, wantVersion string) error
Executable func() (string, error)
}
Options lets tests substitute the network client, the "does this binary actually run" check, and how the running executable is resolved — so the whole flow is exercisable against an httptest.Server and a fake verify function, never a real GitHub API or a real exec. The zero value is what `logos update` uses.
type Release ¶
Release is what the GitHub releases/latest endpoint returns, trimmed to what this package needs.
type Result ¶
type Result struct {
From, To string
Asset string
Size int64
Checksum string
Replaced string
Ahead bool
}
Result is what a successful Update did, printed verbatim by the CLI. To == From (both set, Asset empty) means the check ran and nothing needed doing. Ahead (Asset empty) means the running binary is newer than the latest release, so there was nothing to update to.
func Update ¶
Update is the whole flow, in the order that matters:
- Refuse outright on an unstamped dev build.
- Resolve the running executable. If it resolves to an npx cache, refuse with no network call at all — there is nothing to check.
- Ask GitHub what the latest release is. If it is not newer than what we already are, stop here; nothing downloaded, nothing written.
- Confirm the executable's directory is writable, so a permission failure is reported before a byte is downloaded.
- Download the archive and SHA256SUMS, and verify the archive's checksum before touching anything else.
- Extract the binary, write it into the executable's own directory (so the final rename is same-filesystem), and run it to confirm it reports the version it claims to. Only a binary that passes this is ever installed.
- Swap it in.
Every failure names the step it failed at (see Error) rather than a bare "could not update", and nothing is replaced until step 6 has actually run the new binary — a checksum proves transport, not that the bytes work on this machine.