From b56d9771d7d0deaeb5497b120c988730ca6815cf Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Thu, 20 Jun 2024 13:27:40 +0200 Subject: [PATCH 1/2] util: countLines func Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- __tests__/util.test.ts | 36 ++++++++++++++++++++++++++++++++++++ src/util.ts | 4 ++++ 2 files changed, 40 insertions(+) diff --git a/__tests__/util.test.ts b/__tests__/util.test.ts index 9305a93f..6889c1be 100644 --- a/__tests__/util.test.ts +++ b/__tests__/util.test.ts @@ -380,6 +380,42 @@ describe('stringToUnicodeEntities', () => { }); }); +describe('countLines', () => { + it('counts total number of lines correctly', () => { + const text = `This + +is +a +sample + +text +with +multiple +lines`; + + const result = Util.countLines(text); + expect(result).toEqual(10); // Including empty lines + }); + it('handles edge case with empty string', () => { + const text = ''; + + const result = Util.countLines(text); + expect(result).toEqual(1); // Empty string should have 1 line + }); + it('handles edge case with single line', () => { + const text = 'Single line text'; + + const result = Util.countLines(text); + expect(result).toEqual(1); // Single line should have 1 line + }); + it('handles multiple types of line breaks', () => { + const text = `Line 1\r\nLine 2\rLine 3\nLine 4`; + + const result = Util.countLines(text); + expect(result).toEqual(4); // Different line break types should be counted correctly + }); +}); + // See: https://github.com/actions/toolkit/blob/a1b068ec31a042ff1e10a522d8fdf0b8869d53ca/packages/core/src/core.ts#L89 function getInputName(name: string): string { return `INPUT_${name.replace(/ /g, '_').toUpperCase()}`; diff --git a/src/util.ts b/src/util.ts index c781ab52..91fc2f66 100644 --- a/src/util.ts +++ b/src/util.ts @@ -185,4 +185,8 @@ export class Util { .map(char => `&#x${char.charCodeAt(0).toString(16)};`) .join(''); } + + public static countLines(input: string): number { + return input.split(/\r\n|\r|\n/).length; + } } From e7017a21b8e37064569ed694b0cfb54110d5e30a Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 19 Jun 2024 20:40:35 +0200 Subject: [PATCH 2/2] github(summary): add details to summary sections Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- __tests__/fixtures/hello-err.Dockerfile | 19 +++++ __tests__/github.test.itg.ts | 93 ++++++++++++++++++++----- src/github.ts | 57 ++++++++++----- 3 files changed, 136 insertions(+), 33 deletions(-) create mode 100644 __tests__/fixtures/hello-err.Dockerfile diff --git a/__tests__/fixtures/hello-err.Dockerfile b/__tests__/fixtures/hello-err.Dockerfile new file mode 100644 index 00000000..5bc7b366 --- /dev/null +++ b/__tests__/fixtures/hello-err.Dockerfile @@ -0,0 +1,19 @@ +# syntax=docker/dockerfile:1 + +# Copyright 2024 actions-toolkit authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM busybox:latest +ARGGG NAME=foo +RUN echo "hello $NAME" diff --git a/__tests__/github.test.itg.ts b/__tests__/github.test.itg.ts index 2163a765..12ce5619 100644 --- a/__tests__/github.test.itg.ts +++ b/__tests__/github.test.itg.ts @@ -118,29 +118,20 @@ maybe('writeBuildSummary', () => { test.each([ [ 'single', - [ - 'bake', - '-f', path.join(fixturesDir, 'hello-bake.hcl'), - 'hello' - ], + path.join(fixturesDir, 'hello-bake.hcl'), + 'hello' ], [ 'group', - [ - 'bake', - '-f', path.join(fixturesDir, 'hello-bake.hcl'), - 'hello-all' - ], + path.join(fixturesDir, 'hello-bake.hcl'), + 'hello-all' ], [ 'matrix', - [ - 'bake', - '-f', path.join(fixturesDir, 'hello-bake.hcl'), - 'hello-matrix' - ], + path.join(fixturesDir, 'hello-bake.hcl'), + 'hello-matrix' ] - ])('write bake summary %p', async (_, bargs) => { + ])('write bake summary %p', async (_, file, target) => { const buildx = new Buildx(); const bake = new Bake({buildx: buildx}); @@ -150,7 +141,9 @@ maybe('writeBuildSummary', () => { // prettier-ignore const buildCmd = await buildx.getCommand([ '--builder', process.env.CTN_BUILDER_NAME ?? 'default', - ...bargs, + 'bake', + '-f', file, + target, '--metadata-file', bake.getMetadataFilePath() ]); await Exec.exec(buildCmd.command, buildCmd.args, { @@ -159,6 +152,16 @@ maybe('writeBuildSummary', () => { })() ).resolves.not.toThrow(); + const definition = await bake.getDefinition( + { + files: [file], + targets: [target], + }, + { + cwd: fixturesDir + } + ); + const metadata = bake.resolveMetadata(); expect(metadata).toBeDefined(); const buildRefs = bake.resolveRefs(metadata); @@ -186,6 +189,62 @@ maybe('writeBuildSummary', () => { uploadRes: uploadRes, inputs: { files: path.join(fixturesDir, 'hello-bake.hcl') + }, + bakeDefinition: definition + }); + }); + + it('fails with dockerfile syntax issue', async () => { + const startedTime = new Date(); + const buildx = new Buildx(); + const build = new Build({buildx: buildx}); + + fs.mkdirSync(tmpDir, {recursive: true}); + await expect( + (async () => { + // prettier-ignore + const buildCmd = await buildx.getCommand([ + '--builder', process.env.CTN_BUILDER_NAME ?? 'default', + 'build', + '-f', path.join(fixturesDir, 'hello-err.Dockerfile'), + fixturesDir, + '--metadata-file', build.getMetadataFilePath() + ]); + await Exec.exec(buildCmd.command, buildCmd.args); + })() + ).rejects.toThrow(); + + const refs = Buildx.refs({ + dir: Buildx.refsDir, + builderName: process.env.CTN_BUILDER_NAME ?? 'default', + since: startedTime + }); + expect(refs).toBeDefined(); + expect(Object.keys(refs).length).toBeGreaterThan(0); + + const history = new History({buildx: buildx}); + const exportRes = await history.export({ + refs: [Object.keys(refs)[0] ?? ''] + }); + expect(exportRes).toBeDefined(); + expect(exportRes?.dockerbuildFilename).toBeDefined(); + expect(exportRes?.dockerbuildSize).toBeDefined(); + expect(exportRes?.summaries).toBeDefined(); + + const uploadRes = await GitHub.uploadArtifact({ + filename: exportRes?.dockerbuildFilename, + mimeType: 'application/gzip', + retentionDays: 1 + }); + expect(uploadRes).toBeDefined(); + expect(uploadRes?.url).toBeDefined(); + + await GitHub.writeBuildSummary({ + exportRes: exportRes, + uploadRes: uploadRes, + inputs: { + context: fixturesDir, + file: path.join(fixturesDir, 'hello-err.Dockerfile') } }); }); diff --git a/src/github.ts b/src/github.ts index 5fd1d5a8..a087fb5a 100644 --- a/src/github.ts +++ b/src/github.ts @@ -229,7 +229,7 @@ export class GitHub { // prettier-ignore const sum = core.summary - .addHeading('Docker Build summary', 1) + .addHeading('Docker Build summary', 2) .addRaw(`

`) .addRaw(`For a detailed look at the build, download the following build record archive and import it into Docker Desktop's Builds view. `) .addBreak() @@ -246,8 +246,8 @@ export class GitHub { .addRaw(addLink('Let us know', 'https://docs.docker.com/feedback/gha-build-summary')) .addRaw('

'); - sum.addHeading('Preview', 2); - + // Preview + sum.addRaw(`Preview`).addBreak().addRaw('

'); const summaryTableData: Array> = [ [ {header: true, data: 'ID'}, @@ -257,7 +257,7 @@ export class GitHub { {header: true, data: 'Duration'} ] ]; - let summaryError: string | undefined; + let buildError: string | undefined; for (const ref in opts.exportRes.summaries) { if (Object.prototype.hasOwnProperty.call(opts.exportRes.summaries, ref)) { const summary = opts.exportRes.summaries[ref]; @@ -270,28 +270,53 @@ export class GitHub { {data: summary.duration} ]); if (summary.error) { - summaryError = summary.error; + buildError = summary.error; } } } sum.addTable([...summaryTableData]); - if (summaryError) { - sum.addHeading('Error', 4); - sum.addCodeBlock(summaryError, 'text'); + sum.addRaw(`

`); + + // Build error + if (buildError) { + sum.addRaw(`
`); + if (Util.countLines(buildError) > 10) { + // prettier-ignore + sum + .addRaw(`
Error`) + .addCodeBlock(buildError, 'text') + .addRaw(`
`); + } else { + // prettier-ignore + sum + .addRaw(`Error`) + .addBreak() + .addRaw(`

`) + .addCodeBlock(buildError, 'text') + .addRaw(`

`); + } + sum.addRaw(`
`); } + // Build inputs if (opts.inputs) { - sum.addHeading('Build inputs', 2).addCodeBlock( - jsyaml.dump(opts.inputs, { - indent: 2, - lineWidth: -1 - }), - 'yaml' - ); + // prettier-ignore + sum.addRaw(`
Build inputs`) + .addCodeBlock( + jsyaml.dump(opts.inputs, { + indent: 2, + lineWidth: -1 + }), 'yaml' + ) + .addRaw(`
`); } + // Bake definition if (opts.bakeDefinition) { - sum.addHeading('Bake definition', 2).addCodeBlock(JSON.stringify(opts.bakeDefinition, null, 2), 'json'); + // prettier-ignore + sum.addRaw(`
Bake definition`) + .addCodeBlock(JSON.stringify(opts.bakeDefinition, null, 2), 'json') + .addRaw(`
`); } core.info(`Writing summary`);