Skip to content

Commit

Permalink
test: add integration test for build and SBOMs
Browse files Browse the repository at this point in the history
Signed-off-by: Dan Luhring <[email protected]>
  • Loading branch information
luhring committed Oct 18, 2024
1 parent 4ee1b8f commit f039274
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 4 deletions.
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ module chainguard.dev/melange

go 1.23.2

toolchain go1.23.2

require (
al.essio.dev/pkg/shellescape v1.5.1
chainguard.dev/apko v0.19.2
Expand Down
3 changes: 3 additions & 0 deletions pkg/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ func New(ctx context.Context, opts ...Option) (*Build, error) {
if b.ConfigFileRepositoryCommit == "" {
return nil, fmt.Errorf("config file repository commit was not set")
}
if b.Runner == nil {
return nil, fmt.Errorf("no runner was specified")
}

parsedCfg, err := config.ParseConfiguration(ctx,
b.ConfigFile,
Expand Down
118 changes: 118 additions & 0 deletions pkg/build/build_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//go:build integration
// +build integration

package build

import (
"archive/tar"
"compress/gzip"
"context"
"fmt"
"os"
"path/filepath"
"testing"

"io"

"chainguard.dev/melange/pkg/container/docker"
"github.com/google/go-cmp/cmp"
)

func TestBuild_BuildPackage(t *testing.T) {
tests := []struct {
name string
expectedVersion string
}{
{
name: "crane",
expectedVersion: "0.20.2-r1",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tempDir := t.TempDir()
p := filepath.Join("testdata", "build_configs", tt.name) + ".yaml"
const arch = "aarch64"

t.Run("builds successfully", func(t *testing.T) {
ctx := context.Background()
r, err := docker.NewRunner(ctx) // TODO: is access to Docker a safe assumption in CI?
if err != nil {
t.Fatalf("creating docker runner: %v", err)
}

b, err := New(
ctx,
WithConfig(p),
WithOutDir(tempDir),
WithArch(arch),
WithConfigFileRepositoryURL("https://github.com/wolfi-dev/os"),
WithConfigFileRepositoryCommit("c0ffee"),
WithRunner(r),
WithNamespace("wolfi"),
WithExtraRepos([]string{"https://packages.wolfi.dev/os"}),
WithExtraKeys([]string{"https://packages.wolfi.dev/os/wolfi-signing.rsa.pub"}),
)
if err != nil {
t.Fatalf("setting up build: %v", err)
}

if err := b.BuildPackage(ctx); err != nil {
t.Fatalf("building package: %v", err)
}
})

t.Run("sbom correctness", func(t *testing.T) {
apkPath := filepath.Join(tempDir, arch, fmt.Sprintf("%s-%s.apk", tt.name, tt.expectedVersion))
apkFile, err := os.Open(apkPath)
if err != nil {
t.Fatalf("opening apk: %v", err)
}
defer apkFile.Close()

gr, err := gzip.NewReader(apkFile)
if err != nil {
t.Fatalf("creating gzip reader: %v", err)
}
defer gr.Close()

tr := tar.NewReader(gr)
var sbom io.Reader
sbomPath := fmt.Sprintf("var/lib/db/sbom/%s-%s.spdx.json", tt.name, tt.expectedVersion)
for {
hdr, err := tr.Next()
if err != nil {
t.Fatalf("reading tar header: %v", err)
}
if hdr.Name == sbomPath {
sbom = tr
break
}
}
if sbom == nil {
t.Fatalf("SBOM not found in apk: %s", sbomPath)
}

expectedSBOMPath := filepath.Join("testdata", "goldenfiles", "sboms", fmt.Sprintf("%s-%s.spdx.json", tt.name, tt.expectedVersion))
expectedSbomFile, err := os.Open(expectedSBOMPath)
if err != nil {
t.Fatalf("opening expected SBOM: %v", err)
}

expected, err := io.ReadAll(expectedSbomFile)
if err != nil {
t.Fatalf("reading expected SBOM: %v", err)
}
actual, err := io.ReadAll(sbom)
if err != nil {
t.Fatalf("reading actual SBOM: %v", err)
}

if diff := cmp.Diff(expected, actual); diff != "" {
t.Fatalf("SBOMs differ: \n%s\n", diff)
}
})
})
}
}
5 changes: 4 additions & 1 deletion pkg/build/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"gopkg.in/yaml.v3"
)

const unidentifiablePipeline = "???"

func (t *Test) Compile(ctx context.Context) error {
cfg := t.Configuration

Expand Down Expand Up @@ -324,7 +326,8 @@ func identity(p *config.Pipeline) string {
if p.Uses != "" {
return p.Uses
}
return "???"

return unidentifiablePipeline
}

func (c *Compiled) gatherDeps(ctx context.Context, pipeline *config.Pipeline) error {
Expand Down
2 changes: 1 addition & 1 deletion pkg/build/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ func (r *pipelineRunner) runPipeline(ctx context.Context, pipeline *config.Pipel
defer stop()
}

if id := identity(pipeline); id != "???" {
if id := identity(pipeline); id != unidentifiablePipeline {
log.Infof("running step %q", id)
}

Expand Down
64 changes: 64 additions & 0 deletions pkg/build/testdata/build_configs/crane.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package:
name: crane
version: 0.20.2
epoch: 1
description: Tool for interacting with remote images and registries.
copyright:
- license: Apache-2.0
dependencies:
runtime:
- ca-certificates-bundle

environment:
contents:
packages:
- busybox
- ca-certificates-bundle
- go
environment:
CGO_ENABLED: "0"

pipeline:
- uses: git-checkout
with:
repository: https://github.com/google/go-containerregistry
tag: v${{package.version}}
expected-commit: c195f151efe3369874c72662cd69ad43ee485128

- uses: go/build
with:
packages: ./cmd/crane
ldflags: -s -w -buildid= -X github.com/google/go-containerregistry/cmd/crane/cmd.Version=${{package.version}} -X github.com/google/go-containerregistry/pkg/v1/remote/transport.Version=${{package.version}}
output: crane

- uses: strip

update:
enabled: true
github:
identifier: google/go-containerregistry
strip-prefix: v

test:
environment:
contents:
packages:
- jq
pipeline:
- name: Verify Crane installation
runs: |
crane version || exit 1
crane --help
- name: Fetch and verify manifest
runs: |
crane manifest chainguard/static | jq '.schemaVersion' | grep '2' || exit 1
- name: List tags for a public image
runs: |
crane ls chainguard/static | grep -E 'latest|v[0-9]+.[0-9]+.[0-9]+' || exit 1
- name: Validate image existence
runs: |
crane digest chainguard/static:latest && echo "Image exists" || exit 1
- name: Pull and save an image locally
runs: |
crane pull chainguard/static:latest static_latest.tar || exit 1
[ -f static_latest.tar ] || exit 1
87 changes: 87 additions & 0 deletions pkg/build/testdata/goldenfiles/sboms/crane-0.20.2-r1.spdx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
"SPDXID": "SPDXRef-DOCUMENT",
"name": "apk-crane-0.20.2-r1",
"spdxVersion": "SPDX-2.3",
"creationInfo": {
"created": "0001-01-01T00:00:00Z",
"creators": [
"Tool: melange (devel)",
"Organization: Chainguard, Inc"
],
"licenseListVersion": "3.22"
},
"dataLicense": "CC0-1.0",
"documentNamespace": "https://spdx.org/spdxdocs/chainguard/melange/f5eb3a5b5887866fa76fe4eb2b7b5165f07c9505",
"documentDescribes": [
"SPDXRef-Package-crane-0.20.2-r1"
],
"packages": [
{
"SPDXID": "SPDXRef-Package-crane-0.20.2-r1",
"name": "crane",
"versionInfo": "0.20.2-r1",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "Apache-2.0",
"downloadLocation": "NOASSERTION",
"originator": "Organization: Wolfi",
"supplier": "Organization: Wolfi",
"copyrightText": "\n",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:apk/wolfi/[email protected]?arch=aarch64",
"referenceType": "purl"
}
]
},
{
"SPDXID": "SPDXRef-Package-testdata-buildC95configs-crane.yaml-c0ffee",
"name": "testdata/build_configs/crane.yaml",
"versionInfo": "c0ffee",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"originator": "Organization: Wolfi",
"supplier": "Organization: Wolfi",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:github/wolfi-dev/os@c0ffee#testdata/build_configs/crane.yaml",
"referenceType": "purl"
}
]
},
{
"SPDXID": "SPDXRef-Package-go-containerregistry-v0.20.2",
"name": "go-containerregistry",
"versionInfo": "v0.20.2",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "Apache-2.0",
"downloadLocation": "NOASSERTION",
"originator": "Organization: Google",
"supplier": "Organization: Google",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:github/google/[email protected]",
"referenceType": "purl"
}
]
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-Package-crane-0.20.2-r1",
"relationshipType": "DESCRIBED_BY",
"relatedSpdxElement": "SPDXRef-Package-testdata-buildC95configs-crane.yaml-c0ffee"
},
{
"spdxElementId": "SPDXRef-Package-crane-0.20.2-r1",
"relationshipType": "GENERATED_FROM",
"relatedSpdxElement": "SPDXRef-Package-go-containerregistry-v0.20.2"
}
]
}

0 comments on commit f039274

Please sign in to comment.