Skip to content

Commit 97edad0

Browse files
c0d1ngm0nk3ypbusko
andauthored
feat(cnbbuild): add build summary (#4506)
Co-authored-by: Pavel Busko <[email protected]>
1 parent 086232d commit 97edad0

File tree

4 files changed

+144
-43
lines changed

4 files changed

+144
-43
lines changed

cmd/cnbBuild.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -326,13 +326,18 @@ func callCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData,
326326
return errors.Wrap(err, "failed to process config")
327327
}
328328

329+
buildSummary := cnbutils.NewBuildSummary(dockerImage, utils)
329330
for _, c := range mergedConfigs {
330-
err = runCnbBuild(&c, telemetry, utils, commonPipelineEnvironment, httpClient)
331+
imageSummary := &cnbutils.ImageSummary{}
332+
err = runCnbBuild(&c, telemetry, imageSummary, utils, commonPipelineEnvironment, httpClient)
331333
if err != nil {
332334
return err
333335
}
336+
buildSummary.Images = append(buildSummary.Images, imageSummary)
334337
}
335338

339+
buildSummary.Print()
340+
336341
if config.CreateBOM {
337342
err = syft.GenerateSBOM(config.SyftDownloadURL, filepath.Dir(config.DockerConfigJSON), utils, utils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags)
338343
if err != nil {
@@ -344,7 +349,7 @@ func callCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData,
344349
return telemetry.Export()
345350
}
346351

347-
func runCnbBuild(config *cnbBuildOptions, telemetry *buildpacks.Telemetry, utils cnbutils.BuildUtils, commonPipelineEnvironment *cnbBuildCommonPipelineEnvironment, httpClient piperhttp.Sender) error {
352+
func runCnbBuild(config *cnbBuildOptions, telemetry *buildpacks.Telemetry, imageSummary *cnbutils.ImageSummary, utils cnbutils.BuildUtils, commonPipelineEnvironment *cnbBuildCommonPipelineEnvironment, httpClient piperhttp.Sender) error {
348353
err := cleanDir("/layers", utils)
349354
if err != nil {
350355
log.SetErrorCategory(log.ErrorBuild)
@@ -383,6 +388,7 @@ func runCnbBuild(config *cnbBuildOptions, telemetry *buildpacks.Telemetry, utils
383388
log.SetErrorCategory(log.ErrorConfiguration)
384389
return errors.Wrap(err, "failed to check if project descriptor exists")
385390
}
391+
imageSummary.ProjectDescriptor = projDescPath
386392

387393
var projectID string
388394
if projDescPath != "" {
@@ -439,6 +445,7 @@ func runCnbBuild(config *cnbBuildOptions, telemetry *buildpacks.Telemetry, utils
439445

440446
if config.BuildEnvVars != nil && len(config.BuildEnvVars) > 0 {
441447
log.Entry().Infof("Setting custom environment variables: '%v'", config.BuildEnvVars)
448+
imageSummary.AddEnv(config.BuildEnvVars)
442449
err = cnbutils.CreateEnvFiles(utils, platformPath, config.BuildEnvVars)
443450
if err != nil {
444451
log.SetErrorCategory(log.ErrorConfiguration)
@@ -571,6 +578,7 @@ func runCnbBuild(config *cnbBuildOptions, telemetry *buildpacks.Telemetry, utils
571578
}
572579
commonPipelineEnvironment.container.imageDigest = digest
573580
commonPipelineEnvironment.container.imageDigests = append(commonPipelineEnvironment.container.imageDigests, digest)
581+
imageSummary.ImageRef = fmt.Sprintf("%s@%s", containerImage, digest)
574582

575583
if len(config.PreserveFiles) > 0 {
576584
if pathType != buildpacks.PathEnumArchive {

cmd/cnbBuild_test.go

+45-41
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,12 @@ func TestRunCnbBuild(t *testing.T) {
133133
require.NoError(t, err)
134134
runner := utils.ExecMockRunner
135135
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
136-
assertLifecycleCalls(t, runner, 1)
137-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
138-
assert.Contains(t, runner.Calls[0].Params, "-run-image")
139-
assert.Contains(t, runner.Calls[0].Params, "my-run-image")
140-
assert.Contains(t, runner.Calls[0].Params, "-process-type")
141-
assert.Contains(t, runner.Calls[0].Params, "my-process")
136+
assertLifecycleCalls(t, runner, 2)
137+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
138+
assert.Contains(t, runner.Calls[1].Params, "-run-image")
139+
assert.Contains(t, runner.Calls[1].Params, "my-run-image")
140+
assert.Contains(t, runner.Calls[1].Params, "-process-type")
141+
assert.Contains(t, runner.Calls[1].Params, "my-process")
142142
assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
143143
assert.Equal(t, "my-image:0.0.1", commonPipelineEnvironment.container.imageNameTag)
144144
assert.Equal(t, `{"cnbBuild":[{"dockerImage":"paketobuildpacks/builder:base"}]}`, commonPipelineEnvironment.custom.buildSettingsInfo)
@@ -169,8 +169,8 @@ func TestRunCnbBuild(t *testing.T) {
169169
require.NoError(t, err)
170170
runner := utils.ExecMockRunner
171171
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
172-
assertLifecycleCalls(t, runner, 1)
173-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, "io-buildpacks-my-app", config.ContainerImageTag))
172+
assertLifecycleCalls(t, runner, 2)
173+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, "io-buildpacks-my-app", config.ContainerImageTag))
174174
assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
175175
assert.Equal(t, "io-buildpacks-my-app:0.0.1", commonPipelineEnvironment.container.imageNameTag)
176176

@@ -204,8 +204,8 @@ func TestRunCnbBuild(t *testing.T) {
204204
require.NoError(t, err)
205205
runner := utils.ExecMockRunner
206206
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
207-
assertLifecycleCalls(t, runner, 1)
208-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
207+
assertLifecycleCalls(t, runner, 2)
208+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
209209
assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
210210
assert.Equal(t, "my-image:0.0.1", commonPipelineEnvironment.container.imageNameTag)
211211
})
@@ -229,8 +229,8 @@ func TestRunCnbBuild(t *testing.T) {
229229
require.NoError(t, err)
230230
runner := utils.ExecMockRunner
231231
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
232-
assertLifecycleCalls(t, runner, 1)
233-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
232+
assertLifecycleCalls(t, runner, 2)
233+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
234234
assert.Equal(t, fmt.Sprintf("https://%s", config.ContainerRegistryURL), commonPipelineEnvironment.container.registryURL)
235235
assert.Equal(t, "my-image:0.0.1", commonPipelineEnvironment.container.imageNameTag)
236236
})
@@ -258,11 +258,11 @@ func TestRunCnbBuild(t *testing.T) {
258258
require.NoError(t, err)
259259
runner := utils.ExecMockRunner
260260
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
261-
assert.Equal(t, creatorPath, runner.Calls[0].Exec)
262-
assert.Contains(t, runner.Calls[0].Params, "/tmp/buildpacks")
263-
assert.Contains(t, runner.Calls[0].Params, "/tmp/buildpacks/order.toml")
264-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
265-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:latest", config.ContainerRegistryURL, config.ContainerImageName))
261+
assert.Equal(t, creatorPath, runner.Calls[1].Exec)
262+
assert.Contains(t, runner.Calls[1].Params, "/tmp/buildpacks")
263+
assert.Contains(t, runner.Calls[1].Params, "/tmp/buildpacks/order.toml")
264+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
265+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:latest", config.ContainerRegistryURL, config.ContainerImageName))
266266

267267
copiedFileExists, _ := utils.FileExists("/tmp/config.json")
268268
assert.True(t, copiedFileExists)
@@ -293,11 +293,11 @@ func TestRunCnbBuild(t *testing.T) {
293293
require.NoError(t, err)
294294
runner := utils.ExecMockRunner
295295
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
296-
assert.Equal(t, creatorPath, runner.Calls[0].Exec)
297-
assert.Contains(t, runner.Calls[0].Params, "/tmp/buildpacks")
298-
assert.Contains(t, runner.Calls[0].Params, "/tmp/buildpacks/order.toml")
299-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
300-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:latest", config.ContainerRegistryURL, config.ContainerImageName))
296+
assert.Equal(t, creatorPath, runner.Calls[1].Exec)
297+
assert.Contains(t, runner.Calls[1].Params, "/tmp/buildpacks")
298+
assert.Contains(t, runner.Calls[1].Params, "/tmp/buildpacks/order.toml")
299+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
300+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:latest", config.ContainerRegistryURL, config.ContainerImageName))
301301

302302
copiedFileExists, _ := utils.FileExists("/tmp/config.json")
303303
assert.True(t, copiedFileExists)
@@ -327,11 +327,11 @@ func TestRunCnbBuild(t *testing.T) {
327327
require.NoError(t, err)
328328
runner := utils.ExecMockRunner
329329
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
330-
assert.Equal(t, creatorPath, runner.Calls[0].Exec)
331-
assert.Contains(t, runner.Calls[0].Params, "/tmp/buildpacks")
332-
assert.Contains(t, runner.Calls[0].Params, "/tmp/buildpacks/order.toml")
333-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
334-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:latest", config.ContainerRegistryURL, config.ContainerImageName))
330+
assert.Equal(t, creatorPath, runner.Calls[1].Exec)
331+
assert.Contains(t, runner.Calls[1].Params, "/tmp/buildpacks")
332+
assert.Contains(t, runner.Calls[1].Params, "/tmp/buildpacks/order.toml")
333+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
334+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:latest", config.ContainerRegistryURL, config.ContainerImageName))
335335

336336
copiedFileExists, _ := utils.FileExists("/tmp/config.json")
337337
assert.True(t, copiedFileExists)
@@ -372,8 +372,8 @@ func TestRunCnbBuild(t *testing.T) {
372372
runner := utils.ExecMockRunner
373373
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
374374
assert.Contains(t, runner.Env, fmt.Sprintf("SSL_CERT_FILE=%s", caCertsTmpFile))
375-
assertLifecycleCalls(t, runner, 1)
376-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
375+
assertLifecycleCalls(t, runner, 2)
376+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
377377
})
378378

379379
t.Run("success case (additionalTags)", func(t *testing.T) {
@@ -394,11 +394,11 @@ func TestRunCnbBuild(t *testing.T) {
394394
require.NoError(t, err)
395395

396396
runner := utils.ExecMockRunner
397-
assertLifecycleCalls(t, runner, 1)
398-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
399-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:3", config.ContainerRegistryURL, config.ContainerImageName))
400-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:3.1", config.ContainerRegistryURL, config.ContainerImageName))
401-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:3.1.5", config.ContainerRegistryURL, config.ContainerImageName))
397+
assertLifecycleCalls(t, runner, 2)
398+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
399+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:3", config.ContainerRegistryURL, config.ContainerImageName))
400+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:3.1", config.ContainerRegistryURL, config.ContainerImageName))
401+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:3.1.5", config.ContainerRegistryURL, config.ContainerImageName))
402402
})
403403

404404
t.Run("success case: build environment variables", func(t *testing.T) {
@@ -434,7 +434,7 @@ func TestRunCnbBuild(t *testing.T) {
434434
err := callCnbBuild(&config, &telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
435435

436436
require.NoError(t, err)
437-
assertLifecycleCalls(t, utils.ExecMockRunner, 1)
437+
assertLifecycleCalls(t, utils.ExecMockRunner, 2)
438438

439439
assetBuildEnv(t, utils, "OPTIONS_KEY", "OPTIONS_VALUE")
440440
assetBuildEnv(t, utils, "PROJECT_DESCRIPTOR_KEY", "PROJECT_DESCRIPTOR_VALUE")
@@ -461,7 +461,7 @@ func TestRunCnbBuild(t *testing.T) {
461461
require.NoError(t, err)
462462

463463
runner := utils.ExecMockRunner
464-
assertLifecycleCalls(t, runner, 1)
464+
assertLifecycleCalls(t, runner, 2)
465465

466466
assert.True(t, utils.FilesMock.HasCreatedSymlink("/jenkins/target", "/workspace/target"))
467467
})
@@ -485,7 +485,7 @@ func TestRunCnbBuild(t *testing.T) {
485485
require.NoError(t, err)
486486

487487
runner := utils.ExecMockRunner
488-
assertLifecycleCalls(t, runner, 1)
488+
assertLifecycleCalls(t, runner, 2)
489489

490490
assert.False(t, utils.FilesMock.HasCreatedSymlink("/jenkins/target", "/workspace/target"))
491491
})
@@ -682,7 +682,7 @@ uri = "some-buildpack"`))
682682
require.NoError(t, err)
683683
runner := utils.ExecMockRunner
684684
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
685-
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
685+
assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
686686
assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
687687
assert.Equal(t, "my-image:3.1.5", commonPipelineEnvironment.container.imageNameTag)
688688
})
@@ -772,11 +772,15 @@ uri = "some-buildpack"
772772
require.Equal(t, expectedImageCount, len(customData.Data))
773773

774774
runner := utils.ExecMockRunner
775-
require.Equal(t, expectedImageCount, len(runner.Calls))
775+
require.Equal(t, expectedImageCount, len(runner.Calls)-1)
776776
for i, call := range runner.Calls {
777-
assert.Equal(t, 4, len(customData.Data[i].AdditionalTags))
777+
if i == 0 { // first call is -version
778+
continue
779+
}
780+
lifecycleCall := i - 1
781+
assert.Equal(t, 4, len(customData.Data[lifecycleCall].AdditionalTags))
778782
assertLifecycleCalls(t, runner, i+1)
779-
containerImageName := fmt.Sprintf("my-image-%d", i)
783+
containerImageName := fmt.Sprintf("my-image-%d", lifecycleCall)
780784
assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, containerImageName, config.ContainerImageTag))
781785
assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:3", config.ContainerRegistryURL, containerImageName))
782786
assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:3.1", config.ContainerRegistryURL, containerImageName))

integration/integration_cnb_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,30 @@ func TestCNBIntegrationProjectDescriptor(t *testing.T) {
115115
)
116116
container.terminate(t)
117117
}
118+
func TestCNBIntegrationBuildSummary(t *testing.T) {
119+
t.Parallel()
120+
ctx := context.Background()
121+
registryContainer := setupDockerRegistry(t, ctx)
122+
defer registryContainer.Terminate(ctx)
123+
124+
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
125+
Image: baseBuilder,
126+
User: "cnb",
127+
TestDir: []string{"testdata", "TestCnbIntegration", "project"},
128+
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
129+
})
130+
131+
err := container.whenRunningPiperCommand("cnbBuild", "--noTelemetry", "--verbose", "--containerImageName", "not-found", "--containerImageTag", "0.0.1", "--containerRegistryUrl", registryURL)
132+
assert.NoError(t, err)
133+
134+
container.assertHasOutput(t, "*** Build Summary ***",
135+
" Builder: \"paketobuildpacks/builder:base\"",
136+
" Lifecycle: \"0.16.4+683e1b46\"",
137+
" Image: \"localhost:5000/not-found@sha256:",
138+
" Project descriptor: \"/project/project.toml\"",
139+
" Env: \"TMPDIR, BP_NODE_VERSION\"")
140+
container.terminate(t)
141+
}
118142

119143
func TestCNBIntegrationZipPath(t *testing.T) {
120144
t.Parallel()

pkg/cnbutils/summary.go

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package cnbutils
2+
3+
import (
4+
"bytes"
5+
"strings"
6+
7+
"github.com/SAP/jenkins-library/pkg/log"
8+
)
9+
10+
type BuildSummary struct {
11+
Builder string
12+
LifecycleVersion string
13+
14+
Images []*ImageSummary
15+
}
16+
17+
func (bs *BuildSummary) Print() {
18+
log.Entry().Infoln("")
19+
log.Entry().Info("*** Build Summary ***")
20+
log.Entry().Infof(" Builder: %q", bs.Builder)
21+
log.Entry().Infof(" Lifecycle: %q", bs.LifecycleVersion)
22+
log.Entry().Infof(" %d image(s) build:", len(bs.Images))
23+
log.Entry().Infoln("")
24+
for _, image := range bs.Images {
25+
image.Print()
26+
log.Entry().Infoln("")
27+
}
28+
}
29+
30+
func NewBuildSummary(builder string, utils BuildUtils) *BuildSummary {
31+
return &BuildSummary{
32+
Builder: builder,
33+
LifecycleVersion: lifecycleVersion(utils),
34+
}
35+
}
36+
37+
type ImageSummary struct {
38+
ImageRef string
39+
ProjectDescriptor string
40+
Buildpacks []string
41+
EnvVars []string
42+
}
43+
44+
func (is *ImageSummary) Print() {
45+
log.Entry().Infof(" Image: %q", is.ImageRef)
46+
log.Entry().Infof(" Project descriptor: %q", is.ProjectDescriptor)
47+
log.Entry().Infof(" Env: %q", strings.Join(is.EnvVars, ", "))
48+
}
49+
50+
func (is *ImageSummary) AddEnv(env map[string]interface{}) {
51+
for key := range env {
52+
is.EnvVars = append(is.EnvVars, key)
53+
}
54+
}
55+
56+
func lifecycleVersion(utils BuildUtils) string {
57+
currentStdout := utils.GetStdout()
58+
59+
buf := bytes.NewBufferString("")
60+
utils.Stdout(buf)
61+
_ = utils.RunExecutable("/cnb/lifecycle/lifecycle", "-version")
62+
utils.Stdout(currentStdout)
63+
64+
return strings.Trim(buf.String(), "\n")
65+
}

0 commit comments

Comments
 (0)