From a5f216f425997de165fabc64562f4974697b04ef Mon Sep 17 00:00:00 2001 From: henrygd Date: Mon, 21 Sep 2026 11:27:37 -0400 Subject: [PATCH] exit 0 without key on Windows to avoid Winget Validation-Executable-Error (#2376, #2247) --- internal/cmd/agent/agent.go | 24 +++++++++++++++++++++++- internal/cmd/agent/agent_test.go | 21 +++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/internal/cmd/agent/agent.go b/internal/cmd/agent/agent.go index ea2dbd69..a3b02ef1 100644 --- a/internal/cmd/agent/agent.go +++ b/internal/cmd/agent/agent.go @@ -1,9 +1,11 @@ package main import ( + "errors" "fmt" "log" "os" + "runtime" "strings" "github.com/henrygd/beszel" @@ -14,6 +16,12 @@ import ( "golang.org/x/crypto/ssh" ) +type noKeyProvidedError struct{} + +func (noKeyProvidedError) Error() string { + return "no key provided: must set -key flag, KEY env var, or KEY_FILE env var. Use 'beszel-agent help' for usage" +} + // cli options type cmdOptions struct { key string // key is the public key(s) for SSH authentication. @@ -124,7 +132,7 @@ func (opts *cmdOptions) loadPublicKeys() ([]ssh.PublicKey, error) { // Try key file keyFile, ok := utils.GetEnv("KEY_FILE") if !ok { - return nil, fmt.Errorf("no key provided: must set -key flag, KEY env var, or KEY_FILE env var. Use 'beszel-agent help' for usage") + return nil, noKeyProvidedError{} } pubKey, err := os.ReadFile(keyFile) @@ -138,6 +146,14 @@ func (opts *cmdOptions) getAddress() string { return agent.GetAddress(opts.listen) } +func isBenignStartupError(err error, goos string) bool { + if goos != "windows" { + return false + } + var noKeyErr noKeyProvidedError + return errors.As(err, &noKeyErr) +} + // handleFingerprint handles the "fingerprint" command with subcommands "view" and "reset". func handleFingerprint() { subCmd := "" @@ -182,6 +198,12 @@ func main() { var err error serverConfig.Keys, err = opts.loadPublicKeys() if err != nil { + if isBenignStartupError(err, runtime.GOOS) { + // WinGet launches the executable without configuration during validation. + // Exit successfully in that case while retaining the error on other platforms. + log.Print("Failed to load public keys:", err) + return + } log.Fatal("Failed to load public keys:", err) } diff --git a/internal/cmd/agent/agent_test.go b/internal/cmd/agent/agent_test.go index 26a9bee9..438f6a1a 100644 --- a/internal/cmd/agent/agent_test.go +++ b/internal/cmd/agent/agent_test.go @@ -2,6 +2,7 @@ package main import ( "crypto/ed25519" + "errors" "os" "path/filepath" "testing" @@ -187,6 +188,26 @@ func TestLoadPublicKeys(t *testing.T) { } } +func TestIsBenignStartupError(t *testing.T) { + tests := []struct { + name string + err error + goos string + want bool + }{ + {name: "missing key on windows", err: noKeyProvidedError{}, goos: "windows", want: true}, + {name: "wrapped missing key on windows", err: errors.Join(errors.New("startup failed"), noKeyProvidedError{}), goos: "windows", want: true}, + {name: "missing key on linux", err: noKeyProvidedError{}, goos: "linux", want: false}, + {name: "different error on windows", err: errors.New("invalid key"), goos: "windows", want: false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, isBenignStartupError(tt.err, tt.goos)) + }) + } +} + func TestGetNetwork(t *testing.T) { tests := []struct { name string