From c4f7f6d240107835469000e45918a4adeefcfaca Mon Sep 17 00:00:00 2001 From: Jason Ross Date: Fri, 29 May 2026 17:59:11 -0500 Subject: [PATCH 1/4] package changes --- custom.go | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++ detect.go | 11 +++++++++ packages.go | 43 +++++++++++++++++++++++++++++++++- pkgmgr.go | 21 ++++++++++++++++- system.go | 44 +++++++++++------------------------ 5 files changed, 154 insertions(+), 32 deletions(-) diff --git a/custom.go b/custom.go index f92d8f9..738b909 100644 --- a/custom.go +++ b/custom.go @@ -321,6 +321,48 @@ func installNeovim(_ *CustomPackage, tmp string) { taskPrintf(" Neovim installed to %s, symlinked at %s\n", installDir, symlink) } +func installRustup(archive string) { + out := taskOut() + runCmd([]string{"chmod", "+x", archive}, CmdOpts{Out: out}) + runCmd([]string{archive, "-y", "--no-modify-path"}, CmdOpts{Out: out}) + taskPrintln(" rustup installed.") +} + +func installYq(archive string) { + out := taskOut() + runCmd([]string{"cp", archive, "/usr/local/bin/yq"}, CmdOpts{AsSudo: true, Out: out}) + runCmd([]string{"chmod", "+x", "/usr/local/bin/yq"}, CmdOpts{AsSudo: true, Out: out}) + taskPrintln(" yq installed.") +} + +func installDagger(archive, tmp string) { + out := taskOut() + runCmd([]string{"tar", "-C", tmp, "-xzf", archive}, CmdOpts{Out: out}) + runCmd([]string{"mv", filepath.Join(tmp, "dagger"), "/usr/local/bin/dagger"}, CmdOpts{AsSudo: true, Out: out}) + taskPrintln(" dagger installed.") +} + +func installTrivy(archive, tmp string) { + out := taskOut() + runCmd([]string{"tar", "-C", tmp, "-xzf", archive}, CmdOpts{Out: out}) + runCmd([]string{"mv", filepath.Join(tmp, "trivy"), "/usr/local/bin/trivy"}, CmdOpts{AsSudo: true, Out: out}) + taskPrintln(" trivy installed.") +} + +func installCosign(archive string) { + out := taskOut() + runCmd([]string{"cp", archive, "/usr/local/bin/cosign"}, CmdOpts{AsSudo: true, Out: out}) + runCmd([]string{"chmod", "+x", "/usr/local/bin/cosign"}, CmdOpts{AsSudo: true, Out: out}) + taskPrintln(" cosign installed.") +} + +func installGitleaks(archive, tmp string) { + out := taskOut() + runCmd([]string{"tar", "-C", tmp, "-xzf", archive}, CmdOpts{Out: out}) + runCmd([]string{"mv", filepath.Join(tmp, "gitleaks"), "/usr/local/bin/gitleaks"}, CmdOpts{AsSudo: true, Out: out}) + taskPrintln(" gitleaks installed.") +} + // ── latest-version resolvers ──────────────────────────────────────────── func resolveLatestGo(_ *CustomPackage) (string, string, bool) { @@ -434,10 +476,23 @@ func cmpSemver(a, b string) int { return len(pa) - len(pb) } +func resolveLatestYq(_ *CustomPackage) (string, string, bool) { + var rel ghRelease + if !fetchJSON("https://api.github.com/repos/mikefarah/yq/releases/latest", &rel) { + return "", "", false + } + version := strings.TrimPrefix(rel.TagName, "v") + if version == "" { + return "", "", false + } + return version, "", true +} + var latestResolvers = map[string]func(*CustomPackage) (string, string, bool){ "go": resolveLatestGo, "firecracker": resolveLatestFirecracker, "zig": resolveLatestZig, + "yq": resolveLatestYq, } // resolveLatest best-effort upgrades pkg.Version/SHA256 to the latest release. @@ -598,6 +653,18 @@ func runOneCustomInstall(pkg *CustomPackage) { installFirecracker(archive, tmp) case "zig": installZig(pkg, archive) + case "rustup": + installRustup(archive) + case "yq": + installYq(archive) + case "dagger": + installDagger(archive, tmp) + case "trivy": + installTrivy(archive, tmp) + case "cosign": + installCosign(archive) + case "gitleaks": + installGitleaks(archive, tmp) default: warn(fmt.Sprintf("No install handler for '%s' — skipping", pkg.Name)) } diff --git a/detect.go b/detect.go index 43868e7..e13f677 100644 --- a/detect.go +++ b/detect.go @@ -64,6 +64,12 @@ var ( osZig = map[string]string{"linux": "linux", "macos": "macos"} osNvim = map[string]string{"linux": "linux", "macos": "macos"} + osRustup = map[string]string{"linux": "unknown-linux-gnu", "macos": "apple-darwin"} + archRustup = map[string]string{"x86_64": "x86_64", "aarch64": "aarch64"} + osTrivy = map[string]string{"linux": "Linux", "macos": "macOS"} + archTrivy = map[string]string{"x86_64": "64bit", "aarch64": "ARM64"} + archGitleaks = map[string]string{"x86_64": "x64", "aarch64": "arm64"} + archTokens = map[string][]string{ "x86_64": {"x86_64", "amd64", "x64"}, "aarch64": {"aarch64", "arm64"}, @@ -81,6 +87,11 @@ func formatURL(template, version string) string { "{os_go}", osGo[osName], "{os_zig}", osZig[osName], "{os_nvim}", osNvim[osName], + "{os_rustup}", osRustup[osName], + "{arch_rustup}", archRustup[archName], + "{os_trivy}", osTrivy[osName], + "{arch_trivy}", archTrivy[archName], + "{arch_gitleaks}", archGitleaks[archName], ) return r.Replace(template) } diff --git a/packages.go b/packages.go index ac1e3e0..a3adf44 100644 --- a/packages.go +++ b/packages.go @@ -10,10 +10,11 @@ package main // URL templates use the substitutions described in formatURL. var SystemPackages = []string{ + "age", "ansible", "ansible-core", "aria2", - "bashtop", + "btm", "build-essential", "buildah", "containerd.io", @@ -29,10 +30,14 @@ var SystemPackages = []string{ "git", "github-desktop", "google-chrome-stable", + "helm", + "jq", + "kubectl", "lazygit", "lua", "minisign", "minikube", + "nmap", "obs-studio", "obsidian", "pipx", @@ -42,8 +47,11 @@ var SystemPackages = []string{ "qemu", "restic", "rg", + "semgrep", "shutter", + "sops", "temurin-25-jdk", + "tmux", "vagrant", "virt-manager", "vivaldi-stable", @@ -52,6 +60,8 @@ var SystemPackages = []string{ "yt-dlp", "zoom", "zsh", + "fzf", + "fd", "bzip2", "bzip2-devel", "curl", @@ -142,5 +152,36 @@ func customPackages() []CustomPackage { {Name: "copilot"}, {Name: "playwright"}, {Name: "gh-repo-bootstrap"}, + { + Name: "rustup", + Version: "latest", + URLTemplate: "https://static.rust-lang.org/rustup/dist/{arch_rustup}-{os_rustup}/rustup-init", + }, + { + Name: "yq", + Version: "4.44.1", + URLTemplate: "https://github.com/mikefarah/yq/releases/download/v{version}/yq_{os_go}_{arch_go}", + FetchLatest: "yq", + }, + { + Name: "dagger", + Version: "0.11.4", + URLTemplate: "https://github.com/dagger/dagger/releases/download/v{version}/dagger_v{version}_{os_go}_{arch_go}.tar.gz", + }, + { + Name: "trivy", + Version: "0.52.0", + URLTemplate: "https://github.com/aquasecurity/trivy/releases/download/v{version}/trivy_{version}_{os_trivy}-{arch_trivy}.tar.gz", + }, + { + Name: "cosign", + Version: "2.2.4", + URLTemplate: "https://github.com/sigstore/cosign/releases/download/v{version}/cosign-{os_go}-{arch_go}", + }, + { + Name: "gitleaks", + Version: "8.18.2", + URLTemplate: "https://github.com/gitleaks/gitleaks/releases/download/v{version}/gitleaks_{version}_{os_go}_{arch_gitleaks}.tar.gz", + }, } } diff --git a/pkgmgr.go b/pkgmgr.go index 3b840a3..6f4fcb2 100644 --- a/pkgmgr.go +++ b/pkgmgr.go @@ -77,6 +77,7 @@ var packageOverrides = map[string]map[string]overrideEntry{ "webcamoid": skipOverride(), }, "apt-get": { + "fd": replace("fd-find"), "ffmpeg-free": replace("ffmpeg"), "lua": replace("lua5.4"), "qemu": replace("qemu-system"), @@ -165,7 +166,6 @@ var packageOverrides = map[string]map[string]overrideEntry{ "rg": replace("ripgrep"), "temurin-25-jdk": replace("temurin"), "vivaldi-stable": replace("vivaldi"), - "bashtop": replace("btop"), "buildah": skipOverride(), "shutter": skipOverride(), "virt-manager": skipOverride(), @@ -223,6 +223,25 @@ func resolveSystemPkgs(names []string) ([]string, []string) { return resolved, skipped } +func installSystemPackages(pkgs []string, special []string) { + if len(special) > 0 { + tmp, err := os.MkdirTemp("", "bootstrap-special-") + if err != nil { + errLog(fmt.Sprintf("could not create temp dir for special packages: %v", err)) + return + } + defer osRemoveAll(tmp) + for _, pkg := range special { + fmt.Printf("\n [SPECIAL] Installing %s ...\n", pkg) + installSpecialPkg(pkg, tmp) + } + } + + if pkgMgr == "apt-get" && hasCmd("fdfind") { + runCmd([]string{"ln", "-sf", "/usr/bin/fdfind", "/usr/local/bin/fd"}, CmdOpts{AsSudo: true}) + } +} + // isSystemPkgInstalled queries the host package manager. func isSystemPkgInstalled(pkg string) bool { switch pkgMgr { diff --git a/system.go b/system.go index 16e950c..7b2e356 100644 --- a/system.go +++ b/system.go @@ -16,8 +16,8 @@ func specialPkgs() map[string]bool { } return map[string]bool{ "github-desktop": true, "zoom": true, "obsidian": true, - "minikube": true, "bashtop": true, "pipx": true, - "poetry": true, "pulumi": true, + "minikube": true, "pipx": true, + "poetry": true, "pulumi": true, "semgrep": true, } } @@ -44,14 +44,14 @@ func isSpecialPkgInstalled(pkg string) bool { return exists("/usr/local/bin/obsidian") case "minikube": return exists("/usr/local/bin/minikube") || hasCmd("minikube") - case "bashtop": - return exists("/usr/local/bin/bashtop") || exists(filepath.Join(home, "bashtop")) case "pulumi": return exists("/opt/pulumi/pulumi") || hasCmd("pulumi") case "pipx": return hasCmd("pipx") case "poetry": return hasCmd("poetry") + case "semgrep": + return hasCmd("semgrep") } return isSystemPkgInstalled(pkg) } @@ -259,30 +259,6 @@ func installMinikube(tmp string) { fmt.Printf(" minikube installed to %s\n", installPath) } -func installBashtop(_ string) { - home, _ := os.UserHomeDir() - cloneDir := filepath.Join(home, "bashtop") - if _, err := osStat(cloneDir); err == nil { - fmt.Printf(" Updating existing clone at %s ...\n", cloneDir) - if !runCmd([]string{"git", "-C", cloneDir, "pull"}, CmdOpts{}).OK() { - errLog("bashtop git pull failed") - return - } - } else { - fmt.Printf(" Cloning bashtop to %s ...\n", cloneDir) - if !runCmd([]string{"git", "clone", "https://github.com/aristocratos/bashtop.git", cloneDir}, CmdOpts{}).OK() { - errLog("bashtop git clone failed") - return - } - } - if !runCmd([]string{"make", "install"}, CmdOpts{AsSudo: true, Cwd: cloneDir}).OK() { - errLog("bashtop 'make install' failed") - return - } - appendProfileLine("bashtop", "export PATH=$PATH:"+cloneDir) - fmt.Printf(" bashtop installed. Clone at %s, binary at /usr/local/bin/bashtop\n", cloneDir) -} - func installPulumi(tmp string) { version := fetchText("https://www.pulumi.com/latest-version") if version == "" { @@ -356,6 +332,14 @@ func installPoetry(_ string) { runCmd([]string{"pipx", "install", "poetry"}, CmdOpts{}) } +func installSemgrep(_ string) { + if !hasCmd("pipx") { + errLog("pipx is not installed — cannot install semgrep") + return + } + runCmd([]string{"pipx", "install", "semgrep"}, CmdOpts{}) +} + func installSpecialPkg(pkg, tmp string) { switch pkg { case "github-desktop": @@ -366,14 +350,14 @@ func installSpecialPkg(pkg, tmp string) { installObsidian(tmp) case "minikube": installMinikube(tmp) - case "bashtop": - installBashtop(tmp) case "pulumi": installPulumi(tmp) case "pipx": installPipx(tmp) case "poetry": installPoetry(tmp) + case "semgrep": + installSemgrep(tmp) } } From 430b018aa96a0f792d7bc3bdea3f49a9b6d0c394 Mon Sep 17 00:00:00 2001 From: Jason Ross Date: Fri, 29 May 2026 18:09:30 -0500 Subject: [PATCH 2/4] fixes for bashtop removal --- coverage2_test.go | 50 ----------------------------------------------- pkgmgr.go | 19 ------------------ system.go | 5 ++++- system_test.go | 25 ------------------------ 4 files changed, 4 insertions(+), 95 deletions(-) diff --git a/coverage2_test.go b/coverage2_test.go index fb5d30b..0e5a347 100644 --- a/coverage2_test.go +++ b/coverage2_test.go @@ -610,57 +610,7 @@ func TestSetupVivaldiRepoExisting(t *testing.T) { // ── special installer branches ────────────────────────────────────────── -func TestInstallBashtopExistingClone(t *testing.T) { - defer resetMocks() - osStat = func(_ string) (os.FileInfo, error) { return nil, nil } - runCmd = func(_ []string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} } - installBashtop("/tmp") -} -func TestInstallBashtopPullFails(t *testing.T) { - defer resetMocks() - osStat = func(_ string) (os.FileInfo, error) { return nil, nil } - runCmd = func(argv []string, _ CmdOpts) CmdResult { - if len(argv) > 1 && argv[1] == "-C" { - return CmdResult{ExitCode: 1} - } - return CmdResult{ExitCode: 0} - } - installBashtop("/tmp") - if !hasIssueContaining("bashtop git pull failed") { - t.Error("expected pull failure error") - } -} - -func TestInstallBashtopCloneFails(t *testing.T) { - defer resetMocks() - osStat = func(_ string) (os.FileInfo, error) { return nil, os.ErrNotExist } - runCmd = func(argv []string, _ CmdOpts) CmdResult { - if argv[0] == "git" && argv[1] == "clone" { - return CmdResult{ExitCode: 1} - } - return CmdResult{ExitCode: 0} - } - installBashtop("/tmp") - if !hasIssueContaining("bashtop git clone failed") { - t.Error("expected clone failure error") - } -} - -func TestInstallBashtopMakeFails(t *testing.T) { - defer resetMocks() - osStat = func(_ string) (os.FileInfo, error) { return nil, os.ErrNotExist } - runCmd = func(argv []string, _ CmdOpts) CmdResult { - if argv[0] == "make" { - return CmdResult{ExitCode: 1} - } - return CmdResult{ExitCode: 0} - } - installBashtop("/tmp") - if !hasIssueContaining("make install") { - t.Error("expected make install failure error") - } -} func TestInstallPulumiNoVersion(t *testing.T) { defer resetMocks() diff --git a/pkgmgr.go b/pkgmgr.go index 6f4fcb2..ff26072 100644 --- a/pkgmgr.go +++ b/pkgmgr.go @@ -223,25 +223,6 @@ func resolveSystemPkgs(names []string) ([]string, []string) { return resolved, skipped } -func installSystemPackages(pkgs []string, special []string) { - if len(special) > 0 { - tmp, err := os.MkdirTemp("", "bootstrap-special-") - if err != nil { - errLog(fmt.Sprintf("could not create temp dir for special packages: %v", err)) - return - } - defer osRemoveAll(tmp) - for _, pkg := range special { - fmt.Printf("\n [SPECIAL] Installing %s ...\n", pkg) - installSpecialPkg(pkg, tmp) - } - } - - if pkgMgr == "apt-get" && hasCmd("fdfind") { - runCmd([]string{"ln", "-sf", "/usr/bin/fdfind", "/usr/local/bin/fd"}, CmdOpts{AsSudo: true}) - } -} - // isSystemPkgInstalled queries the host package manager. func isSystemPkgInstalled(pkg string) bool { switch pkgMgr { diff --git a/system.go b/system.go index 7b2e356..eb5516f 100644 --- a/system.go +++ b/system.go @@ -37,7 +37,6 @@ var guiSystemPkgs = map[string]bool{ } func isSpecialPkgInstalled(pkg string) bool { - home, _ := os.UserHomeDir() exists := func(p string) bool { _, err := osStat(p); return err == nil } switch pkg { case "obsidian": @@ -497,6 +496,10 @@ func installSystemPackages(regular, special []string) { installSpecialPkg(pkg, tmp) } } + + if pkgMgr == "apt-get" && hasCmd("fdfind") { + runCmd([]string{"ln", "-sf", "/usr/bin/fdfind", "/usr/local/bin/fd"}, CmdOpts{AsSudo: true}) + } } // appendProfileLine adds a PATH/env line to a system-wide login-shell profile, diff --git a/system_test.go b/system_test.go index 7bc76c1..fc9b460 100644 --- a/system_test.go +++ b/system_test.go @@ -247,32 +247,7 @@ func TestInstallMinikube(t *testing.T) { } } -func TestInstallBashtop(t *testing.T) { - defer resetMocks() - - osStat = func(name string) (os.FileInfo, error) { - return nil, os.ErrNotExist // Not cloned yet - } - var runCmdCalls [][]string - runCmd = func(argv []string, opts CmdOpts) CmdResult { - runCmdCalls = append(runCmdCalls, argv) - return CmdResult{ExitCode: 0} - } - - installBashtop("/tmp") - - // Git clone and make install should be run - if len(runCmdCalls) < 2 { - t.Fatalf("expected git clone and make install, got calls: %v", runCmdCalls) - } - if runCmdCalls[0][1] != "clone" { - t.Errorf("expected git clone, got %v", runCmdCalls[0]) - } - if runCmdCalls[1][0] != "make" || runCmdCalls[1][1] != "install" { - t.Errorf("expected make install, got %v", runCmdCalls[1]) - } -} func TestInstallPulumi(t *testing.T) { defer resetMocks() From 83c929c24be7791e20917d0019ab39ac9f19e1d1 Mon Sep 17 00:00:00 2001 From: Jason Ross Date: Sat, 30 May 2026 17:08:14 -0500 Subject: [PATCH 3/4] test and logic fixes --- ci/main.go | 8 +++++--- coverage2_test.go | 1 + coverage3_test.go | 4 ++++ custom.go | 15 +++++++++------ helper_test.go | 9 +++++++-- macos.go | 18 ++++++++++++++++-- main.go | 6 +++++- packages.go | 2 +- pkgmgr_test.go | 8 ++++---- system.go | 4 ++-- system_test.go | 13 ++----------- 11 files changed, 56 insertions(+), 32 deletions(-) diff --git a/ci/main.go b/ci/main.go index b57500f..9bf9961 100644 --- a/ci/main.go +++ b/ci/main.go @@ -34,7 +34,7 @@ func main() { From("golang:1.25"). WithMountedDirectory("/src", src). WithWorkdir("/src"). - WithExec([]string{"go", "build", "-o", "bootstrap_environment", "."}) + WithExec([]string{"go", "build", "-buildvcs=false", "-o", "bootstrap_environment", "."}) binaryFile := builder.File("bootstrap_environment") @@ -105,7 +105,8 @@ func main() { fmt.Printf("[%s] Verifying package installations on PATH and running version checks...\n", target) verifyCmd := []string{ "sh", "-c", - "export PATH=$PATH:/usr/local/go/bin; " + + "set -e -x; " + + "export PATH=$PATH:/usr/local/go/bin:/usr/local/bin; " + "which go && go version && " + "which nvim && nvim --version && " + "which zig && zig version && " + @@ -113,7 +114,8 @@ func main() { } verifyOutput, err := testContainer.WithExec(verifyCmd).Stdout(ctx) if err != nil { - return fmt.Errorf("verification failed on %s: package not found on PATH or exited with error (%w)", target, err) + // To see the stdout/stderr of the failing command, we can try to extract it from dagger's ExecError + return fmt.Errorf("verification failed on %s: %v", target, err) } diff --git a/coverage2_test.go b/coverage2_test.go index 0e5a347..082aef5 100644 --- a/coverage2_test.go +++ b/coverage2_test.go @@ -260,6 +260,7 @@ func TestPromptGitHubTokenAcceptThenEmpty(t *testing.T) { // password read can't be cleanly mocked. stdin = strings.NewReader("y\n") defer func() { stdin = os.Stdin }() + readPassword = func() ([]byte, error) { return nil, errors.New("mocked error") } captureStdout(t, func() { // term.ReadPassword on a non-terminal returns an error, // landing in the "could not read token" warn branch. diff --git a/coverage3_test.go b/coverage3_test.go index 1e5a61f..9733bcb 100644 --- a/coverage3_test.go +++ b/coverage3_test.go @@ -229,6 +229,7 @@ func TestEnsureHomebrewInstallerFails(t *testing.T) { defer resetMocks() isMacOS = true hasCmd = func(_ string) bool { return false } + runCmd = func(_ []string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} } runShell = func(_ string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 1} } called := false osExit = func(_ int) { called = true } @@ -245,6 +246,7 @@ func TestEnsureHomebrewBrewNotAtExpectedPath(t *testing.T) { isMacOS = true archName = "x86_64" hasCmd = func(_ string) bool { return false } + runCmd = func(_ []string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} } runShell = func(_ string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} } osStat = func(_ string) (os.FileInfo, error) { return nil, os.ErrNotExist } called := false @@ -614,6 +616,8 @@ func TestRunMainHasErrorsExitsOne(t *testing.T) { hasCmd = func(_ string) bool { return true } runCmd = func(_ []string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} } runShell = func(_ string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 1} } // install fails + fetchJSON = func(_ string, _ any) bool { return false } + download = func(_, dest string) bool { return true } stdin = strings.NewReader("y\n") defer func() { stdin = os.Stdin }() exitCode := -1 diff --git a/custom.go b/custom.go index 738b909..8b6e4b7 100644 --- a/custom.go +++ b/custom.go @@ -53,10 +53,16 @@ var defaultInstallPaths = map[string]string{ "zig": "/usr/local/bin/zig", "nvm": "~/.nvm", "pyenv": "~/.pyenv", - "neovim": "/usr/local/bin/nvim", - "oh-my-zsh": "~/.oh-my-zsh", + "neovim": "/usr/local/bin/nvim", + "oh-my-zsh": "~/.oh-my-zsh", "agy": "~/.local/bin/agy", "gh-repo-bootstrap": "~/.local/share/gh/extensions/gh-repo-bootstrap", + "yq": "/usr/local/bin/yq", + "rustup": "~/.cargo/bin/rustup", + "dagger": "/usr/local/bin/dagger", + "trivy": "/usr/local/bin/trivy", + "cosign": "/usr/local/bin/cosign", + "gitleaks": "/usr/local/bin/gitleaks", } func expandHome(p string) string { @@ -224,10 +230,7 @@ func installFirecracker(archive, tmp string) { if strings.HasSuffix(name, ".tgz") || strings.HasSuffix(name, ".tar.gz") { return nil } - if !strings.HasPrefix(name, "firecracker") { - return nil - } - if strings.HasSuffix(name, ".debug") || strings.Contains(name, "debug") { + if !strings.HasPrefix(name, "firecracker-v") || strings.Contains(name, "debug") { return nil } if binary == "" { diff --git a/helper_test.go b/helper_test.go index db6bdb9..857f22f 100644 --- a/helper_test.go +++ b/helper_test.go @@ -1,7 +1,9 @@ package main import ( + "errors" "os" + "strings" ) func resetMocks() { @@ -20,9 +22,12 @@ func resetMocks() { osMkdirAll = os.MkdirAll osRemove = os.Remove osRemoveAll = os.RemoveAll - osRename = os.Rename osExit = os.Exit - stdin = os.Stdin + // Default to an empty reader so un-mocked tests don't hang waiting for user input. + stdin = strings.NewReader("") + readPassword = func() ([]byte, error) { + return nil, errors.New("terminal blocked in test") + } // Reset global state variables to safe defaults isMacOS = false diff --git a/macos.go b/macos.go index 1b1166c..94168ad 100644 --- a/macos.go +++ b/macos.go @@ -380,7 +380,14 @@ func waitForVMSSH(privKey string, timeout time.Duration) bool { fmt.Println(" VM SSH ready.") return true } - time.Sleep(5 * time.Second) + rem := time.Until(deadline) + if rem <= 0 { + break + } + if rem > 5*time.Second { + rem = 5 * time.Second + } + time.Sleep(rem) } return false } @@ -393,7 +400,14 @@ func waitForFirecrackerInVM(privKey string, timeout time.Duration) bool { fmt.Println(" firecracker is installed inside the VM.") return true } - time.Sleep(10 * time.Second) + rem := time.Until(deadline) + if rem <= 0 { + break + } + if rem > 10*time.Second { + rem = 10 * time.Second + } + time.Sleep(rem) } return false } diff --git a/main.go b/main.go index dbe3f48..de79f49 100644 --- a/main.go +++ b/main.go @@ -179,6 +179,10 @@ func runMain(args []string) { } } +var readPassword = func() ([]byte, error) { + return term.ReadPassword(int(os.Stdin.Fd())) +} + // promptGitHubToken asks the user if they want to supply a GitHub token // after they've authenticated sudo. With a token, our HTTP-bound worker // pool uncaps from the conservative 8-worker default up to runtime.NumCPU(), @@ -196,7 +200,7 @@ func promptGitHubToken() { return } fmt.Print(" Paste token (input hidden): ") - tokenBytes, err := term.ReadPassword(int(os.Stdin.Fd())) + tokenBytes, err := readPassword() fmt.Println() if err != nil { warn(fmt.Sprintf("could not read token: %v — continuing without uncap", err)) diff --git a/packages.go b/packages.go index a3adf44..284a0f2 100644 --- a/packages.go +++ b/packages.go @@ -170,7 +170,7 @@ func customPackages() []CustomPackage { }, { Name: "trivy", - Version: "0.52.0", + Version: "0.70.0", URLTemplate: "https://github.com/aquasecurity/trivy/releases/download/v{version}/trivy_{version}_{os_trivy}-{arch_trivy}.tar.gz", }, { diff --git a/pkgmgr_test.go b/pkgmgr_test.go index aaf8d58..4a9112d 100644 --- a/pkgmgr_test.go +++ b/pkgmgr_test.go @@ -85,11 +85,11 @@ func TestResolveSystemPkgs(t *testing.T) { t.Errorf("expected skipped to be [docker-compose], got %v", skipped) } - // brew: bashtop -> btop (replace), buildah -> skipped + // brew: buildah -> skipped pkgMgr = "brew" - resolved, skipped = resolveSystemPkgs([]string{"bashtop", "buildah", "rg"}) - if len(resolved) != 2 || resolved[0] != "btop" || resolved[1] != "ripgrep" { - t.Errorf("expected [btop ripgrep], got %v", resolved) + resolved, skipped = resolveSystemPkgs([]string{"buildah", "rg"}) + if len(resolved) != 1 || resolved[0] != "ripgrep" { + t.Errorf("expected [ripgrep], got %v", resolved) } // pacman: docker-ce-rootless-extras and vagrant are AUR-only -> skipped; // pipx is replaced with python-pipx. diff --git a/system.go b/system.go index eb5516f..4705ae0 100644 --- a/system.go +++ b/system.go @@ -461,7 +461,7 @@ func installSystemPackages(regular, special []string) { if pkgMgr == "brew" { failed := pkgInstallMany(regular) for _, p := range failed { - errLog(fmt.Sprintf("System package failed to install: %s", p)) + taskPrintf(" [WARN] System package failed to install: %s\n", p) } // No special packages on macOS — brew covers all of them. return @@ -481,7 +481,7 @@ func installSystemPackages(regular, special []string) { failed := pkgInstallMany(regular) for _, p := range failed { - errLog(fmt.Sprintf("System package failed to install: %s", p)) + taskPrintf(" [WARN] System package failed to install: %s\n", p) } if len(special) > 0 { diff --git a/system_test.go b/system_test.go index fc9b460..e132449 100644 --- a/system_test.go +++ b/system_test.go @@ -377,9 +377,7 @@ func TestSystemGoEdgeCases(t *testing.T) { } hasCmd = func(name string) bool { return true } - if !isSpecialPkgInstalled("bashtop") { - t.Error("expected bashtop to be installed") - } + if !isSpecialPkgInstalled("pulumi") { t.Error("expected pulumi to be installed") } @@ -445,14 +443,7 @@ func TestSystemGoEdgeCases(t *testing.T) { fetchText = func(url string) string { return "mismatch-checksum minikube" } installSpecialPkg("minikube", "/tmp") - resetMocks() - osStat = func(name string) (os.FileInfo, error) { - return nil, nil - } - runCmd = func(argv []string, opts CmdOpts) CmdResult { - return CmdResult{ExitCode: 1} - } - installSpecialPkg("bashtop", "/tmp") + resetMocks() fetchText = func(url string) string { return "some-sha pulumi-v3.90.0-linux-x64.tar.gz" } From 206b4cb999eac1e6db349de859066a142b2b00bc Mon Sep 17 00:00:00 2001 From: Jason Ross Date: Sat, 30 May 2026 17:19:10 -0500 Subject: [PATCH 4/4] fixed unit tests --- coverage2_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coverage2_test.go b/coverage2_test.go index 082aef5..0a175bf 100644 --- a/coverage2_test.go +++ b/coverage2_test.go @@ -314,11 +314,11 @@ func TestInstallSystemPackagesBrewFailures(t *testing.T) { defer resetMocks() pkgMgr = "brew" runCmd = func(_ []string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 1} } - captureStdout(t, func() { + out := captureStdout(t, func() { installSystemPackages([]string{"git"}, nil) }) - if !hasIssueContaining("System package failed to install") { - t.Error("expected error logged for brew failure") + if !strings.Contains(out, "System package failed to install") { + t.Error("expected warning logged for brew failure") } }