Add digest output

Fix platforms and allow inputs

Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax 2020-08-17 22:18:15 +02:00
parent 44d05b9191
commit 38c1f188ca
No known key found for this signature in database
GPG Key ID: 3248E46B6BB8C7F7
8 changed files with 244 additions and 68 deletions

@ -11,7 +11,54 @@ on:
- v2-working-branch # remove when merged to master - v2-working-branch # remove when merged to master
jobs: jobs:
main: single:
runs-on: ubuntu-latest
steps:
-
name: Run local registry
run: |
docker run -d -p 5000:5000 registry:2
-
name: Checkout
uses: actions/checkout@v2.3.1
-
name: Set up QEMU
uses: ./setup-qemu/ # change to docker/setup-qemu-action@master
with:
platforms: all
-
name: Set up Docker Buildx
id: buildx
uses: ./setup-buildx/ # change to docker/setup-buildx-action@master
with:
driver-opt: network=host
buildkitd-flags: --allow-insecure-entitlement security.insecure
-
name: Build and push
id: docker_build
uses: ./
with:
context: ./test
file: ./test/Dockerfile
builder: ${{ steps.buildx.outputs.name }}
allow: network.host,security.insecure
push: true
tags: |
localhost:5000/name/app:latest
localhost:5000/name/app:1.0.0
-
name: Inspect
run: |
docker buildx imagetools inspect localhost:5000/name/app:1.0.0
-
name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
-
name: Dump context
if: always()
uses: crazy-max/ghaction-dump-context@v1
multi:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false

@ -32,7 +32,56 @@ on:
tags: tags:
jobs: jobs:
buildx: main:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
with:
platforms: all
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
uses: crazy-max/ghaction-docker-login@v1 # switch to docker/login-action@v1 when available
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
builder: ${{ steps.buildx.outputs.name }}
push: true
tags: |
user/app:latest
user/app:1.0.0
-
name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
```
### Multi-platform image
```yaml
name: ci
on:
pull_request:
branches: master
push:
branches: master
tags:
jobs:
multi:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- -
@ -58,7 +107,6 @@ jobs:
uses: docker/build-push-action@v2 uses: docker/build-push-action@v2
with: with:
builder: ${{ steps.buildx.outputs.name }} builder: ${{ steps.buildx.outputs.name }}
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/ppc64le,linux/s390x
push: true push: true
tags: | tags: |
user/app:latest user/app:latest

15
__tests__/buildx.test.ts Normal file

@ -0,0 +1,15 @@
import fs from 'fs';
import * as buildx from '../src/buildx';
const digest = 'sha256:bfb45ab72e46908183546477a08f8867fc40cebadd00af54b071b097aed127a9';
describe('getImageID', () => {
it('matches', async () => {
const imageIDFile = await buildx.getImageIDFile();
console.log(`imageIDFile: ${imageIDFile}`);
await fs.writeFileSync(imageIDFile, digest);
const imageID = await buildx.getImageID();
console.log(`imageID: ${imageID}`);
expect(imageID).toEqual(digest);
});
});

@ -19,13 +19,13 @@ inputs:
required: false required: false
default: './Dockerfile' default: './Dockerfile'
build-args: build-args:
description: "Newline-delimited list of build-time variables" description: "List of build-time variables"
required: false required: false
labels: labels:
description: "Newline-delimited list of metadata for an image" description: "List of metadata for an image"
required: false required: false
tags: tags:
description: "Newline-delimited list of tags" description: "List of tags"
required: false required: false
pull: pull:
description: "Always attempt to pull a newer version of the image" description: "Always attempt to pull a newer version of the image"
@ -35,14 +35,14 @@ inputs:
description: "Sets the target stage to build" description: "Sets the target stage to build"
required: false required: false
allow: allow:
description: "Allow extra privileged entitlement (eg. network.host,security.insecure)" description: "List of extra privileged entitlement (eg. network.host,security.insecure)"
required: false required: false
no-cache: no-cache:
description: "Do not use cache when building the image" description: "Do not use cache when building the image"
required: false required: false
default: 'false' default: 'false'
platforms: platforms:
description: "Comma-delimited list of target platforms for build" description: "List of target platforms for build"
required: false required: false
load: load:
description: "Load is a shorthand for --output=type=docker" description: "Load is a shorthand for --output=type=docker"
@ -53,13 +53,13 @@ inputs:
required: false required: false
default: 'false' default: 'false'
outputs: outputs:
description: "Newline-delimited list of output destinations (format: type=local,dest=path)" description: "List of output destinations (format: type=local,dest=path)"
required: false required: false
cache-from: cache-from:
description: "Newline-delimited list of external cache sources for buildx (eg. user/app:cache, type=local,src=path/to/dir)" description: "List of external cache sources for buildx (eg. user/app:cache, type=local,src=path/to/dir)"
required: false required: false
cache-to: cache-to:
description: "Newline-delimited list of cache export destinations for buildx (eg. user/app:cache, type=local,dest=path/to/dir)" description: "List of cache export destinations for buildx (eg. user/app:cache, type=local,dest=path/to/dir)"
required: false required: false
outputs: outputs:

