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: 8 additions & 4 deletions internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,12 @@ func (b *Builder) Build(ctx context.Context, cfg BuildConfig) (string, error) {
}

func (b *Builder) buildDockerfile(ctx context.Context, tag, buildDir, contextSub, dockerfile, platform string) error {
// The plain build keeps its historical byte-identical command;
// BuildKit is only forced on for --secret mounts (D4 build_secrets).
cmd := "docker build -t " + ssh.ShellQuote(tag)
// --pull always: a base-image tag must resolve to what the registry
// serves now, never what the build host happens to have cached (a
// stale runner cache once shipped a pre-security-release Node,
// 2026-10-22). BuildKit is only forced on for --secret mounts (D4
// build_secrets).
cmd := "docker build --pull -t " + ssh.ShellQuote(tag)
if len(b.secrets) > 0 {
cmd = "DOCKER_BUILDKIT=1 " + cmd
}
Expand Down Expand Up @@ -261,7 +264,8 @@ func LocalBuild(ctx context.Context, cfg LocalBuildConfig, stdout io.Writer) (st
}

func localBuildDockerfile(ctx context.Context, tag, dir, contextSub, dockerfile, platform string, stdout io.Writer) error {
args := []string{"build", "-t", tag}
// --pull: same stale-base-image rule as the remote build.
args := []string{"build", "--pull", "-t", tag}

// Explicit config, else the shared local-build default (Apple silicon
// cross-compiles for linux/amd64) — one rule for the build and its
Expand Down
6 changes: 3 additions & 3 deletions internal/build/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func TestBuild_Dockerfile(t *testing.T) {
if len(mock.Calls) != 1 {
t.Fatalf("expected 1 call, got %d", len(mock.Calls))
}
if !strings.Contains(mock.Calls[0], "docker build -t 'myapp-build-abc1234' /deployments/myapp/build") {
if !strings.Contains(mock.Calls[0], "docker build --pull -t 'myapp-build-abc1234' /deployments/myapp/build") {
t.Errorf("unexpected command: %s", mock.Calls[0])
}
}
Expand Down Expand Up @@ -496,7 +496,7 @@ func TestBuild_SubdirDockerfileCommand(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
want := "docker build -t 'myapp-build-abc1234' -f '/deployments/myapp/build/server/monolith/Dockerfile' /deployments/myapp/build"
want := "docker build --pull -t 'myapp-build-abc1234' -f '/deployments/myapp/build/server/monolith/Dockerfile' /deployments/myapp/build"
if !strings.Contains(mock.Calls[0], want) {
t.Errorf("unexpected command:\n got: %s\nwant contains: %s", mock.Calls[0], want)
}
Expand Down Expand Up @@ -541,7 +541,7 @@ func TestBuild_DockerfileWithSecrets(t *testing.T) {
}); err != nil {
t.Fatal(err)
}
want := "DOCKER_BUILDKIT=1 docker build -t 'myapp-build-abc1234' --secret id='NPM_TOKEN',src='/run/secrets/NPM_TOKEN' /deployments/myapp/build"
want := "DOCKER_BUILDKIT=1 docker build --pull -t 'myapp-build-abc1234' --secret id='NPM_TOKEN',src='/run/secrets/NPM_TOKEN' /deployments/myapp/build"
if got := exec.Calls[len(exec.Calls)-1]; got != want {
t.Fatalf("build command = %q, want %q", got, want)
}
Expand Down
Loading