From 5d68f029b46da735cefe0faef2adae0397ed3f0a Mon Sep 17 00:00:00 2001 From: Simon de Vlieger Date: Mon, 28 Jul 2025 09:09:44 +0200 Subject: [PATCH 1/4] osbuild: osbuild inspect wrapper Introduce a wrapper function to call `osbuild --inspect`. This command validates a manifest and fills in all the ids used. This is to be used later in `osbuild-composer` to extract cacheable identifiers from stages. Signed-off-by: Simon de Vlieger --- pkg/osbuild/osbuild-exec.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/osbuild/osbuild-exec.go b/pkg/osbuild/osbuild-exec.go index e49929e038..225178b7bc 100644 --- a/pkg/osbuild/osbuild-exec.go +++ b/pkg/osbuild/osbuild-exec.go @@ -144,3 +144,16 @@ func OSBuildVersion() (string, error) { version = strings.TrimSpace(version) return version, nil } + +// OSBuildInspect converts a manifest to an inspected manifest. +func OSBuildInspect(manifest []byte) ([]byte, error) { + cmd := exec.Command("osbuild", "--inspect") + cmd.Stdin = bytes.NewBuffer(manifest) + + out, err := cmd.Output() + if err != nil { + return nil, err + } + + return out, nil +} From 1a096338821ae4e28e529b30523541253f9b1e4c Mon Sep 17 00:00:00 2001 From: Simon de Vlieger Date: Mon, 28 Jul 2025 09:19:06 +0200 Subject: [PATCH 2/4] osbuild: store IDs Allow for the ability to store stage identifiers on structs, these can be filled in when deserializing an inspected manifest but should not generally affect any generated manifest. Signed-off-by: Simon de Vlieger --- pkg/osbuild/osbuild.go | 20 ++++++++++++++++++++ pkg/osbuild/stage.go | 3 +++ 2 files changed, 23 insertions(+) diff --git a/pkg/osbuild/osbuild.go b/pkg/osbuild/osbuild.go index 9950dcab0a..b5b910a482 100644 --- a/pkg/osbuild/osbuild.go +++ b/pkg/osbuild/osbuild.go @@ -2,6 +2,10 @@ // OSBuild (schema v2) types. package osbuild +import ( + "fmt" +) + const ( // should be "^\\/(?!\\.\\.)((?!\\/\\.\\.\\/).)+$" but Go doesn't support lookaheads // therefore we have to instead check for the invalid cases, which is much simpler @@ -51,3 +55,19 @@ func (p *Pipeline) AddStages(stages ...*Stage) { p.AddStage(stage) } } + +// GetID gets the pipeline identifiers for an *inspected* manifest. These are +// not available for non-inspected manifests and will return an error there. +func (p *Pipeline) GetID() (string, error) { + if len(p.Stages) == 0 { + return "", fmt.Errorf("no stages in manifest") + } + + lastStage := p.Stages[len(p.Stages)-1] + + if len(lastStage.ID) == 0 { + return "", fmt.Errorf("un-inspected manifest, identifiers are not available") + } + + return lastStage.ID, nil +} diff --git a/pkg/osbuild/stage.go b/pkg/osbuild/stage.go index d192e5ff55..0669117c6d 100644 --- a/pkg/osbuild/stage.go +++ b/pkg/osbuild/stage.go @@ -5,6 +5,9 @@ type Stage struct { // Well-known name in reverse domain-name notation, uniquely identifying // the stage type. Type string `json:"type"` + + ID string `json:"id,omitempty"` + // Stage-type specific options fully determining the operations of the Inputs Inputs `json:"inputs,omitempty"` From a12942804f73476cc652d4d46656a1ad81530a6e Mon Sep 17 00:00:00 2001 From: Simon de Vlieger Date: Fri, 1 Aug 2025 10:17:19 +0200 Subject: [PATCH 3/4] osbuild: manifest from bytes Add a new constructor to create an `osbuild.Manifest` instance straight from a bunch of bytes. Can be used on the result of an inspected manifest as implemented in previous commits. Signed-off-by: Simon de Vlieger --- pkg/osbuild/osbuild.go | 13 +++++++++++++ pkg/osbuild/osbuild_test.go | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/pkg/osbuild/osbuild.go b/pkg/osbuild/osbuild.go index b5b910a482..ac7aa7bb21 100644 --- a/pkg/osbuild/osbuild.go +++ b/pkg/osbuild/osbuild.go @@ -3,6 +3,7 @@ package osbuild import ( + "encoding/json" "fmt" ) @@ -56,6 +57,18 @@ func (p *Pipeline) AddStages(stages ...*Stage) { } } +// Take some bytes and deserialize them into a Manifest; mostly used to take +// an inspected manifest +func NewManifestFromBytes(data []byte) (*Manifest, error) { + manifest := &Manifest{} + + if err := json.Unmarshal(data, &manifest); err != nil { + return nil, err + } + + return manifest, nil +} + // GetID gets the pipeline identifiers for an *inspected* manifest. These are // not available for non-inspected manifests and will return an error there. func (p *Pipeline) GetID() (string, error) { diff --git a/pkg/osbuild/osbuild_test.go b/pkg/osbuild/osbuild_test.go index 817532023f..304ea3298d 100644 --- a/pkg/osbuild/osbuild_test.go +++ b/pkg/osbuild/osbuild_test.go @@ -26,3 +26,9 @@ func TestPipeline_AddStage(t *testing.T) { assert.Equal(t, expectedPipeline, actualPipeline) assert.Equal(t, 1, len(actualPipeline.Stages)) } + +func TestManifestFromBytes(t *testing.T) { + bytes := []byte(`{}`) + _, err := NewManifestFromBytes(bytes) + assert.NoError(t, err) +} From c8514c75c2248951412fab44a585c40a0ac98ff2 Mon Sep 17 00:00:00 2001 From: Simon de Vlieger Date: Wed, 27 Aug 2025 09:07:16 +0200 Subject: [PATCH 4/4] test: slightly expand ID testing Signed-off-by: Simon de Vlieger --- pkg/osbuild/osbuild_test.go | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/pkg/osbuild/osbuild_test.go b/pkg/osbuild/osbuild_test.go index 304ea3298d..32a2ee1fb0 100644 --- a/pkg/osbuild/osbuild_test.go +++ b/pkg/osbuild/osbuild_test.go @@ -27,8 +27,34 @@ func TestPipeline_AddStage(t *testing.T) { assert.Equal(t, 1, len(actualPipeline.Stages)) } +var fakeOsbuildManifestWithIdentifiers = []byte(`{ + "version": "2", + "pipelines": [ + { + "name": "build", + "stages": [ + { + "id": "1234", + "type": "org.osbuild.rpm" + }, + { + "id": "5678", + "type": "org.osbuild.mkdir" + } + ] + } + ] +}`) + func TestManifestFromBytes(t *testing.T) { - bytes := []byte(`{}`) - _, err := NewManifestFromBytes(bytes) + manifest, err := NewManifestFromBytes(fakeOsbuildManifestWithIdentifiers) assert.NoError(t, err) + + assert.Equal(t, manifest.Pipelines[0].Stages[0].ID, "1234") + assert.Equal(t, manifest.Pipelines[0].Stages[1].ID, "5678") + + pID, err := manifest.Pipelines[0].GetID() + assert.NoError(t, err) + + assert.Equal(t, pID, "5678") }