switch to actions-toolkit implementation

Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax 2023-02-20 11:11:15 +01:00
parent 7944f9a25b
commit d8823bfaed
No known key found for this signature in database
GPG key ID: 3248E46B6BB8C7F7
18 changed files with 847 additions and 1083 deletions

View file

@ -1,282 +0,0 @@
import {parse} from 'csv-parse/sync';
import fs from 'fs';
import path from 'path';
import * as semver from 'semver';
import * as exec from '@actions/exec';
import * as context from './context';
export type Builder = {
name?: string;
driver?: string;
nodes: Node[];
};
export type Node = {
name?: string;
endpoint?: string;
'driver-opts'?: Array<string>;
status?: string;
'buildkitd-flags'?: string;
buildkit?: string;
platforms?: string;
};
export async function getImageIDFile(): Promise<string> {
return path.join(context.tmpDir(), 'iidfile').split(path.sep).join(path.posix.sep);
}
export async function getImageID(): Promise<string | undefined> {
const iidFile = await getImageIDFile();
if (!fs.existsSync(iidFile)) {
return undefined;
}
return fs.readFileSync(iidFile, {encoding: 'utf-8'}).trim();
}
export async function getMetadataFile(): Promise<string> {
return path.join(context.tmpDir(), 'metadata-file').split(path.sep).join(path.posix.sep);
}
export async function getMetadata(): Promise<string | undefined> {
const metadataFile = await getMetadataFile();
if (!fs.existsSync(metadataFile)) {
return undefined;
}
const content = fs.readFileSync(metadataFile, {encoding: 'utf-8'}).trim();
if (content === 'null') {
return undefined;
}
return content;
}
export async function getDigest(metadata: string | undefined): Promise<string | undefined> {
if (metadata === undefined) {
return undefined;
}
const metadataJSON = JSON.parse(metadata);
if (metadataJSON['containerimage.digest']) {
return metadataJSON['containerimage.digest'];
}
return undefined;
}
export async function getSecretString(kvp: string): Promise<string> {
return getSecret(kvp, false);
}
export async function getSecretFile(kvp: string): Promise<string> {
return getSecret(kvp, true);
}
export async function getSecret(kvp: string, file: boolean): Promise<string> {
const delimiterIndex = kvp.indexOf('=');
const key = kvp.substring(0, delimiterIndex);
let value = kvp.substring(delimiterIndex + 1);
if (key.length == 0 || value.length == 0) {
throw new Error(`${kvp} is not a valid secret`);
}
if (file) {
if (!fs.existsSync(value)) {
throw new Error(`secret file ${value} not found`);
}
value = fs.readFileSync(value, {encoding: 'utf-8'});
}
const secretFile = context.tmpNameSync({
tmpdir: context.tmpDir()
});
fs.writeFileSync(secretFile, value);
return `id=${key},src=${secretFile}`;
}
export function isLocalOrTarExporter(outputs: string[]): boolean {
const records = parse(outputs.join(`\n`), {
delimiter: ',',
trim: true,
columns: false,
relaxColumnCount: true
});
for (const record of records) {
// Local if no type is defined
// https://github.com/docker/buildx/blob/d2bf42f8b4784d83fde17acb3ed84703ddc2156b/build/output.go#L29-L43
if (record.length == 1 && !record[0].startsWith('type=')) {
return true;
}
for (const [key, value] of record.map(chunk => chunk.split('=').map(item => item.trim()))) {
if (key == 'type' && (value == 'local' || value == 'tar')) {
return true;
}
}
}
return false;
}
export function hasGitAuthToken(secrets: string[]): boolean {
for (const secret of secrets) {
if (secret.startsWith('GIT_AUTH_TOKEN=')) {
return true;
}
}
return false;
}
export async function isAvailable(standalone?: boolean): Promise<boolean> {
const cmd = getCommand([], standalone);
return await exec
.getExecOutput(cmd.command, cmd.args, {
ignoreReturnCode: true,
silent: true
})
.then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
return false;
}
return res.exitCode == 0;
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.catch(error => {
return false;
});
}
export async function satisfiesBuildKitVersion(builderName: string, range: string, standalone?: boolean): Promise<boolean> {
const builderInspect = await inspect(builderName, standalone);
for (const node of builderInspect.nodes) {
if (!node.buildkit) {
return false;
}
// BuildKit version reported by moby is in the format of `v0.11.0-moby`
if (builderInspect.driver == 'docker' && !node.buildkit.endsWith('-moby')) {
return false;
}
const version = node.buildkit.replace(/-moby$/, '');
if (!semver.satisfies(version, range)) {
return false;
}
}
return true;
}
async function inspect(name: string, standalone?: boolean): Promise<Builder> {
const cmd = getCommand(['inspect', name], standalone);
return await exec
.getExecOutput(cmd.command, cmd.args, {
ignoreReturnCode: true,
silent: true
})
.then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.trim());
}
return parseInspect(res.stdout);
});
}
async function parseInspect(data: string): Promise<Builder> {
const builder: Builder = {
nodes: []
};
let node: Node = {};
for (const line of data.trim().split(`\n`)) {
const [key, ...rest] = line.split(':');
const value = rest.map(v => v.trim()).join(':');
if (key.length == 0 || value.length == 0) {
continue;
}
switch (key.toLowerCase()) {
case 'name': {
if (builder.name == undefined) {
builder.name = value;
} else {
if (Object.keys(node).length > 0) {
builder.nodes.push(node);
node = {};
}
node.name = value;
}
break;
}
case 'driver': {
builder.driver = value;
break;
}
case 'endpoint': {
node.endpoint = value;
break;
}
case 'driver options': {
node['driver-opts'] = (value.match(/(\w+)="([^"]*)"/g) || []).map(v => v.replace(/^(.*)="(.*)"$/g, '$1=$2'));
break;
}
case 'status': {
node.status = value;
break;
}
case 'flags': {
node['buildkitd-flags'] = value;
break;
}
case 'buildkit': {
node.buildkit = value;
break;
}
case 'platforms': {
let platforms: Array<string> = [];
// if a preferred platform is being set then use only these
// https://docs.docker.com/engine/reference/commandline/buildx_inspect/#get-information-about-a-builder-instance
if (value.includes('*')) {
for (const platform of value.split(', ')) {
if (platform.includes('*')) {
platforms.push(platform.replace('*', ''));
}
}
} else {
// otherwise set all platforms available
platforms = value.split(', ');
}
node.platforms = platforms.join(',');
break;
}
}
}
if (Object.keys(node).length > 0) {
builder.nodes.push(node);
}
return builder;
}
export async function getVersion(standalone?: boolean): Promise<string> {
const cmd = getCommand(['version'], standalone);
return await exec
.getExecOutput(cmd.command, cmd.args, {
ignoreReturnCode: true,
silent: true
})
.then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.trim());
}
return parseVersion(res.stdout.trim());
});
}
export function parseVersion(stdout: string): string {
const matches = /\sv?([0-9a-f]{7}|[0-9.]+)/.exec(stdout);
if (!matches) {
throw new Error(`Cannot parse buildx version`);
}
return matches[1];
}
export function satisfies(version: string, range: string): boolean {
return semver.satisfies(version, range) || /^[0-9a-f]{7}$/.exec(version) !== null;
}
export function getCommand(args: Array<string>, standalone?: boolean) {
return {
command: standalone ? 'buildx' : 'docker',
args: standalone ? args : ['buildx', ...args]
};
}

