From 8be103ff82b8f62ac371f7ca4ec638f9ae622a54 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Sun, 16 Aug 2020 17:18:08 +0200 Subject: [PATCH] Handle build bake through bake, bake-files and bake-targets Signed-off-by: CrazyMax --- .github/workflows/ci.yml | 44 ++++++ README.md | 78 +++++++++-- action.yml | 16 ++- dist/index.js | 290 ++++++++++++++++++++++----------------- src/context-helper.ts | 49 ------- src/context.ts | 139 +++++++++++++++++++ src/main.ts | 57 +------- test/Dockerfile-bake | 4 + test/config.hcl | 39 ++++++ 9 files changed, 475 insertions(+), 241 deletions(-) delete mode 100644 src/context-helper.ts create mode 100644 src/context.ts create mode 100644 test/Dockerfile-bake create mode 100644 test/config.hcl diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb29664..06788cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,4 +54,48 @@ jobs: localhost:5000/name/app:1.0.0 - name: Dump context + if: always() + uses: crazy-max/ghaction-dump-context@v1 + + bake: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - default + - release + 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 + - + name: Build and push + uses: ./ + with: + builder: ${{ steps.buildx.outputs.name }} + bake: true + #push: true + bake-files: | + ./test/config.hcl + bake-targets: | + ${{ matrix.target }} + - + name: Dump context + if: always() uses: crazy-max/ghaction-dump-context@v1 diff --git a/README.md b/README.md index fa28eab..a90ffc8 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,59 @@ jobs: user/app:1.0.0 ``` +### Bake + +[Buildx bake](https://github.com/docker/buildx#buildx-bake-options-target) is also available with this action through +the [`bake` inputs](#inputs). + +```yaml +name: ci + +on: + pull_request: + branches: master + push: + branches: master + tags: + +jobs: + buildx: + 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 + with: + install: true + - + name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - + name: Build and push + uses: docker/build-push-action@v2 + with: + builder: ${{ steps.buildx.outputs.name }} + push: true + bake: true + bake-files: | + ./config.hcl + bake-targets: | + default + release +``` + ## Customizing ### inputs @@ -74,22 +127,25 @@ Following inputs can be used as `step.with` keys | Name | Type | Default | Description | |---------------------|---------|-----------------------------------|------------------------------------| -| `builder` | String | | Builder instance | +| `builder` | String | | Builder instance (see [setup-buildx](https://github.com/docker/setup-buildx-action) action) | | `context` | String | `.` | Build's context is the set of files located in the specified `PATH` or `URL` | | `file` | String | `./Dockerfile` | Path to the Dockerfile. | -| `build-args` | String | | Newline-delimited list of build-time variables | -| `labels` | String | | Newline-delimited list of metadata for an image | -| `tags` | String | | Newline-delimited list of tags **required** | +| `build-args` | List | | Newline-delimited list of build-time variables | +| `labels` | List | | Newline-delimited list of metadata for an image | +| `tags` | List | | Newline-delimited list of tags | | `pull` | Bool | `false` | Always attempt to pull a newer version of the image | | `target` | String | | Sets the target stage to build | -| `allow` | String | | Allow extra privileged entitlement (eg. network.host,security.insecure) | +| `allow` | String | | [Allow](https://github.com/docker/buildx#--allowentitlement) extra privileged entitlement (eg. network.host,security.insecure) | | `no-cache` | Bool | `false` | Do not use cache when building the image | -| `platforms` | String | | Comma-delimited list of target platforms for build | -| `load` | Bool | `false` | Shorthand for `--output=type=docker` | -| `push` | Bool | `false` | Shorthand for `--output=type=registry` | -| `outputs` | String | | Newline-delimited list of output destinations (format: `type=local,dest=path`) | -| `cache-from` | String | | Newline-delimited list of external cache sources (eg. `user/app:cache`, `type=local,src=path/to/dir`) | -| `cache-to` | String | | Newline-delimited list of cache export destinations (eg. `user/app:cache`, `type=local,dest=path/to/dir`) | +| `platforms` | String | | Comma-delimited list of [target platforms](https://github.com/docker/buildx#---platformvaluevalue) for build | +| `load` | Bool | `false` | [Load](https://github.com/docker/buildx#--load) is a shorthand for `--output=type=docker` | +| `push` | Bool | `false` | [Push](https://github.com/docker/buildx#--push) is a shorthand for `--output=type=registry` | +| `outputs` | List | | Newline-delimited list of [output destinations](https://github.com/docker/buildx#-o---outputpath-typetypekeyvalue) (format: `type=local,dest=path`) | +| `cache-from` | List | | Newline-delimited list of [external cache sources](https://github.com/docker/buildx#--cache-fromnametypetypekeyvalue) (eg. `user/app:cache`, `type=local,src=path/to/dir`) | +| `cache-to` | List | | Newline-delimited list of [cache export destinations](https://github.com/docker/buildx#--cache-tonametypetypekeyvalue) (eg. `user/app:cache`, `type=local,dest=path/to/dir`) | +| `bake` | Bool | `false` | Use [bake](https://github.com/docker/buildx#buildx-bake-options-target) as the high-level build command | +| `bake-files` | List | | Newline-delimited list of [bake definition files](https://github.com/docker/buildx#file-definition) | +| `bake-targets` | List | | Newline-delimited list of bake targets | ### outputs diff --git a/action.yml b/action.yml index b69fbab..1f2cb72 100644 --- a/action.yml +++ b/action.yml @@ -25,7 +25,7 @@ inputs: required: false tags: description: "Newline-delimited list of tags" - required: true + required: false pull: description: "Always attempt to pull a newer version of the image" required: false @@ -44,11 +44,11 @@ inputs: description: "Comma-delimited list of target platforms for build" required: false load: - description: "Shorthand for --output=type=docker" + description: "Load is a shorthand for --output=type=docker" required: false default: 'false' push: - description: "Shorthand for --output=type=registry" + description: "Push is a shorthand for --output=type=registry" required: false default: 'false' outputs: @@ -60,6 +60,16 @@ inputs: cache-to: description: "Newline-delimited list of cache export destinations for buildx (eg. user/app:cache, type=local,dest=path/to/dir)" required: false + bake: + description: "Use bake as the high-level build command" + required: false + default: 'false' + bake-files: + description: "Newline-delimited list of bake definition files" + required: false + bake-targets: + description: "Newline-delimited list of bake targets" + required: false outputs: digest: diff --git a/dist/index.js b/dist/index.js index 39253ad..ac8f509 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1003,7 +1003,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge Object.defineProperty(exports, "__esModule", { value: true }); const os = __importStar(__webpack_require__(87)); const buildx = __importStar(__webpack_require__(982)); -const context_helper_1 = __webpack_require__(338); +const context_1 = __webpack_require__(482); const core = __importStar(__webpack_require__(470)); const exec = __importStar(__webpack_require__(986)); function run() { @@ -1013,149 +1013,28 @@ function run() { core.setFailed('Only supported on linux platform'); return; } - const inputs = yield context_helper_1.loadInputs(); if (!(yield buildx.isAvailable())) { core.setFailed(`Buildx is required. See https://github.com/docker/setup-buildx-action to set up buildx.`); return; } - let buildArgs = ['buildx', 'build']; + const inputs = yield context_1.getInputs(); if (inputs.builder) { core.info(`📌 Using builder instance ${inputs.builder}`); yield buildx.use(inputs.builder); } - if (inputs.file) { - buildArgs.push('--file', inputs.file); - } - yield asyncForEach(inputs.buildArgs, (buildArg) => __awaiter(this, void 0, void 0, function* () { - buildArgs.push('--build-arg', buildArg); - })); - yield asyncForEach(inputs.labels, (label) => __awaiter(this, void 0, void 0, function* () { - buildArgs.push('--label', label); - })); - yield asyncForEach(inputs.tags, (tag) => __awaiter(this, void 0, void 0, function* () { - buildArgs.push('--tag', tag); - })); - if (inputs.pull) { - buildArgs.push('--pull'); - } - if (inputs.target) { - buildArgs.push('--target', inputs.target); - } - if (inputs.allow) { - buildArgs.push('--allow', inputs.allow); - } - if (inputs.noCache) { - buildArgs.push('--no-cache'); - } - if (inputs.platforms) { - buildArgs.push('--platform', inputs.platforms); - } - if (inputs.load) { - buildArgs.push('--load'); - } - if (inputs.push) { - buildArgs.push('--push'); - } - yield asyncForEach(inputs.outputs, (output) => __awaiter(this, void 0, void 0, function* () { - buildArgs.push('--output', output); - })); - yield asyncForEach(inputs.cacheFrom, (cacheFrom) => __awaiter(this, void 0, void 0, function* () { - buildArgs.push('--cache-from', cacheFrom); - })); - yield asyncForEach(inputs.cacheTo, (cacheTo) => __awaiter(this, void 0, void 0, function* () { - buildArgs.push('--cache-from', cacheTo); - })); - buildArgs.push(inputs.context); core.info(`🏃 Starting build...`); - yield exec.exec('docker', buildArgs); + yield exec.exec('docker', yield context_1.getArgs(inputs)); } catch (error) { core.setFailed(error.message); } }); } -const asyncForEach = (array, callback) => __awaiter(void 0, void 0, void 0, function* () { - for (let index = 0; index < array.length; index++) { - yield callback(array[index], index, array); - } -}); run(); //# sourceMappingURL=main.js.map /***/ }), -/***/ 338: -/***/ (function(__unusedmodule, exports, __webpack_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.loadInputs = void 0; -const core = __importStar(__webpack_require__(470)); -function loadInputs() { - return __awaiter(this, void 0, void 0, function* () { - return { - context: core.getInput('context') || '.', - file: core.getInput('file') || './Dockerfile', - buildArgs: yield getInputList('build-args'), - labels: yield getInputList('labels'), - tags: yield getInputList('tags'), - pull: /true/i.test(core.getInput('pull')), - target: core.getInput('target'), - allow: core.getInput('allow'), - noCache: /true/i.test(core.getInput('no-cache')), - builder: core.getInput('builder'), - platforms: core.getInput('platforms'), - load: /true/i.test(core.getInput('load')), - push: /true/i.test(core.getInput('push')), - outputs: yield getInputList('outputs'), - cacheFrom: yield getInputList('cache-from'), - cacheTo: yield getInputList('cache-to') - }; - }); -} -exports.loadInputs = loadInputs; -function getInputList(name) { - return __awaiter(this, void 0, void 0, function* () { - const items = core.getInput(name); - if (items == '') { - return []; - } - return items.split(/\r?\n/).reduce((acc, line) => acc.concat(line.split(',')).map(pat => pat.trim()), []); - }); -} -//# sourceMappingURL=context-helper.js.map - -/***/ }), - /***/ 357: /***/ (function(module) { @@ -1491,6 +1370,169 @@ exports.getState = getState; /***/ }), +/***/ 482: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getArgs = exports.getInputs = void 0; +const core = __importStar(__webpack_require__(470)); +function getInputs() { + return __awaiter(this, void 0, void 0, function* () { + return { + context: core.getInput('context') || '.', + file: core.getInput('file') || './Dockerfile', + buildArgs: yield getInputList('build-args'), + labels: yield getInputList('labels'), + tags: yield getInputList('tags'), + pull: /true/i.test(core.getInput('pull')), + target: core.getInput('target'), + allow: core.getInput('allow'), + noCache: /true/i.test(core.getInput('no-cache')), + builder: core.getInput('builder'), + platforms: core.getInput('platforms'), + load: /true/i.test(core.getInput('load')), + push: /true/i.test(core.getInput('push')), + outputs: yield getInputList('outputs'), + cacheFrom: yield getInputList('cache-from'), + cacheTo: yield getInputList('cache-to'), + bake: /true/i.test(core.getInput('bake')), + bakeFiles: yield getInputList('bake-files'), + bakeTargets: yield getInputList('bake-targets') + }; + }); +} +exports.getInputs = getInputs; +function getArgs(inputs) { + return __awaiter(this, void 0, void 0, function* () { + let args = ['buildx']; + if (inputs.bake) { + args.concat(yield getBakeArgs(inputs)); + } + else { + args.concat(yield getBuildArgs(inputs)); + } + args.concat(yield getCommonArgs(inputs)); + if (!inputs.bake) { + args.push(inputs.context); + } + else { + args.concat(inputs.bakeTargets); + } + return args; + }); +} +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 getBakeArgs(inputs) { + return __awaiter(this, void 0, void 0, function* () { + let args = ['bake']; + yield asyncForEach(inputs.bakeFiles, (bakeFile) => __awaiter(this, void 0, void 0, function* () { + args.push('--file', bakeFile); + })); + return args; + }); +} +function getBuildArgs(inputs) { + return __awaiter(this, void 0, void 0, function* () { + let args = ['build']; + yield asyncForEach(inputs.buildArgs, (buildArg) => __awaiter(this, void 0, void 0, function* () { + args.push('--build-arg', buildArg); + })); + yield asyncForEach(inputs.labels, (label) => __awaiter(this, void 0, void 0, function* () { + args.push('--label', label); + })); + yield asyncForEach(inputs.tags, (tag) => __awaiter(this, void 0, void 0, function* () { + args.push('--tag', tag); + })); + if (inputs.target) { + args.push('--target', inputs.target); + } + if (inputs.allow) { + args.push('--allow', inputs.allow); + } + if (inputs.platforms) { + args.push('--platform', inputs.platforms); + } + yield asyncForEach(inputs.outputs, (output) => __awaiter(this, void 0, void 0, function* () { + args.push('--output', output); + })); + yield asyncForEach(inputs.cacheFrom, (cacheFrom) => __awaiter(this, void 0, void 0, function* () { + args.push('--cache-from', cacheFrom); + })); + yield asyncForEach(inputs.cacheTo, (cacheTo) => __awaiter(this, void 0, void 0, function* () { + args.push('--cache-from', cacheTo); + })); + if (inputs.file) { + args.push('--file', inputs.file); + } + return args; + }); +} +function getInputList(name) { + return __awaiter(this, void 0, void 0, function* () { + const items = core.getInput(name); + if (items == '') { + return []; + } + return items.split(/\r?\n/).reduce((acc, line) => acc.concat(line.split(',')).map(pat => pat.trim()), []); + }); +} +const asyncForEach = (array, callback) => __awaiter(void 0, void 0, void 0, function* () { + for (let index = 0; index < array.length; index++) { + yield callback(array[index], index, array); + } +}); +//# sourceMappingURL=context.js.map + +/***/ }), + /***/ 614: /***/ (function(module) { diff --git a/src/context-helper.ts b/src/context-helper.ts deleted file mode 100644 index 79881f8..0000000 --- a/src/context-helper.ts +++ /dev/null @@ -1,49 +0,0 @@ -import * as core from '@actions/core'; - -export interface Inputs { - context: string; - file: string; - buildArgs: string[]; - labels: string[]; - tags: string[]; - pull: boolean; - target: string; - allow: string; - noCache: boolean; - builder: string; - platforms: string; - load: boolean; - push: boolean; - outputs: string[]; - cacheFrom: string[]; - cacheTo: string[]; -} - -export async function loadInputs(): Promise { - return { - context: core.getInput('context') || '.', - file: core.getInput('file') || './Dockerfile', - buildArgs: await getInputList('build-args'), - labels: await getInputList('labels'), - tags: await getInputList('tags'), - pull: /true/i.test(core.getInput('pull')), - target: core.getInput('target'), - allow: core.getInput('allow'), - noCache: /true/i.test(core.getInput('no-cache')), - builder: core.getInput('builder'), - platforms: core.getInput('platforms'), - load: /true/i.test(core.getInput('load')), - push: /true/i.test(core.getInput('push')), - outputs: await getInputList('outputs'), - cacheFrom: await getInputList('cache-from'), - cacheTo: await getInputList('cache-to') - }; -} - -async function getInputList(name: string): Promise { - const items = core.getInput(name); - if (items == '') { - return []; - } - return items.split(/\r?\n/).reduce((acc, line) => acc.concat(line.split(',')).map(pat => pat.trim()), []); -} diff --git a/src/context.ts b/src/context.ts new file mode 100644 index 0000000..54a5c9a --- /dev/null +++ b/src/context.ts @@ -0,0 +1,139 @@ +import * as core from '@actions/core'; + +export interface Inputs { + context: string; + file: string; + buildArgs: string[]; + labels: string[]; + tags: string[]; + pull: boolean; + target: string; + allow: string; + noCache: boolean; + builder: string; + platforms: string; + load: boolean; + push: boolean; + outputs: string[]; + cacheFrom: string[]; + cacheTo: string[]; + bake: boolean; + bakeFiles: string[]; + bakeTargets: string[]; +} + +export async function getInputs(): Promise { + return { + context: core.getInput('context') || '.', + file: core.getInput('file') || './Dockerfile', + buildArgs: await getInputList('build-args'), + labels: await getInputList('labels'), + tags: await getInputList('tags'), + pull: /true/i.test(core.getInput('pull')), + target: core.getInput('target'), + allow: core.getInput('allow'), + noCache: /true/i.test(core.getInput('no-cache')), + builder: core.getInput('builder'), + platforms: core.getInput('platforms'), + load: /true/i.test(core.getInput('load')), + push: /true/i.test(core.getInput('push')), + outputs: await getInputList('outputs'), + cacheFrom: await getInputList('cache-from'), + cacheTo: await getInputList('cache-to'), + bake: /true/i.test(core.getInput('bake')), + bakeFiles: await getInputList('bake-files'), + bakeTargets: await getInputList('bake-targets') + }; +} + +export async function getArgs(inputs: Inputs): Promise { + let args: Array = ['buildx']; + if (inputs.bake) { + args.concat(await getBakeArgs(inputs)); + } else { + args.concat(await getBuildArgs(inputs)); + } + args.concat(await getCommonArgs(inputs)); + + if (!inputs.bake) { + args.push(inputs.context); + } else { + args.concat(inputs.bakeTargets); + } + + return args; +} + +async function getCommonArgs(inputs: Inputs): Promise { + let args: Array = []; + 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; +} + +async function getBakeArgs(inputs: Inputs): Promise { + let args: Array = ['bake']; + await asyncForEach(inputs.bakeFiles, async bakeFile => { + args.push('--file', bakeFile); + }); + return args; +} + +async function getBuildArgs(inputs: Inputs): Promise { + let args: Array = ['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); + } + if (inputs.platforms) { + args.push('--platform', inputs.platforms); + } + 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-from', cacheTo); + }); + if (inputs.file) { + args.push('--file', inputs.file); + } + return args; +} + +async function getInputList(name: string): Promise { + const items = core.getInput(name); + if (items == '') { + return []; + } + return items.split(/\r?\n/).reduce((acc, line) => acc.concat(line.split(',')).map(pat => pat.trim()), []); +} + +const asyncForEach = async (array, callback) => { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index, array); + } +}; diff --git a/src/main.ts b/src/main.ts index e2b4b92..2fc36ef 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,6 @@ import * as os from 'os'; import * as buildx from './buildx'; -import {Inputs, loadInputs} from './context-helper'; +import {Inputs, getInputs, getArgs} from './context'; import * as core from '@actions/core'; import * as exec from '@actions/exec'; @@ -11,74 +11,23 @@ async function run(): Promise { return; } - const inputs: Inputs = await loadInputs(); - if (!(await buildx.isAvailable())) { core.setFailed(`Buildx is required. See https://github.com/docker/setup-buildx-action to set up buildx.`); return; } - let buildArgs: Array = ['buildx', 'build']; + const inputs: Inputs = await getInputs(); if (inputs.builder) { core.info(`📌 Using builder instance ${inputs.builder}`); await buildx.use(inputs.builder); } - if (inputs.file) { - buildArgs.push('--file', inputs.file); - } - await asyncForEach(inputs.buildArgs, async buildArg => { - buildArgs.push('--build-arg', buildArg); - }); - await asyncForEach(inputs.labels, async label => { - buildArgs.push('--label', label); - }); - await asyncForEach(inputs.tags, async tag => { - buildArgs.push('--tag', tag); - }); - if (inputs.pull) { - buildArgs.push('--pull'); - } - if (inputs.target) { - buildArgs.push('--target', inputs.target); - } - if (inputs.allow) { - buildArgs.push('--allow', inputs.allow); - } - if (inputs.noCache) { - buildArgs.push('--no-cache'); - } - if (inputs.platforms) { - buildArgs.push('--platform', inputs.platforms); - } - if (inputs.load) { - buildArgs.push('--load'); - } - if (inputs.push) { - buildArgs.push('--push'); - } - await asyncForEach(inputs.outputs, async output => { - buildArgs.push('--output', output); - }); - await asyncForEach(inputs.cacheFrom, async cacheFrom => { - buildArgs.push('--cache-from', cacheFrom); - }); - await asyncForEach(inputs.cacheTo, async cacheTo => { - buildArgs.push('--cache-from', cacheTo); - }); - buildArgs.push(inputs.context); core.info(`🏃 Starting build...`); - await exec.exec('docker', buildArgs); + await exec.exec('docker', await getArgs(inputs)); } catch (error) { core.setFailed(error.message); } } -const asyncForEach = async (array, callback) => { - for (let index = 0; index < array.length; index++) { - await callback(array[index], index, array); - } -}; - run(); diff --git a/test/Dockerfile-bake b/test/Dockerfile-bake new file mode 100644 index 0000000..8153110 --- /dev/null +++ b/test/Dockerfile-bake @@ -0,0 +1,4 @@ +FROM alpine + +ARG name=world +RUN echo "Hello ${name}!" diff --git a/test/config.hcl b/test/config.hcl new file mode 100644 index 0000000..2ee66e5 --- /dev/null +++ b/test/config.hcl @@ -0,0 +1,39 @@ +group "default" { + targets = ["db", "app"] +} + +group "release" { + targets = ["db", "app-plus"] +} + +target "db" { + context = "./test" + tags = ["docker.io/tonistiigi/db"] +} + +target "app" { + context = "./test" + dockerfile = "Dockerfile-bake" + args = { + name = "foo" + } + tags = [ + "localhost:5000/name/app:latest", + "localhost:5000/name/app:1.0.0" + ] +} + +target "cross" { + platforms = [ + "linux/amd64", + "linux/arm64", + "linux/386" + ] +} + +target "app-plus" { + inherits = ["app", "cross"] + args = { + IAMPLUS = "true" + } +}