Merge pull request #208 from crazy-max/build-summary
Some checks failed
ci-subaction / list-targets-group (push) Has been cancelled
ci-subaction / list-targets-group-matrix (push) Has been cancelled
ci / bake (default) (push) Has been cancelled
ci / bake (release) (push) Has been cancelled
ci / error-msg (push) Has been cancelled
ci / error-check (push) Has been cancelled
ci / standalone (push) Has been cancelled
ci / source (push) Has been cancelled
ci / provenance () (push) Has been cancelled
ci / provenance (builder-id=foo) (push) Has been cancelled
ci / provenance (false) (push) Has been cancelled
ci / provenance (mode=max) (push) Has been cancelled
ci / provenance (true) (push) Has been cancelled
ci / sbom (/tmp/bake-build, binary) (push) Has been cancelled
ci / sbom (type=image,name=localhost:5000/name/app:latest,push=true, image) (push) Has been cancelled
ci / set (push) Has been cancelled
ci / group (push) Has been cancelled
ci / docker-config-malformed (push) Has been cancelled
ci / proxy-docker-config (push) Has been cancelled
ci / proxy-buildkitd (push) Has been cancelled
ci / git-context (push) Has been cancelled
ci / git-context-and-local (push) Has been cancelled
ci / multi-output (push) Has been cancelled
ci / load-and-push (push) Has been cancelled
ci / disable-summary (push) Has been cancelled
Some checks failed
ci-subaction / list-targets-group (push) Has been cancelled
ci-subaction / list-targets-group-matrix (push) Has been cancelled
ci / bake (default) (push) Has been cancelled
ci / bake (release) (push) Has been cancelled
ci / error-msg (push) Has been cancelled
ci / error-check (push) Has been cancelled
ci / standalone (push) Has been cancelled
ci / source (push) Has been cancelled
ci / provenance () (push) Has been cancelled
ci / provenance (builder-id=foo) (push) Has been cancelled
ci / provenance (false) (push) Has been cancelled
ci / provenance (mode=max) (push) Has been cancelled
ci / provenance (true) (push) Has been cancelled
ci / sbom (/tmp/bake-build, binary) (push) Has been cancelled
ci / sbom (type=image,name=localhost:5000/name/app:latest,push=true, image) (push) Has been cancelled
ci / set (push) Has been cancelled
ci / group (push) Has been cancelled
ci / docker-config-malformed (push) Has been cancelled
ci / proxy-docker-config (push) Has been cancelled
ci / proxy-buildkitd (push) Has been cancelled
ci / git-context (push) Has been cancelled
ci / git-context-and-local (push) Has been cancelled
ci / multi-output (push) Has been cancelled
ci / load-and-push (push) Has been cancelled
ci / disable-summary (push) Has been cancelled
export build record and generate summary
This commit is contained in:
24
.github/workflows/ci.yml
vendored
24
.github/workflows/ci.yml
vendored
@@ -522,3 +522,27 @@ jobs:
|
|||||||
name: Check docker
|
name: Check docker
|
||||||
run: |
|
run: |
|
||||||
docker image inspect localhost:5000/name/app:latest
|
docker image inspect localhost:5000/name/app:latest
|
||||||
|
|
||||||
|
disable-summary:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
-
|
||||||
|
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 }}
|
||||||
|
network=host
|
||||||
|
-
|
||||||
|
name: Build
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
./test/config.hcl
|
||||||
|
targets: app
|
||||||
|
env:
|
||||||
|
DOCKER_BUILD_NO_SUMMARY: true
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ ___
|
|||||||
* [Customizing](#customizing)
|
* [Customizing](#customizing)
|
||||||
* [inputs](#inputs)
|
* [inputs](#inputs)
|
||||||
* [outputs](#outputs)
|
* [outputs](#outputs)
|
||||||
|
* [environment variables](#environment-variables)
|
||||||
* [Subactions](#subactions)
|
* [Subactions](#subactions)
|
||||||
* [`list-targets`](#list-targets)
|
* [`list-targets`](#list-targets)
|
||||||
* [Contributing](#contributing)
|
* [Contributing](#contributing)
|
||||||
@@ -255,6 +256,12 @@ The following outputs are available
|
|||||||
|------------|----------|----------------------------|
|
|------------|----------|----------------------------|
|
||||||
| `targets` | List/CSV | List of extracted targest |
|
| `targets` | List/CSV | List of extracted targest |
|
||||||
|
|
||||||
|
### 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 |
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Want to contribute? Awesome! You can find information about contributing to
|
Want to contribute? Awesome! You can find information about contributing to
|
||||||
|
|||||||
4
dist/index.js
generated
vendored
4
dist/index.js
generated
vendored
File diff suppressed because one or more lines are too long
2
dist/index.js.map
generated
vendored
2
dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@@ -44,6 +44,28 @@ 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;
|
||||||
|
}
|
||||||
|
if (key === 'workdir' && value === '.') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
res[key] = value;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getArgs(inputs: Inputs, definition: BakeDefinition, toolkit: Toolkit): Promise<Array<string>> {
|
export async function getArgs(inputs: Inputs, definition: BakeDefinition, toolkit: Toolkit): Promise<Array<string>> {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return [
|
return [
|
||||||
|
|||||||
84
src/main.ts
84
src/main.ts
@@ -3,11 +3,14 @@ import * as path from 'path';
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as actionsToolkit from '@docker/actions-toolkit';
|
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 {Context} from '@docker/actions-toolkit/lib/context';
|
||||||
import {Docker} from '@docker/actions-toolkit/lib/docker/docker';
|
import {Docker} from '@docker/actions-toolkit/lib/docker/docker';
|
||||||
import {Exec} from '@docker/actions-toolkit/lib/exec';
|
import {Exec} from '@docker/actions-toolkit/lib/exec';
|
||||||
import {GitHub} from '@docker/actions-toolkit/lib/github';
|
import {GitHub} from '@docker/actions-toolkit/lib/github';
|
||||||
import {Toolkit} from '@docker/actions-toolkit/lib/toolkit';
|
import {Toolkit} from '@docker/actions-toolkit/lib/toolkit';
|
||||||
|
import {Util} from '@docker/actions-toolkit/lib/util';
|
||||||
|
|
||||||
import {BakeDefinition} from '@docker/actions-toolkit/lib/types/buildx/bake';
|
import {BakeDefinition} from '@docker/actions-toolkit/lib/types/buildx/bake';
|
||||||
import {ConfigFile} from '@docker/actions-toolkit/lib/types/docker/docker';
|
import {ConfigFile} from '@docker/actions-toolkit/lib/types/docker/docker';
|
||||||
@@ -18,7 +21,12 @@ import * as stateHelper from './state-helper';
|
|||||||
actionsToolkit.run(
|
actionsToolkit.run(
|
||||||
// main
|
// main
|
||||||
async () => {
|
async () => {
|
||||||
|
const startedTime = new Date();
|
||||||
|
|
||||||
const inputs: context.Inputs = await context.getInputs();
|
const inputs: context.Inputs = await context.getInputs();
|
||||||
|
core.debug(`inputs: ${JSON.stringify(inputs)}`);
|
||||||
|
stateHelper.setInputs(inputs);
|
||||||
|
|
||||||
const toolkit = new Toolkit();
|
const toolkit = new Toolkit();
|
||||||
const gitAuthToken = process.env.BUILDX_BAKE_GIT_AUTH_TOKEN ?? inputs['github-token'];
|
const gitAuthToken = process.env.BUILDX_BAKE_GIT_AUTH_TOKEN ?? inputs['github-token'];
|
||||||
|
|
||||||
@@ -78,6 +86,7 @@ actionsToolkit.run(
|
|||||||
await core.group(`Builder info`, async () => {
|
await core.group(`Builder info`, async () => {
|
||||||
const builder = await toolkit.builder.inspect(inputs.builder);
|
const builder = await toolkit.builder.inspect(inputs.builder);
|
||||||
core.info(JSON.stringify(builder, null, 2));
|
core.info(JSON.stringify(builder, null, 2));
|
||||||
|
stateHelper.setBuilder(builder);
|
||||||
});
|
});
|
||||||
|
|
||||||
let definition: BakeDefinition | undefined;
|
let definition: BakeDefinition | undefined;
|
||||||
@@ -103,6 +112,7 @@ actionsToolkit.run(
|
|||||||
if (!definition) {
|
if (!definition) {
|
||||||
throw new Error('Bake definition not set');
|
throw new Error('Bake definition not set');
|
||||||
}
|
}
|
||||||
|
stateHelper.setBakeDefinition(definition);
|
||||||
|
|
||||||
const args: string[] = await context.getArgs(inputs, definition, toolkit);
|
const args: string[] = await context.getArgs(inputs, definition, toolkit);
|
||||||
const buildCmd = await toolkit.buildx.getCommand(args);
|
const buildCmd = await toolkit.buildx.getCommand(args);
|
||||||
@@ -119,13 +129,14 @@ actionsToolkit.run(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let err: Error | undefined;
|
||||||
await Exec.getExecOutput(buildCmd.command, buildCmd.args, {
|
await Exec.getExecOutput(buildCmd.command, buildCmd.args, {
|
||||||
cwd: inputs.workdir,
|
cwd: inputs.workdir,
|
||||||
env: buildEnv,
|
env: buildEnv,
|
||||||
ignoreReturnCode: true
|
ignoreReturnCode: true
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
if (res.stderr.length > 0 && res.exitCode != 0) {
|
if (res.stderr.length > 0 && res.exitCode != 0) {
|
||||||
throw new Error(`buildx bake failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
|
err = Error(`buildx bake failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -137,9 +148,55 @@ actionsToolkit.run(
|
|||||||
core.setOutput('metadata', metadatadt);
|
core.setOutput('metadata', metadatadt);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
await core.group(`Build references`, async () => {
|
||||||
|
const refs = await buildRefs(toolkit, startedTime, inputs.builder);
|
||||||
|
if (refs) {
|
||||||
|
for (const ref of refs) {
|
||||||
|
core.info(ref);
|
||||||
|
}
|
||||||
|
stateHelper.setBuildRefs(refs);
|
||||||
|
} else {
|
||||||
|
core.warning('No build refs found');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// post
|
// post
|
||||||
async () => {
|
async () => {
|
||||||
|
if (stateHelper.buildRefs.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.buildRefs
|
||||||
|
});
|
||||||
|
core.info(`Build records 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,
|
||||||
|
bakeDefinition: stateHelper.bakeDefinition
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
core.warning(e.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
if (stateHelper.tmpDir.length > 0) {
|
if (stateHelper.tmpDir.length > 0) {
|
||||||
await core.group(`Removing temp folder ${stateHelper.tmpDir}`, async () => {
|
await core.group(`Removing temp folder ${stateHelper.tmpDir}`, async () => {
|
||||||
fs.rmSync(stateHelper.tmpDir, {recursive: true});
|
fs.rmSync(stateHelper.tmpDir, {recursive: true});
|
||||||
@@ -147,3 +204,28 @@ actionsToolkit.run(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
async function buildRefs(toolkit: Toolkit, since: Date, builder?: string): Promise<Array<string>> {
|
||||||
|
// get refs from metadata file
|
||||||
|
const metaRefs = toolkit.buildxBake.resolveRefs();
|
||||||
|
if (metaRefs) {
|
||||||
|
return metaRefs;
|
||||||
|
}
|
||||||
|
// 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 res = Buildx.refs({
|
||||||
|
dir: Buildx.refsDir,
|
||||||
|
builderName: builder,
|
||||||
|
since: since
|
||||||
|
});
|
||||||
|
const refs: Array<string> = [];
|
||||||
|
for (const ref in res) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(res, ref)) {
|
||||||
|
refs.push(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return refs;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,32 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
|
|
||||||
|
import {BakeDefinition} from '@docker/actions-toolkit/lib/types/buildx/bake';
|
||||||
|
import {BuilderInfo} from '@docker/actions-toolkit/lib/types/buildx/builder';
|
||||||
|
|
||||||
|
import {Inputs, sanitizeInputs} from './context';
|
||||||
|
|
||||||
export const tmpDir = process.env['STATE_tmpDir'] || '';
|
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 bakeDefinition = process.env['STATE_bakeDefinition'] ? <BakeDefinition>JSON.parse(process.env['STATE_bakeDefinition']) : undefined;
|
||||||
|
export const buildRefs = process.env['STATE_buildRefs'] ? process.env['STATE_buildRefs'].split(',') : [];
|
||||||
|
|
||||||
export function setTmpDir(tmpDir: string) {
|
export function setTmpDir(tmpDir: string) {
|
||||||
core.saveState('tmpDir', tmpDir);
|
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 setBakeDefinition(bakeDefinition: BakeDefinition) {
|
||||||
|
core.saveState('bakeDefinition', JSON.stringify(bakeDefinition));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setBuildRefs(buildRefs: Array<string>) {
|
||||||
|
core.saveState('buildRefs', buildRefs.join(','));
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user