80
dist/index.js generated vendored

@ -1025,6 +1025,12 @@ function run() {
core.info(`🏃 Starting build...`); core.info(`🏃 Starting build...`);
const args = yield context_1.getArgs(inputs); const args = yield context_1.getArgs(inputs);
yield exec.exec('docker', args); yield exec.exec('docker', args);
const imageID = yield buildx.getImageID();
if (imageID) {
core.info('🛒 Extracting digest...');
core.info(`${imageID}`);
core.setOutput('digest', imageID);
}
} }
catch (error) { catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
@ -1405,8 +1411,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.asyncForEach = exports.getInputList = exports.getArgs = exports.getInputs = void 0; exports.asyncForEach = exports.getInputList = exports.getArgs = exports.getInputs = exports.tmpDir = void 0;
const fs = __importStar(__webpack_require__(747));
const os = __importStar(__webpack_require__(87));
const path = __importStar(__webpack_require__(622));
const buildx = __importStar(__webpack_require__(982));
const core = __importStar(__webpack_require__(470)); const core = __importStar(__webpack_require__(470));
exports.tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-build-push-'));
function getInputs() { function getInputs() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
return { return {
@ -1440,24 +1451,6 @@ function getArgs(inputs) {
}); });
} }
exports.getArgs = getArgs; exports.getArgs = getArgs;
function getCommonArgs(inputs) {
return __awaiter(this, void 0, void 0, function* () {
let args = [];
if (inputs.noCache) {
args.push('--no-cache');
}
if (inputs.pull) {
args.push('--pull');
}
if (inputs.load) {
args.push('--load');
}
if (inputs.push) {
args.push('--push');
}
return args;
});
}
function getBuildArgs(inputs) { function getBuildArgs(inputs) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let args = ['build']; let args = ['build'];
@ -1473,12 +1466,15 @@ function getBuildArgs(inputs) {
if (inputs.target) { if (inputs.target) {
args.push('--target', inputs.target); args.push('--target', inputs.target);
} }
if (inputs.allow) { if (inputs.allow.length > 0) {
args.push('--allow', inputs.allow.join(',')); args.push('--allow', inputs.allow.join(','));
} }
if (inputs.platforms) { if (inputs.platforms.length > 0) {
args.push('--platform', inputs.platforms.join(',')); args.push('--platform', inputs.platforms.join(','));
} }
else {
args.push('--iidfile', yield buildx.getImageIDFile());
}
yield exports.asyncForEach(inputs.outputs, (output) => __awaiter(this, void 0, void 0, function* () { yield exports.asyncForEach(inputs.outputs, (output) => __awaiter(this, void 0, void 0, function* () {
args.push('--output', output); args.push('--output', output);
})); }));
@ -1494,6 +1490,24 @@ function getBuildArgs(inputs) {
return args; return args;
}); });
} }
function getCommonArgs(inputs) {
return __awaiter(this, void 0, void 0, function* () {
let args = [];
if (inputs.noCache) {
args.push('--no-cache');
}
if (inputs.pull) {
args.push('--pull');
}
if (inputs.load) {
args.push('--load');
}
if (inputs.push) {
args.push('--push');
}
return args;
});
}
function getInputList(name) { function getInputList(name) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const items = core.getInput(name); const items = core.getInput(name);
@ -1838,9 +1852,31 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
step((generator = generator.apply(thisArg, _arguments || [])).next()); step((generator = generator.apply(thisArg, _arguments || [])).next());
}); });
}; };
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.use = exports.isAvailable = void 0; exports.use = exports.isAvailable = exports.getImageID = exports.getImageIDFile = void 0;
const fs_1 = __importDefault(__webpack_require__(747));
const path_1 = __importDefault(__webpack_require__(622));
const context = __importStar(__webpack_require__(482));
const exec = __importStar(__webpack_require__(807)); const exec = __importStar(__webpack_require__(807));
function getImageIDFile() {
return __awaiter(this, void 0, void 0, function* () {
return path_1.default.join(context.tmpDir, 'iidfile');
});
}
exports.getImageIDFile = getImageIDFile;
function getImageID() {
return __awaiter(this, void 0, void 0, function* () {
const iidFile = yield getImageIDFile();
if (!fs_1.default.existsSync(iidFile)) {
return undefined;
}
return fs_1.default.readFileSync(iidFile, { encoding: 'utf-8' });
});
}
exports.getImageID = getImageID;
function isAvailable() { function isAvailable() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
return yield exec.exec(`docker`, ['buildx'], true).then(res => { return yield exec.exec(`docker`, ['buildx'], true).then(res => {

@ -1,5 +1,20 @@
import fs from 'fs';
import path from 'path';
import * as context from './context';
import * as exec from './exec'; import * as exec from './exec';
export async function getImageIDFile(): Promise<string> {
return path.join(context.tmpDir, 'iidfile');
}
export async function getImageID(): Promise<string | undefined> {
const iidFile = await getImageIDFile();
if (!fs.existsSync(iidFile)) {
return undefined;
}
return fs.readFileSync(iidFile, {encoding: 'utf-8'});
}
export async function isAvailable(): Promise<Boolean> { export async function isAvailable(): Promise<Boolean> {
return await exec.exec(`docker`, ['buildx'], true).then(res => { return await exec.exec(`docker`, ['buildx'], true).then(res => {
if (res.stderr != '' && !res.success) { if (res.stderr != '' && !res.success) {

@ -1,5 +1,11 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as buildx from './buildx';
import * as core from '@actions/core'; import * as core from '@actions/core';
export const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-build-push-'));
export interface Inputs { export interface Inputs {
context: string; context: string;
file: string; file: string;
@ -48,6 +54,43 @@ export async function getArgs(inputs: Inputs): Promise<Array<string>> {
return args; return args;
} }
async function getBuildArgs(inputs: Inputs): Promise<Array<string>> {
let args: Array<string> = ['build'];
await asyncForEach(inputs.buildArgs, async buildArg => {
args.push('--build-arg', buildArg);
});
await asyncForEach(inputs.labels, async label => {
args.push('--label', label);
});
await asyncForEach(inputs.tags, async tag => {
args.push('--tag', tag);
});
if (inputs.target) {
args.push('--target', inputs.target);
}
if (inputs.allow.length > 0) {
args.push('--allow', inputs.allow.join(','));
}
if (inputs.platforms.length > 0) {
args.push('--platform', inputs.platforms.join(','));
} else {
args.push('--iidfile', await buildx.getImageIDFile());
}
await asyncForEach(inputs.outputs, async output => {
args.push('--output', output);
});
await asyncForEach(inputs.cacheFrom, async cacheFrom => {
args.push('--cache-from', cacheFrom);
});
await asyncForEach(inputs.cacheTo, async cacheTo => {
args.push('--cache-to', cacheTo);
});
if (inputs.file) {
args.push('--file', inputs.file);
}
return args;
}
async function getCommonArgs(inputs: Inputs): Promise<Array<string>> { async function getCommonArgs(inputs: Inputs): Promise<Array<string>> {
let args: Array<string> = []; let args: Array<string> = [];
if (inputs.noCache) { if (inputs.noCache) {
@ -65,41 +108,6 @@ async function getCommonArgs(inputs: Inputs): Promise<Array<string>> {
return args; return args;
} }
async function getBuildArgs(inputs: Inputs): Promise<Array<string>> {
let args: Array<string> = ['build'];
await asyncForEach(inputs.buildArgs, async buildArg => {
args.push('--build-arg', buildArg);
});
await asyncForEach(inputs.labels, async label => {
args.push('--label', label);
});
await asyncForEach(inputs.tags, async tag => {
args.push('--tag', tag);
});
if (inputs.target) {
args.push('--target', inputs.target);
}
if (inputs.allow) {
args.push('--allow', inputs.allow.join(','));
}
if (inputs.platforms) {
args.push('--platform', inputs.platforms.join(','));
}
await asyncForEach(inputs.outputs, async output => {
args.push('--output', output);
});
await asyncForEach(inputs.cacheFrom, async cacheFrom => {
args.push('--cache-from', cacheFrom);
});
await asyncForEach(inputs.cacheTo, async cacheTo => {
args.push('--cache-to', cacheTo);
});
if (inputs.file) {
args.push('--file', inputs.file);
}
return args;
}
export async function getInputList(name: string): Promise<string[]> { export async function getInputList(name: string): Promise<string[]> {
const items = core.getInput(name); const items = core.getInput(name);
if (items == '') { if (items == '') {

@ -25,6 +25,13 @@ async function run(): Promise<void> {
core.info(`🏃 Starting build...`); core.info(`🏃 Starting build...`);
const args: string[] = await getArgs(inputs); const args: string[] = await getArgs(inputs);
await exec.exec('docker', args); await exec.exec('docker', args);
const imageID = await buildx.getImageID();
if (imageID) {
core.info('🛒 Extracting digest...');
core.info(`${imageID}`);
core.setOutput('digest', imageID);
}
} catch (error) { } catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
} }