diff --git a/CHANGELOG.md b/CHANGELOG.md index 979aad2dbe..6d20da6b91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [v3.40.0](https://github.com/buildkite/agent/tree/v3.40.0) (2022-11-08) +[Full Changelog](https://github.com/buildkite/agent/compare/v3.39.0...v3.40.0) + +### Added + +- Agent binaries for windows/arm64 [#1767](https://github.com/buildkite/agent/pull/1767) (@yob) +- Alpine k8s image [#1771](https://github.com/buildkite/agent/pull/1771) (@dabarrell) + +### Security + +- (Fixed in 3.39.1) A security issue in environment handling between buildkite-agent and Bash 5.2 [#1781](https://github.com/buildkite/agent/pull/1781) (@moskyb) +- Secret redaction now handles secrets containing UTF-8 code points greater than 255 [#1809](https://github.com/buildkite/agent/pull/1809) (@DrJosh9000) +- The update to Go 1.19.3 fixes two Go security issues (particularly on Windows): + - The current directory (`.`) in `$PATH` is now ignored for finding executables - see https://go.dev/blog/path-security + - Environment variable values containing null bytes are now sanitised - see https://github.com/golang/go/issues/56284 + +### Changed + +- 5xx responses are now retried when attempting to start a job [#1777](https://github.com/buildkite/agent/pull/1777) (@jonahbull) +- 🧹 A variety of dependency updates and cleanups! + ## [v3.39.0](https://github.com/buildkite/agent/tree/v3.39.0) (2022-09-08) [Full Changelog](https://github.com/buildkite/agent/compare/v3.38.0...v3.39.0) diff --git a/agent/VERSION b/agent/VERSION index 4a8c3b9681..7e16c94210 100644 --- a/agent/VERSION +++ b/agent/VERSION @@ -1 +1 @@ -3.39.0 +3.40.0 diff --git a/bootstrap/integration/command_integration_test.go b/bootstrap/integration/command_integration_test.go index d6ea274199..8c84d3cd8f 100644 --- a/bootstrap/integration/command_integration_test.go +++ b/bootstrap/integration/command_integration_test.go @@ -16,7 +16,7 @@ func TestMultilineCommandRunUnderBatch(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -25,9 +25,8 @@ func TestMultilineCommandRunUnderBatch(t *testing.T) { setup.Expect().Once() build.Expect().Once().AndCallFunc(func(c *bintest.Call) { - llamas := c.GetEnv(`LLAMAS`) - if llamas != "COOL" { - t.Errorf("Expected LLAMAS=COOL, got %s", llamas) + if got, want := c.GetEnv("LLAMAS"), "COOL"; got != want { + t.Errorf("c.GetEnv(LLAMAS) = %q, want %q", got, want) c.Exit(1) } else { c.Exit(0) @@ -45,7 +44,7 @@ func TestMultilineCommandRunUnderBatch(t *testing.T) { func TestPreExitHooksRunsAfterCommandFails(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -56,9 +55,8 @@ func TestPreExitHooksRunsAfterCommandFails(t *testing.T) { AndExitWith(0) preExitFunc := func(c *bintest.Call) { - cmdExitStatus := c.GetEnv(`BUILDKITE_COMMAND_EXIT_STATUS`) - if cmdExitStatus != "1" { - t.Errorf("Expected an exit status of 1, got %v", cmdExitStatus) + if got, want := c.GetEnv("BUILDKITE_COMMAND_EXIT_STATUS"), "1"; got != want { + t.Errorf("c.GetEnv(BUILDKITE_COMMAND_EXIT_STATUS) = %q, want %q", got, want) } c.Exit(0) } @@ -66,8 +64,8 @@ func TestPreExitHooksRunsAfterCommandFails(t *testing.T) { tester.ExpectGlobalHook("pre-exit").Once().AndCallFunc(preExitFunc) tester.ExpectLocalHook("pre-exit").Once().AndCallFunc(preExitFunc) - if err = tester.Run(t, "BUILDKITE_COMMAND=false"); err == nil { - t.Fatal("Expected the bootstrap to fail") + if err := tester.Run(t, "BUILDKITE_COMMAND=false"); err == nil { + t.Fatalf("tester.Run(t, BUILDKITE_COMMAND=false) = %v, want non-nil error", err) } tester.CheckMocks(t) diff --git a/bootstrap/integration/docker_integration_test.go b/bootstrap/integration/docker_integration_test.go index e778f90c85..6e5907d38f 100644 --- a/bootstrap/integration/docker_integration_test.go +++ b/bootstrap/integration/docker_integration_test.go @@ -19,7 +19,7 @@ func argumentForCommand(cmd string) interface{} { func TestRunningCommandWithDocker(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -52,7 +52,7 @@ func TestRunningCommandWithDocker(t *testing.T) { func TestRunningCommandWithDockerAndCustomDockerfile(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -86,7 +86,7 @@ func TestRunningCommandWithDockerAndCustomDockerfile(t *testing.T) { func TestRunningFailingCommandWithDocker(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -116,7 +116,7 @@ func TestRunningFailingCommandWithDocker(t *testing.T) { expectCommandHooks("1", t, tester) if err = tester.Run(t, env...); err == nil { - t.Fatal("Expected bootstrap to fail") + t.Fatalf("tester.Run(t, %v) = %v, want non-nil error", env, err) } tester.CheckMocks(t) @@ -125,7 +125,7 @@ func TestRunningFailingCommandWithDocker(t *testing.T) { func TestRunningCommandWithDockerCompose(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -158,7 +158,7 @@ func TestRunningCommandWithDockerCompose(t *testing.T) { func TestRunningFailingCommandWithDockerCompose(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -189,7 +189,7 @@ func TestRunningFailingCommandWithDockerCompose(t *testing.T) { expectCommandHooks("1", t, tester) if err = tester.Run(t, env...); err == nil { - t.Fatal("Expected bootstrap to fail") + t.Fatalf("tester.Run(t, %v) = %v, want non-nil error", env, err) } tester.CheckMocks(t) @@ -198,7 +198,7 @@ func TestRunningFailingCommandWithDockerCompose(t *testing.T) { func TestRunningCommandWithDockerComposeAndExtraConfig(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -232,7 +232,7 @@ func TestRunningCommandWithDockerComposeAndExtraConfig(t *testing.T) { func TestRunningCommandWithDockerComposeAndBuildAll(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -261,9 +261,8 @@ func expectCommandHooks(exitStatus string, t *testing.T, tester *BootstrapTester tester.ExpectLocalHook("post-command").Once() preExitFunc := func(c *bintest.Call) { - cmdExitStatus := c.GetEnv(`BUILDKITE_COMMAND_EXIT_STATUS`) - if cmdExitStatus != exitStatus { - t.Errorf("Expected an exit status of %s, got %v", exitStatus, cmdExitStatus) + if got, want := c.GetEnv("BUILDKITE_COMMAND_EXIT_STATUS"), exitStatus; got != want { + t.Errorf("c.GetEnv(BUILDKITE_COMMAND_EXIT_STATUS) = %q, want %q", got, want) } c.Exit(0) } diff --git a/bootstrap/integration/hooks_integration_test.go b/bootstrap/integration/hooks_integration_test.go index 329aeb78b0..dbfb2e5618 100644 --- a/bootstrap/integration/hooks_integration_test.go +++ b/bootstrap/integration/hooks_integration_test.go @@ -19,30 +19,25 @@ func TestEnvironmentVariablesPassBetweenHooks(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() - if runtime.GOOS != "windows" { - var script = []string{ - "#!/bin/bash", - "export LLAMAS_ROCK=absolutely", - } - - if err := os.WriteFile(filepath.Join(tester.HooksDir, "environment"), - []byte(strings.Join(script, "\n")), 0700); err != nil { - t.Fatal(err) - } - } else { - var script = []string{ + filename := "environment" + script := []string{ + "#!/bin/bash", + "export LLAMAS_ROCK=absolutely", + } + if runtime.GOOS == "windows" { + filename = "environment.bat" + script = []string{ "@echo off", "set LLAMAS_ROCK=absolutely", } + } - if err := os.WriteFile(filepath.Join(tester.HooksDir, "environment.bat"), - []byte(strings.Join(script, "\r\n")), 0700); err != nil { - t.Fatal(err) - } + if err := os.WriteFile(filepath.Join(tester.HooksDir, filename), []byte(strings.Join(script, "\n")), 0700); err != nil { + t.Fatalf("os.WriteFile(%q, script, 0700) = %v", filename, err) } git := tester.MustMock(t, "git").PassthroughToLocalCommand().Before(func(i bintest.Invocation) error { @@ -71,46 +66,38 @@ func TestHooksCanUnsetEnvironmentVariables(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() + preCmdFile, postCmdFile := "pre-command", "post-command" + preCommand := []string{ + "#!/bin/bash", + "export LLAMAS_ROCK=absolutely", + } + postCommand := []string{ + "#!/bin/bash", + "unset LLAMAS_ROCK", + } + if runtime.GOOS == "windows" { - var preCommand = []string{ + preCmdFile, postCmdFile = "pre-command.bat", "post-command.bat" + preCommand = []string{ "@echo off", "set LLAMAS_ROCK=absolutely", } - if err := os.WriteFile(filepath.Join(tester.HooksDir, "pre-command.bat"), - []byte(strings.Join(preCommand, "\r\n")), 0700); err != nil { - t.Fatal(err) - } - - var postCommand = []string{ + postCommand = []string{ "@echo off", "set LLAMAS_ROCK=", } - if err := os.WriteFile(filepath.Join(tester.HooksDir, "post-command.bat"), - []byte(strings.Join(postCommand, "\n")), 0700); err != nil { - t.Fatal(err) - } - } else { - var preCommand = []string{ - "#!/bin/bash", - "export LLAMAS_ROCK=absolutely", - } - if err := os.WriteFile(filepath.Join(tester.HooksDir, "pre-command"), - []byte(strings.Join(preCommand, "\n")), 0700); err != nil { - t.Fatal(err) - } + } - var postCommand = []string{ - "#!/bin/bash", - "unset LLAMAS_ROCK", - } - if err := os.WriteFile(filepath.Join(tester.HooksDir, "post-command"), - []byte(strings.Join(postCommand, "\n")), 0700); err != nil { - t.Fatal(err) - } + if err := os.WriteFile(filepath.Join(tester.HooksDir, preCmdFile), []byte(strings.Join(preCommand, "\n")), 0700); err != nil { + t.Fatalf("os.WriteFile(%q, preCommand, 0700) = %v", preCmdFile, err) + } + + if err := os.WriteFile(filepath.Join(tester.HooksDir, postCmdFile), []byte(strings.Join(postCommand, "\n")), 0700); err != nil { + t.Fatalf("os.WriteFile(%q, postCommand, 0700) = %v", postCmdFile, err) } tester.ExpectGlobalHook("command").Once().AndExitWith(0).AndCallFunc(func(c *bintest.Call) { @@ -139,7 +126,7 @@ func TestDirectoryPassesBetweenHooks(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -147,7 +134,7 @@ func TestDirectoryPassesBetweenHooks(t *testing.T) { t.Skip("Not implemented for windows yet") } - var script = []string{ + script := []string{ "#!/bin/bash", "mkdir -p ./mysubdir", "export MY_CUSTOM_SUBDIR=$(cd mysubdir; pwd)", @@ -155,7 +142,7 @@ func TestDirectoryPassesBetweenHooks(t *testing.T) { } if err := os.WriteFile(filepath.Join(tester.HooksDir, "pre-command"), []byte(strings.Join(script, "\n")), 0700); err != nil { - t.Fatal(err) + t.Fatalf("os.WriteFile(pre-command, script, 0700) = %v", err) } tester.ExpectGlobalHook("command").Once().AndExitWith(0).AndCallFunc(func(c *bintest.Call) { @@ -173,7 +160,7 @@ func TestDirectoryPassesBetweenHooks(t *testing.T) { func TestDirectoryPassesBetweenHooksIgnoredUnderExit(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -181,7 +168,7 @@ func TestDirectoryPassesBetweenHooksIgnoredUnderExit(t *testing.T) { t.Skip("Not implemented for windows yet") } - var script = []string{ + script := []string{ "#!/bin/bash", "mkdir -p ./mysubdir", "export MY_CUSTOM_SUBDIR=$(cd mysubdir; pwd)", @@ -190,7 +177,7 @@ func TestDirectoryPassesBetweenHooksIgnoredUnderExit(t *testing.T) { } if err := os.WriteFile(filepath.Join(tester.HooksDir, "pre-command"), []byte(strings.Join(script, "\n")), 0700); err != nil { - t.Fatal(err) + t.Fatalf("os.WriteFile(pre-command, script, 0700) = %v", err) } tester.ExpectGlobalHook("command").Once().AndExitWith(0).AndCallFunc(func(c *bintest.Call) { @@ -210,7 +197,7 @@ func TestCheckingOutFiresCorrectHooks(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -240,7 +227,7 @@ func TestReplacingCheckoutHook(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -269,7 +256,7 @@ func TestReplacingGlobalCommandHook(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -294,7 +281,7 @@ func TestReplacingLocalCommandHook(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -320,15 +307,15 @@ func TestPreExitHooksFireAfterCommandFailures(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() tester.ExpectGlobalHook("pre-exit").Once() tester.ExpectLocalHook("pre-exit").Once() - if err = tester.Run(t, "BUILDKITE_COMMAND=false"); err == nil { - t.Fatal("Expected the bootstrap to fail") + if err := tester.Run(t, "BUILDKITE_COMMAND=false"); err == nil { + t.Fatalf("tester.Run(t, BUILDKITE_COMMAND=false) = %v, want non-nil error", err) } tester.CheckMocks(t) @@ -361,7 +348,7 @@ func TestPreExitHooksFireAfterHookFailures(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -397,8 +384,8 @@ func TestPreExitHooksFireAfterHookFailures(t *testing.T) { AndExitWith(0) } - if err = tester.Run(t, "BUILDKITE_ARTIFACT_PATHS=test.txt"); err == nil { - t.Fatal("Expected the bootstrap to fail") + if err := tester.Run(t, "BUILDKITE_ARTIFACT_PATHS=test.txt"); err == nil { + t.Fatalf("tester.Run(t, BUILDKITE_ARTIFACT_PATHS=test.txt) = %v, want non-nil error", err) } tester.CheckMocks(t) @@ -411,7 +398,7 @@ func TestNoLocalHooksCalledWhenConfigSet(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -421,7 +408,7 @@ func TestNoLocalHooksCalledWhenConfigSet(t *testing.T) { tester.ExpectLocalHook("pre-command").NotCalled() if err = tester.Run(t, "BUILDKITE_COMMAND=true"); err == nil { - t.Fatal("Expected the bootstrap to fail due to local hook being called") + t.Fatalf("tester.Run(t, BUILDKITE_COMMAND=true) = %v, want non-nil error", err) } tester.CheckMocks(t) @@ -445,21 +432,19 @@ func TestExitCodesPropagateOutFromGlobalHooks(t *testing.T) { t.Run(hook, func(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() tester.ExpectGlobalHook(hook).Once().AndExitWith(5) err = tester.Run(t) + if err == nil { - t.Fatalf("Expected the bootstrap to fail because %s hook exits", hook) + t.Fatalf("tester.Run(t) = %v, want non-nil error", err) } - - exitCode := shell.GetExitCode(err) - - if exitCode != 5 { - t.Fatalf("Expected an exit code of %d, got %d", 5, exitCode) + if got, want := shell.GetExitCode(err), 5; got != want { + t.Fatalf("shell.GetExitCode(%v) = %d, want %d", err, got, want) } tester.CheckMocks(t) @@ -468,6 +453,7 @@ func TestExitCodesPropagateOutFromGlobalHooks(t *testing.T) { } func TestPreExitHooksFireAfterCancel(t *testing.T) { + // TODO: Why is this test skipped on windows and darwin? if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { t.Skip() } @@ -476,7 +462,7 @@ func TestPreExitHooksFireAfterCancel(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -488,8 +474,8 @@ func TestPreExitHooksFireAfterCancel(t *testing.T) { go func() { defer wg.Done() - if err = tester.Run(t, "BUILDKITE_COMMAND=sleep 5"); err == nil { - t.Errorf("Expected tester to fail with error") + if err := tester.Run(t, "BUILDKITE_COMMAND=sleep 5"); err == nil { + t.Errorf(`tester.Run(t, "BUILDKITE_COMMAND=sleep 5") = %v, want non-nil error`, err) } t.Logf("Command finished") }() diff --git a/bootstrap/integration/plugin_integration_test.go b/bootstrap/integration/plugin_integration_test.go index a58a1d49b3..7425172f64 100644 --- a/bootstrap/integration/plugin_integration_test.go +++ b/bootstrap/integration/plugin_integration_test.go @@ -19,35 +19,34 @@ func TestRunningPlugins(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() pluginMock := tester.MustMock(t, "my-plugin") - var p *testPlugin - + hooks := map[string][]string{ + "environment": { + "#!/bin/bash", + "export LLAMAS_ROCK=absolutely", + pluginMock.Path + " testing", + }, + } if runtime.GOOS == "windows" { - p = createTestPlugin(t, map[string][]string{ + hooks = map[string][]string{ "environment.bat": { "@echo off", "set LLAMAS_ROCK=absolutely", pluginMock.Path + " testing", }, - }) - } else { - p = createTestPlugin(t, map[string][]string{ - "environment": { - "#!/bin/bash", - "export LLAMAS_ROCK=absolutely", - pluginMock.Path + " testing", - }, - }) + } } + p := createTestPlugin(t, hooks) + json, err := p.ToJSON() if err != nil { - t.Fatal(err) + t.Fatalf("testPlugin.ToJSON() error = %v", err) } env := []string{ @@ -81,31 +80,30 @@ func TestExitCodesPropagateOutFromPlugins(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() - var p *testPlugin - + hooks := map[string][]string{ + "environment": { + "#!/bin/bash", + "exit 5", + }, + } if runtime.GOOS == "windows" { - p = createTestPlugin(t, map[string][]string{ + hooks = map[string][]string{ "environment.bat": { "@echo off", "exit 5", }, - }) - } else { - p = createTestPlugin(t, map[string][]string{ - "environment": { - "#!/bin/bash", - "exit 5", - }, - }) + } } + p := createTestPlugin(t, hooks) + json, err := p.ToJSON() if err != nil { - t.Fatal(err) + t.Fatalf("testPlugin.ToJSON() error = %v", err) } env := []string{ @@ -113,14 +111,12 @@ func TestExitCodesPropagateOutFromPlugins(t *testing.T) { } err = tester.Run(t, env...) + if err == nil { - t.Fatal("Expected the bootstrap to fail") + t.Fatalf("tester.Run(t, %v) = %v, want non-nil error", env, err) } - - exitCode := shell.GetExitCode(err) - - if exitCode != 5 { - t.Fatalf("Expected an exit code of %d, got %d", 5, exitCode) + if got, want := shell.GetExitCode(err), 5; got != want { + t.Fatalf("shell.GetExitCode(%v) = %d, want %d", err, got, want) } tester.CheckMocks(t) @@ -131,7 +127,7 @@ func TestMalformedPluginNamesDontCrashBootstrap(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -139,8 +135,8 @@ func TestMalformedPluginNamesDontCrashBootstrap(t *testing.T) { `BUILDKITE_PLUGINS=["sdgmdgn.@$!sdf,asdf#llamas"]`, } - if err = tester.Run(t, env...); err == nil { - t.Fatal("Expected the bootstrap to fail") + if err := tester.Run(t, env...); err == nil { + t.Fatalf("tester.Run(t, %v) = %v, want non-nil error", env, err) } tester.CheckMocks(t) @@ -155,7 +151,7 @@ func TestOverlappingPluginHooks(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -199,7 +195,7 @@ func TestOverlappingPluginHooks(t *testing.T) { pluginsJSON, err := json.Marshal(testPlugins) if err != nil { - t.Fatal(err) + t.Fatalf("json.Marshal(testPlugins) error = %v", err) } env := []string{ @@ -219,32 +215,33 @@ func TestPluginCloneRetried(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() - var p *testPlugin + hooks := map[string][]string{ + "environment": { + "#!/bin/bash", + "export LLAMAS_ROCK=absolutely", + }, + } + if runtime.GOOS == "windows" { - p = createTestPlugin(t, map[string][]string{ + hooks = map[string][]string{ "environment.bat": { "@echo off", "set LLAMAS_ROCK=absolutely", }, - }) - } else { - p = createTestPlugin(t, map[string][]string{ - "environment": { - "#!/bin/bash", - "export LLAMAS_ROCK=absolutely", - }, - }) + } } + p := createTestPlugin(t, hooks) + callCount := 0 realGit, err := exec.LookPath("git") if err != nil { - t.Fatal(err) + t.Fatalf("exec.LookPath(git) error = %v", err) } git := tester.MustMock(t, "git") @@ -263,7 +260,7 @@ func TestPluginCloneRetried(t *testing.T) { json, err := p.ToJSON() if err != nil { - t.Fatal(err) + t.Fatalf("testPlugin.ToJSON() error = %v", err) } env := []string{ @@ -284,7 +281,7 @@ func TestPluginCloneRetried(t *testing.T) { func TestModifiedPluginNoForcePull(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -293,7 +290,7 @@ func TestModifiedPluginNoForcePull(t *testing.T) { // test run, too. pluginsDir, err := os.MkdirTemp("", "bootstrap-plugins") if err != nil { - t.Fatal(err) + t.Fatalf(`os.MkdirTemp("", "bootstrap-plugins") error = %v`, err) } tester.PluginsDir = pluginsDir @@ -303,22 +300,21 @@ func TestModifiedPluginNoForcePull(t *testing.T) { tester.Env = replacePluginPathInEnv(tester.Env, pluginsDir) // Create a test plugin that sets an environment variable. - var p *testPlugin + hooks := map[string][]string{ + "environment": { + "#!/bin/bash", + "export OSTRICH_EGGS=quite_large", + }, + } if runtime.GOOS == "windows" { - p = createTestPlugin(t, map[string][]string{ + hooks = map[string][]string{ "environment.bat": { "@echo off", "set OSTRICH_EGGS=quite_large", }, - }) - } else { - p = createTestPlugin(t, map[string][]string{ - "environment": { - "#!/bin/bash", - "export OSTRICH_EGGS=quite_large", - }, - }) + } } + p := createTestPlugin(t, hooks) // You may be surprised that we're creating a branch here. This is so we can test the behaviour // when a branch has had new commits added to it. @@ -329,7 +325,7 @@ func TestModifiedPluginNoForcePull(t *testing.T) { json, err := p.ToJSON() if err != nil { - t.Fatal(err) + t.Fatalf("testPlugin.ToJSON() error = %v", err) } env := []string{ @@ -350,7 +346,7 @@ func TestModifiedPluginNoForcePull(t *testing.T) { // Now, we want to "repeat" the test build, having modified the plugin's contents. tester2, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester2.Close() @@ -358,21 +354,21 @@ func TestModifiedPluginNoForcePull(t *testing.T) { tester2.PluginsDir = pluginsDir tester2.Env = replacePluginPathInEnv(tester2.Env, pluginsDir) + hooks2 := map[string][]string{ + "environment": { + "#!/bin/bash", + "export OSTRICH_EGGS=huge_actually", + }, + } if runtime.GOOS == "windows" { - modifyTestPlugin(t, map[string][]string{ + hooks2 = map[string][]string{ "environment.bat": { "@echo off", "set OSTRICH_EGGS=huge_actually", }, - }, p) - } else { - modifyTestPlugin(t, map[string][]string{ - "environment": { - "#!/bin/bash", - "export OSTRICH_EGGS=huge_actually", - }, - }, p) + } } + modifyTestPlugin(t, hooks2, p) tester2.ExpectGlobalHook("command").Once().AndExitWith(0).AndCallFunc(func(c *bintest.Call) { if err := bintest.ExpectEnv(t, c.Env, `OSTRICH_EGGS=quite_large`); err != nil { @@ -392,7 +388,7 @@ func TestModifiedPluginNoForcePull(t *testing.T) { func TestModifiedPluginWithForcePull(t *testing.T) { tester, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester.Close() @@ -400,29 +396,28 @@ func TestModifiedPluginWithForcePull(t *testing.T) { // which defeats our test. pluginsDir, err := os.MkdirTemp("", "bootstrap-plugins") if err != nil { - t.Fatal(err) + t.Fatalf(`os.MkdirTemp("", "bootstrap-plugins") error = %v`, err) } // Same resetting code for BUILDKITE_PLUGINS_PATH as in the previous test tester.PluginsDir = pluginsDir tester.Env = replacePluginPathInEnv(tester.Env, pluginsDir) - var p *testPlugin + hooks := map[string][]string{ + "environment": { + "#!/bin/bash", + "export OSTRICH_EGGS=quite_large", + }, + } if runtime.GOOS == "windows" { - p = createTestPlugin(t, map[string][]string{ + hooks = map[string][]string{ "environment.bat": { "@echo off", "set OSTRICH_EGGS=quite_large", }, - }) - } else { - p = createTestPlugin(t, map[string][]string{ - "environment": { - "#!/bin/bash", - "export OSTRICH_EGGS=quite_large", - }, - }) + } } + p := createTestPlugin(t, hooks) // Same branch-name jiggery pokery as in the previous integration test p.gitRepository.CreateBranch("something-fixed") @@ -430,7 +425,7 @@ func TestModifiedPluginWithForcePull(t *testing.T) { json, err := p.ToJSON() if err != nil { - t.Fatal(err) + t.Fatalf("testPlugin.ToJSON() error = %v", err) } env := []string{ @@ -450,7 +445,7 @@ func TestModifiedPluginWithForcePull(t *testing.T) { tester2, err := NewBootstrapTester() if err != nil { - t.Fatal(err) + t.Fatalf("NewBootstrapTester() error = %v", err) } defer tester2.Close() @@ -460,21 +455,21 @@ func TestModifiedPluginWithForcePull(t *testing.T) { // Notice the all-important setting, BUILDKITE_PLUGINS_ALWAYS_CLONE_FRESH, being enabled. tester2.Env = append(tester2.Env, "BUILDKITE_PLUGINS_ALWAYS_CLONE_FRESH=true") + hooks2 := map[string][]string{ + "environment": { + "#!/bin/bash", + "export OSTRICH_EGGS=huge_actually", + }, + } if runtime.GOOS == "windows" { - modifyTestPlugin(t, map[string][]string{ + hooks2 = map[string][]string{ "environment.bat": { "@echo off", "set OSTRICH_EGGS=huge_actually", }, - }, p) - } else { - modifyTestPlugin(t, map[string][]string{ - "environment": { - "#!/bin/bash", - "export OSTRICH_EGGS=huge_actually", - }, - }, p) + } } + modifyTestPlugin(t, hooks2, p) // This time, we expect the value of OSTRICH_EGGS to have changed compared to the first test // run. @@ -500,31 +495,31 @@ type testPlugin struct { func createTestPlugin(t *testing.T, hooks map[string][]string) *testPlugin { repo, err := newGitRepository() if err != nil { - t.Fatal(err) + t.Fatalf("newGitRepository() error = %v", err) } if err := os.MkdirAll(filepath.Join(repo.Path, "hooks"), 0700); err != nil { - t.Fatal(err) + t.Fatalf("os.MkdirAll(hooks, 0700) = %v", err) } for hook, lines := range hooks { data := []byte(strings.Join(lines, "\n")) if err := os.WriteFile(filepath.Join(repo.Path, "hooks", hook), data, 0600); err != nil { - t.Fatal(err) + t.Fatalf("os.WriteFile(hooks/%s, data, 0600) = %v", hook, err) } } - if err = repo.Add("."); err != nil { - t.Fatal(err) + if err := repo.Add("."); err != nil { + t.Fatalf("repo.Add(.) = %v", err) } - if err = repo.Commit("Initial commit of plugin hooks"); err != nil { - t.Fatal(err) + if err := repo.Commit("Initial commit of plugin hooks"); err != nil { + t.Fatalf(`repo.Commit("Initial commit of plugin hooks") = %v`, err) } commitHash, err := repo.RevParse("HEAD") if err != nil { - t.Fatal(err) + t.Fatalf(`repo.RevParse("HEAD") error = %v`, err) } return &testPlugin{repo, commitHash} } @@ -537,16 +532,16 @@ func modifyTestPlugin(t *testing.T, hooks map[string][]string, testPlugin *testP for hook, lines := range hooks { data := []byte(strings.Join(lines, "\n")) if err := os.WriteFile(filepath.Join(repo.Path, "hooks", hook), data, 0600); err != nil { - t.Fatal(err) + t.Fatalf("os.WriteFile(hooks/%s, data, 0600) = %v", hook, err) } } if err := repo.Add("."); err != nil { - t.Fatal(err) + t.Fatalf("repo.Add(.) = %v", err) } if err := repo.Commit("Updating content of plugin"); err != nil { - t.Fatal(err) + t.Fatalf(`repo.Commit("Updating content of plugin") = %v`, err) } } diff --git a/bootstrap/shell/logger_test.go b/bootstrap/shell/logger_test.go index 866a392c8d..46c917b720 100644 --- a/bootstrap/shell/logger_test.go +++ b/bootstrap/shell/logger_test.go @@ -7,11 +7,12 @@ import ( "testing" "github.com/buildkite/agent/v3/bootstrap/shell" + "github.com/google/go-cmp/cmp" ) func TestAnsiLogger(t *testing.T) { - b := &bytes.Buffer{} - l := shell.WriterLogger{Writer: b, Ansi: false} + got := &bytes.Buffer{} + l := shell.WriterLogger{Writer: got, Ansi: false} l.Headerf("Testing header: %q", "llamas") l.Printf("Testing print: %q", "llamas") @@ -20,33 +21,30 @@ func TestAnsiLogger(t *testing.T) { l.Warningf("Testing warning: %q", "llamas") l.Promptf("Testing prompt: %q", "llamas") - expected := &bytes.Buffer{} + want := &bytes.Buffer{} - fmt.Fprintln(expected, `~~~ Testing header: "llamas"`) - fmt.Fprintln(expected, `Testing print: "llamas"`) - fmt.Fprintln(expected, `# Testing comment: "llamas"`) - fmt.Fprintln(expected, `🚨 Error: Testing error: "llamas"`) - fmt.Fprintln(expected, `^^^ +++`) - fmt.Fprintln(expected, `⚠️ Warning: Testing warning: "llamas"`) - fmt.Fprintln(expected, `^^^ +++`) + fmt.Fprintln(want, `~~~ Testing header: "llamas"`) + fmt.Fprintln(want, `Testing print: "llamas"`) + fmt.Fprintln(want, `# Testing comment: "llamas"`) + fmt.Fprintln(want, `🚨 Error: Testing error: "llamas"`) + fmt.Fprintln(want, `^^^ +++`) + fmt.Fprintln(want, `⚠️ Warning: Testing warning: "llamas"`) + fmt.Fprintln(want, `^^^ +++`) if runtime.GOOS == "windows" { - fmt.Fprintln(expected, `> Testing prompt: "llamas"`) + fmt.Fprintln(want, `> Testing prompt: "llamas"`) } else { - - fmt.Fprintln(expected, `$ Testing prompt: "llamas"`) + fmt.Fprintln(want, `$ Testing prompt: "llamas"`) } - actual := b.String() - - if actual != expected.String() { - t.Fatalf("Expected %q, got %q", expected.String(), actual) + if diff := cmp.Diff(got.String(), want.String()); diff != "" { + t.Fatalf("shell.WriterLogger output buffer diff (-got +want):\n%s", diff) } } func TestLoggerStreamer(t *testing.T) { - b := &bytes.Buffer{} - l := &shell.WriterLogger{Writer: b, Ansi: false} + got := &bytes.Buffer{} + l := &shell.WriterLogger{Writer: got, Ansi: false} streamer := shell.NewLoggerStreamer(l) streamer.Prefix = "TEST>" @@ -58,18 +56,16 @@ func TestLoggerStreamer(t *testing.T) { fmt.Fprint(streamer, "# No line end") if err := streamer.Close(); err != nil { - t.Fatal(err) + t.Errorf("streamer.Close() = %v", err) } - expected := &bytes.Buffer{} - - fmt.Fprintln(expected, `TEST># Rest of the line`) - fmt.Fprintln(expected, `TEST># And another`) - fmt.Fprintln(expected, `TEST># No line end`) + want := &bytes.Buffer{} - actual := b.String() + fmt.Fprintln(want, `TEST># Rest of the line`) + fmt.Fprintln(want, `TEST># And another`) + fmt.Fprintln(want, `TEST># No line end`) - if actual != expected.String() { - t.Fatalf("Expected %q, got %q", expected.String(), actual) + if diff := cmp.Diff(got.String(), want.String()); diff != "" { + t.Fatalf("shell.WriterLogger output buffer diff (-got +want):\n%s", diff) } } diff --git a/bootstrap/shell/shell_test.go b/bootstrap/shell/shell_test.go index 7181271871..aa6c49f5fe 100644 --- a/bootstrap/shell/shell_test.go +++ b/bootstrap/shell/shell_test.go @@ -17,13 +17,13 @@ import ( "github.com/buildkite/agent/v3/bootstrap/shell" "github.com/buildkite/agent/v3/experiments" "github.com/buildkite/bintest/v3" - "github.com/stretchr/testify/assert" + "github.com/google/go-cmp/cmp" ) func TestRunAndCaptureWithTTY(t *testing.T) { sshKeygen, err := bintest.CompileProxy("ssh-keygen") if err != nil { - t.Fatal(err) + t.Fatalf("bintest.CompileProxy(ssh-keygen) error = %v", err) } defer sshKeygen.Close() @@ -36,20 +36,20 @@ func TestRunAndCaptureWithTTY(t *testing.T) { call.Exit(0) }() - actual, err := sh.RunAndCapture(sshKeygen.Path, "-f", "my_hosts", "-F", "llamas.com") + got, err := sh.RunAndCapture(sshKeygen.Path, "-f", "my_hosts", "-F", "llamas.com") if err != nil { - t.Error(err) + t.Errorf(`sh.RunAndCapture(ssh-keygen, "-f", "my_hosts", "-F", "llamas.com") error = %v`, err) } - if expected := "Llama party! 🎉"; string(actual) != expected { - t.Errorf("Expected %q, got %q", expected, actual) + if want := "Llama party! 🎉"; got != want { + t.Errorf(`sh.RunAndCapture(ssh-keygen, "-f", "my_hosts", "-F", "llamas.com") output = %q, want %q`, got, want) } } func TestRunAndCaptureWithExitCode(t *testing.T) { sshKeygen, err := bintest.CompileProxy("ssh-keygen") if err != nil { - t.Fatal(err) + t.Fatalf("bintest.CompileProxy(ssh-keygen) error = %v", err) } defer sshKeygen.Close() @@ -63,18 +63,18 @@ func TestRunAndCaptureWithExitCode(t *testing.T) { _, err = sh.RunAndCapture(sshKeygen.Path) if err == nil { - t.Error("Expected an error, got nil") + t.Errorf("sh.RunAndCapture(ssh-keygen) error = %v, want non-nil error", err) } - if exitCode := shell.GetExitCode(err); exitCode != 24 { - t.Fatalf("Expected %d, got %d", 24, exitCode) + if got, want := shell.GetExitCode(err), 24; got != want { + t.Errorf("shell.GetExitCode(%v) = %d, want %d", err, got, want) } } func TestRun(t *testing.T) { sshKeygen, err := bintest.CompileProxy("ssh-keygen") if err != nil { - t.Fatal(err) + t.Fatalf("bintest.CompileProxy(ssh-keygen) error = %v", err) } defer sshKeygen.Close() @@ -91,19 +91,18 @@ func TestRun(t *testing.T) { call.Exit(0) }() - if err = sh.Run(sshKeygen.Path, "-f", "my_hosts", "-F", "llamas.com"); err != nil { - t.Fatal(err) + if err := sh.Run(sshKeygen.Path, "-f", "my_hosts", "-F", "llamas.com"); err != nil { + t.Errorf(`sh.Run(ssh-keygen, "-f", "my_hosts", "-F", "llamas.com") error = %v`, err) } - actual := out.String() - promptPrefix := "$" if runtime.GOOS == "windows" { promptPrefix = ">" } - if expected := promptPrefix + " " + sshKeygen.Path + " -f my_hosts -F llamas.com\nLlama party! 🎉\n"; actual != expected { - t.Fatalf("Expected %q, got %q", expected, actual) + want := promptPrefix + " " + sshKeygen.Path + " -f my_hosts -F llamas.com\nLlama party! 🎉\n" + if diff := cmp.Diff(out.String(), want); diff != "" { + t.Fatalf("sh.Writer diff (-got +want):\n%s", diff) } } @@ -112,23 +111,22 @@ func TestRunWithStdin(t *testing.T) { sh := newShellForTest(t) sh.Writer = out - err := sh.WithStdin(strings.NewReader("hello stdin")).Run("tr", "hs", "HS") - if err != nil { - t.Fatal(err) + if err := sh.WithStdin(strings.NewReader("hello stdin")).Run("tr", "hs", "HS"); err != nil { + t.Fatalf(`sh.WithStdin("hello stdin").Run("tr", "hs", "HS") error = %v`, err) } - if expected, actual := "Hello Stdin", out.String(); expected != actual { - t.Errorf("expected %q, got %q", expected, actual) + if got, want := out.String(), "Hello Stdin"; want != got { + t.Errorf(`sh.WithStdin("hello stdin").Run("tr", "hs", "HS") output = %q, want %q`, got, want) } } func TestContextCancelTerminates(t *testing.T) { - if runtime.GOOS == `windows` { + if runtime.GOOS == "windows" { t.Skip("Not supported in windows") } sleepCmd, err := bintest.CompileProxy("sleep") if err != nil { - t.Fatal(err) + t.Fatalf("bintest.CompileProxy(sleep) error = %v", err) } defer sleepCmd.Close() @@ -137,7 +135,7 @@ func TestContextCancelTerminates(t *testing.T) { sh, err := shell.NewWithContext(ctx) if err != nil { - t.Fatal(err) + t.Fatalf("shell.NewWithContext(ctx) error = %v", err) } sh.Logger = shell.DiscardLogger @@ -150,9 +148,8 @@ func TestContextCancelTerminates(t *testing.T) { cancel() - err = sh.Run(sleepCmd.Path) - if !shell.IsExitSignaled(err) { - t.Fatalf("Expected signal exit, got %#v", err) + if err := sh.Run(sleepCmd.Path); !shell.IsExitSignaled(err) { + t.Errorf("sh.Run(sleep) error = %v, want shell.IsExitSignaled(err) = true", err) } } @@ -163,7 +160,7 @@ func TestInterrupt(t *testing.T) { sleepCmd, err := bintest.CompileProxy("sleep") if err != nil { - t.Fatal(err) + t.Fatalf("bintest.CompileProxy(sleep) error = %v", err) } defer sleepCmd.Close() @@ -172,7 +169,7 @@ func TestInterrupt(t *testing.T) { sh, err := shell.NewWithContext(ctx) if err != nil { - t.Fatal(err) + t.Fatalf("shell.NewWithContext(ctx) error = %v", err) } sh.Logger = shell.DiscardLogger @@ -189,85 +186,100 @@ func TestInterrupt(t *testing.T) { sh.Interrupt() }() - err = sh.Run(sleepCmd.Path) - if err == nil { - t.Error("Expected an error") + if err := sh.Run(sleepCmd.Path); err == nil { + t.Errorf("sh.Run(sleep) = %v, want non-nil error", err) } } func TestDefaultWorkingDirFromSystem(t *testing.T) { sh, err := shell.New() if err != nil { - t.Fatal(err) + t.Fatalf("shell.New() error = %v", err) } - currentWd, _ := os.Getwd() - if actual := sh.Getwd(); actual != currentWd { - t.Fatalf("Expected working dir %q, got %q", currentWd, actual) + want, err := os.Getwd() + if err != nil { + t.Fatalf("os.Getwd() error = %v", err) + } + if got := sh.Getwd(); got != want { + t.Fatalf("sh.Getwd() = %q, want %q", got, want) } } func TestWorkingDir(t *testing.T) { tempDir, err := os.MkdirTemp("", "shelltest") if err != nil { - t.Fatal(err) + t.Fatalf(`os.MkdirTemp("", "shelltest") error = %v`, err) } defer os.RemoveAll(tempDir) // macos has a symlinked temp dir if runtime.GOOS == "darwin" { - tempDir, _ = filepath.EvalSymlinks(tempDir) + td, err := filepath.EvalSymlinks(tempDir) + if err != nil { + t.Fatalf("filepath.EvalSymlinks(tempDir) error = %v", err) + } + tempDir = td } dirs := []string{tempDir, "my", "test", "dirs"} if err := os.MkdirAll(filepath.Join(dirs...), 0700); err != nil { - t.Fatal(err) + t.Fatalf("os.MkdirAll(dirs, 0700) = %v", err) } - currentWd, _ := os.Getwd() + currentWd, err := os.Getwd() + if err != nil { + t.Fatalf("os.Getwd() error = %v", err) + } sh, err := shell.New() - sh.Logger = shell.DiscardLogger - if err != nil { - t.Fatal(err) + t.Fatalf("shell.New() error = %v", err) } + sh.Logger = shell.DiscardLogger + for idx := range dirs { - dir := filepath.Join(dirs[0 : idx+1]...) + dir := filepath.Join(dirs[:idx+1]...) if err := sh.Chdir(dir); err != nil { - t.Fatal(err) + t.Fatalf("sh.Chdir(%q) = %v", dir, err) } - if actual := sh.Getwd(); actual != dir { - t.Fatalf("Expected working dir %q, got %q", dir, actual) + if got, want := sh.Getwd(), dir; got != want { + t.Fatalf("sh.Getwd() = %q, want %q", got, want) } - var out string + var pwd string // there is no pwd for windows, and getting it requires using a shell builtin if runtime.GOOS == "windows" { - out, err = sh.RunAndCapture("cmd", "/c", "echo", "%cd%") + out, err := sh.RunAndCapture("cmd", "/c", "echo", "%cd%") if err != nil { - t.Fatal(err) + t.Fatalf("sh.RunAndCapture(cmd /c echo %%cd%%) error = %v", err) } + pwd = out } else { - out, err = sh.RunAndCapture("pwd") + out, err := sh.RunAndCapture("pwd") if err != nil { - t.Fatal(err) + t.Fatalf("sh.RunAndCapture(pwd) error = %v", err) } + pwd = out } - if actual := out; actual != dir { - t.Fatalf("Expected working dir (from pwd command) %q, got %q", dir, actual) + if got, want := pwd, dir; got != want { + t.Fatalf("sh.RunAndCapture(pwd or equivalent) = %q, want %q", got, want) } } - afterWd, _ := os.Getwd() - if afterWd != currentWd { - t.Fatalf("Expected working dir to be the same as before shell commands ran") + afterWd, err := os.Getwd() + if err != nil { + t.Fatalf("os.Getwd() error = %v", err) + } + if got, want := afterWd, currentWd; got != want { + // Expect working dir to be the same as before shell commands ran. + t.Fatalf("os.Getwd() = %q, want %q", got, want) } } @@ -278,7 +290,7 @@ func TestLockFileRetriesAndTimesOut(t *testing.T) { dir, err := os.MkdirTemp("", "shelltest") if err != nil { - t.Fatal(err) + t.Fatalf(`os.MkdirTemp("", "shelltest") error = %v`, err) } defer os.RemoveAll(dir) @@ -290,15 +302,13 @@ func TestLockFileRetriesAndTimesOut(t *testing.T) { // acquire a lock in another process cmd, err := acquireLockInOtherProcess(lockPath) if err != nil { - t.Fatal(err) + t.Errorf("acquireLockInOtherProcess(%q) error = %v", lockPath, err) } - defer cmd.Process.Kill() - // acquire lock - _, err = sh.LockFile(lockPath, time.Second*2) - if err != context.DeadlineExceeded { - t.Fatalf("Expected DeadlineExceeded error, got %v", err) + timeout := time.Second * 2 + if _, err := sh.LockFile(lockPath, timeout); err != context.DeadlineExceeded { + t.Errorf("sh.LockFile(%q, %v) error = %v, want context.DeadlineExceeded", lockPath, timeout, err) } } @@ -365,7 +375,7 @@ func TestAcquiringLockHelperProcess(t *testing.T) { func newShellForTest(t *testing.T) *shell.Shell { sh, err := shell.New() if err != nil { - t.Fatal(err) + t.Fatalf("shell.New() error = %v", err) } sh.Logger = shell.DiscardLogger return sh @@ -374,67 +384,76 @@ func newShellForTest(t *testing.T) *shell.Shell { func TestRunWithoutPrompt(t *testing.T) { sh, err := shell.New() if err != nil { - t.Fatal(err) + t.Fatalf("shell.New() error = %v", err) } - out := bytes.NewBufferString("") + out := &bytes.Buffer{} sh.Writer = out - err = sh.RunWithoutPrompt("echo", "hi") - assert.NoError(t, err) - assert.Equal(t, "hi\n", out.String()) + if err := sh.RunWithoutPrompt("echo", "hi"); err != nil { + t.Fatalf("sh.RunWithoutPrompt(echo hi) = %v", err) + } + if got, want := out.String(), "hi\n"; got != want { + t.Errorf("sh.RunWithoutPrompt(echo hi) output = %q, want %q", got, want) + } out.Reset() - err = sh.RunWithoutPrompt("asdasdasdasdzxczxczxzxc") - assert.Error(t, err) + if err := sh.RunWithoutPrompt("asdasdasdasdzxczxczxzxc"); err == nil { + t.Errorf("sh.RunWithoutPrompt(asdasdasdasdzxczxczxzxc) = %v, want non-nil error", err) + } } func TestRunWithoutPromptWithContext(t *testing.T) { - sh, err := shell.New() ctx := context.Background() + + sh, err := shell.New() if err != nil { - t.Fatal(err) + t.Fatalf("shell.New() error = %v", err) } - out := bytes.NewBufferString("") + out := &bytes.Buffer{} sh.Writer = out - err = sh.RunWithoutPromptWithContext(ctx, "echo", "hi") - assert.NoError(t, err) - assert.Equal(t, "hi\n", out.String()) + if err := sh.RunWithoutPromptWithContext(ctx, "echo", "hi"); err != nil { + t.Fatalf("sh.RunWithoutPromptWithContext(echo hi) = %v", err) + } + if got, want := out.String(), "hi\n"; got != want { + t.Errorf("sh.RunWithoutPrompt(echo hi) output = %q, want %q", got, want) + } out.Reset() - err = sh.RunWithoutPromptWithContext(ctx, "asdasdasdasdzxczxczxzxc") - assert.Error(t, err) -} - -var roundTests = []struct { - in time.Duration - want time.Duration - outStr string -}{ - {3 * time.Nanosecond, 3 * time.Nanosecond, "3ns"}, - {32 * time.Nanosecond, 32 * time.Nanosecond, "32ns"}, - {321 * time.Nanosecond, 321 * time.Nanosecond, "321ns"}, - {4321 * time.Nanosecond, 4321 * time.Nanosecond, "4.321µs"}, - {54321 * time.Nanosecond, 54321 * time.Nanosecond, "54.321µs"}, - {654321 * time.Nanosecond, 654320 * time.Nanosecond, "654.32µs"}, - {7654321 * time.Nanosecond, 7654300 * time.Nanosecond, "7.6543ms"}, - {87654321 * time.Nanosecond, 87654000 * time.Nanosecond, "87.654ms"}, - {987654321 * time.Nanosecond, 987650000 * time.Nanosecond, "987.65ms"}, - {1987654321 * time.Nanosecond, 1987700000 * time.Nanosecond, "1.9877s"}, - {21987654321 * time.Nanosecond, 21988000000 * time.Nanosecond, "21.988s"}, - {321987654321 * time.Nanosecond, 321990000000 * time.Nanosecond, "5m21.99s"}, - {4321987654321 * time.Nanosecond, 4320000000000 * time.Nanosecond, "1h12m0s"}, - {54321987654321 * time.Nanosecond, 54320000000000 * time.Nanosecond, "15h5m20s"}, + if err := sh.RunWithoutPromptWithContext(ctx, "asdasdasdasdzxczxczxzxc"); err == nil { + t.Errorf("sh.RunWithoutPromptWithContext(asdasdasdasdzxczxczxzxc) = %v, want non-nil error", err) + } } func TestRound(t *testing.T) { - for _, tt := range roundTests { + tests := []struct { + in time.Duration + want time.Duration + wantStr string + }{ + {3 * time.Nanosecond, 3 * time.Nanosecond, "3ns"}, + {32 * time.Nanosecond, 32 * time.Nanosecond, "32ns"}, + {321 * time.Nanosecond, 321 * time.Nanosecond, "321ns"}, + {4321 * time.Nanosecond, 4321 * time.Nanosecond, "4.321µs"}, + {54321 * time.Nanosecond, 54321 * time.Nanosecond, "54.321µs"}, + {654321 * time.Nanosecond, 654320 * time.Nanosecond, "654.32µs"}, + {7654321 * time.Nanosecond, 7654300 * time.Nanosecond, "7.6543ms"}, + {87654321 * time.Nanosecond, 87654000 * time.Nanosecond, "87.654ms"}, + {987654321 * time.Nanosecond, 987650000 * time.Nanosecond, "987.65ms"}, + {1987654321 * time.Nanosecond, 1987700000 * time.Nanosecond, "1.9877s"}, + {21987654321 * time.Nanosecond, 21988000000 * time.Nanosecond, "21.988s"}, + {321987654321 * time.Nanosecond, 321990000000 * time.Nanosecond, "5m21.99s"}, + {4321987654321 * time.Nanosecond, 4320000000000 * time.Nanosecond, "1h12m0s"}, + {54321987654321 * time.Nanosecond, 54320000000000 * time.Nanosecond, "15h5m20s"}, + } + + for _, tt := range tests { got := shell.Round(tt.in) if got != tt.want { - t.Errorf("round(%v): got %v, want %v", tt.in, got, tt.want) + t.Errorf("shell.Round(%v): got %v, want %v", tt.in, got, tt.want) } - if got.String() != tt.outStr { - t.Errorf("round(%v): got %q, want %v", tt.in, got.String(), tt.outStr) + if got.String() != tt.wantStr { + t.Errorf("shell.Round(%v): got %q, want %v", tt.in, got.String(), tt.wantStr) } } } diff --git a/bootstrap/shell/test.go b/bootstrap/shell/test.go index 32d6121600..f2025b80cb 100644 --- a/bootstrap/shell/test.go +++ b/bootstrap/shell/test.go @@ -13,7 +13,7 @@ import ( func NewTestShell(t *testing.T) *Shell { sh, err := New() if err != nil { - t.Fatal(err) + t.Fatalf("shell.New() error = %v", err) } sh.Logger = DiscardLogger diff --git a/install.ps1 b/install.ps1 index a5f387f4a0..f1f4eaeb56 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1,9 +1,15 @@ $installDir = "C:\buildkite-agent" -$arch = "amd64" $beta = $env:buildkiteAgentBeta $token = $env:buildkiteAgentToken $tags = $env:buildkiteAgentTags +if ($(Get-ComputerInfo -Property OsArchitecture).OsArchitecture -eq "ARM 64-bit Processor") { + $arch = "arm64" +} else { + # The value is "64-bit" on my intel laptop with windows in a virtualbox VM + $arch = "amd64" +} + if ([string]::IsNullOrEmpty($token)) { throw "No token specified, set `$env:buildkiteAgentToken" }