diff --git a/envbuilder.go b/envbuilder.go index a6dc36af..94a6ecc4 100644 --- a/envbuilder.go +++ b/envbuilder.go @@ -477,14 +477,19 @@ func Run(ctx context.Context, options Options) error { if val, ok := os.LookupEnv("KANIKO_REGISTRY_MIRROR"); ok { registryMirror = strings.Split(val, ";") } - image, err := executor.DoBuild(&config.KanikoOptions{ + var destinations []string + if options.CacheRepo != "" { + destinations = append(destinations, options.CacheRepo) + } + opts := &config.KanikoOptions{ // Boilerplate! CustomPlatform: platforms.Format(platforms.Normalize(platforms.DefaultSpec())), SnapshotMode: "redo", RunV2: true, RunStdout: stdoutWriter, RunStderr: stderrWriter, - Destinations: []string{"local"}, + Destinations: destinations, + NoPush: len(destinations) == 0, CacheRunLayers: true, CacheCopyLayers: true, CompressedCaching: true, @@ -515,11 +520,16 @@ func Run(ctx context.Context, options Options) error { RegistryMirrors: registryMirror, }, SrcContext: buildParams.BuildContext, - }) + } + image, err := executor.DoBuild(opts) if err != nil { - return nil, err + return nil, xerrors.Errorf("do build: %w", err) + } + if err := executor.DoPush(image, opts); err != nil { + return nil, xerrors.Errorf("do push: %w", err) } endStage("🏗️ Built image!") + return image, err } diff --git a/integration/integration_test.go b/integration/integration_test.go index 8733788d..8456139c 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -34,6 +34,8 @@ import ( "github.com/go-git/go-billy/v5/memfs" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/registry" + "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -987,6 +989,84 @@ COPY %s .`, testImageAlpine, inclFile) } } +func TestPushImage(t *testing.T) { + t.Parallel() + + t.Run("Push", func(t *testing.T) { + t.Parallel() + + srv := createGitServer(t, gitServerOptions{ + files: map[string]string{ + ".devcontainer/Dockerfile": fmt.Sprintf("FROM %s\nRUN date --utc > /root/date.txt", testImageAlpine), + ".devcontainer/devcontainer.json": `{ + "name": "Test", + "build": { + "dockerfile": "Dockerfile" + }, + }`, + }, + }) + + // Given: an empty registry + testReg := setupInMemoryRegistry(t) + testRepo := testReg + "/test" + ref, err := name.ParseReference(testRepo + ":latest") + require.NoError(t, err) + _, err = remote.Image(ref) + require.ErrorContains(t, err, "NAME_UNKNOWN", "expected image to not be present before build + push") + + // When: we run envbuilder with PUSH_IMAGE set + _, err = runEnvbuilder(t, options{env: []string{ + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("CACHE_REPO", testRepo), + }}) + require.NoError(t, err) + + // Then: the image should be pushed + _, err = remote.Image(ref) + require.NoError(t, err, "expected image to be present after build + push") + }) + + t.Run("PushErr", func(t *testing.T) { + t.Parallel() + + srv := createGitServer(t, gitServerOptions{ + files: map[string]string{ + ".devcontainer/Dockerfile": fmt.Sprintf("FROM %s\nRUN date --utc > /root/date.txt", testImageAlpine), + ".devcontainer/devcontainer.json": `{ + "name": "Test", + "build": { + "dockerfile": "Dockerfile" + }, + }`, + }, + }) + + // Given: registry is not set up (in this case, not a registry) + notRegSrv := httptest.NewServer(http.NotFoundHandler()) + notRegURL := strings.TrimPrefix(notRegSrv.URL, "http://") + "/test" + + // When: we run envbuilder with PUSH_IMAGE set + _, err := runEnvbuilder(t, options{env: []string{ + envbuilderEnv("GIT_URL", srv.URL), + envbuilderEnv("CACHE_REPO", notRegURL), + }}) + // Then: should fail with a descriptive error + require.ErrorContains(t, err, "unexpected status code 404 Not Found") + }) +} + +func setupInMemoryRegistry(t *testing.T) string { + t.Helper() + tempDir := t.TempDir() + testReg := registry.New(registry.WithBlobHandler(registry.NewDiskBlobHandler(tempDir))) + regSrv := httptest.NewServer(testReg) + t.Cleanup(func() { regSrv.Close() }) + regSrvURL, err := url.Parse(regSrv.URL) + require.NoError(t, err) + return fmt.Sprintf("localhost:%s", regSrvURL.Port()) +} + // TestMain runs before all tests to build the envbuilder image. func TestMain(m *testing.M) { checkTestRegistry()