diff --git a/cmd/skaffold/app/cmd/build.go b/cmd/skaffold/app/cmd/build.go index 87c4039e3a4..597e24830be 100644 --- a/cmd/skaffold/app/cmd/build.go +++ b/cmd/skaffold/app/cmd/build.go @@ -48,12 +48,14 @@ func NewCmdBuild() *cobra.Command { WithExample("Build artifacts whose image name contains ", "build -b "). WithExample("Quietly build artifacts and output the image names as json", "build -q > build_result.json"). WithExample("Build the artifacts and then deploy them", "build -q | skaffold deploy --build-artifacts -"). + WithExample("Print the final image names", "build -q --dry-run"). WithCommonFlags(). WithFlags(func(f *pflag.FlagSet) { f.StringSliceVarP(&opts.TargetImages, "build-image", "b", nil, "Choose which artifacts to build. Artifacts with image names that contain the expression will be built only. Default is to build sources for all artifacts") f.BoolVarP(&quietFlag, "quiet", "q", false, "Suppress the build output and print image built on success. See --output to format output.") f.VarP(buildFormatFlag, "output", "o", "Used in conjunction with --quiet flag. "+buildFormatFlag.Usage()) f.StringVar(&buildOutputFlag, "file-output", "", "Filename to write build images to") + f.BoolVar(&opts.DryRun, "dry-run", false, "Don't build images, just compute the tag for each artifact.") }). NoArgs(doBuild) } diff --git a/docs/content/en/docs/references/cli/_index.md b/docs/content/en/docs/references/cli/_index.md index 3171885bfcc..2f5cd135716 100644 --- a/docs/content/en/docs/references/cli/_index.md +++ b/docs/content/en/docs/references/cli/_index.md @@ -122,12 +122,16 @@ Examples: # Build the artifacts and then deploy them skaffold build -q | skaffold deploy --build-artifacts - + # Print the final image names + skaffold build -q --dry-run + Options: -b, --build-image=[]: Choose which artifacts to build. Artifacts with image names that contain the expression will be built only. Default is to build sources for all artifacts --cache-artifacts=true: Set to false to disable default caching of artifacts --cache-file='': Specify the location of the cache file (default $HOME/.skaffold/cache) -c, --config='': File for global configurations (defaults to $HOME/.skaffold/config) -d, --default-repo='': Default repository value (overrides global config) + --dry-run=false: Don't build images, just compute the tag for each artifact. --enable-rpc=false: Enable gRPC for exposing Skaffold events (true by default for `skaffold dev`) --file-output='': Filename to write build images to -f, --filename='skaffold.yaml': Path or URL to the Skaffold config file @@ -159,6 +163,7 @@ Env vars: * `SKAFFOLD_CACHE_FILE` (same as `--cache-file`) * `SKAFFOLD_CONFIG` (same as `--config`) * `SKAFFOLD_DEFAULT_REPO` (same as `--default-repo`) +* `SKAFFOLD_DRY_RUN` (same as `--dry-run`) * `SKAFFOLD_ENABLE_RPC` (same as `--enable-rpc`) * `SKAFFOLD_FILE_OUTPUT` (same as `--file-output`) * `SKAFFOLD_FILENAME` (same as `--filename`) diff --git a/pkg/skaffold/config/options.go b/pkg/skaffold/config/options.go index 602ae36abd6..d0a3fbd2e19 100644 --- a/pkg/skaffold/config/options.go +++ b/pkg/skaffold/config/options.go @@ -50,6 +50,7 @@ type SkaffoldOptions struct { AutoDeploy bool RenderOnly bool ProfileAutoActivation bool + DryRun bool PortForward PortForwardOptions CustomTag string Namespace string @@ -66,6 +67,7 @@ type SkaffoldOptions struct { Command string RPCPort int RPCHTTPPort int + // TODO(https://github.com/GoogleContainerTools/skaffold/issues/3668): // remove minikubeProfile from here and instead detect it by matching the // kubecontext API Server to minikube profiles diff --git a/pkg/skaffold/runner/build_deploy.go b/pkg/skaffold/runner/build_deploy.go index 6b21078ce36..19a6ae29b47 100644 --- a/pkg/skaffold/runner/build_deploy.go +++ b/pkg/skaffold/runner/build_deploy.go @@ -39,6 +39,20 @@ func (r *SkaffoldRunner) BuildAndTest(ctx context.Context, out io.Writer, artifa return nil, err } + // In dry-run mode, we don't build anything, just return the tag for each artifact. + if r.runCtx.Opts.DryRun { + var bRes []build.Artifact + + for _, artifact := range artifacts { + bRes = append(bRes, build.Artifact{ + ImageName: artifact.ImageName, + Tag: tags[artifact.ImageName], + }) + } + + return bRes, nil + } + bRes, err := r.cache.Build(ctx, out, tags, artifacts, func(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact) ([]build.Artifact, error) { if len(artifacts) == 0 { return nil, nil diff --git a/pkg/skaffold/runner/build_deploy_test.go b/pkg/skaffold/runner/build_deploy_test.go index 1928393e6f3..4ab3254abce 100644 --- a/pkg/skaffold/runner/build_deploy_test.go +++ b/pkg/skaffold/runner/build_deploy_test.go @@ -24,6 +24,7 @@ import ( "k8s.io/client-go/tools/clientcmd/api" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" "github.com/GoogleContainerTools/skaffold/testutil" @@ -89,3 +90,23 @@ func TestBuildTestDeploy(t *testing.T) { }) } } + +func TestBuildAndTestDryRun(t *testing.T) { + testutil.Run(t, "", func(t *testutil.T) { + testBench := &TestBench{} + runner := createRunner(t, testBench, nil) + runner.runCtx.Opts.DryRun = true + + bRes, err := runner.BuildAndTest(context.Background(), ioutil.Discard, []*latest.Artifact{ + {ImageName: "img1"}, + {ImageName: "img2"}, + }) + + t.CheckNoError(err) + t.CheckDeepEqual([]build.Artifact{ + {ImageName: "img1", Tag: "img1:latest"}, + {ImageName: "img2", Tag: "img2:latest"}}, bRes) + // Nothing was built, tested or deployed + t.CheckDeepEqual([]Actions{{}}, testBench.Actions()) + }) +}