diff --git a/docs/md/melange_build.md b/docs/md/melange_build.md index a4b89f1a9..2f305d7f9 100644 --- a/docs/md/melange_build.md +++ b/docs/md/melange_build.md @@ -55,6 +55,7 @@ melange build [flags] --overlay-binsh string use specified file as /bin/sh overlay in build environment --pipeline-dir string directory used to extend defined built-in pipelines -r, --repository-append strings path to extra repositories to include in the build environment + --rm clean up intermediate artifacts (e.g. container images) --runner string which runner to use to enable running commands, default is based on your platform. Options are ["bubblewrap" "docker" "lima" "kubernetes"] --signing-key string key to use for signing --source-dir string directory used for included sources diff --git a/pkg/build/build.go b/pkg/build/build.go index 959074d94..334e3a832 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -86,6 +86,7 @@ type Build struct { containerConfig *container.Config Debug bool DebugRunner bool + Remove bool LogPolicy []string FailOnLintWarning bool DefaultCPU string @@ -220,6 +221,11 @@ func New(ctx context.Context, opts ...Option) (*Build, error) { } func (b *Build) Close() error { + if b.Remove { + if err := b.Runner.OCIImageLoader().RemoveImage(context.TODO(), b.containerConfig.ImgRef); err != nil { + return err + } + } return b.Runner.Close() } @@ -499,6 +505,15 @@ func WithDebugRunner(debug bool) Option { } } +// WithRemove indicates whether the the build will clean up after itself. +// This includes deleting any intermediate artifacts like container images. +func WithRemove(remove bool) Option { + return func(b *Build) error { + b.Remove = remove + return nil + } +} + // WithLogPolicy sets the logging policy to use during builds. func WithLogPolicy(policy []string) Option { return func(b *Build) error { diff --git a/pkg/cli/build.go b/pkg/cli/build.go index e57fbc89d..30ba42b94 100644 --- a/pkg/cli/build.go +++ b/pkg/cli/build.go @@ -61,6 +61,7 @@ func Build() *cobra.Command { var createBuildLog bool var debug bool var debugRunner bool + var remove bool var runner string var failOnLintWarning bool var cpu, memory string @@ -107,6 +108,7 @@ func Build() *cobra.Command { build.WithCreateBuildLog(createBuildLog), build.WithDebug(debug), build.WithDebugRunner(debugRunner), + build.WithRemove(remove), build.WithLogPolicy(logPolicy), build.WithRunner(runner), build.WithFailOnLintWarning(failOnLintWarning), @@ -160,6 +162,7 @@ func Build() *cobra.Command { cmd.Flags().BoolVar(&createBuildLog, "create-build-log", false, "creates a package.log file containing a list of packages that were built by the command") cmd.Flags().BoolVar(&debug, "debug", false, "enables debug logging of build pipelines") cmd.Flags().BoolVar(&debugRunner, "debug-runner", false, "when enabled, the builder pod will persist after the build succeeds or fails") + cmd.Flags().BoolVar(&remove, "rm", false, "clean up intermediate artifacts (e.g. container images)") cmd.Flags().BoolVar(&failOnLintWarning, "fail-on-lint-warning", false, "turns linter warnings into failures") cmd.Flags().StringVar(&cpu, "cpu", "", "default CPU resources to use for builds") cmd.Flags().StringVar(&memory, "memory", "", "default memory resources to use for builds") diff --git a/pkg/container/bubblewrap_runner.go b/pkg/container/bubblewrap_runner.go index 8262c81e1..1f00e8dec 100644 --- a/pkg/container/bubblewrap_runner.go +++ b/pkg/container/bubblewrap_runner.go @@ -191,3 +191,7 @@ func (b bubblewrapOCILoader) LoadImage(ctx context.Context, layer v1.Layer, arch } return guestDir, nil } + +func (b bubblewrapOCILoader) RemoveImage(ctx context.Context, ref string) error { + return os.RemoveAll(ref) +} diff --git a/pkg/container/docker_runner.go b/pkg/container/docker_runner.go index e94df740d..bfcc140da 100644 --- a/pkg/container/docker_runner.go +++ b/pkg/container/docker_runner.go @@ -160,7 +160,9 @@ func (dk *docker) TestUsability(ctx context.Context) bool { // OCIImageLoader create a loader to load an OCI image into the docker daemon. func (dk *docker) OCIImageLoader() Loader { - return &dockerLoader{} + return &dockerLoader{ + cli: dk.cli, + } } // TempDir returns the base for temporary directory. For docker @@ -241,9 +243,11 @@ func (dk *docker) WorkspaceTar(ctx context.Context, cfg *Config) (io.ReadCloser, return nil, nil } -type dockerLoader struct{} +type dockerLoader struct { + cli *client.Client +} -func (d dockerLoader) LoadImage(ctx context.Context, layer v1.Layer, arch apko_types.Architecture, bc *apko_build.Context) (string, error) { +func (d *dockerLoader) LoadImage(ctx context.Context, layer v1.Layer, arch apko_types.Architecture, bc *apko_build.Context) (string, error) { ctx, span := otel.Tracer("melange").Start(ctx, "docker.LoadImage") defer span.End() @@ -263,3 +267,26 @@ func (d dockerLoader) LoadImage(ctx context.Context, layer v1.Layer, arch apko_t } return ref.String(), nil } + +func (d *dockerLoader) RemoveImage(ctx context.Context, ref string) error { + log := clog.FromContext(ctx) + log.Infof("deleting image %q", ref) + resps, err := d.cli.ImageRemove(ctx, ref, types.ImageRemoveOptions{ + Force: true, + PruneChildren: true, + }) + if err != nil { + return err + } + + for _, resp := range resps { + if resp.Untagged != "" { + log.Infof("untagged %q", resp.Untagged) + } + if resp.Deleted != "" { + log.Infof("deleted %q", resp.Deleted) + } + } + + return nil +} diff --git a/pkg/container/kubernetes_runner.go b/pkg/container/kubernetes_runner.go index 117a639ad..4c9856cba 100644 --- a/pkg/container/kubernetes_runner.go +++ b/pkg/container/kubernetes_runner.go @@ -680,6 +680,14 @@ func (k *k8sLoader) LoadImage(ctx context.Context, layer ggcrv1.Layer, arch apko return ref.String(), nil } +func (k *k8sLoader) RemoveImage(ctx context.Context, str string) error { + ref, err := name.ParseReference(str) + if err != nil { + return err + } + return remote.Delete(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain), remote.WithContext(ctx)) +} + type k8sTarFetcher struct { client kubernetes.Interface restconfig *rest.Config diff --git a/pkg/container/runner.go b/pkg/container/runner.go index 96dd0a373..e0c58a03e 100644 --- a/pkg/container/runner.go +++ b/pkg/container/runner.go @@ -45,6 +45,7 @@ type Runner interface { type Loader interface { LoadImage(ctx context.Context, layer v1.Layer, arch apko_types.Architecture, bc *apko_build.Context) (ref string, err error) + RemoveImage(ctx context.Context, ref string) error } // GetRunner returns the requested runner implementation.