stamp builds with an epoch and refuse remote downgrades; fix the version check losing its PATH prefix
1234345678930 unmodified lines{ pkgs ? import <nixpkgs> { }, # baked into the binary as the rpc version string; the flake passes its own # rev, a plain nix-build reports "dev" - mismatches force remote re-uploads # baked into the binary as the rpc version string; the flake passes # "<rev>+<epoch>" and a plain nix-build reports "dev" - the epoch is what # remote upgrade checks compare, so older builds never overwrite newer ones rev ? "dev",}:let inherit (pkgs) lib;30 unmodified lines10 unmodified lines111213141516151718192021229 unmodified lines10 unmodified lines systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ]; perSystem = { pkgs, ... }: { packages.default = import ./default.nix { inherit pkgs; # the "+epoch" suffix is what remote upgrade checks compare rev = let rev = inp.self.shortRev or inp.self.dirtyShortRev or "dev"; r = inp.self.shortRev or inp.self.dirtyShortRev or "dev"; in "${r}+${toString inp.self.lastModified}"; };
devShells.default = pkgs.mkShell { packages = with pkgs; [9 unmodified lines5 unmodified lines678910111213141516171828 unmodified lines5 unmodified lines# `git describe`, but nix passes it explicitly because the sandbox has# no .git to describeout_dir="${1:-.}"VERSION="${VERSION:-$(git describe --always --dirty 2>/dev/null || echo dev)}"# every build carries a comparison epoch; nix passes one alreadycase "${VERSION}" in *+*) ;; *) VERSION="${VERSION}+$(git log -1 --format=%ct 2>/dev/null || echo 0)" ;;esacLDFLAGS="-s -w -X tobi/internal/remote.Version=${VERSION}"
targets=( "linux/amd64"28 unmodified lines13 unmodified lines141516171819202122138 unmodified linesExpand all16116216316416516616716816917017117217317417517617717817959 unmodified lines239240241242231232233234243244245246247248249250251252253254255256257258194 unmodified linesExpand all13 unmodified lines "os/user" "path/filepath" "runtime" "slices" "strconv" "strings" "sync" "time"
138 unmodified linesExpand all remoteInstalled sync.Map Version = "dev")
// versionEpoch parses the "+<unix time>" build suffix of a version like// "9e3d87f-dirty+1758451200"; builds without one report -1func versionEpoch(v string) int64 { if i := strings.LastIndexByte(v, '+'); i >= 0 { if e, err := strconv.ParseInt(v[i+1:], 10, 64); err == nil { return e } } return -1}
type sshOpts struct { cmd string batch, master bool timeout string59 unmodified lines dest := hostDest(host) if _, ok := remoteInstalled.Load(dest); ok { return nil } check := remotePathEnv + " " + peerEnv() + `command -v tobi >/dev/null 2>&1 && TOBI_RPC=version tobi` if out, err := sshRun(ctx, dest, nil, sshOpts{cmd: check, batch: true, master: true, timeout: "3"}); err == nil && strings.TrimSpace(out) == Version { remoteInstalled.Store(dest, true) return nil // the PATH prefix is a per-command assignment: it must wrap the version // rpc too, or the check 127s on hosts where tobi is not in default PATH check := remotePathEnv + " " + peerEnv() + `command -v tobi >/dev/null 2>&1 && ` + remotePathEnv + ` TOBI_RPC=version tobi` if out, err := sshRun(ctx, dest, nil, sshOpts{cmd: check, batch: true, master: true, timeout: "3"}); err == nil { remoteVer := strings.TrimSpace(out) if remoteVer == Version || versionEpoch(Version) <= versionEpoch(remoteVer) { if versionEpoch(remoteVer) > versionEpoch(Version) { fmt.Fprintf(os.Stderr, "not downgrading %s: remote tobi %s is newer than local %s\n", dest, remoteVer, Version) } remoteInstalled.Store(dest, true) return nil } } out, err := sshRun(ctx, dest, nil, sshOpts{cmd: "uname -s && uname -m", batch: true, timeout: "3"}) if err != nil { return fmt.Errorf("detect %s: %w", dest, err)194 unmodified linesExpand all