From f40e8894f18db341574cf1aae8defcc6b5a8cbbf Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Fri, 2 Jul 2021 07:02:22 +0200 Subject: [PATCH] Allow building buildx from source Signed-off-by: CrazyMax --- .github/workflows/ci.yml | 34 ++++++++ .prettierrc.json | 2 +- README.md | 2 +- __tests__/buildx.test.ts | 39 +++++++-- __tests__/context.test.ts | 10 +++ __tests__/git.test.ts | 9 ++ __tests__/util.test.ts | 11 +++ codecov.yml | 3 + dist/index.js | 167 ++++++++++++++++++++++++++++++++++---- src/buildx.ts | 49 +++++++++-- src/context.ts | 19 +++-- src/git.ts | 19 +++++ src/main.ts | 14 ++-- src/util.ts | 8 ++ 14 files changed, 342 insertions(+), 44 deletions(-) create mode 100644 __tests__/git.test.ts create mode 100644 __tests__/util.test.ts create mode 100644 codecov.yml create mode 100644 src/git.ts create mode 100644 src/util.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c55fdea..d78e96c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -313,3 +313,37 @@ jobs: echo "Status: ${{ steps.buildx.outputs.status }}" echo "Flags: ${{ steps.buildx.outputs.flags }}" echo "Platforms: ${{ steps.buildx.outputs.platforms }}" + + build-ref: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ref: + - master + - refs/tags/v0.5.1 + - refs/pull/648/head + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Set up Docker Buildx + uses: ./ + with: + version: https://github.com/docker/buildx.git#${{ matrix.ref }} + - + name: Check version + run: | + docker buildx version + - + name: Create Dockerfile + run: | + cat > ./Dockerfile < { + const tmpDir = path.join('/tmp/.docker-setup-buildx-jest').split(path.sep).join(path.posix.sep); + if (!fs.existsSync(tmpDir)) { + fs.mkdirSync(tmpDir, {recursive: true}); + } + return tmpDir; +}); + describe('isAvailable', () => { const execSpy: jest.SpyInstance = jest.spyOn(exec, 'getExecOutput'); buildx.isAvailable(); @@ -41,9 +50,20 @@ describe('parseVersion', () => { test.each([ ['github.com/docker/buildx 0.4.1+azure bda4882a65349ca359216b135896bddc1d92461c', '0.4.1'], ['github.com/docker/buildx v0.4.1 bda4882a65349ca359216b135896bddc1d92461c', '0.4.1'], - ['github.com/docker/buildx v0.4.2 fb7b670b764764dc4716df3eba07ffdae4cc47b2', '0.4.2'] + ['github.com/docker/buildx v0.4.2 fb7b670b764764dc4716df3eba07ffdae4cc47b2', '0.4.2'], + ['github.com/docker/buildx f117971 f11797113e5a9b86bd976329c5dbb8a8bfdfadfa', 'f117971'] ])('given %p', async (stdout, expected) => { - expect(await buildx.parseVersion(stdout)).toEqual(expected); + expect(buildx.parseVersion(stdout)).toEqual(expected); + }); +}); + +describe('satisfies', () => { + test.each([ + ['0.4.1', '>=0.3.2', true], + ['bda4882a65349ca359216b135896bddc1d92461c', '>0.1.0', false], + ['f117971', '>0.6.0', true] + ])('given %p', async (version, range, expected) => { + expect(buildx.satisfies(version, range)).toBe(expected); }); }); @@ -72,6 +92,15 @@ describe('inspect', () => { ); }); +describe('build', () => { + it.skip('valid', async () => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'setup-buildx-')); + const buildxBin = await buildx.build('https://github.com/docker/buildx.git#refs/pull/648/head', tmpDir); + console.log(buildxBin); + expect(fs.existsSync(buildxBin)).toBe(true); + }, 100000); +}); + describe('install', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'setup-buildx-')); it('acquires v0.4.1 version of buildx', async () => { diff --git a/__tests__/context.test.ts b/__tests__/context.test.ts index 7d884c6..c61ad8a 100644 --- a/__tests__/context.test.ts +++ b/__tests__/context.test.ts @@ -1,6 +1,16 @@ +import * as fs from 'fs'; import * as os from 'os'; +import * as path from 'path'; import * as context from '../src/context'; +jest.spyOn(context, 'tmpDir').mockImplementation((): string => { + const tmpDir = path.join('/tmp/.docker-setup-buildx-jest').split(path.sep).join(path.posix.sep); + if (!fs.existsSync(tmpDir)) { + fs.mkdirSync(tmpDir, {recursive: true}); + } + return tmpDir; +}); + describe('getInputList', () => { it('handles single line correctly', async () => { await setInput('foo', 'bar'); diff --git a/__tests__/git.test.ts b/__tests__/git.test.ts new file mode 100644 index 0000000..1fd57ef --- /dev/null +++ b/__tests__/git.test.ts @@ -0,0 +1,9 @@ +import * as git from '../src/git'; + +describe('git', () => { + it('returns git remote ref', async () => { + const ref: string = await git.getRemoteSha('https://github.com/docker/buildx.git', 'refs/pull/648/head'); + console.log(`ref: ${ref}`); + expect(ref).toEqual('f11797113e5a9b86bd976329c5dbb8a8bfdfadfa'); + }); +}); diff --git a/__tests__/util.test.ts b/__tests__/util.test.ts new file mode 100644 index 0000000..afad05e --- /dev/null +++ b/__tests__/util.test.ts @@ -0,0 +1,11 @@ +import * as util from '../src/util'; + +describe('isValidUrl', () => { + test.each([ + ['https://github.com/docker/buildx.git', true], + ['https://github.com/docker/buildx.git#refs/pull/648/head', true], + ['v0.4.1', false] + ])('given %p', async (url, expected) => { + expect(util.isValidUrl(url)).toEqual(expected); + }); +}); diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..dc46984 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,3 @@ +comment: false +github_checks: + annotations: false diff --git a/dist/index.js b/dist/index.js index 745e138..08c3e21 100644 --- a/dist/index.js +++ b/dist/index.js @@ -35,12 +35,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getBuildKitVersion = exports.install = exports.inspect = exports.parseVersion = exports.getVersion = exports.isAvailable = void 0; +exports.getBuildKitVersion = exports.install = exports.build = exports.inspect = exports.satisfies = exports.parseVersion = exports.getVersion = exports.isAvailable = void 0; const fs = __importStar(__nccwpck_require__(5747)); const path = __importStar(__nccwpck_require__(5622)); const semver = __importStar(__nccwpck_require__(1383)); const util = __importStar(__nccwpck_require__(1669)); const context = __importStar(__nccwpck_require__(3842)); +const git = __importStar(__nccwpck_require__(3374)); const github = __importStar(__nccwpck_require__(5928)); const core = __importStar(__nccwpck_require__(2186)); const exec = __importStar(__nccwpck_require__(1514)); @@ -78,15 +79,17 @@ function getVersion() { } exports.getVersion = getVersion; function parseVersion(stdout) { - return __awaiter(this, void 0, void 0, function* () { - const matches = /\sv?([0-9.]+)/.exec(stdout); - if (!matches) { - throw new Error(`Cannot parse buildx version`); - } - return semver.clean(matches[1]); - }); + const matches = /\sv?([0-9a-f]{7}|[0-9.]+)/.exec(stdout); + if (!matches) { + throw new Error(`Cannot parse buildx version`); + } + return matches[1]; } exports.parseVersion = parseVersion; +function satisfies(version, range) { + return semver.satisfies(version, range) || /^[0-9a-f]{7}$/.exec(version) !== null; +} +exports.satisfies = satisfies; function inspect(name) { return __awaiter(this, void 0, void 0, function* () { return yield exec @@ -142,6 +145,33 @@ function inspect(name) { }); } exports.inspect = inspect; +function build(inputBuildRef, dockerConfigHome) { + return __awaiter(this, void 0, void 0, function* () { + let [repo, ref] = inputBuildRef.split('#'); + if (ref.length == 0) { + ref = 'master'; + } + const sha = yield git.getRemoteSha(repo, ref); + core.debug(`Remote ref ${sha} found`); + let toolPath; + toolPath = tc.find('buildx', sha); + if (!toolPath) { + const outFolder = path.join(context.tmpDir(), 'out').split(path.sep).join(path.posix.sep); + toolPath = yield exec + .getExecOutput('docker', ['buildx', 'build', '--target', 'binaries', '--build-arg', 'BUILDKIT_CONTEXT_KEEP_GIT_DIR=1', '--output', `type=local,dest=${outFolder}`, inputBuildRef], { + ignoreReturnCode: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + core.warning(res.stderr.trim()); + } + return tc.cacheFile(`${outFolder}/buildx`, context.osPlat == 'win32' ? 'docker-buildx.exe' : 'docker-buildx', 'buildx', sha); + }); + } + return setPlugin(toolPath, dockerConfigHome); + }); +} +exports.build = build; function install(inputVersion, dockerConfigHome) { return __awaiter(this, void 0, void 0, function* () { const release = yield github.getRelease(inputVersion); @@ -159,6 +189,12 @@ function install(inputVersion, dockerConfigHome) { } toolPath = yield download(version); } + return setPlugin(toolPath, dockerConfigHome); + }); +} +exports.install = install; +function setPlugin(toolPath, dockerConfigHome) { + return __awaiter(this, void 0, void 0, function* () { const pluginsDir = path.join(dockerConfigHome, 'cli-plugins'); core.debug(`Plugins dir is ${pluginsDir}`); if (!fs.existsSync(pluginsDir)) { @@ -173,7 +209,6 @@ function install(inputVersion, dockerConfigHome) { return pluginPath; }); } -exports.install = install; function download(version) { return __awaiter(this, void 0, void 0, function* () { const targetFile = context.osPlat == 'win32' ? 'docker-buildx.exe' : 'docker-buildx'; @@ -286,21 +321,33 @@ 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.setOutput = exports.asyncForEach = exports.getInputList = exports.getInputs = exports.osArch = exports.osPlat = void 0; +exports.setOutput = exports.asyncForEach = exports.getInputList = exports.getInputs = exports.tmpDir = exports.osArch = exports.osPlat = void 0; +const fs_1 = __importDefault(__nccwpck_require__(5747)); const os = __importStar(__nccwpck_require__(2087)); +const path_1 = __importDefault(__nccwpck_require__(5622)); const core = __importStar(__nccwpck_require__(2186)); const command_1 = __nccwpck_require__(7351); +let _tmpDir; exports.osPlat = os.platform(); exports.osArch = os.arch(); +function tmpDir() { + if (!_tmpDir) { + _tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os.tmpdir(), 'docker-setup-buildx-')).split(path_1.default.sep).join(path_1.default.posix.sep); + } + return _tmpDir; +} +exports.tmpDir = tmpDir; function getInputs() { return __awaiter(this, void 0, void 0, function* () { return { version: core.getInput('version'), driver: core.getInput('driver') || 'docker-container', driverOpts: yield getInputList('driver-opts', true), - buildkitdFlags: core.getInput('buildkitd-flags') || - '--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host', + buildkitdFlags: core.getInput('buildkitd-flags') || '--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host', install: core.getBooleanInput('install'), use: core.getBooleanInput('use'), endpoint: core.getInput('endpoint'), @@ -337,6 +384,66 @@ exports.setOutput = setOutput; /***/ }), +/***/ 3374: +/***/ (function(__unused_webpack_module, exports, __nccwpck_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.prototype.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.getRemoteSha = void 0; +const exec = __importStar(__nccwpck_require__(1514)); +function getRemoteSha(repo, ref) { + return __awaiter(this, void 0, void 0, function* () { + return yield exec + .getExecOutput(`git`, ['ls-remote', repo, ref], { + ignoreReturnCode: true, + silent: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + throw new Error(res.stderr); + } + const [rsha, rref] = res.stdout.trim().split(/[\s\t]/); + if (rsha.length == 0) { + throw new Error(`Cannot find remote ref for ${repo}#${ref}`); + } + return rsha; + }); + }); +} +exports.getRemoteSha = getRemoteSha; +//# sourceMappingURL=git.js.map + +/***/ }), + /***/ 5928: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { @@ -419,10 +526,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge Object.defineProperty(exports, "__esModule", ({ value: true })); const os = __importStar(__nccwpck_require__(2087)); const path = __importStar(__nccwpck_require__(5622)); -const semver = __importStar(__nccwpck_require__(1383)); const buildx = __importStar(__nccwpck_require__(9295)); const context = __importStar(__nccwpck_require__(3842)); const stateHelper = __importStar(__nccwpck_require__(8647)); +const util = __importStar(__nccwpck_require__(4024)); const core = __importStar(__nccwpck_require__(2186)); const exec = __importStar(__nccwpck_require__(1514)); function run() { @@ -435,8 +542,13 @@ function run() { core.endGroup(); const inputs = yield context.getInputs(); const dockerConfigHome = process.env.DOCKER_CONFIG || path.join(os.homedir(), '.docker'); - if (!(yield buildx.isAvailable()) || inputs.version) { - core.startGroup(`Installing buildx`); + if (util.isValidUrl(inputs.version)) { + core.startGroup(`Build and install buildx`); + yield buildx.build(inputs.version, dockerConfigHome); + core.endGroup(); + } + else if (!(yield buildx.isAvailable()) || inputs.version) { + core.startGroup(`Download and install buildx`); yield buildx.install(inputs.version || 'latest', dockerConfigHome); core.endGroup(); } @@ -447,7 +559,7 @@ function run() { if (inputs.driver !== 'docker') { core.startGroup(`Creating a new builder instance`); let createArgs = ['buildx', 'create', '--name', builderName, '--driver', inputs.driver]; - if (semver.satisfies(buildxVersion, '>=0.3.0')) { + if (buildx.satisfies(buildxVersion, '>=0.3.0')) { yield context.asyncForEach(inputs.driverOpts, (driverOpt) => __awaiter(this, void 0, void 0, function* () { createArgs.push('--driver-opt', driverOpt); })); @@ -468,7 +580,7 @@ function run() { core.endGroup(); core.startGroup(`Booting builder`); let bootstrapArgs = ['buildx', 'inspect', '--bootstrap']; - if (semver.satisfies(buildxVersion, '>=0.4.0')) { + if (buildx.satisfies(buildxVersion, '>=0.4.0')) { bootstrapArgs.push('--builder', builderName); } yield exec.exec('docker', bootstrapArgs); @@ -593,6 +705,27 @@ if (!exports.IsPost) { /***/ }), +/***/ 4024: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isValidUrl = void 0; +function isValidUrl(url) { + try { + new URL(url); + } + catch (e) { + return false; + } + return true; +} +exports.isValidUrl = isValidUrl; +//# sourceMappingURL=util.js.map + +/***/ }), + /***/ 7351: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { diff --git a/src/buildx.ts b/src/buildx.ts index 7c2152f..97bb475 100644 --- a/src/buildx.ts +++ b/src/buildx.ts @@ -3,6 +3,7 @@ import * as path from 'path'; import * as semver from 'semver'; import * as util from 'util'; import * as context from './context'; +import * as git from './git'; import * as github from './github'; import * as core from '@actions/core'; import * as exec from '@actions/exec'; @@ -46,12 +47,16 @@ export async function getVersion(): Promise { }); } -export async function parseVersion(stdout: string): Promise { - const matches = /\sv?([0-9.]+)/.exec(stdout); +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 semver.clean(matches[1]); + return matches[1]; +} + +export function satisfies(version: string, range: string): boolean { + return semver.satisfies(version, range) || /^[0-9a-f]{7}$/.exec(version) !== null; } export async function inspect(name: string): Promise { @@ -106,6 +111,34 @@ export async function inspect(name: string): Promise { }); } +export async function build(inputBuildRef: string, dockerConfigHome: string): Promise { + let [repo, ref] = inputBuildRef.split('#'); + if (ref.length == 0) { + ref = 'master'; + } + + const sha = await git.getRemoteSha(repo, ref); + core.debug(`Remote ref ${sha} found`); + + let toolPath: string; + toolPath = tc.find('buildx', sha); + if (!toolPath) { + const outFolder = path.join(context.tmpDir(), 'out').split(path.sep).join(path.posix.sep); + toolPath = await exec + .getExecOutput('docker', ['buildx', 'build', '--target', 'binaries', '--build-arg', 'BUILDKIT_CONTEXT_KEEP_GIT_DIR=1', '--output', `type=local,dest=${outFolder}`, inputBuildRef], { + ignoreReturnCode: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + core.warning(res.stderr.trim()); + } + return tc.cacheFile(`${outFolder}/buildx`, context.osPlat == 'win32' ? 'docker-buildx.exe' : 'docker-buildx', 'buildx', sha); + }); + } + + return setPlugin(toolPath, dockerConfigHome); +} + export async function install(inputVersion: string, dockerConfigHome: string): Promise { const release: github.GitHubRelease | null = await github.getRelease(inputVersion); if (!release) { @@ -124,6 +157,10 @@ export async function install(inputVersion: string, dockerConfigHome: string): P toolPath = await download(version); } + return setPlugin(toolPath, dockerConfigHome); +} + +async function setPlugin(toolPath: string, dockerConfigHome: string): Promise { const pluginsDir: string = path.join(dockerConfigHome, 'cli-plugins'); core.debug(`Plugins dir is ${pluginsDir}`); if (!fs.existsSync(pluginsDir)) { @@ -143,11 +180,7 @@ export async function install(inputVersion: string, dockerConfigHome: string): P async function download(version: string): Promise { const targetFile: string = context.osPlat == 'win32' ? 'docker-buildx.exe' : 'docker-buildx'; - const downloadUrl = util.format( - 'https://github.com/docker/buildx/releases/download/v%s/%s', - version, - await filename(version) - ); + const downloadUrl = util.format('https://github.com/docker/buildx/releases/download/v%s/%s', version, await filename(version)); let downloadPath: string; try { diff --git a/src/context.ts b/src/context.ts index 234ccd4..0d9d3be 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,10 +1,20 @@ +import fs from 'fs'; import * as os from 'os'; +import path from 'path'; import * as core from '@actions/core'; import {issueCommand} from '@actions/core/lib/command'; +let _tmpDir: string; export const osPlat: string = os.platform(); export const osArch: string = os.arch(); +export function tmpDir(): string { + if (!_tmpDir) { + _tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-setup-buildx-')).split(path.sep).join(path.posix.sep); + } + return _tmpDir; +} + export interface Inputs { version: string; driver: string; @@ -21,9 +31,7 @@ export async function getInputs(): Promise { version: core.getInput('version'), driver: core.getInput('driver') || 'docker-container', driverOpts: await getInputList('driver-opts', true), - buildkitdFlags: - core.getInput('buildkitd-flags') || - '--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host', + buildkitdFlags: core.getInput('buildkitd-flags') || '--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host', install: core.getBooleanInput('install'), use: core.getBooleanInput('use'), endpoint: core.getInput('endpoint'), @@ -39,10 +47,7 @@ export async function getInputList(name: string, ignoreComma?: boolean): Promise return items .split(/\r?\n/) .filter(x => x) - .reduce( - (acc, line) => acc.concat(!ignoreComma ? line.split(',').filter(x => x) : line).map(pat => pat.trim()), - [] - ); + .reduce((acc, line) => acc.concat(!ignoreComma ? line.split(',').filter(x => x) : line).map(pat => pat.trim()), []); } export const asyncForEach = async (array, callback) => { diff --git a/src/git.ts b/src/git.ts new file mode 100644 index 0000000..85d514b --- /dev/null +++ b/src/git.ts @@ -0,0 +1,19 @@ +import * as exec from '@actions/exec'; + +export async function getRemoteSha(repo: string, ref: string): Promise { + return await exec + .getExecOutput(`git`, ['ls-remote', repo, ref], { + ignoreReturnCode: true, + silent: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + throw new Error(res.stderr); + } + const [rsha, rref] = res.stdout.trim().split(/[\s\t]/); + if (rsha.length == 0) { + throw new Error(`Cannot find remote ref for ${repo}#${ref}`); + } + return rsha; + }); +} diff --git a/src/main.ts b/src/main.ts index 0d5d202..70df6f9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,9 @@ import * as os from 'os'; import * as path from 'path'; -import * as semver from 'semver'; import * as buildx from './buildx'; import * as context from './context'; import * as stateHelper from './state-helper'; +import * as util from './util'; import * as core from '@actions/core'; import * as exec from '@actions/exec'; @@ -17,8 +17,12 @@ async function run(): Promise { const inputs: context.Inputs = await context.getInputs(); const dockerConfigHome: string = process.env.DOCKER_CONFIG || path.join(os.homedir(), '.docker'); - if (!(await buildx.isAvailable()) || inputs.version) { - core.startGroup(`Installing buildx`); + if (util.isValidUrl(inputs.version)) { + core.startGroup(`Build and install buildx`); + await buildx.build(inputs.version, dockerConfigHome); + core.endGroup(); + } else if (!(await buildx.isAvailable()) || inputs.version) { + core.startGroup(`Download and install buildx`); await buildx.install(inputs.version || 'latest', dockerConfigHome); core.endGroup(); } @@ -31,7 +35,7 @@ async function run(): Promise { if (inputs.driver !== 'docker') { core.startGroup(`Creating a new builder instance`); let createArgs: Array = ['buildx', 'create', '--name', builderName, '--driver', inputs.driver]; - if (semver.satisfies(buildxVersion, '>=0.3.0')) { + if (buildx.satisfies(buildxVersion, '>=0.3.0')) { await context.asyncForEach(inputs.driverOpts, async driverOpt => { createArgs.push('--driver-opt', driverOpt); }); @@ -53,7 +57,7 @@ async function run(): Promise { core.startGroup(`Booting builder`); let bootstrapArgs: Array = ['buildx', 'inspect', '--bootstrap']; - if (semver.satisfies(buildxVersion, '>=0.4.0')) { + if (buildx.satisfies(buildxVersion, '>=0.4.0')) { bootstrapArgs.push('--builder', builderName); } await exec.exec('docker', bootstrapArgs); diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 0000000..1336453 --- /dev/null +++ b/src/util.ts @@ -0,0 +1,8 @@ +export function isValidUrl(url: string): boolean { + try { + new URL(url); + } catch (e) { + return false; + } + return true; +}