Skip to content

Commit c382f71

Browse files
authored
Merge pull request #1120 from crazy-max/build-summary
export build record and generate summary
2 parents 86c2bd0 + 5a5b70d commit c382f71

File tree

8 files changed

+142
-5
lines changed

8 files changed

+142
-5
lines changed

.github/workflows/ci.yml

+23
Original file line numberDiff line numberDiff line change
@@ -1344,3 +1344,26 @@ jobs:
13441344
name: Check docker
13451345
run: |
13461346
docker image inspect localhost:5000/name/app:latest
1347+
1348+
disable-summary:
1349+
runs-on: ubuntu-latest
1350+
steps:
1351+
-
1352+
name: Checkout
1353+
uses: actions/checkout@v4
1354+
with:
1355+
path: action
1356+
-
1357+
name: Set up Docker Buildx
1358+
uses: docker/setup-buildx-action@v3
1359+
with:
1360+
version: ${{ inputs.buildx-version || env.BUILDX_VERSION }}
1361+
driver-opts: |
1362+
image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }}
1363+
-
1364+
name: Build
1365+
uses: ./action
1366+
with:
1367+
file: ./test/Dockerfile
1368+
env:
1369+
DOCKER_BUILD_NO_SUMMARY: true

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ ___
3737
* [Customizing](#customizing)
3838
* [inputs](#inputs)
3939
* [outputs](#outputs)
40+
* [environment variables](#environment-variables)
4041
* [Troubleshooting](#troubleshooting)
4142
* [Contributing](#contributing)
4243

@@ -256,6 +257,12 @@ The following outputs are available:
256257
| `digest` | String | Image digest |
257258
| `metadata` | JSON | Build result metadata |
258259

260+
### environment variables
261+
262+
| Name | Type | Description |
263+
|---------------------------|------|-------------------------------------------------------------------------------------------------------------------|
264+
| `DOCKER_BUILD_NO_SUMMARY` | Bool | If `true`, [build summary](https://docs.docker.com/build/ci/github-actions/build-summary/) generation is disabled |
265+
259266
## Troubleshooting
260267

261268
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md)

dist/index.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/licenses.txt

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/context.ts

+19
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,25 @@ export async function getInputs(): Promise<Inputs> {
7979
};
8080
}
8181

82+
export function sanitizeInputs(inputs: Inputs) {
83+
const res = {};
84+
for (const key of Object.keys(inputs)) {
85+
if (key === 'github-token') {
86+
continue;
87+
}
88+
const value: string | string[] | boolean = inputs[key];
89+
if (typeof value === 'boolean' && value === false) {
90+
continue;
91+
} else if (Array.isArray(value) && value.length === 0) {
92+
continue;
93+
} else if (!value) {
94+
continue;
95+
}
96+
res[key] = value;
97+
}
98+
return res;
99+
}
100+
82101
export async function getArgs(inputs: Inputs, toolkit: Toolkit): Promise<Array<string>> {
83102
const context = handlebars.compile(inputs.context)({
84103
defaultContext: Context.gitContext()

src/main.ts

+70-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ import * as stateHelper from './state-helper';
44
import * as core from '@actions/core';
55
import * as actionsToolkit from '@docker/actions-toolkit';
66

7+
import {Buildx} from '@docker/actions-toolkit/lib/buildx/buildx';
8+
import {History as BuildxHistory} from '@docker/actions-toolkit/lib/buildx/history';
79
import {Context} from '@docker/actions-toolkit/lib/context';
810
import {Docker} from '@docker/actions-toolkit/lib/docker/docker';
911
import {Exec} from '@docker/actions-toolkit/lib/exec';
1012
import {GitHub} from '@docker/actions-toolkit/lib/github';
1113
import {Toolkit} from '@docker/actions-toolkit/lib/toolkit';
14+
import {Util} from '@docker/actions-toolkit/lib/util';
1215

1316
import {ConfigFile} from '@docker/actions-toolkit/lib/types/docker/docker';
1417

@@ -17,8 +20,10 @@ import * as context from './context';
1720
actionsToolkit.run(
1821
// main
1922
async () => {
23+
const startedTime = new Date();
2024
const inputs: context.Inputs = await context.getInputs();
2125
core.debug(`inputs: ${JSON.stringify(inputs)}`);
26+
stateHelper.setInputs(inputs);
2227

2328
const toolkit = new Toolkit();
2429

@@ -78,6 +83,7 @@ actionsToolkit.run(
7883
await core.group(`Builder info`, async () => {
7984
const builder = await toolkit.builder.inspect(inputs.builder);
8085
core.info(JSON.stringify(builder, null, 2));
86+
stateHelper.setBuilder(builder);
8187
});
8288

8389
const args: string[] = await context.getArgs(inputs, toolkit);
@@ -87,11 +93,12 @@ actionsToolkit.run(
8793
core.debug(`buildCmd.command: ${buildCmd.command}`);
8894
core.debug(`buildCmd.args: ${JSON.stringify(buildCmd.args)}`);
8995

96+
let err: Error | undefined;
9097
await Exec.getExecOutput(buildCmd.command, buildCmd.args, {
9198
ignoreReturnCode: true
9299
}).then(res => {
93100
if (res.stderr.length > 0 && res.exitCode != 0) {
94-
throw new Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
101+
err = Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
95102
}
96103
});
97104

@@ -118,13 +125,75 @@ actionsToolkit.run(
118125
core.setOutput('metadata', metadatadt);
119126
});
120127
}
128+
await core.group(`Reference`, async () => {
129+
const ref = await buildRef(toolkit, startedTime, inputs.builder);
130+
if (ref) {
131+
core.info(ref);
132+
stateHelper.setBuildRef(ref);
133+
} else {
134+
core.warning('No build ref found');
135+
}
136+
});
137+
if (err) {
138+
throw err;
139+
}
121140
},
122141
// post
123142
async () => {
143+
if (stateHelper.buildRef.length > 0) {
144+
await core.group(`Generating build summary`, async () => {
145+
if (process.env.DOCKER_BUILD_NO_SUMMARY && Util.parseBool(process.env.DOCKER_BUILD_NO_SUMMARY)) {
146+
core.info('Summary disabled');
147+
return;
148+
}
149+
if (stateHelper.builder && stateHelper.builder.driver === 'cloud') {
150+
core.info('Summary is not yet supported with Docker Build Cloud');
151+
return;
152+
}
153+
try {
154+
const buildxHistory = new BuildxHistory();
155+
const exportRes = await buildxHistory.export({
156+
refs: [stateHelper.buildRef]
157+
});
158+
core.info(`Build record exported to ${exportRes.dockerbuildFilename} (${Util.formatFileSize(exportRes.dockerbuildSize)})`);
159+
const uploadRes = await GitHub.uploadArtifact({
160+
filename: exportRes.dockerbuildFilename,
161+
mimeType: 'application/gzip',
162+
retentionDays: 90
163+
});
164+
await GitHub.writeBuildSummary({
165+
exportRes: exportRes,
166+
uploadRes: uploadRes,
167+
inputs: stateHelper.inputs
168+
});
169+
} catch (e) {
170+
core.warning(e.message);
171+
}
172+
});
173+
}
124174
if (stateHelper.tmpDir.length > 0) {
125175
await core.group(`Removing temp folder ${stateHelper.tmpDir}`, async () => {
126176
fs.rmSync(stateHelper.tmpDir, {recursive: true});
127177
});
128178
}
129179
}
130180
);
181+
182+
async function buildRef(toolkit: Toolkit, since: Date, builder?: string): Promise<string> {
183+
// get ref from metadata file
184+
const ref = toolkit.buildxBuild.resolveRef();
185+
if (ref) {
186+
return ref;
187+
}
188+
// otherwise, look for the very first build ref since the build has started
189+
if (!builder) {
190+
const currentBuilder = await toolkit.builder.inspect();
191+
builder = currentBuilder.name;
192+
}
193+
const refs = Buildx.refs({
194+
dir: Buildx.refsDir,
195+
builderName: builder,
196+
since: since
197+
});
198+
return Object.keys(refs).length > 0 ? Object.keys(refs)[0] : '';
199+
}

src/state-helper.ts

+19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,26 @@
11
import * as core from '@actions/core';
22

3+
import {BuilderInfo} from '@docker/actions-toolkit/lib/types/buildx/builder';
4+
5+
import {Inputs, sanitizeInputs} from './context';
6+
37
export const tmpDir = process.env['STATE_tmpDir'] || '';
8+
export const inputs = process.env['STATE_inputs'] ? JSON.parse(process.env['STATE_inputs']) : undefined;
9+
export const builder = process.env['STATE_builder'] ? <BuilderInfo>JSON.parse(process.env['STATE_builder']) : undefined;
10+
export const buildRef = process.env['STATE_buildRef'] || '';
411

512
export function setTmpDir(tmpDir: string) {
613
core.saveState('tmpDir', tmpDir);
714
}
15+
16+
export function setInputs(inputs: Inputs) {
17+
core.saveState('inputs', JSON.stringify(sanitizeInputs(inputs)));
18+
}
19+
20+
export function setBuilder(builder: BuilderInfo) {
21+
core.saveState('builder', JSON.stringify(builder));
22+
}
23+
24+
export function setBuildRef(buildRef: string) {
25+
core.saveState('buildRef', buildRef);
26+
}

0 commit comments

Comments
 (0)