Skip to content
Open
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
56 changes: 56 additions & 0 deletions internal/devbox/generate/devcontainer_util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2024 Jetify Inc. and contributors. All rights reserved.
// Use of this source code is governed by the license in the LICENSE file.

package generate

import (
"os"
"path/filepath"
"strings"
"testing"
)

// TestCreateDockerfileDevOmitsNixStoreOptimise ensures the generated dev
// Dockerfile does not run `nix-store --optimise`. That command fails on Docker's
// default overlay2 storage driver ("cannot rename: Stale file handle") and
// breaks the image build, while providing negligible size savings in a freshly
// built image. Regression test for
// https://github.com/jetify-com/devbox/issues/2616.
func TestCreateDockerfileDevOmitsNixStoreOptimise(t *testing.T) {
for _, rootUser := range []bool{false, true} {
dockerfile := generateDevDockerfile(t, rootUser)

if strings.Contains(dockerfile, "nix-store --optimise") {
t.Errorf("generated dev Dockerfile (rootUser=%t) should not run "+
"`nix-store --optimise`; it breaks Docker builds on overlay2.\n%s",
rootUser, dockerfile)
}
// The garbage-collection pass is still wanted: it removes build-time
// dependencies and is what actually shrinks the image.
if !strings.Contains(dockerfile, "nix-store --gc") {
t.Errorf("generated dev Dockerfile (rootUser=%t) should still run "+
"`nix-store --gc`.\n%s", rootUser, dockerfile)
}
}
}

func generateDevDockerfile(t *testing.T, rootUser bool) string {
t.Helper()

dir := t.TempDir()
g := &Options{
Path: dir,
RootUser: rootUser,
}
if err := g.CreateDockerfile(t.Context(), CreateDockerfileOptions{
ForType: "dev",
}); err != nil {
t.Fatalf("CreateDockerfile failed: %v", err)
}

contents, err := os.ReadFile(filepath.Join(dir, "Dockerfile"))
if err != nil {
t.Fatalf("reading generated Dockerfile: %v", err)
}
return string(contents)
}
11 changes: 10 additions & 1 deletion internal/devbox/generate/tmpl/dev.Dockerfile.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,16 @@ COPY devbox.lock devbox.lock
{{range $i, $element := .LocalFlakeDirs -}}
COPY {{$element}} {{$element}}
{{end}}
RUN devbox run -- echo "Installed Packages." && nix-store --gc && nix-store --optimise
{{- /*
We run `nix-store --gc` to drop build-time dependencies and shrink the image,
but intentionally do NOT run `nix-store --optimise`. Optimise hard-links
duplicate files in the Nix store, and on Docker's default overlay2 storage
driver the rename it performs fails with "cannot rename: Stale file handle",
which breaks the image build. In a freshly built, single-purpose image there is
little to no duplication to reclaim anyway, so the optimise pass provides
negligible benefit. See https://github.com/jetify-com/devbox/issues/2616.
*/ -}}
RUN devbox run -- echo "Installed Packages." && nix-store --gc
{{if .IsDevcontainer}}
RUN devbox shellenv --init-hook >> ~/.profile
{{- else}}
Expand Down
Loading