Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 8 additions & 18 deletions pkg/binaries/ssh-to-age/ssh-to-age.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,14 @@ package sshtoage

import (
"context"
"crypto/sha256"
"fmt"
"runtime"
"strings"

"github.com/fentas/b/pkg/binaries"
"github.com/fentas/b/pkg/binary"
)

// workaround for version lookup
// wait for fix https://github.com/Mic92/ssh-to-age/issues/180
// or get binary sha https://github.com/fentas/b/issues/76
var versions = map[string]string{
"73513156cc8821915ff96b83a9a5780a2993199497c2a3106795de1c54429578": "1.2.0",
}

func Binary(options *binaries.BinaryOptions) *binary.Binary {
if options == nil {
options = &binaries.BinaryOptions{
Expand All @@ -35,20 +28,17 @@ func Binary(options *binaries.BinaryOptions) *binary.Binary {
VersionF: binary.GithubLatest,
IsTarGz: false,
VersionLocalF: func(b *binary.Binary) (string, error) {
s, err := b.Exec("-h")
// ssh-to-age -version prints a bare version like "1.2.0".
// GithubLatest returns "v1.2.0", so prepend "v" for comparison.
s, err := b.Exec("-version")
if err != nil {
return "", err
}
// create hash from output
hash := sha256.New()
if _, err := hash.Write([]byte(s)); err != nil {
return "", err
}
v := fmt.Sprintf("%x", hash.Sum(nil))
if _, ok := versions[v]; !ok {
return v, nil
v := strings.TrimSpace(s)
if v != "" && !strings.HasPrefix(v, "v") {
v = "v" + v
}
return versions[v], nil
return v, nil
},
}
}
10 changes: 2 additions & 8 deletions pkg/binary/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,8 @@ func (b *Binary) EnsureBinary(update bool) error {
}
local := b.LocalBinary(true)

// Don't short-circuit when we can't tell what's installed. An empty
// local.Version here means VersionLocalF either didn't run or
// errored (e.g. preset uses the wrong subcommand and the binary
// errors out); we must not mistake that for "matches the pin".
if local.Version != "" {
if local.Version == local.Enforced || local.Enforced == "" && local.Latest == local.Version {
return nil
}
if local.Version == local.Enforced || local.Enforced == "" && local.Latest == local.Version {
return nil
}
}

Expand Down
39 changes: 13 additions & 26 deletions pkg/binary/binary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,46 +258,33 @@ func TestBinary_EnsureBinary_UpdateUpToDate(t *testing.T) {
}
}

// TestBinary_EnsureBinary_UpdateWhenLocalVersionUnknown guards against a
// regression where a preset whose VersionLocalF errored (e.g. the binary
// doesn't support the expected subcommand) would silently skip updates —
// local.Version="" == local.Enforced="" used to satisfy the skip check,
// so 'b update' did nothing even when the upstream release had moved.
//
// Now an empty local.Version means "unknown"; EnsureBinary must proceed
// to DownloadBinary instead of early-returning.
func TestBinary_EnsureBinary_UpdateWhenLocalVersionUnknown(t *testing.T) {
// TestBinary_EnsureBinary_UpdateSkipsWhenVersionUnknown verifies that
// when VersionLocalF fails (returns empty version), EnsureBinary skips
// the download — the binary is on disk and we can't prove it's stale.
// This avoids re-downloading large binaries (e.g. tilt, 41MB) every
// time just because the version probe fails (e.g. missing `getent` in
// PATH). The trade-off: a genuinely outdated binary with a broken
// version probe won't auto-update until the probe is fixed or the user
// runs `b update --force`.
func TestBinary_EnsureBinary_UpdateSkipsWhenVersionUnknown(t *testing.T) {
tmp := t.TempDir()
t.Setenv("PATH_BIN", tmp)
// Pre-existing binary on disk, so the BinaryExists branch runs.
if err := os.WriteFile(filepath.Join(tmp, "broken"), []byte("stale"), 0755); err != nil {
t.Fatal(err)
}
b := &Binary{
Name: "broken",
// No pin.
Name: "broken",
Version: "",
VersionF: func(b *Binary) (string, error) {
return "v2", nil // upstream has a newer version
return "v2", nil
},
VersionLocalF: func(b *Binary) (string, error) {
// Preset's probe command failed — classic 'argsh version' bug:
// subcommand doesn't exist so the binary exits non-zero and
// VersionLocalF has no version to report.
return "", os.ErrNotExist
},
// No URL/URLF/GitHubRepo so DownloadBinary will fail with
// "no URL provided". That error is our sentinel: it proves the
// download branch ran instead of the broken skip path. Before
// the fix EnsureBinary returned nil here (Version=="" ==
// Enforced=="") and the preset silently appeared up to date.
}
err := b.EnsureBinary(true)
if err == nil {
t.Fatal("expected DownloadBinary to be attempted (and fail without a download source), got nil")
}
if !strings.Contains(err.Error(), "no URL provided") {
t.Errorf("expected the 'no URL provided' sentinel error, got: %v", err)
if err != nil {
t.Errorf("expected skip (nil), got: %v — broken version probe should not trigger re-download", err)
}
}

Expand Down
Loading