Repository: GoogleContainerTools/skaffold
Pull request #7751 (2022-08-05 23:24:55)

docs: add skaffold verify feature maturity
Add `skaffold verify` feature maturity (`beta`) to `https://skaffold-v2.web.app/docs/references/deprecation/`

codecov[bot]: # [Codecov](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) Report
> Merging [#7751](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) (4a12043) into [main](https://codecov.io/gh/GoogleContainerTools/skaffold/commit/290280e8c6c19231e9ed5b5c027f2c01d22f75be?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) (290280e) will **decrease** coverage by `3.86%`.
> The diff coverage is `53.59%`.

> :exclamation: Current head 4a12043 differs from pull request most recent head 0999d86. Consider uploading reports for the commit 0999d86 to get more accurate results

```diff
@@            Coverage Diff             @@
##             main    #7751      +/-   ##
==========================================
- Coverage   70.48%   66.61%   -3.87%     
==========================================
  Files         515      587      +72     
  Lines       23150    28249    +5099     
==========================================
+ Hits        16317    18819    +2502     
- Misses       5776     8050    +2274     
- Partials     1057     1380     +323     
```


| [Impacted Files](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) | Coverage Δ | |
|---|---|---|
| [cmd/skaffold/app/cmd/credits/export.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY3JlZGl0cy9leHBvcnQuZ28=) | `0.00% <0.00%> (ø)` | |
| [cmd/skaffold/app/cmd/deploy.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZGVwbG95Lmdv) | `40.90% <0.00%> (-12.94%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/test.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvdGVzdC5nbw==) | `44.44% <0.00%> (ø)` | |
| [cmd/skaffold/skaffold.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL3NrYWZmb2xkLmdv) | `0.00% <0.00%> (ø)` | |
| [cmd/skaffold/app/cmd/inspect\_tests.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvaW5zcGVjdF90ZXN0cy5nbw==) | `62.50% <14.28%> (-1.14%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/render.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvcmVuZGVyLmdv) | `35.48% <18.18%> (-5.90%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/lsp.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvbHNwLmdv) | `28.12% <28.12%> (ø)` | |
| [cmd/skaffold/app/cmd/run.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvcnVuLmdv) | `64.28% <33.33%> (-9.63%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/fix.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZml4Lmdv) | `56.41% <37.50%> (-20.07%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/verify.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvdmVyaWZ5Lmdv) | `41.17% <41.17%> (ø)` | |
| ... and [344 more](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7751/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) | |

:mega: Codecov can now indicate which changes are the most critical in Pull Requests. [Learn more](https://about.codecov.io/product/feature/runtime-insights/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools)


Patch:
diff --git a/docs-v2/data/maturity.json b/docs-v2/data/maturity.json
index 09aa2a540..ed2c91506 100644
--- a/docs-v2/data/maturity.json
+++ b/docs-v2/data/maturity.json
@@ -231,6 +231,15 @@
     "maturity": "alpha",
     "description": "User can generate a starter tekton pipeline using skaffold"
   },
+  "verify": {
+    "dev": "x",
+    "debug": "x",
+    "run": "x",
+    "deploy": "x",
+    "area": "skaffold verify",
+    "maturity": "beta",
+    "description": "User can run post deployment tests via skaffold monitored test containers"
+  },
   "global_config": {
     "dev": "x",
     "build": "x",
diff --git a/pkg/skaffold/build/builder_mux.go b/pkg/skaffold/build/builder_mux.go
index 788c201a1..d2bdfac2c 100644
--- a/pkg/skaffold/build/builder_mux.go
+++ b/pkg/skaffold/build/builder_mux.go
@@ -37,6 +37,11 @@ type BuilderMux struct {
 	byImageName map[string]PipelineBuilder
 	store       ArtifactStore
 	concurrency int
+	cache       Cache
+}
+
+type Cache interface {
+	AddArtifact(ctx context.Context, a graph.Artifact) error
 }
 
 // Config represents an interface for getting all config pipelines.
@@ -49,7 +54,7 @@ type Config interface {
 }
 
 // NewBuilderMux returns an implementation of `build.BuilderMux`.
-func NewBuilderMux(cfg Config, store ArtifactStore, builder func(p latest.Pipeline) (PipelineBuilder, error)) (*BuilderMux, error) {
+func NewBuilderMux(cfg Config, store ArtifactStore, cache Cache, builder func(p latest.Pipeline) (PipelineBuilder, error)) (*BuilderMux, error) {
 	pipelines := cfg.GetPipelines()
 	m := make(map[string]PipelineBuilder)
 	var pbs []PipelineBuilder
@@ -64,7 +69,7 @@ func NewBuilderMux(cfg Config, store ArtifactStore, builder func(p latest.Pipeli
 		}
 	}
 	concurrency := getConcurrency(pbs, cfg.BuildConcurrency())
-	return &BuilderMux{builders: pbs, byImageName: m, store: store, concurrency: concurrency}, nil
+	return &BuilderMux{builders: pbs, byImageName: m, store: store, concurrency: concurrency, cache: cache}, nil
 }
 
 // Build executes the specific image builder for each artifact in the given artifact slice.
@@ -101,6 +106,14 @@ func (b *BuilderMux) Build(ctx context.Context, out io.Writer, tags tag.ImageTag
 		if built, err = artifactBuilder(ctx, out, artifact, tag, platforms); err != nil {
 			return "", err
 		}
+
+		if err := b.cache.AddArtifact(ctx, graph.Artifact{
+			ImageName: artifact.ImageName,
+			Tag:       built,
+		}); err != nil {
+			log.Entry(ctx).Warnf("error adding artifact to cache; caching may not work as expected: %v", err)
+		}
+
 		if err = r.RunPostHooks(ctx, out); err != nil {
 			return "", err
 		}
diff --git a/pkg/skaffold/build/cache/cache.go b/pkg/skaffold/build/cache/cache.go
index d6755f88d..6dbda434f 100644
--- a/pkg/skaffold/build/cache/cache.go
+++ b/pkg/skaffold/build/cache/cache.go
@@ -48,6 +48,7 @@ type ArtifactCache map[string]ImageDetails
 // cache holds any data necessary for accessing the cache
 type cache struct {
 	artifactCache      ArtifactCache
+	hashByName         map[string]string
 	artifactGraph      graph.ArtifactGraph
 	artifactStore      build.ArtifactStore
 	cacheMutex         sync.RWMutex
@@ -91,6 +92,8 @@ func NewCache(ctx context.Context, cfg Config, isLocalImage func(imageName strin
 		return &noCache{}, nil
 	}
 
+	hashByName := make(map[string]string)
+
 	client, err := docker.NewAPIClient(ctx, cfg)
 	if err != nil {
 		// error only if any pipeline is local.
@@ -117,6 +120,7 @@ func NewCache(ctx context.Context, cfg Config, isLocalImage func(imageName strin
 
 	return &cache{
 		artifactCache:      artifactCache,
+		hashByName:         hashByName,
 		artifactGraph:      graph,
 		artifactStore:      store,
 		client:             client,
diff --git a/pkg/skaffold/build/cache/retrieve.go b/pkg/skaffold/build/cache/retrieve.go
index f7c816426..df1221343 100644
--- a/pkg/skaffold/build/cache/retrieve.go
+++ b/pkg/skaffold/build/cache/retrieve.go
@@ -57,7 +57,6 @@ func (c *cache) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, ar
 	case results = <-lookup:
 	}
 
-	hashByName := make(map[string]string)
 	var needToBuild []*latest.Artifact
 	var alreadyBuilt []graph.Artifact
 	for i, artifact := range artifacts {
@@ -75,7 +74,7 @@ func (c *cache) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, ar
 		case needsBuilding:
 			eventV2.CacheCheckMiss(artifact.ImageName, platforms.GetPlatforms(artifact.ImageName).String())
 			output.Yellow.Fprintln(out, "Not found. Building")
-			hashByName[artifact.ImageName] = result.Hash()
+			c.hashByName[artifact.ImageName] = result.Hash()
 			needToBuild = append(needToBuild, artifact)
 			continue
 
@@ -141,22 +140,19 @@ func (c *cache) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, ar
 
 	log.Entry(ctx).Infoln("Cache check completed in", timeutil.Humanize(time.Since(start)))
 
+	defer func() {
+		if err := saveArtifactCache(c.cacheFile, c.artifactCache); err != nil {
+			log.Entry(ctx).Warnf("error saving cache file; caching may not work as expected: %v", err)
+		}
+	}()
+
 	bRes, err := buildAndTest(ctx, out, tags, needToBuild, platforms)
+
 	if err != nil {
 		endTrace(instrumentation.TraceEndError(err))
 		return nil, err
 	}
 
-	if err := c.addArtifacts(ctx, bRes, hashByName); err != nil {
-		log.Entry(ctx).Warnf("error adding artifacts to cache; caching may not work as expected: %v", err)
-		return append(bRes, alreadyBuilt...), nil
-	}
-
-	if err := saveArtifactCache(c.cacheFile, c.artifactCache); err != nil {
-		log.Entry(ctx).Warnf("error saving cache file; caching may not work as expected: %v", err)
-		return append(bRes, alreadyBuilt...), nil
-	}
-
 	return maintainArtifactOrder(append(bRes, alreadyBuilt...), artifacts), err
 }
 
@@ -175,32 +171,31 @@ func maintainArtifactOrder(built []graph.Artifact, artifacts []*latest.Artifact)
 	return ordered
 }
 
-func (c *cache) addArtifacts(ctx context.Context, bRes []graph.Artifact, hashByName map[string]string) error {
-	for _, a := range bRes {
-		entry := ImageDetails{}
-		isLocal, err := c.isLocalImage(a.ImageName)
+func (c *cache) AddArtifact(ctx context.Context, a graph.Artifact) error {
+	entry := ImageDetails{}
+	isLocal, err := c.isLocalImage(a.ImageName)
+	if err != nil {
+		return err
+	}
+	if isLocal {
+		imageID, err := c.client.ImageID(ctx, a.Tag)
 		if err != nil {
 			return err
 		}
-		if isLocal {
-			imageID, err := c.client.ImageID(ctx, a.Tag)
-			if err != nil {
-				return err
-			}
 
-			if imageID != "" {
-				entry.ID = imageID
-			}
-		} else {
-			ref, err := docker.ParseReference(a.Tag)
-			if err != nil {
-				return fmt.Errorf("parsing reference %q: %w", a.Tag, err)
-			}
-			entry.Digest = ref.Digest
+		if imageID != "" {
+			entry.ID = imageID
+		}
+	} else {
+		ref, err := docker.ParseReference(a.Tag)
+		if err != nil {
+			return fmt.Errorf("parsing reference %q: %w", a.Tag, err)
 		}
-		c.cacheMutex.Lock()
-		c.artifactCache[hashByName[a.ImageName]] = entry
-		c.cacheMutex.Unlock()
+		entry.Digest = ref.Digest
 	}
+	c.cacheMutex.Lock()
+	c.artifactCache[c.hashByName[a.ImageName]] = entry
+	c.cacheMutex.Unlock()
+
 	return nil
 }
diff --git a/pkg/skaffold/build/cache/types.go b/pkg/skaffold/build/cache/types.go
index 276c106ee..1cd28e9ea 100644
--- a/pkg/skaffold/build/cache/types.go
+++ b/pkg/skaffold/build/cache/types.go
@@ -30,6 +30,7 @@ type BuildAndTestFn func(context.Context, io.Writer, tag.ImageTags, []*latest.Ar
 
 type Cache interface {
 	Build(context.Context, io.Writer, tag.ImageTags, []*latest.Artifact, platform.Resolver, BuildAndTestFn) ([]graph.Artifact, error)
+	AddArtifact(ctx context.Context, a graph.Artifact) error
 }
 
 type noCache struct{}
@@ -37,3 +38,8 @@ type noCache struct{}
 func (n *noCache) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact, platforms platform.Resolver, buildAndTest BuildAndTestFn) ([]graph.Artifact, error) {
 	return buildAndTest(ctx, out, tags, artifacts, platforms)
 }
+
+func (n *noCache) AddArtifact(ctx context.Context, a graph.Artifact) error {
+	// noop
+	return nil
+}
diff --git a/pkg/skaffold/runner/v2/new.go b/pkg/skaffold/runner/v2/new.go
index 681d044bc..cc3b69367 100644
--- a/pkg/skaffold/runner/v2/new.go
+++ b/pkg/skaffold/runner/v2/new.go
@@ -105,17 +105,6 @@ func NewForConfig(ctx context.Context, runCtx *runcontext.RunContext) (*Skaffold
 		return nil, fmt.Errorf("creating verifier: %w", err)
 	}
 
-	// The Builder must be instantiated AFTER the Deployer, because the Deploy target influences
-	// the Cluster object on the RunContext, which in turn influences whether or not we will push images.
-	var builder build.Builder
-	builder, err = build.NewBuilderMux(runCtx, store, func(p latest.Pipeline) (build.PipelineBuilder, error) {
-		return runner.GetBuilder(ctx, runCtx, store, sourceDependencies, p)
-	})
-	if err != nil {
-		endTrace(instrumentation.TraceEndError(err))
-		return nil, fmt.Errorf("creating builder: %w", err)
-	}
-
 	depLister := func(ctx context.Context, artifact *latest.Artifact) ([]string, error) {
 		ctx, endTrace := instrumentation.StartTrace(ctx, "NewForConfig_depLister")
 		defer endTrace()
@@ -139,6 +128,16 @@ func NewForConfig(ctx context.Context, runCtx *runcontext.RunContext) (*Skaffold
 		endTrace(instrumentation.TraceEndError(err))
 		return nil, fmt.Errorf("initializing cache: %w", err)
 	}
+	// The Builder must be instantiated AFTER the Deployer, because the Deploy target influences
+	// the Cluster object on the RunContext, which in turn influences whether or not we will push images.
+	var builder build.Builder
+	builder, err = build.NewBuilderMux(runCtx, store, artifactCache, func(p latest.Pipeline) (build.PipelineBuilder, error) {
+		return runner.GetBuilder(ctx, runCtx, store, sourceDependencies, p)
+	})
+	if err != nil {
+		endTrace(instrumentation.TraceEndError(err))
+		return nil, fmt.Errorf("creating builder: %w", err)
+	}
 
 	builder, tester, renderer, deployer = runner.WithTimings(builder, tester, renderer, deployer, runCtx.CacheArtifacts())
 	if runCtx.Notification() {


Test patch:
diff --git a/integration/cache_test.go b/integration/cache_test.go
index 882af4f93..1b0cd3449 100644
--- a/integration/cache_test.go
+++ b/integration/cache_test.go
@@ -17,6 +17,9 @@ limitations under the License.
 package integration
 
 import (
+	"fmt"
+	"os"
+	"path"
 	"testing"
 	"time"
 
@@ -65,3 +68,22 @@ func TestCacheHits(t *testing.T) {
 func waitForEvent(t *testing.T, entries chan *proto.LogEntry, condition func(*proto.LogEntry) bool) {
 	failNowIfError(t, wait.PollImmediate(time.Millisecond*500, 2*time.Minute, func() (bool, error) { return condition(<-entries), nil }))
 }
+
+func TestCacheIfBuildFail(t *testing.T) {
+	MarkIntegrationTest(t, CanRunWithoutGcp)
+
+	ns, _ := SetupNamespace(t)
+
+	cacheFile := "cache_" + ns.Name
+	testDir := "testdata/cache"
+	relativePath := path.Join(testDir, cacheFile)
+	defer os.Remove(relativePath)
+
+	skaffold.Build("--cache-file", cacheFile).InDir(testDir).InNs(ns.Name).Run(t)
+
+	fInfo, err := os.Stat(relativePath)
+	failNowIfError(t, err)
+	if b := fInfo.Size(); b == 0 {
+		failNowIfError(t, fmt.Errorf("expected to see content in the cache file, saw %d bytes", b))
+	}
+}
diff --git a/integration/testdata/cache/app/Dockerfile b/integration/testdata/cache/app/Dockerfile
new file mode 100644
index 000000000..df0d88271
--- /dev/null
+++ b/integration/testdata/cache/app/Dockerfile
@@ -0,0 +1,16 @@
+ARG BASE
+FROM golang:1.15 as builder
+COPY main.go .
+# `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
+ARG SKAFFOLD_GO_GCFLAGS
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app .
+
+FROM $BASE
+# Define GOTRACEBACK to mark this container as using the Go language runtime
+# for `skaffold debug` (https://skaffold.dev/docs/workflows/debug/).
+ENV GOTRACEBACK=single
+CMD ["./app"]
+COPY --from=builder /app .
+
+# intended to cause failure for test case
+RUN
\ No newline at end of file
diff --git a/integration/testdata/cache/app/k8s-pod.yaml b/integration/testdata/cache/app/k8s-pod.yaml
new file mode 100644
index 000000000..62e06f629
--- /dev/null
+++ b/integration/testdata/cache/app/k8s-pod.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: simple-artifact-dependency-app
+spec:
+  containers:
+  - name: simple-artifact-dependency-app
+    image: app
diff --git a/integration/testdata/cache/app/main.go b/integration/testdata/cache/app/main.go
new file mode 100644
index 000000000..700ab4591
--- /dev/null
+++ b/integration/testdata/cache/app/main.go
@@ -0,0 +1,19 @@
+package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"time"
+)
+
+func main() {
+	dat, err := ioutil.ReadFile("hello.txt")
+	if err != nil {
+		panic(err)
+	}
+
+	for {
+		fmt.Println(string(dat))
+		time.Sleep(time.Second * 1)
+	}
+}
diff --git a/integration/testdata/cache/base/Dockerfile b/integration/testdata/cache/base/Dockerfile
new file mode 100644
index 000000000..02480fbb9
--- /dev/null
+++ b/integration/testdata/cache/base/Dockerfile
@@ -0,0 +1,4 @@
+FROM alpine:3
+COPY hello.txt .
+CMD ["./app"]
+
diff --git a/integration/testdata/cache/base/hello.txt b/integration/testdata/cache/base/hello.txt
new file mode 100644
index 000000000..5e1c309da
--- /dev/null
+++ b/integration/testdata/cache/base/hello.txt
@@ -0,0 +1,1 @@
+Hello World
\ No newline at end of file
diff --git a/integration/testdata/cache/skaffold.yaml b/integration/testdata/cache/skaffold.yaml
new file mode 100644
index 000000000..7a6b6abfb
--- /dev/null
+++ b/integration/testdata/cache/skaffold.yaml
@@ -0,0 +1,16 @@
+apiVersion: skaffold/v2beta28
+kind: Config
+build:
+  artifacts:
+  - image: app
+    context: app
+    requires:
+    - image: base
+      alias: BASE
+  - image: base
+    context: base
+deploy:
+  kubectl:
+    manifests:
+    - app/k8s-pod.yaml
+
diff --git a/pkg/skaffold/build/builder_mux_test.go b/pkg/skaffold/build/builder_mux_test.go
index 39371183f..d5ca2f31a 100644
--- a/pkg/skaffold/build/builder_mux_test.go
+++ b/pkg/skaffold/build/builder_mux_test.go
@@ -78,7 +78,7 @@ func TestNewBuilderMux(t *testing.T) {
 		testutil.Run(t, test.description, func(t *testutil.T) {
 			cfg := &mockConfig{pipelines: test.pipelines}
 
-			b, err := NewBuilderMux(cfg, nil, test.pipeBuilder)
+			b, err := NewBuilderMux(cfg, nil, nil, test.pipeBuilder)
 			t.CheckError(test.shouldErr, err)
 			if test.shouldErr {
 				return
diff --git a/pkg/skaffold/build/cache/retrieve_test.go b/pkg/skaffold/build/cache/retrieve_test.go
index b573c8432..48f86dffb 100644
--- a/pkg/skaffold/build/cache/retrieve_test.go
+++ b/pkg/skaffold/build/cache/retrieve_test.go
@@ -59,6 +59,7 @@ type mockBuilder struct {
 	push         bool
 	dockerDaemon docker.LocalDaemon
 	store        build.ArtifactStore
+	cache        Cache
 }
 
 func (b *mockBuilder) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact, _ platform.Resolver) ([]graph.Artifact, error) {
@@ -79,16 +80,21 @@ func (b *mockBuilder) Build(ctx context.Context, out io.Writer, tags tag.ImageTa
 				return nil, err
 			}
 
-			built = append(built, graph.Artifact{
+			a := graph.Artifact{
 				ImageName: artifact.ImageName,
 				Tag:       build.TagWithDigest(tag, digest),
-			})
+			}
+
+			built = append(built, a)
+			b.cache.AddArtifact(ctx, a)
 		} else {
-			built = append(built, graph.Artifact{
+			a := graph.Artifact{
 				ImageName: artifact.ImageName,
 				Tag:       tag,
-			})
+			}
+			built = append(built, a)
 			b.store.Record(artifact, tag)
+			b.cache.AddArtifact(ctx, a)
 		}
 	}
 
@@ -147,7 +153,7 @@ func TestCacheBuildLocal(t *testing.T) {
 		t.CheckNoError(err)
 
 		// First build: Need to build both artifacts
-		builder := &mockBuilder{dockerDaemon: dockerDaemon, push: false, store: store}
+		builder := &mockBuilder{dockerDaemon: dockerDaemon, push: false, store: store, cache: artifactCache}
 		bRes, err := artifactCache.Build(context.Background(), io.Discard, tags, artifacts, platform.Resolver{}, builder.Build)
 
 		t.CheckNoError(err)
@@ -156,7 +162,7 @@ func TestCacheBuildLocal(t *testing.T) {
 
 		// Second build: both artifacts are read from cache
 		// Artifacts should always be returned in their original order
-		builder = &mockBuilder{dockerDaemon: dockerDaemon, push: false, store: store}
+		builder = &mockBuilder{dockerDaemon: dockerDaemon, push: false, store: store, cache: artifactCache}
 		bRes, err = artifactCache.Build(context.Background(), io.Discard, tags, artifacts, platform.Resolver{}, builder.Build)
 
 		t.CheckNoError(err)
@@ -168,7 +174,7 @@ func TestCacheBuildLocal(t *testing.T) {
 		// Third build: change first artifact's dependency
 		// Artifacts should always be returned in their original order
 		tmpDir.Write("dep1", "new content")
-		builder = &mockBuilder{dockerDaemon: dockerDaemon, push: false, store: store}
+		builder = &mockBuilder{dockerDaemon: dockerDaemon, push: false, store: store, cache: artifactCache}
 		bRes, err = artifactCache.Build(context.Background(), io.Discard, tags, artifacts, platform.Resolver{}, builder.Build)
 
 		t.CheckNoError(err)
@@ -180,7 +186,7 @@ func TestCacheBuildLocal(t *testing.T) {
 		// Fourth build: change second artifact's dependency
 		// Artifacts should always be returned in their original order
 		tmpDir.Write("dep3", "new content")
-		builder = &mockBuilder{dockerDaemon: dockerDaemon, push: false, store: store}
+		builder = &mockBuilder{dockerDaemon: dockerDaemon, push: false, store: store, cache: artifactCache}
 		bRes, err = artifactCache.Build(context.Background(), io.Discard, tags, artifacts, platform.Resolver{}, builder.Build)
 
 		t.CheckNoError(err)
@@ -243,7 +249,7 @@ func TestCacheBuildRemote(t *testing.T) {
 		t.CheckNoError(err)
 
 		// First build: Need to build both artifacts
-		builder := &mockBuilder{dockerDaemon: dockerDaemon, push: true}
+		builder := &mockBuilder{dockerDaemon: dockerDaemon, push: true, cache: artifactCache}
 		bRes, err := artifactCache.Build(context.Background(), io.Discard, tags, artifacts, platform.Resolver{}, builder.Build)
 
 		t.CheckNoError(err)
@@ -254,7 +260,7 @@ func TestCacheBuildRemote(t *testing.T) {
 		t.CheckDeepEqual("artifact2", bRes[1].ImageName)
 
 		// Second build: both artifacts are read from cache
-		builder = &mockBuilder{dockerDaemon: dockerDaemon, push: true}
+		builder = &mockBuilder{dockerDaemon: dockerDaemon, push: true, cache: artifactCache}
 		bRes, err = artifactCache.Build(context.Background(), io.Discard, tags, artifacts, platform.Resolver{}, builder.Build)
 
 		t.CheckNoError(err)
@@ -265,7 +271,7 @@ func TestCacheBuildRemote(t *testing.T) {
 
 		// Third build: change one artifact's dependencies
 		tmpDir.Write("dep1", "new content")
-		builder = &mockBuilder{dockerDaemon: dockerDaemon, push: true}
+		builder = &mockBuilder{dockerDaemon: dockerDaemon, push: true, cache: artifactCache}
 		bRes, err = artifactCache.Build(context.Background(), io.Discard, tags, artifacts, platform.Resolver{}, builder.Build)
 
 		t.CheckNoError(err)