mirror of
https://github.com/docker/build-push-action.git
synced 2024-12-23 15:22:15 -05:00
Add digest output
Fix platforms and allow inputs Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
parent
44d05b9191
commit
38c1f188ca
8 changed files with 244 additions and 68 deletions
49
.github/workflows/ci.yml
vendored
49
.github/workflows/ci.yml
vendored
|
@ -11,7 +11,54 @@ on:
|
|||
- v2-working-branch # remove when merged to master
|
||||
|
||||
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
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
|
52
README.md
52
README.md
|
@ -32,7 +32,56 @@ on:
|
|||
tags:
|
||||
|
||||
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
|
||||
steps:
|
||||
-
|
||||
|
@ -58,7 +107,6 @@ jobs:
|
|||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/ppc64le,linux/s390x
|
||||
push: true
|
||||
tags: |
|
||||
user/app:latest
|
||||
|
|
15
__tests__/buildx.test.ts
Normal file
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);
|
||||
});
|
||||
});
|
16
action.yml
16
action.yml
|
@ -19,13 +19,13 @@ inputs:
|
|||
required: false
|
||||
default: './Dockerfile'
|
||||
build-args:
|
||||
description: "Newline-delimited list of build-time variables"
|
||||
description: "List of build-time variables"
|
||||
required: false
|
||||
labels:
|
||||
description: "Newline-delimited list of metadata for an image"
|
||||
description: "List of metadata for an image"
|
||||
required: false
|
||||
tags:
|
||||
description: "Newline-delimited list of tags"
|
||||
description: "List of tags"
|
||||
required: false
|
||||
pull:
|
||||
description: "Always attempt to pull a newer version of the image"
|
||||
|
@ -35,14 +35,14 @@ inputs:
|
|||
description: "Sets the target stage to build"
|
||||
required: false
|
||||
allow:
|
||||
description: "Allow extra privileged entitlement (eg. network.host,security.insecure)"
|
||||
description: "List of extra privileged entitlement (eg. network.host,security.insecure)"
|
||||
required: false
|
||||
no-cache:
|
||||
description: "Do not use cache when building the image"
|
||||
required: false
|
||||
default: 'false'
|
||||
platforms:
|
||||
description: "Comma-delimited list of target platforms for build"
|
||||
description: "List of target platforms for build"
|
||||
required: false
|
||||
load:
|
||||
description: "Load is a shorthand for --output=type=docker"
|
||||
|
@ -53,13 +53,13 @@ inputs:
|
|||
required: false
|
||||
default: 'false'
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
outputs:
|
||||
|
|
80
dist/index.js
generated
vendored
80
dist/index.js
generated
vendored
|
@ -1025,6 +1025,12 @@ function run() {
|
|||
core.info(`🏃 Starting build...`);
|
||||
const args = yield context_1.getArgs(inputs);
|
||||
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) {
|
||||
core.setFailed(error.message);
|
||||
|
@ -1405,8 +1411,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|||
});
|
||||
};
|
||||
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));
|
||||
exports.tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-build-push-'));
|
||||
function getInputs() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return {
|
||||
|
@ -1440,24 +1451,6 @@ function getArgs(inputs) {
|
|||
});
|
||||
}
|
||||
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) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let args = ['build'];
|
||||
|
@ -1473,12 +1466,15 @@ function getBuildArgs(inputs) {
|
|||
if (inputs.target) {
|
||||
args.push('--target', inputs.target);
|
||||
}
|
||||
if (inputs.allow) {
|
||||
if (inputs.allow.length > 0) {
|
||||
args.push('--allow', inputs.allow.join(','));
|
||||
}
|
||||
if (inputs.platforms) {
|
||||
if (inputs.platforms.length > 0) {
|
||||
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* () {
|
||||
args.push('--output', output);
|
||||
}));
|
||||
|
@ -1494,6 +1490,24 @@ function getBuildArgs(inputs) {
|
|||
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) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
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());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
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));
|
||||
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() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
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';
|
||||
|
||||
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> {
|
||||
return await exec.exec(`docker`, ['buildx'], true).then(res => {
|
||||
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';
|
||||
|
||||
export const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-build-push-'));
|
||||
|
||||
export interface Inputs {
|
||||
context: string;
|
||||
file: string;
|
||||
|
@ -48,6 +54,43 @@ export async function getArgs(inputs: Inputs): Promise<Array<string>> {
|
|||
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>> {
|
||||
let args: Array<string> = [];
|
||||
if (inputs.noCache) {
|
||||
|
@ -65,41 +108,6 @@ async function getCommonArgs(inputs: Inputs): Promise<Array<string>> {
|
|||
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[]> {
|
||||
const items = core.getInput(name);
|
||||
if (items == '') {
|
||||
|
|
|
@ -25,6 +25,13 @@ async function run(): Promise<void> {
|
|||
core.info(`🏃 Starting build...`);
|
||||
const args: string[] = await getArgs(inputs);
|
||||
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) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue