Merge pull request #1120 from crazy-max/build-summary

export build record and generate summary
This commit is contained in:
CrazyMax 2024-06-17 09:23:45 +02:00 committed by GitHub
commit c382f710d3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 142 additions and 5 deletions

View file

@ -1344,3 +1344,26 @@ jobs:
name: Check docker
run: |
docker image inspect localhost:5000/name/app:latest
disable-summary:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
with:
path: action
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ inputs.buildx-version || env.BUILDX_VERSION }}
driver-opts: |
image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }}
-
name: Build
uses: ./action
with:
file: ./test/Dockerfile
env:
DOCKER_BUILD_NO_SUMMARY: true

View file

@ -37,6 +37,7 @@ ___
* [Customizing](#customizing)
* [inputs](#inputs)
* [outputs](#outputs)
* [environment variables](#environment-variables)
* [Troubleshooting](#troubleshooting)
* [Contributing](#contributing)
@ -256,6 +257,12 @@ The following outputs are available:
| `digest` | String | Image digest |
| `metadata` | JSON | Build result metadata |
### environment variables
| Name | Type | Description |
|---------------------------|------|-------------------------------------------------------------------------------------------------------------------|
| `DOCKER_BUILD_NO_SUMMARY` | Bool | If `true`, [build summary](https://docs.docker.com/build/ci/github-actions/build-summary/) generation is disabled |
## Troubleshooting
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md)

4
dist/index.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

2
dist/licenses.txt generated vendored
View file

@ -2807,7 +2807,7 @@ minimatch
ISC
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Copyright (c) 2011-2023 Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above

View file

@ -79,6 +79,25 @@ export async function getInputs(): Promise<Inputs> {
};
}
export function sanitizeInputs(inputs: Inputs) {
const res = {};
for (const key of Object.keys(inputs)) {
if (key === 'github-token') {
continue;
}
const value: string | string[] | boolean = inputs[key];
if (typeof value === 'boolean' && value === false) {
continue;
} else if (Array.isArray(value) && value.length === 0) {
continue;
} else if (!value) {
continue;
}
res[key] = value;
}
return res;
}
export async function getArgs(inputs: Inputs, toolkit: Toolkit): Promise<Array<string>> {
const context = handlebars.compile(inputs.context)({
defaultContext: Context.gitContext()

View file

@ -4,11 +4,14 @@ import * as stateHelper from './state-helper';
import * as core from '@actions/core';
import * as actionsToolkit from '@docker/actions-toolkit';
import {Buildx} from '@docker/actions-toolkit/lib/buildx/buildx';
import {History as BuildxHistory} from '@docker/actions-toolkit/lib/buildx/history';
import {Context} from '@docker/actions-toolkit/lib/context';
import {Docker} from '@docker/actions-toolkit/lib/docker/docker';
import {Exec} from '@docker/actions-toolkit/lib/exec';
import {GitHub} from '@docker/actions-toolkit/lib/github';
import {Toolkit} from '@docker/actions-toolkit/lib/toolkit';
import {Util} from '@docker/actions-toolkit/lib/util';
import {ConfigFile} from '@docker/actions-toolkit/lib/types/docker/docker';
@ -17,8 +20,10 @@ import * as context from './context';
actionsToolkit.run(
// main
async () => {
const startedTime = new Date();
const inputs: context.Inputs = await context.getInputs();
core.debug(`inputs: ${JSON.stringify(inputs)}`);
stateHelper.setInputs(inputs);
const toolkit = new Toolkit();
@ -78,6 +83,7 @@ actionsToolkit.run(
await core.group(`Builder info`, async () => {
const builder = await toolkit.builder.inspect(inputs.builder);
core.info(JSON.stringify(builder, null, 2));
stateHelper.setBuilder(builder);
});
const args: string[] = await context.getArgs(inputs, toolkit);
@ -87,11 +93,12 @@ actionsToolkit.run(
core.debug(`buildCmd.command: ${buildCmd.command}`);
core.debug(`buildCmd.args: ${JSON.stringify(buildCmd.args)}`);
let err: Error | undefined;
await Exec.getExecOutput(buildCmd.command, buildCmd.args, {
ignoreReturnCode: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
err = Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
}
});
@ -118,9 +125,52 @@ actionsToolkit.run(
core.setOutput('metadata', metadatadt);
});
}
await core.group(`Reference`, async () => {
const ref = await buildRef(toolkit, startedTime, inputs.builder);
if (ref) {
core.info(ref);
stateHelper.setBuildRef(ref);
} else {
core.warning('No build ref found');
}
});
if (err) {
throw err;
}
},
// post
async () => {
if (stateHelper.buildRef.length > 0) {
await core.group(`Generating build summary`, async () => {
if (process.env.DOCKER_BUILD_NO_SUMMARY && Util.parseBool(process.env.DOCKER_BUILD_NO_SUMMARY)) {
core.info('Summary disabled');
return;
}
if (stateHelper.builder && stateHelper.builder.driver === 'cloud') {
core.info('Summary is not yet supported with Docker Build Cloud');
return;
}
try {
const buildxHistory = new BuildxHistory();
const exportRes = await buildxHistory.export({
refs: [stateHelper.buildRef]
});
core.info(`Build record exported to ${exportRes.dockerbuildFilename} (${Util.formatFileSize(exportRes.dockerbuildSize)})`);
const uploadRes = await GitHub.uploadArtifact({
filename: exportRes.dockerbuildFilename,
mimeType: 'application/gzip',
retentionDays: 90
});
await GitHub.writeBuildSummary({
exportRes: exportRes,
uploadRes: uploadRes,
inputs: stateHelper.inputs
});
} catch (e) {
core.warning(e.message);
}
});
}
if (stateHelper.tmpDir.length > 0) {
await core.group(`Removing temp folder ${stateHelper.tmpDir}`, async () => {
fs.rmSync(stateHelper.tmpDir, {recursive: true});
@ -128,3 +178,22 @@ actionsToolkit.run(
}
}
);
async function buildRef(toolkit: Toolkit, since: Date, builder?: string): Promise<string> {
// get ref from metadata file
const ref = toolkit.buildxBuild.resolveRef();
if (ref) {
return ref;
}
// otherwise, look for the very first build ref since the build has started
if (!builder) {
const currentBuilder = await toolkit.builder.inspect();
builder = currentBuilder.name;
}
const refs = Buildx.refs({
dir: Buildx.refsDir,
builderName: builder,
since: since
});
return Object.keys(refs).length > 0 ? Object.keys(refs)[0] : '';
}

View file

@ -1,7 +1,26 @@
import * as core from '@actions/core';
import {BuilderInfo} from '@docker/actions-toolkit/lib/types/buildx/builder';
import {Inputs, sanitizeInputs} from './context';
export const tmpDir = process.env['STATE_tmpDir'] || '';
export const inputs = process.env['STATE_inputs'] ? JSON.parse(process.env['STATE_inputs']) : undefined;
export const builder = process.env['STATE_builder'] ? <BuilderInfo>JSON.parse(process.env['STATE_builder']) : undefined;
export const buildRef = process.env['STATE_buildRef'] || '';
export function setTmpDir(tmpDir: string) {
core.saveState('tmpDir', tmpDir);
}
export function setInputs(inputs: Inputs) {
core.saveState('inputs', JSON.stringify(sanitizeInputs(inputs)));
}
export function setBuilder(builder: BuilderInfo) {
core.saveState('builder', JSON.stringify(builder));
}
export function setBuildRef(buildRef: string) {
core.saveState('buildRef', buildRef);
}