Skip to content

fix(build): always give go-dockerclient an output stream - #193

Merged
kcq merged 2 commits into
mintoolkit:masterfrom
Eljees:fix/87-missing-output-stream
Sep 12, 2026
Merged

kcq merged 2 commits into
mintoolkit:masterfrom
Eljees:fix/87-missing-output-stream

Conversation

@Eljees

@Eljees Eljees commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Fixes #87.

mint build (and mint slim building a fat image first) without --show-blogs failed immediately with missing output stream. dockercrtclient.BuildImage only set buildOptions.OutputStream when showBuildLogs was requested, but the vendored fsouza/go-dockerclient rejects a nil OutputStream unconditionally (image.go, ErrMissingOutputStream) — before a single request reaches the daemon.

The fix always buffers the build output into ref.buildLog, which is exactly the pattern slimbuilder.go already uses for the slim-image build path; --show-blogs keeps controlling only whether that buffer is printed afterwards.

The regression test drives the real BuildImage against the local Docker daemon (auto-skipped when no daemon is reachable) with the same options shape buildFatImage constructs — a nil OutputStream and showBuildLogs=false. Without the fix it fails with the exact error from the issue; with it, the image builds and the build log is captured. Verified red→green against a live daemon; go vet and gofmt are clean.

The vendored fsouza/go-dockerclient rejects BuildImage calls with a nil
OutputStream before sending anything to the daemon, but dockercrtclient
only set one when showBuildLogs was requested. A plain 'mint build'
without --show-blogs therefore failed immediately with 'missing output
stream'. Always buffer the build output into ref.buildLog, the same
pattern slimbuilder.go already uses.

The regression test drives the real BuildImage against the local Docker
daemon (skipped when no daemon is reachable) with exactly the options
shape buildFatImage constructs.

Fixes mintoolkit#87

Signed-off-by: Eljees <3.14hell@gmail.com>
@kilo-code-bot

kilo-code-bot Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (2 files)
  • pkg/crt/docker/dockercrtclient/dockercrtclient.go
  • pkg/crt/docker/dockercrtclient/repro_test.go

Reviewed by free · Input: 28.8K · Output: 5.2K · Cached: 143.1K

@kcq

kcq commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

thanks for the PR @Eljees !

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new integration test has unsafe cleanup/tagging behavior and the build-log buffer can return stale logs when a caller provides a custom OutputStream.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR fixes mint build / mint slim (fat-image build path) failing immediately with missing output stream when --show-blogs is not used, by ensuring go-dockerclient.BuildImage() always receives a non-nil OutputStream. It also adds a regression test to reproduce and prevent issue #87.

Changes:

  • Always provide a non-nil BuildImageOptions.OutputStream by buffering output into ref.buildLog when the caller doesn’t supply a stream.
  • Add a live Docker daemon regression test reproducing #87 and asserting the build log is captured.
File summaries
File Description
pkg/crt/docker/dockercrtclient/dockercrtclient.go Ensures Docker build output always has a destination stream to avoid ErrMissingOutputStream.
pkg/crt/docker/dockercrtclient/repro_test.go Adds a regression test exercising the nil-OutputStream + showBuildLogs=false scenario against a real Docker daemon.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 107 to 116
if options.OutputStream != nil {
buildOptions.OutputStream = options.OutputStream
} else if ref.showBuildLogs {
} else {
// go-dockerclient's BuildImage() unconditionally rejects a nil
// OutputStream (see vendor/github.com/fsouza/go-dockerclient/image.go,
// ErrMissingOutputStream), so a destination must always be provided,
// not just when showBuildLogs is requested (mintoolkit/mint#87).
ref.buildLog.Reset()
buildOptions.OutputStream = &ref.buildLog
}
Comment on lines +30 to +51
dir := t.TempDir()
dockerfile := "FROM scratch\nCOPY hello.txt /hello.txt\n"
if err := os.WriteFile(filepath.Join(dir, "Dockerfile"), []byte(dockerfile), 0o644); err != nil {
t.Fatalf("write Dockerfile: %v", err)
}
if err := os.WriteFile(filepath.Join(dir, "hello.txt"), []byte("hello\n"), 0o644); err != nil {
t.Fatalf("write hello.txt: %v", err)
}

b := NewBuilder(client, false) // showBuildLogs=false, matches the user's repro command (no --show-blogs)

opts := imagebuilder.DockerfileBuildOptions{
Dockerfile: "Dockerfile",
BuildContext: dir,
ImagePath: "el-mint87-repro:latest",
// OutputStream intentionally left nil - this is the exact shape
// buildFatImage() in pkg/app/master/command/build/image.go constructs.
}

buildErr := b.BuildImage(opts)
defer client.RemoveImage("el-mint87-repro:latest")

Comment on lines +21 to +29
func TestBuildImage_MissingOutputStream_Issue87(t *testing.T) {
client, err := docker.NewClientFromEnv()
if err != nil {
t.Fatalf("docker.NewClientFromEnv: %v", err)
}
if err := client.Ping(); err != nil {
t.Skipf("no docker daemon reachable, skipping live repro: %v", err)
}

Reset ref.buildLog unconditionally so BuildOutputLog() always reflects the
current build, including when the caller supplies its own OutputStream --
previously the buffer was only reset on the showBuildLogs path, so a
custom-stream build left the previous build's output readable.

The regression test now builds a uniquely named image and removes it only
when it actually created one, so it cannot touch a pre-existing image or
collide with a parallel run.

Signed-off-by: Eljees <3.14hell@gmail.com>
@Eljees

Eljees commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @kcq, and thanks to the reviewer bot — both points were worth acting on. Pushed 7d1b2f7:

Stale build log. Correct observation, and it turned out to predate this PR: on master the buffer is only reset inside the showBuildLogs branch, so a build that supplied its own OutputStream left the previous build's output readable through BuildOutputLog(). The revision moves ref.buildLog.Reset() above the branch, so it now runs on every build regardless of which stream is used — that closes the pre-existing case as well as any new one.

Test hygiene. Fixed: the image tag is now unique per run (mint-issue87-repro-<nanos>:test), so the test can never overwrite or delete an image that already exists on the machine and can't collide with a parallel run; cleanup moved to t.Cleanup and only registered when the build actually created an image. The daemon-less path now skips instead of failing at client construction too.

Re-verified after the change against a live daemon: the test passes with the fix and still fails with the exact missing output stream error when dockercrtclient.go is reverted to master. go vet and gofmt are clean.

@kcq
kcq merged commit 5ebd420 into mintoolkit:master Sep 12, 2026
1 check failed
@kcq

kcq commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

thanks again for the PR @Eljees

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing output stream

3 participants