View file

@ -1,14 +1,11 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as tmp from 'tmp';
import * as buildx from './buildx';
import * as core from '@actions/core';
import * as github from '@actions/github';
import {parse} from 'csv-parse/sync';
import * as handlebars from 'handlebars';
let _defaultContext, _tmpDir: string;
import {Context} from '@docker/actions-toolkit/lib/context';
import {GitHub} from '@docker/actions-toolkit/lib/github';
import {Inputs as BuildxInputs} from '@docker/actions-toolkit/lib/buildx/inputs';
import {Toolkit} from '@docker/actions-toolkit/lib/toolkit';
import {Util} from '@docker/actions-toolkit/lib/util';
export interface Inputs {
addHosts: string[];
@ -43,105 +40,78 @@ export interface Inputs {
githubToken: string;
}
export function defaultContext(): string {
if (!_defaultContext) {
let ref = github.context.ref;
if (github.context.sha && ref && !ref.startsWith('refs/')) {
ref = `refs/heads/${github.context.ref}`;
}
if (github.context.sha && !ref.startsWith(`refs/pull/`)) {
ref = github.context.sha;
}
_defaultContext = `${process.env.GITHUB_SERVER_URL || 'https://github.com'}/${github.context.repo.owner}/${github.context.repo.repo}.git#${ref}`;
}
return _defaultContext;
}
export function tmpDir(): string {
if (!_tmpDir) {
_tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-build-push-')).split(path.sep).join(path.posix.sep);
}
return _tmpDir;
}
export function tmpNameSync(options?: tmp.TmpNameOptions): string {
return tmp.tmpNameSync(options);
}
export function provenanceBuilderID(): string {
return `${process.env.GITHUB_SERVER_URL || 'https://github.com'}/${github.context.repo.owner}/${github.context.repo.repo}/actions/runs/${github.context.runId}`;
}
export async function getInputs(defaultContext: string): Promise<Inputs> {
export async function getInputs(): Promise<Inputs> {
return {
addHosts: await getInputList('add-hosts'),
allow: await getInputList('allow'),
attests: await getInputList('attests', true),
buildArgs: await getInputList('build-args', true),
buildContexts: await getInputList('build-contexts', true),
addHosts: Util.getInputList('add-hosts'),
allow: Util.getInputList('allow'),
attests: Util.getInputList('attests', {ignoreComma: true}),
buildArgs: Util.getInputList('build-args', {ignoreComma: true}),
buildContexts: Util.getInputList('build-contexts', {ignoreComma: true}),
builder: core.getInput('builder'),
cacheFrom: await getInputList('cache-from', true),
cacheTo: await getInputList('cache-to', true),
cacheFrom: Util.getInputList('cache-from', {ignoreComma: true}),
cacheTo: Util.getInputList('cache-to', {ignoreComma: true}),
cgroupParent: core.getInput('cgroup-parent'),
context: core.getInput('context') || defaultContext,
context: core.getInput('context') || Context.gitContext(),
file: core.getInput('file'),
labels: await getInputList('labels', true),
labels: Util.getInputList('labels', {ignoreComma: true}),
load: core.getBooleanInput('load'),
network: core.getInput('network'),
noCache: core.getBooleanInput('no-cache'),
noCacheFilters: await getInputList('no-cache-filters'),
outputs: await getInputList('outputs', true),
platforms: await getInputList('platforms'),
noCacheFilters: Util.getInputList('no-cache-filters'),
outputs: Util.getInputList('outputs', {ignoreComma: true}),
platforms: Util.getInputList('platforms'),
provenance: getProvenanceInput('provenance'),
pull: core.getBooleanInput('pull'),
push: core.getBooleanInput('push'),
sbom: core.getInput('sbom'),
secrets: await getInputList('secrets', true),
secretFiles: await getInputList('secret-files', true),
secrets: Util.getInputList('secrets', {ignoreComma: true}),
secretFiles: Util.getInputList('secret-files', {ignoreComma: true}),
shmSize: core.getInput('shm-size'),
ssh: await getInputList('ssh'),
tags: await getInputList('tags'),
ssh: Util.getInputList('ssh'),
tags: Util.getInputList('tags'),
target: core.getInput('target'),
ulimit: await getInputList('ulimit', true),
ulimit: Util.getInputList('ulimit', {ignoreComma: true}),
githubToken: core.getInput('github-token')
};
}
export async function getArgs(inputs: Inputs, defaultContext: string, buildxVersion: string, standalone?: boolean): Promise<Array<string>> {
const context = handlebars.compile(inputs.context)({defaultContext});
export async function getArgs(inputs: Inputs, toolkit: Toolkit): Promise<Array<string>> {
const context = handlebars.compile(inputs.context)({
defaultContext: Context.gitContext()
});
// prettier-ignore
return [
...await getBuildArgs(inputs, defaultContext, context, buildxVersion, standalone),
...await getCommonArgs(inputs, buildxVersion),
...await getBuildArgs(inputs, context, toolkit),
...await getCommonArgs(inputs, toolkit),
context
];
}
async function getBuildArgs(inputs: Inputs, defaultContext: string, context: string, buildxVersion: string, standalone?: boolean): Promise<Array<string>> {
async function getBuildArgs(inputs: Inputs, context: string, toolkit: Toolkit): Promise<Array<string>> {
const args: Array<string> = ['build'];
await asyncForEach(inputs.addHosts, async addHost => {
await Util.asyncForEach(inputs.addHosts, async addHost => {
args.push('--add-host', addHost);
});
if (inputs.allow.length > 0) {
args.push('--allow', inputs.allow.join(','));
}
if (buildx.satisfies(buildxVersion, '>=0.10.0')) {
await asyncForEach(inputs.attests, async attest => {
if (await toolkit.buildx.versionSatisfies('>=0.10.0')) {
await Util.asyncForEach(inputs.attests, async attest => {
args.push('--attest', attest);
});
}
await asyncForEach(inputs.buildArgs, async buildArg => {
await Util.asyncForEach(inputs.buildArgs, async buildArg => {
args.push('--build-arg', buildArg);
});
if (buildx.satisfies(buildxVersion, '>=0.8.0')) {
await asyncForEach(inputs.buildContexts, async buildContext => {
if (await toolkit.buildx.versionSatisfies('>=0.8.0')) {
await Util.asyncForEach(inputs.buildContexts, async buildContext => {
args.push('--build-context', buildContext);
});
}
await asyncForEach(inputs.cacheFrom, async cacheFrom => {
await Util.asyncForEach(inputs.cacheFrom, async cacheFrom => {
args.push('--cache-from', cacheFrom);
});
await asyncForEach(inputs.cacheTo, async cacheTo => {
await Util.asyncForEach(inputs.cacheTo, async cacheTo => {
args.push('--cache-to', cacheTo);
});
if (inputs.cgroupParent) {
@ -150,29 +120,29 @@ async function getBuildArgs(inputs: Inputs, defaultContext: string, context: str
if (inputs.file) {
args.push('--file', inputs.file);
}
if (!buildx.isLocalOrTarExporter(inputs.outputs) && (inputs.platforms.length == 0 || buildx.satisfies(buildxVersion, '>=0.4.2'))) {
args.push('--iidfile', await buildx.getImageIDFile());
if (!BuildxInputs.hasLocalExporter(inputs.outputs) && !BuildxInputs.hasTarExporter(inputs.outputs) && (inputs.platforms.length == 0 || (await toolkit.buildx.versionSatisfies('>=0.4.2')))) {
args.push('--iidfile', toolkit.buildx.inputs.getBuildImageIDFilePath());
}
await asyncForEach(inputs.labels, async label => {
await Util.asyncForEach(inputs.labels, async label => {
args.push('--label', label);
});
await asyncForEach(inputs.noCacheFilters, async noCacheFilter => {
await Util.asyncForEach(inputs.noCacheFilters, async noCacheFilter => {
args.push('--no-cache-filter', noCacheFilter);
});
await asyncForEach(inputs.outputs, async output => {
await Util.asyncForEach(inputs.outputs, async output => {
args.push('--output', output);
});
if (inputs.platforms.length > 0) {
args.push('--platform', inputs.platforms.join(','));
}
if (buildx.satisfies(buildxVersion, '>=0.10.0')) {
if (await toolkit.buildx.versionSatisfies('>=0.10.0')) {
if (inputs.provenance) {
args.push('--provenance', inputs.provenance);
} else if ((await buildx.satisfiesBuildKitVersion(inputs.builder, '>=0.11.0', standalone)) && !hasDockerExport(inputs)) {
} else if ((await toolkit.buildkit.versionSatisfies(inputs.builder, '>=0.11.0')) && !BuildxInputs.hasDockerExporter(inputs.outputs, inputs.load)) {
// if provenance not specified and BuildKit version compatible for
// attestation, set default provenance. Also needs to make sure user
// doesn't want to explicitly load the image to docker.
if (fromPayload('repository.private') !== false) {
if (GitHub.context.payload.repository?.private ?? false) {
// if this is a private repository, we set the default provenance
// attributes being set in buildx: https://github.com/docker/buildx/blob/fb27e3f919dcbf614d7126b10c2bc2d0b1927eb6/build/build.go#L603
args.push('--provenance', getProvenanceAttrs(`mode=min,inline-only=true`));
@ -185,42 +155,42 @@ async function getBuildArgs(inputs: Inputs, defaultContext: string, context: str
args.push('--sbom', inputs.sbom);
}
}
await asyncForEach(inputs.secrets, async secret => {
await Util.asyncForEach(inputs.secrets, async secret => {
try {
args.push('--secret', await buildx.getSecretString(secret));
args.push('--secret', toolkit.buildx.inputs.resolveBuildSecretString(secret));
} catch (err) {
core.warning(err.message);
}
});
await asyncForEach(inputs.secretFiles, async secretFile => {
await Util.asyncForEach(inputs.secretFiles, async secretFile => {
try {
args.push('--secret', await buildx.getSecretFile(secretFile));
args.push('--secret', toolkit.buildx.inputs.resolveBuildSecretFile(secretFile));
} catch (err) {
core.warning(err.message);
}
});
if (inputs.githubToken && !buildx.hasGitAuthToken(inputs.secrets) && context.startsWith(defaultContext)) {
args.push('--secret', await buildx.getSecretString(`GIT_AUTH_TOKEN=${inputs.githubToken}`));
if (inputs.githubToken && !BuildxInputs.hasGitAuthTokenSecret(inputs.secrets) && context.startsWith(Context.gitContext())) {
args.push('--secret', toolkit.buildx.inputs.resolveBuildSecretString(`GIT_AUTH_TOKEN=${inputs.githubToken}`));
}
if (inputs.shmSize) {
args.push('--shm-size', inputs.shmSize);
}
await asyncForEach(inputs.ssh, async ssh => {
await Util.asyncForEach(inputs.ssh, async ssh => {
args.push('--ssh', ssh);
});
await asyncForEach(inputs.tags, async tag => {
await Util.asyncForEach(inputs.tags, async tag => {
args.push('--tag', tag);
});
if (inputs.target) {
args.push('--target', inputs.target);
}
await asyncForEach(inputs.ulimit, async ulimit => {
await Util.asyncForEach(inputs.ulimit, async ulimit => {
args.push('--ulimit', ulimit);
});
return args;
}
async function getCommonArgs(inputs: Inputs, buildxVersion: string): Promise<Array<string>> {
async function getCommonArgs(inputs: Inputs, toolkit: Toolkit): Promise<Array<string>> {
const args: Array<string> = [];
if (inputs.builder) {
args.push('--builder', inputs.builder);
@ -228,8 +198,8 @@ async function getCommonArgs(inputs: Inputs, buildxVersion: string): Promise<Arr
if (inputs.load) {
args.push('--load');
}
if (buildx.satisfies(buildxVersion, '>=0.6.0')) {
args.push('--metadata-file', await buildx.getMetadataFile());
if (await toolkit.buildx.versionSatisfies('>=0.6.0')) {
args.push('--metadata-file', toolkit.buildx.inputs.getBuildMetadataFilePath());
}
if (inputs.network) {
args.push('--network', inputs.network);
@ -246,68 +216,14 @@ async function getCommonArgs(inputs: Inputs, buildxVersion: string): Promise<Arr
return args;
}
export async function getInputList(name: string, ignoreComma?: boolean): Promise<string[]> {
const res: Array<string> = [];
const items = core.getInput(name);
if (items == '') {
return res;
}
const records = await parse(items, {
columns: false,
relaxQuotes: true,
relaxColumnCount: true,
skipEmptyLines: true
});
for (const record of records as Array<string[]>) {
if (record.length == 1) {
res.push(record[0]);
continue;
} else if (!ignoreComma) {
res.push(...record);
continue;
}
res.push(record.join(','));
}
return res.filter(item => item).map(pat => pat.trim());
}
export const asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function fromPayload(path: string): any {
return select(github.context.payload, path);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function select(obj: any, path: string): any {
if (!obj) {
return undefined;
}
const i = path.indexOf('.');
if (i < 0) {
return obj[path];
}
const key = path.slice(0, i);
return select(obj[key], path.slice(i + 1));
}
function getProvenanceInput(name: string): string {
const input = core.getInput(name);
if (!input) {
// if input is not set, default values will be set later.
return input;
}
const builderID = provenanceBuilderID();
try {
return core.getBooleanInput(name) ? `builder-id=${builderID}` : 'false';
return core.getBooleanInput(name) ? `builder-id=${Context.provenanceBuilderID()}` : 'false';
} catch (err) {
// not a valid boolean, so we assume it's a string
return getProvenanceAttrs(input);
@ -315,7 +231,6 @@ function getProvenanceInput(name: string): string {
}
function getProvenanceAttrs(input: string): string {
const builderID = provenanceBuilderID();
// parse attributes from input
const fields = parse(input, {
relaxColumnCount: true,
@ -332,30 +247,5 @@ function getProvenanceAttrs(input: string): string {
}
}
// if not add builder-id attribute
return `${input},builder-id=${builderID}`;
}
function hasDockerExport(inputs: Inputs): boolean {
if (inputs.load) {
return true;
}
for (const output of inputs.outputs) {
const fields = parse(output, {
relaxColumnCount: true,
skipEmptyLines: true
})[0];
for (const field of fields) {
const parts = field
.toString()
.split(/(?<=^[^=]+?)=/)
.map(item => item.trim());
if (parts.length != 2) {
continue;
}
if (parts[0] == 'type' && parts[1] == 'docker') {
return true;
}
}
}
return false;
return `${input},builder-id=${Context.provenanceBuilderID()}`;
}

View file

@ -1,19 +0,0 @@
import * as exec from '@actions/exec';
export async function isAvailable(): Promise<boolean> {
return await exec
.getExecOutput('docker', undefined, {
ignoreReturnCode: true,
silent: true
})
.then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
return false;
}
return res.exitCode == 0;
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.catch(error => {
return false;
});
}

View file

@ -1,9 +0,0 @@
import jwt_decode, {JwtPayload} from 'jwt-decode';
interface Jwt extends JwtPayload {
ac?: string;
}
export const parseRuntimeToken = (token: string): Jwt => {
return jwt_decode<Jwt>(token);
};

View file

@ -1,71 +1,62 @@
import * as fs from 'fs';
import * as buildx from './buildx';
import * as context from './context';
import * as docker from './docker';
import * as github from './github';
import * as stateHelper from './state-helper';
import * as core from '@actions/core';
import * as exec from '@actions/exec';
import * as actionsToolkit from '@docker/actions-toolkit';
import {Context} from '@docker/actions-toolkit/lib/context';
import {Docker} from '@docker/actions-toolkit/lib/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';
async function run(): Promise<void> {
try {
const defContext = context.defaultContext();
const inputs: context.Inputs = await context.getInputs(defContext);
import * as context from './context';
// standalone if docker cli not available
const standalone = !(await docker.isAvailable());
actionsToolkit.run(
// main
async () => {
const inputs: context.Inputs = await context.getInputs();
const toolkit = new Toolkit();
await core.group(`GitHub Actions runtime token access controls`, async () => {
const actionsRuntimeToken = process.env['ACTIONS_RUNTIME_TOKEN'];
if (actionsRuntimeToken) {
core.info(JSON.stringify(JSON.parse(github.parseRuntimeToken(actionsRuntimeToken).ac as string), undefined, 2));
} else {
core.info(`ACTIONS_RUNTIME_TOKEN not set`);
await core.group(`GitHub Actions runtime token ACs`, async () => {
try {
await GitHub.printActionsRuntimeTokenACs();
} catch (e) {
core.warning(e.message);
}
});
core.startGroup(`Docker info`);
if (standalone) {
core.info(`Docker info skipped in standalone mode`);
} else {
await exec.exec('docker', ['version'], {
failOnStdErr: false
});
await exec.exec('docker', ['info'], {
failOnStdErr: false
});
}
core.endGroup();
await core.group(`Docker info`, async () => {
try {
await Docker.printVersion();
await Docker.printInfo();
} catch (e) {
core.info(e.message);
}
});
if (!(await buildx.isAvailable(standalone))) {
if (!(await toolkit.buildx.isAvailable())) {
core.setFailed(`Docker buildx is required. See https://github.com/docker/setup-buildx-action to set up buildx.`);
return;
}
stateHelper.setTmpDir(context.tmpDir());
const buildxVersion = await buildx.getVersion(standalone);
stateHelper.setTmpDir(Context.tmpDir());
await core.group(`Buildx version`, async () => {
const versionCmd = buildx.getCommand(['version'], standalone);
await exec.exec(versionCmd.command, versionCmd.args, {
failOnStdErr: false
});
await toolkit.buildx.printVersion();
});
const args: string[] = await context.getArgs(inputs, defContext, buildxVersion, standalone);
const buildCmd = buildx.getCommand(args, standalone);
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'}`);
}
});
const args: string[] = await context.getArgs(inputs, toolkit);
const buildCmd = await toolkit.buildx.getCommand(args);
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'}`);
}
});
const imageID = await buildx.getImageID();
const metadata = await buildx.getMetadata();
const digest = await buildx.getDigest(metadata);
const imageID = await toolkit.buildx.inputs.resolveBuildImageID();
const metadata = await toolkit.buildx.inputs.resolveBuildMetadata();
const digest = await toolkit.buildx.inputs.resolveDigest();
if (imageID) {
await core.group(`ImageID`, async () => {
@ -85,21 +76,13 @@ async function run(): Promise<void> {
core.setOutput('metadata', metadata);
});
}
} catch (error) {
core.setFailed(error.message);
},
// post
async () => {
if (stateHelper.tmpDir.length > 0) {
await core.group(`Removing temp folder ${stateHelper.tmpDir}`, async () => {
fs.rmSync(stateHelper.tmpDir, {recursive: true});
});
}
}
}
async function cleanup(): Promise<void> {
if (stateHelper.tmpDir.length > 0) {
core.startGroup(`Removing temp folder ${stateHelper.tmpDir}`);
fs.rmSync(stateHelper.tmpDir, {recursive: true});
core.endGroup();
}
}
if (!stateHelper.IsPost) {
run();
} else {
cleanup();
}
);

View file

@ -1,12 +1,7 @@
import * as core from '@actions/core';
export const IsPost = !!process.env['STATE_isPost'];
export const tmpDir = process.env['STATE_tmpDir'] || '';
export function setTmpDir(tmpDir: string) {
core.saveState('tmpDir', tmpDir);
}
if (!IsPost) {
core.saveState('isPost', 'true');
}