diff --git a/README.md b/README.md index b04db07..9fc4df4 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,10 @@ $ sudo image-builder build qcow2 --distro centos-9 this will create a directory `centos-9-qcow2-x86_64` under which the output is stored. +With the `--extra-artifacts=manifest` an +[osbuild](https://github.com/osbuild/osbuild) manifest will be +placed in the output directory too. + ### Blueprints Blueprints are supported, first create a `config.toml` and put e.g. diff --git a/cmd/image-builder/build.go b/cmd/image-builder/build.go index 2b7e156..26b6151 100644 --- a/cmd/image-builder/build.go +++ b/cmd/image-builder/build.go @@ -1,6 +1,10 @@ package main import ( + "fmt" + "os" + "path/filepath" + "github.com/osbuild/images/pkg/imagefilter" "github.com/osbuild/images/pkg/osbuild" ) @@ -8,6 +12,8 @@ import ( type buildOptions struct { OutputDir string StoreDir string + + WriteManifest bool } func buildImage(res *imagefilter.Result, osbuildManifest []byte, opts *buildOptions) error { @@ -18,7 +24,13 @@ func buildImage(res *imagefilter.Result, osbuildManifest []byte, opts *buildOpti // XXX: support output filename via commandline (c.f. // https://github.com/osbuild/images/pull/1039) if opts.OutputDir == "" { - opts.OutputDir = outputDirFor(res) + opts.OutputDir = outputNameFor(res) + } + if opts.WriteManifest { + p := filepath.Join(opts.OutputDir, fmt.Sprintf("%s.osbuild-manifest.json", outputNameFor(res))) + if err := os.WriteFile(p, osbuildManifest, 0644); err != nil { + return err + } } // XXX: support stremaing via images/pkg/osbuild/monitor.go diff --git a/cmd/image-builder/main.go b/cmd/image-builder/main.go index a0b9dff..83b1249 100644 --- a/cmd/image-builder/main.go +++ b/cmd/image-builder/main.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "slices" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -23,7 +24,7 @@ var ( ) // generate the default output directory name for the given image -func outputDirFor(img *imagefilter.Result) string { +func outputNameFor(img *imagefilter.Result) string { return fmt.Sprintf("%s-%s-%s", img.Distro.Name(), img.ImgType.Name(), img.Arch.Name()) } @@ -158,6 +159,10 @@ func cmdBuild(cmd *cobra.Command, args []string) error { if err != nil { return err } + extraArtifacts, err := cmd.Flags().GetStringArray("extra-artifacts") + if err != nil { + return err + } var mf bytes.Buffer // XXX: check env here, i.e. if user is root and osbuild is installed @@ -172,8 +177,9 @@ func cmdBuild(cmd *cobra.Command, args []string) error { } buildOpts := &buildOptions{ - OutputDir: outputDir, - StoreDir: storeDir, + OutputDir: outputDir, + StoreDir: storeDir, + WriteManifest: slices.Contains(extraArtifacts, "manifest"), } return buildImage(res, mf.Bytes(), buildOpts) } @@ -195,7 +201,7 @@ operating sytsems like centos and RHEL with easy customizations support.`, } rootCmd.PersistentFlags().String("datadir", "", `Override the default data direcotry for e.g. custom repositories/*.json data`) rootCmd.PersistentFlags().String("output-dir", "", `Put output into the specified direcotry`) - rootCmd.PersistentFlags().StringArray("extra-artifacts", nil, `Export extra artifacts to the output dir (e.g. "sbom")`) + rootCmd.PersistentFlags().StringArray("extra-artifacts", nil, `Export extra artifacts to the output dir (supported: "sbom","manifest", can be given multiple times)`) rootCmd.SetOut(osStdout) rootCmd.SetErr(osStderr) diff --git a/cmd/image-builder/main_test.go b/cmd/image-builder/main_test.go index 5023574..497abce 100644 --- a/cmd/image-builder/main_test.go +++ b/cmd/image-builder/main_test.go @@ -394,6 +394,41 @@ func TestBuildIntegrationSwitchOutputDir(t *testing.T) { assert.Equal(t, "some-output-dir", osbuildCall[outputDirPos+1]) } +func TestBuildIntegrationExtraArtifactsManifest(t *testing.T) { + if testing.Short() { + t.Skip("manifest generation takes a while") + } + if !hasDepsolveDnf() { + t.Skip("no osbuild-depsolve-dnf binary found") + } + + restore := main.MockNewRepoRegistry(testrepos.New) + defer restore() + + outputDir := t.TempDir() + restore = main.MockOsArgs([]string{ + "build", + "qcow2", + "--distro", "centos-9", + "--store", outputDir, + "--extra-artifacts", "manifest", + "--output-dir", outputDir, + }) + defer restore() + + script := `cat - > "$0".stdin` + fakeOsbuildCmd := testutil.MockCommand(t, "osbuild", script) + defer fakeOsbuildCmd.Restore() + + err := main.Run() + assert.NoError(t, err) + + manifest, err := filepath.Glob(filepath.Join(outputDir, "*.osbuild-manifest.json")) + assert.Equal(t, len(manifest), 1) + assert.Equal(t, filepath.Join(outputDir, "centos-9-qcow2-x86_64.osbuild-manifest.json"), manifest[0]) + +} + func TestBuildIntegrationErrors(t *testing.T) { if testing.Short() { t.Skip("manifest generation takes a while") diff --git a/cmd/image-builder/manifest.go b/cmd/image-builder/manifest.go index 414fdd3..eb8fc83 100644 --- a/cmd/image-builder/manifest.go +++ b/cmd/image-builder/manifest.go @@ -49,7 +49,7 @@ func generateManifest(dataDir string, img *imagefilter.Result, output io.Writer, if slices.Contains(opts.ExtraArtifacts, "sbom") { outputDir := opts.OutputDir if outputDir == "" { - outputDir = outputDirFor(img) + outputDir = outputNameFor(img) } if err := os.MkdirAll(outputDir, 0755); err != nil { return err