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
12 changes: 9 additions & 3 deletions internal/apt/apt.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,19 @@ func (a *APT) Update(_ context.Context) error {
)
}

// Install runs apt-get install -y for the given packages.
// Does nothing if no packages are provided.
func (a *APT) Install(_ context.Context, packages ...string) error {
// Install runs apt-get update followed by apt-get install -y for the given
// packages. The update ensures the package index is fresh so that superseded
// package versions (e.g. a .4 → .5 point release on the Ubuntu mirrors) do
// not produce 404 errors. Does nothing if no packages are provided.
func (a *APT) Install(ctx context.Context, packages ...string) error {
if len(packages) == 0 {
return nil
}

if err := a.Update(ctx); err != nil {
return fmt.Errorf("apt-get update: %w", err)
}

args := append([]string{"install", "-y"}, packages...)
return a.Runner.RunWithEnv(
map[string]string{"DEBIAN_FRONTEND": "noninteractive"},
Expand Down
8 changes: 6 additions & 2 deletions internal/apt/apt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ func TestInstallPackages(t *testing.T) {
err := a.Install(context.Background(), "pkg1", "pkg2")
require.NoError(t, err)

require.Len(t, f.Calls, 1)
// Install always runs apt-get update first, then apt-get install.
require.Len(t, f.Calls, 2)
assert.Equal(t, "apt-get", f.Calls[0].Name)
assert.Equal(t, []string{"install", "-y", "pkg1", "pkg2"}, f.Calls[0].Args)
assert.Equal(t, []string{"update"}, f.Calls[0].Args)
assert.Equal(t, "noninteractive", f.Calls[0].Env["DEBIAN_FRONTEND"])
assert.Equal(t, "apt-get", f.Calls[1].Name)
assert.Equal(t, []string{"install", "-y", "pkg1", "pkg2"}, f.Calls[1].Args)
assert.Equal(t, "noninteractive", f.Calls[1].Env["DEBIAN_FRONTEND"])
}

func TestInstallNoPackages(t *testing.T) {
Expand Down
62 changes: 45 additions & 17 deletions internal/compiler/compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,30 @@ func TestGCCSetupCflinuxfs4(t *testing.T) {
err := gcc.Setup(context.Background())
require.NoError(t, err)

// Expect: install software-properties-common (from ToolPackages), add-apt-repository, apt-get update,
// install gcc-12 g++-12, update-alternatives
// Expect: update+install software-properties-common (from ToolPackages), add-apt-repository,
// apt-get update (from AddPPA), update+install gcc-12 g++-12, update-alternatives
var callNames []string
for _, c := range f.Calls {
callNames = append(callNames, c.Name)
}

// Should see software-properties-common installed first (from ToolPackages).
require.NotEmpty(t, f.Calls)
firstAptInstall := f.Calls[0]
assert.Equal(t, "apt-get", firstAptInstall.Name)
assert.Contains(t, firstAptInstall.Args, "software-properties-common")
// software-properties-common must be installed before add-apt-repository is called.
var toolInstallIdx, addPPAIdx = -1, -1
for i, c := range f.Calls {
if c.Name == "add-apt-repository" && addPPAIdx < 0 {
addPPAIdx = i
}
if c.Name == "apt-get" && toolInstallIdx < 0 {
for _, arg := range c.Args {
if arg == "software-properties-common" {
toolInstallIdx = i
}
}
}
}
require.True(t, toolInstallIdx >= 0, "software-properties-common not installed")
require.True(t, addPPAIdx >= 0, "add-apt-repository not called")
assert.Less(t, toolInstallIdx, addPPAIdx, "tool packages must be installed before add-apt-repository")

// Should see add-apt-repository (PPA is non-empty).
assert.Contains(t, callNames, "add-apt-repository")
Expand Down Expand Up @@ -83,11 +95,24 @@ func TestGCCSetupCflinuxfs5(t *testing.T) {
err := gcc.Setup(context.Background())
require.NoError(t, err)

// ToolPackages should be installed even when PPA is empty.
// ToolPackages should be installed before the GCC packages, even when PPA is empty.
require.NotEmpty(t, f.Calls)
firstAptInstall := f.Calls[0]
assert.Equal(t, "apt-get", firstAptInstall.Name)
assert.Contains(t, firstAptInstall.Args, "software-properties-common")
var toolInstallIdx2, gccInstallIdx = -1, -1
for i, c := range f.Calls {
if c.Name == "apt-get" {
for _, arg := range c.Args {
if arg == "software-properties-common" && toolInstallIdx2 < 0 {
toolInstallIdx2 = i
}
if arg == "gcc-14" && gccInstallIdx < 0 {
gccInstallIdx = i
}
}
}
}
require.True(t, toolInstallIdx2 >= 0, "software-properties-common not installed")
require.True(t, gccInstallIdx >= 0, "gcc-14 not installed")
assert.Less(t, toolInstallIdx2, gccInstallIdx, "tool packages must be installed before gcc packages")

// Should NOT see add-apt-repository (PPA is empty).
for _, c := range f.Calls {
Expand Down Expand Up @@ -144,11 +169,13 @@ func TestGfortranSetupCflinuxfs4(t *testing.T) {
err := gf.Setup(context.Background())
require.NoError(t, err)

// Should install gfortran packages.
require.Len(t, f.Calls, 1)
// Should install gfortran packages (update + install = 2 calls).
require.Len(t, f.Calls, 2)
assert.Equal(t, "apt-get", f.Calls[0].Name)
assert.Contains(t, f.Calls[0].Args, "gfortran")
assert.Contains(t, f.Calls[0].Args, "libgfortran-12-dev")
assert.Equal(t, []string{"update"}, f.Calls[0].Args)
assert.Equal(t, "apt-get", f.Calls[1].Name)
assert.Contains(t, f.Calls[1].Args, "gfortran")
assert.Contains(t, f.Calls[1].Args, "libgfortran-12-dev")
}

func TestGfortranSetupCflinuxfs5(t *testing.T) {
Expand All @@ -167,8 +194,9 @@ func TestGfortranSetupCflinuxfs5(t *testing.T) {
err := gf.Setup(context.Background())
require.NoError(t, err)

require.Len(t, f.Calls, 1)
assert.Contains(t, f.Calls[0].Args, "libgfortran-13-dev")
require.Len(t, f.Calls, 2)
assert.Equal(t, []string{"update"}, f.Calls[0].Args)
assert.Contains(t, f.Calls[1].Args, "libgfortran-13-dev")
}

func TestGfortranCopyLibsCflinuxfs4(t *testing.T) {
Expand Down