From 29f1eeb9e5c39ce2f1034c608f385c0abee2d367 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Wed, 23 Jun 2021 16:11:52 +0200 Subject: [PATCH] Use built-in `getExecOutput` Signed-off-by: CrazyMax --- __tests__/buildx.test.ts | 45 +- dist/index.js | 860 +++++++++++++++++++-------------------- src/buildx.ts | 169 ++++---- src/docker.ts | 7 - src/exec.ts | 34 -- src/main.ts | 33 +- 6 files changed, 569 insertions(+), 579 deletions(-) delete mode 100644 src/docker.ts delete mode 100644 src/exec.ts diff --git a/__tests__/buildx.test.ts b/__tests__/buildx.test.ts index de68108..9b98f87 100644 --- a/__tests__/buildx.test.ts +++ b/__tests__/buildx.test.ts @@ -1,18 +1,40 @@ import fs = require('fs'); -import * as docker from '../src/docker'; import * as buildx from '../src/buildx'; import * as path from 'path'; import * as os from 'os'; import * as semver from 'semver'; import * as exec from '@actions/exec'; +describe('isAvailable', () => { + const execSpy: jest.SpyInstance = jest.spyOn(exec, 'getExecOutput'); + buildx.isAvailable(); + + expect(execSpy).toHaveBeenCalledWith(`docker`, ['buildx'], { + silent: true, + ignoreReturnCode: true + }); +}); + describe('getVersion', () => { - it('valid', async () => { - await exec.exec('docker', ['buildx', 'version']); - const version = await buildx.getVersion(); - console.log(`version: ${version}`); - expect(semver.valid(version)).not.toBeNull(); - }, 100000); + async function isDaemonRunning() { + return await exec + .getExecOutput(`docker`, ['version', '--format', '{{.Server.Os}}'], { + ignoreReturnCode: true, + silent: true + }) + .then(res => { + return !res.stdout.includes(' ') && res.exitCode == 0; + }); + } + (isDaemonRunning() ? it : it.skip)( + 'valid', + async () => { + const version = await buildx.getVersion(); + console.log(`version: ${version}`); + expect(semver.valid(version)).not.toBeNull(); + }, + 100000 + ); }); describe('parseVersion', () => { @@ -27,7 +49,14 @@ describe('parseVersion', () => { describe('inspect', () => { async function isDaemonRunning() { - return await docker.isDaemonRunning(); + return await exec + .getExecOutput(`docker`, ['version', '--format', '{{.Server.Os}}'], { + ignoreReturnCode: true, + silent: true + }) + .then(res => { + return !res.stdout.includes(' ') && res.exitCode == 0; + }); } (isDaemonRunning() ? it : it.skip)( 'valid', diff --git a/dist/index.js b/dist/index.js index 39522db..ddf7a2a 100644 --- a/dist/index.js +++ b/dist/index.js @@ -49,6 +49,13 @@ module.exports = /************************************************************************/ /******/ ({ +/***/ 4: +/***/ (function(module) { + +module.exports = require("child_process"); + +/***/ }), + /***/ 8: /***/ (function(module, __unusedexports, __webpack_require__) { @@ -509,15 +516,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -const core = __importStar(__webpack_require__(186)); -const exec = __importStar(__webpack_require__(514)); const os = __importStar(__webpack_require__(87)); const path = __importStar(__webpack_require__(622)); const semver = __importStar(__webpack_require__(383)); const buildx = __importStar(__webpack_require__(295)); const context = __importStar(__webpack_require__(842)); -const mexec = __importStar(__webpack_require__(757)); const stateHelper = __importStar(__webpack_require__(647)); +const core = __importStar(__webpack_require__(186)); +const exec = __importStar(__webpack_require__(514)); function run() { var _a; return __awaiter(this, void 0, void 0, function* () { @@ -600,18 +606,26 @@ function cleanup() { return __awaiter(this, void 0, void 0, function* () { if (stateHelper.IsDebug && stateHelper.containerName.length > 0) { core.startGroup(`BuildKit container logs`); - yield mexec.exec('docker', ['logs', `${stateHelper.containerName}`], false).then(res => { - if (res.stderr.length > 0 && !res.success) { - core.warning(res.stderr); + yield exec + .getExecOutput('docker', ['logs', `${stateHelper.containerName}`], { + ignoreReturnCode: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + core.warning(res.stderr.trim()); } }); core.endGroup(); } if (stateHelper.builderName.length > 0) { core.startGroup(`Removing builder`); - yield mexec.exec('docker', ['buildx', 'rm', `${stateHelper.builderName}`], false).then(res => { - if (res.stderr.length > 0 && !res.success) { - core.warning(res.stderr); + yield exec + .getExecOutput('docker', ['buildx', 'rm', `${stateHelper.builderName}`], { + ignoreReturnCode: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + core.warning(res.stderr.trim()); } }); core.endGroup(); @@ -706,9 +720,344 @@ module.exports = gt /***/ }), /***/ 129: -/***/ (function(module) { +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +// A linked list to keep track of recently-used-ness +const Yallist = __webpack_require__(665) + +const MAX = Symbol('max') +const LENGTH = Symbol('length') +const LENGTH_CALCULATOR = Symbol('lengthCalculator') +const ALLOW_STALE = Symbol('allowStale') +const MAX_AGE = Symbol('maxAge') +const DISPOSE = Symbol('dispose') +const NO_DISPOSE_ON_SET = Symbol('noDisposeOnSet') +const LRU_LIST = Symbol('lruList') +const CACHE = Symbol('cache') +const UPDATE_AGE_ON_GET = Symbol('updateAgeOnGet') + +const naiveLength = () => 1 + +// lruList is a yallist where the head is the youngest +// item, and the tail is the oldest. the list contains the Hit +// objects as the entries. +// Each Hit object has a reference to its Yallist.Node. This +// never changes. +// +// cache is a Map (or PseudoMap) that matches the keys to +// the Yallist.Node object. +class LRUCache { + constructor (options) { + if (typeof options === 'number') + options = { max: options } + + if (!options) + options = {} + + if (options.max && (typeof options.max !== 'number' || options.max < 0)) + throw new TypeError('max must be a non-negative number') + // Kind of weird to have a default max of Infinity, but oh well. + const max = this[MAX] = options.max || Infinity + + const lc = options.length || naiveLength + this[LENGTH_CALCULATOR] = (typeof lc !== 'function') ? naiveLength : lc + this[ALLOW_STALE] = options.stale || false + if (options.maxAge && typeof options.maxAge !== 'number') + throw new TypeError('maxAge must be a number') + this[MAX_AGE] = options.maxAge || 0 + this[DISPOSE] = options.dispose + this[NO_DISPOSE_ON_SET] = options.noDisposeOnSet || false + this[UPDATE_AGE_ON_GET] = options.updateAgeOnGet || false + this.reset() + } + + // resize the cache when the max changes. + set max (mL) { + if (typeof mL !== 'number' || mL < 0) + throw new TypeError('max must be a non-negative number') + + this[MAX] = mL || Infinity + trim(this) + } + get max () { + return this[MAX] + } + + set allowStale (allowStale) { + this[ALLOW_STALE] = !!allowStale + } + get allowStale () { + return this[ALLOW_STALE] + } + + set maxAge (mA) { + if (typeof mA !== 'number') + throw new TypeError('maxAge must be a non-negative number') + + this[MAX_AGE] = mA + trim(this) + } + get maxAge () { + return this[MAX_AGE] + } + + // resize the cache when the lengthCalculator changes. + set lengthCalculator (lC) { + if (typeof lC !== 'function') + lC = naiveLength + + if (lC !== this[LENGTH_CALCULATOR]) { + this[LENGTH_CALCULATOR] = lC + this[LENGTH] = 0 + this[LRU_LIST].forEach(hit => { + hit.length = this[LENGTH_CALCULATOR](hit.value, hit.key) + this[LENGTH] += hit.length + }) + } + trim(this) + } + get lengthCalculator () { return this[LENGTH_CALCULATOR] } + + get length () { return this[LENGTH] } + get itemCount () { return this[LRU_LIST].length } + + rforEach (fn, thisp) { + thisp = thisp || this + for (let walker = this[LRU_LIST].tail; walker !== null;) { + const prev = walker.prev + forEachStep(this, fn, walker, thisp) + walker = prev + } + } + + forEach (fn, thisp) { + thisp = thisp || this + for (let walker = this[LRU_LIST].head; walker !== null;) { + const next = walker.next + forEachStep(this, fn, walker, thisp) + walker = next + } + } + + keys () { + return this[LRU_LIST].toArray().map(k => k.key) + } + + values () { + return this[LRU_LIST].toArray().map(k => k.value) + } + + reset () { + if (this[DISPOSE] && + this[LRU_LIST] && + this[LRU_LIST].length) { + this[LRU_LIST].forEach(hit => this[DISPOSE](hit.key, hit.value)) + } + + this[CACHE] = new Map() // hash of items by key + this[LRU_LIST] = new Yallist() // list of items in order of use recency + this[LENGTH] = 0 // length of items in the list + } + + dump () { + return this[LRU_LIST].map(hit => + isStale(this, hit) ? false : { + k: hit.key, + v: hit.value, + e: hit.now + (hit.maxAge || 0) + }).toArray().filter(h => h) + } + + dumpLru () { + return this[LRU_LIST] + } + + set (key, value, maxAge) { + maxAge = maxAge || this[MAX_AGE] + + if (maxAge && typeof maxAge !== 'number') + throw new TypeError('maxAge must be a number') + + const now = maxAge ? Date.now() : 0 + const len = this[LENGTH_CALCULATOR](value, key) + + if (this[CACHE].has(key)) { + if (len > this[MAX]) { + del(this, this[CACHE].get(key)) + return false + } + + const node = this[CACHE].get(key) + const item = node.value + + // dispose of the old one before overwriting + // split out into 2 ifs for better coverage tracking + if (this[DISPOSE]) { + if (!this[NO_DISPOSE_ON_SET]) + this[DISPOSE](key, item.value) + } + + item.now = now + item.maxAge = maxAge + item.value = value + this[LENGTH] += len - item.length + item.length = len + this.get(key) + trim(this) + return true + } + + const hit = new Entry(key, value, len, now, maxAge) + + // oversized objects fall out of cache automatically. + if (hit.length > this[MAX]) { + if (this[DISPOSE]) + this[DISPOSE](key, value) + + return false + } + + this[LENGTH] += hit.length + this[LRU_LIST].unshift(hit) + this[CACHE].set(key, this[LRU_LIST].head) + trim(this) + return true + } + + has (key) { + if (!this[CACHE].has(key)) return false + const hit = this[CACHE].get(key).value + return !isStale(this, hit) + } + + get (key) { + return get(this, key, true) + } + + peek (key) { + return get(this, key, false) + } + + pop () { + const node = this[LRU_LIST].tail + if (!node) + return null + + del(this, node) + return node.value + } + + del (key) { + del(this, this[CACHE].get(key)) + } + + load (arr) { + // reset the cache + this.reset() + + const now = Date.now() + // A previous serialized cache has the most recent items first + for (let l = arr.length - 1; l >= 0; l--) { + const hit = arr[l] + const expiresAt = hit.e || 0 + if (expiresAt === 0) + // the item was created without expiration in a non aged cache + this.set(hit.k, hit.v) + else { + const maxAge = expiresAt - now + // dont add already expired items + if (maxAge > 0) { + this.set(hit.k, hit.v, maxAge) + } + } + } + } + + prune () { + this[CACHE].forEach((value, key) => get(this, key, false)) + } +} + +const get = (self, key, doUse) => { + const node = self[CACHE].get(key) + if (node) { + const hit = node.value + if (isStale(self, hit)) { + del(self, node) + if (!self[ALLOW_STALE]) + return undefined + } else { + if (doUse) { + if (self[UPDATE_AGE_ON_GET]) + node.value.now = Date.now() + self[LRU_LIST].unshiftNode(node) + } + } + return hit.value + } +} + +const isStale = (self, hit) => { + if (!hit || (!hit.maxAge && !self[MAX_AGE])) + return false + + const diff = Date.now() - hit.now + return hit.maxAge ? diff > hit.maxAge + : self[MAX_AGE] && (diff > self[MAX_AGE]) +} + +const trim = self => { + if (self[LENGTH] > self[MAX]) { + for (let walker = self[LRU_LIST].tail; + self[LENGTH] > self[MAX] && walker !== null;) { + // We know that we're about to delete this one, and also + // what the next least recently used key will be, so just + // go ahead and set it now. + const prev = walker.prev + del(self, walker) + walker = prev + } + } +} + +const del = (self, node) => { + if (node) { + const hit = node.value + if (self[DISPOSE]) + self[DISPOSE](hit.key, hit.value) + + self[LENGTH] -= hit.length + self[CACHE].delete(hit.key) + self[LRU_LIST].removeNode(node) + } +} + +class Entry { + constructor (key, value, length, now, maxAge) { + this.key = key + this.value = value + this.length = length + this.now = now + this.maxAge = maxAge || 0 + } +} + +const forEachStep = (self, fn, node, thisp) => { + let hit = node.value + if (isStale(self, hit)) { + del(self, node) + if (!self[ALLOW_STALE]) + hit = undefined + } + if (hit) + fn.call(thisp, hit.value, hit.key, self) +} + +module.exports = LRUCache -module.exports = require("child_process"); /***/ }), @@ -763,7 +1112,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.argStringToArray = exports.ToolRunner = void 0; const os = __importStar(__webpack_require__(87)); const events = __importStar(__webpack_require__(614)); -const child = __importStar(__webpack_require__(129)); +const child = __importStar(__webpack_require__(4)); const path = __importStar(__webpack_require__(622)); const io = __importStar(__webpack_require__(436)); const ioUtil = __importStar(__webpack_require__(962)); @@ -2243,21 +2592,42 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getBuildKitVersion = exports.install = exports.inspect = exports.isAvailable = exports.parseVersion = exports.getVersion = void 0; +exports.getBuildKitVersion = exports.install = exports.inspect = exports.parseVersion = exports.getVersion = exports.isAvailable = void 0; const fs = __importStar(__webpack_require__(747)); const path = __importStar(__webpack_require__(622)); const semver = __importStar(__webpack_require__(383)); const util = __importStar(__webpack_require__(669)); const context = __importStar(__webpack_require__(842)); -const exec = __importStar(__webpack_require__(757)); const github = __importStar(__webpack_require__(928)); const core = __importStar(__webpack_require__(186)); +const exec = __importStar(__webpack_require__(514)); const tc = __importStar(__webpack_require__(784)); +function isAvailable() { + return __awaiter(this, void 0, void 0, function* () { + return yield exec + .getExecOutput('docker', ['buildx'], { + ignoreReturnCode: true, + silent: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + return false; + } + return res.exitCode == 0; + }); + }); +} +exports.isAvailable = isAvailable; function getVersion() { return __awaiter(this, void 0, void 0, function* () { - return yield exec.exec(`docker`, ['buildx', 'version'], true).then(res => { - if (res.stderr.length > 0 && !res.success) { - throw new Error(res.stderr); + return yield exec + .getExecOutput('docker', ['buildx', 'version'], { + ignoreReturnCode: true, + silent: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + throw new Error(res.stderr.trim()); } return parseVersion(res.stdout); }); @@ -2268,28 +2638,22 @@ 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`); + throw new Error(`Cannot parse buildx version`); } return semver.clean(matches[1]); }); } exports.parseVersion = parseVersion; -function isAvailable() { - return __awaiter(this, void 0, void 0, function* () { - return yield exec.exec(`docker`, ['buildx'], true).then(res => { - if (res.stderr.length > 0 && !res.success) { - return false; - } - return res.success; - }); - }); -} -exports.isAvailable = isAvailable; function inspect(name) { return __awaiter(this, void 0, void 0, function* () { - return yield exec.exec(`docker`, ['buildx', 'inspect', name], true).then(res => { - if (res.stderr.length > 0 && !res.success) { - throw new Error(res.stderr); + return yield exec + .getExecOutput(`docker`, ['buildx', 'inspect', name], { + ignoreReturnCode: true, + silent: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + throw new Error(res.stderr.trim()); } const builder = {}; itlines: for (const line of res.stdout.trim().split(`\n`)) { @@ -2412,20 +2776,30 @@ function filename(version) { } function getBuildKitVersion(containerID) { return __awaiter(this, void 0, void 0, function* () { - return exec.exec(`docker`, ['inspect', '--format', '{{.Config.Image}}', containerID], true).then(bkitimage => { - if (bkitimage.success && bkitimage.stdout.length > 0) { - return exec.exec(`docker`, ['run', '--rm', bkitimage.stdout, '--version'], true).then(bkitversion => { - if (bkitversion.success && bkitversion.stdout.length > 0) { + return exec + .getExecOutput(`docker`, ['inspect', '--format', '{{.Config.Image}}', containerID], { + ignoreReturnCode: true, + silent: true + }) + .then(bkitimage => { + if (bkitimage.exitCode == 0 && bkitimage.stdout.length > 0) { + return exec + .getExecOutput(`docker`, ['run', '--rm', bkitimage.stdout, '--version'], { + ignoreReturnCode: true, + silent: true + }) + .then(bkitversion => { + if (bkitversion.exitCode == 0 && bkitversion.stdout.length > 0) { return `${bkitimage.stdout} => ${bkitversion.stdout}`; } else if (bkitversion.stderr.length > 0) { - core.warning(bkitversion.stderr); + core.warning(bkitversion.stderr.trim()); } return bkitversion.stdout; }); } else if (bkitimage.stderr.length > 0) { - core.warning(bkitimage.stderr); + core.warning(bkitimage.stderr.trim()); } return bkitimage.stdout; }); @@ -2879,7 +3253,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge Object.defineProperty(exports, "__esModule", { value: true }); exports.findInPath = exports.which = exports.mkdirP = exports.rmRF = exports.mv = exports.cp = void 0; const assert_1 = __webpack_require__(357); -const childProcess = __importStar(__webpack_require__(129)); +const childProcess = __importStar(__webpack_require__(4)); const path = __importStar(__webpack_require__(622)); const util_1 = __webpack_require__(669); const ioUtil = __importStar(__webpack_require__(962)); @@ -3430,7 +3804,7 @@ const core_1 = __webpack_require__(186); // needs to be require for core node modules to be mocked /* eslint @typescript-eslint/no-require-imports: 0 */ const os = __webpack_require__(87); -const cp = __webpack_require__(129); +const cp = __webpack_require__(4); const fs = __webpack_require__(747); function _findMatch(versionSpec, stable, candidates, archFilter) { return __awaiter(this, void 0, void 0, function* () { @@ -6458,348 +6832,6 @@ function issueCommand(command, message) { exports.issueCommand = issueCommand; //# sourceMappingURL=file-command.js.map -/***/ }), - -/***/ 722: -/***/ (function(module, __unusedexports, __webpack_require__) { - -"use strict"; - - -// A linked list to keep track of recently-used-ness -const Yallist = __webpack_require__(665) - -const MAX = Symbol('max') -const LENGTH = Symbol('length') -const LENGTH_CALCULATOR = Symbol('lengthCalculator') -const ALLOW_STALE = Symbol('allowStale') -const MAX_AGE = Symbol('maxAge') -const DISPOSE = Symbol('dispose') -const NO_DISPOSE_ON_SET = Symbol('noDisposeOnSet') -const LRU_LIST = Symbol('lruList') -const CACHE = Symbol('cache') -const UPDATE_AGE_ON_GET = Symbol('updateAgeOnGet') - -const naiveLength = () => 1 - -// lruList is a yallist where the head is the youngest -// item, and the tail is the oldest. the list contains the Hit -// objects as the entries. -// Each Hit object has a reference to its Yallist.Node. This -// never changes. -// -// cache is a Map (or PseudoMap) that matches the keys to -// the Yallist.Node object. -class LRUCache { - constructor (options) { - if (typeof options === 'number') - options = { max: options } - - if (!options) - options = {} - - if (options.max && (typeof options.max !== 'number' || options.max < 0)) - throw new TypeError('max must be a non-negative number') - // Kind of weird to have a default max of Infinity, but oh well. - const max = this[MAX] = options.max || Infinity - - const lc = options.length || naiveLength - this[LENGTH_CALCULATOR] = (typeof lc !== 'function') ? naiveLength : lc - this[ALLOW_STALE] = options.stale || false - if (options.maxAge && typeof options.maxAge !== 'number') - throw new TypeError('maxAge must be a number') - this[MAX_AGE] = options.maxAge || 0 - this[DISPOSE] = options.dispose - this[NO_DISPOSE_ON_SET] = options.noDisposeOnSet || false - this[UPDATE_AGE_ON_GET] = options.updateAgeOnGet || false - this.reset() - } - - // resize the cache when the max changes. - set max (mL) { - if (typeof mL !== 'number' || mL < 0) - throw new TypeError('max must be a non-negative number') - - this[MAX] = mL || Infinity - trim(this) - } - get max () { - return this[MAX] - } - - set allowStale (allowStale) { - this[ALLOW_STALE] = !!allowStale - } - get allowStale () { - return this[ALLOW_STALE] - } - - set maxAge (mA) { - if (typeof mA !== 'number') - throw new TypeError('maxAge must be a non-negative number') - - this[MAX_AGE] = mA - trim(this) - } - get maxAge () { - return this[MAX_AGE] - } - - // resize the cache when the lengthCalculator changes. - set lengthCalculator (lC) { - if (typeof lC !== 'function') - lC = naiveLength - - if (lC !== this[LENGTH_CALCULATOR]) { - this[LENGTH_CALCULATOR] = lC - this[LENGTH] = 0 - this[LRU_LIST].forEach(hit => { - hit.length = this[LENGTH_CALCULATOR](hit.value, hit.key) - this[LENGTH] += hit.length - }) - } - trim(this) - } - get lengthCalculator () { return this[LENGTH_CALCULATOR] } - - get length () { return this[LENGTH] } - get itemCount () { return this[LRU_LIST].length } - - rforEach (fn, thisp) { - thisp = thisp || this - for (let walker = this[LRU_LIST].tail; walker !== null;) { - const prev = walker.prev - forEachStep(this, fn, walker, thisp) - walker = prev - } - } - - forEach (fn, thisp) { - thisp = thisp || this - for (let walker = this[LRU_LIST].head; walker !== null;) { - const next = walker.next - forEachStep(this, fn, walker, thisp) - walker = next - } - } - - keys () { - return this[LRU_LIST].toArray().map(k => k.key) - } - - values () { - return this[LRU_LIST].toArray().map(k => k.value) - } - - reset () { - if (this[DISPOSE] && - this[LRU_LIST] && - this[LRU_LIST].length) { - this[LRU_LIST].forEach(hit => this[DISPOSE](hit.key, hit.value)) - } - - this[CACHE] = new Map() // hash of items by key - this[LRU_LIST] = new Yallist() // list of items in order of use recency - this[LENGTH] = 0 // length of items in the list - } - - dump () { - return this[LRU_LIST].map(hit => - isStale(this, hit) ? false : { - k: hit.key, - v: hit.value, - e: hit.now + (hit.maxAge || 0) - }).toArray().filter(h => h) - } - - dumpLru () { - return this[LRU_LIST] - } - - set (key, value, maxAge) { - maxAge = maxAge || this[MAX_AGE] - - if (maxAge && typeof maxAge !== 'number') - throw new TypeError('maxAge must be a number') - - const now = maxAge ? Date.now() : 0 - const len = this[LENGTH_CALCULATOR](value, key) - - if (this[CACHE].has(key)) { - if (len > this[MAX]) { - del(this, this[CACHE].get(key)) - return false - } - - const node = this[CACHE].get(key) - const item = node.value - - // dispose of the old one before overwriting - // split out into 2 ifs for better coverage tracking - if (this[DISPOSE]) { - if (!this[NO_DISPOSE_ON_SET]) - this[DISPOSE](key, item.value) - } - - item.now = now - item.maxAge = maxAge - item.value = value - this[LENGTH] += len - item.length - item.length = len - this.get(key) - trim(this) - return true - } - - const hit = new Entry(key, value, len, now, maxAge) - - // oversized objects fall out of cache automatically. - if (hit.length > this[MAX]) { - if (this[DISPOSE]) - this[DISPOSE](key, value) - - return false - } - - this[LENGTH] += hit.length - this[LRU_LIST].unshift(hit) - this[CACHE].set(key, this[LRU_LIST].head) - trim(this) - return true - } - - has (key) { - if (!this[CACHE].has(key)) return false - const hit = this[CACHE].get(key).value - return !isStale(this, hit) - } - - get (key) { - return get(this, key, true) - } - - peek (key) { - return get(this, key, false) - } - - pop () { - const node = this[LRU_LIST].tail - if (!node) - return null - - del(this, node) - return node.value - } - - del (key) { - del(this, this[CACHE].get(key)) - } - - load (arr) { - // reset the cache - this.reset() - - const now = Date.now() - // A previous serialized cache has the most recent items first - for (let l = arr.length - 1; l >= 0; l--) { - const hit = arr[l] - const expiresAt = hit.e || 0 - if (expiresAt === 0) - // the item was created without expiration in a non aged cache - this.set(hit.k, hit.v) - else { - const maxAge = expiresAt - now - // dont add already expired items - if (maxAge > 0) { - this.set(hit.k, hit.v, maxAge) - } - } - } - } - - prune () { - this[CACHE].forEach((value, key) => get(this, key, false)) - } -} - -const get = (self, key, doUse) => { - const node = self[CACHE].get(key) - if (node) { - const hit = node.value - if (isStale(self, hit)) { - del(self, node) - if (!self[ALLOW_STALE]) - return undefined - } else { - if (doUse) { - if (self[UPDATE_AGE_ON_GET]) - node.value.now = Date.now() - self[LRU_LIST].unshiftNode(node) - } - } - return hit.value - } -} - -const isStale = (self, hit) => { - if (!hit || (!hit.maxAge && !self[MAX_AGE])) - return false - - const diff = Date.now() - hit.now - return hit.maxAge ? diff > hit.maxAge - : self[MAX_AGE] && (diff > self[MAX_AGE]) -} - -const trim = self => { - if (self[LENGTH] > self[MAX]) { - for (let walker = self[LRU_LIST].tail; - self[LENGTH] > self[MAX] && walker !== null;) { - // We know that we're about to delete this one, and also - // what the next least recently used key will be, so just - // go ahead and set it now. - const prev = walker.prev - del(self, walker) - walker = prev - } - } -} - -const del = (self, node) => { - if (node) { - const hit = node.value - if (self[DISPOSE]) - self[DISPOSE](hit.key, hit.value) - - self[LENGTH] -= hit.length - self[CACHE].delete(hit.key) - self[LRU_LIST].removeNode(node) - } -} - -class Entry { - constructor (key, value, length, now, maxAge) { - this.key = key - this.value = value - this.length = length - this.now = now - this.maxAge = maxAge || 0 - } -} - -const forEachStep = (self, fn, node, thisp) => { - let hit = node.value - if (isStale(self, hit)) { - del(self, node) - if (!self[ALLOW_STALE]) - hit = undefined - } - if (hit) - fn.call(thisp, hit.value, hit.key, self) -} - -module.exports = LRUCache - - /***/ }), /***/ 741: @@ -6879,68 +6911,6 @@ module.exports = require("fs"); /***/ }), -/***/ 757: -/***/ (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.exec = void 0; -const aexec = __importStar(__webpack_require__(514)); -exports.exec = (command, args = [], silent) => __awaiter(void 0, void 0, void 0, function* () { - let stdout = ''; - let stderr = ''; - const options = { - silent: silent, - ignoreReturnCode: true - }; - options.listeners = { - stdout: (data) => { - stdout += data.toString(); - }, - stderr: (data) => { - stderr += data.toString(); - } - }; - const returnCode = yield aexec.exec(command, args, options); - return { - success: returnCode === 0, - stdout: stdout.trim(), - stderr: stderr.trim() - }; -}); -//# sourceMappingURL=exec.js.map - -/***/ }), - /***/ 783: /***/ (function(__unusedmodule, exports, __webpack_require__) { @@ -7907,7 +7877,7 @@ class Range { } module.exports = Range -const LRU = __webpack_require__(722) +const LRU = __webpack_require__(129) const cache = new LRU({ max: 1000 }) const parseOptions = __webpack_require__(785) diff --git a/src/buildx.ts b/src/buildx.ts index 65a1dcc..d3045c5 100644 --- a/src/buildx.ts +++ b/src/buildx.ts @@ -3,9 +3,9 @@ import * as path from 'path'; import * as semver from 'semver'; import * as util from 'util'; import * as context from './context'; -import * as exec from './exec'; import * as github from './github'; import * as core from '@actions/core'; +import * as exec from '@actions/exec'; import * as tc from '@actions/tool-cache'; export type Builder = { @@ -18,77 +18,92 @@ export type Builder = { node_platforms?: string; }; +export async function isAvailable(): Promise { + return await exec + .getExecOutput('docker', ['buildx'], { + ignoreReturnCode: true, + silent: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + return false; + } + return res.exitCode == 0; + }); +} + export async function getVersion(): Promise { - return await exec.exec(`docker`, ['buildx', 'version'], true).then(res => { - if (res.stderr.length > 0 && !res.success) { - throw new Error(res.stderr); - } - return parseVersion(res.stdout); - }); + return await exec + .getExecOutput('docker', ['buildx', 'version'], { + ignoreReturnCode: true, + silent: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + throw new Error(res.stderr.trim()); + } + return parseVersion(res.stdout); + }); } export async function parseVersion(stdout: string): Promise { const matches = /\sv?([0-9.]+)/.exec(stdout); if (!matches) { - throw new Error(`Cannot parse Buildx version`); + throw new Error(`Cannot parse buildx version`); } return semver.clean(matches[1]); } -export async function isAvailable(): Promise { - return await exec.exec(`docker`, ['buildx'], true).then(res => { - if (res.stderr.length > 0 && !res.success) { - return false; - } - return res.success; - }); -} - export async function inspect(name: string): Promise { - return await exec.exec(`docker`, ['buildx', 'inspect', name], true).then(res => { - if (res.stderr.length > 0 && !res.success) { - throw new Error(res.stderr); - } - const builder: Builder = {}; - itlines: for (const line of res.stdout.trim().split(`\n`)) { - const [key, ...rest] = line.split(':'); - const value = rest.map(v => v.trim()).join(':'); - if (key.length == 0 || value.length == 0) { - continue; + return await exec + .getExecOutput(`docker`, ['buildx', 'inspect', name], { + ignoreReturnCode: true, + silent: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + throw new Error(res.stderr.trim()); } - switch (key) { - case 'Name': { - if (builder.name == undefined) { - builder.name = value; - } else { - builder.node_name = value; + const builder: Builder = {}; + itlines: for (const line of res.stdout.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) { + case 'Name': { + if (builder.name == undefined) { + builder.name = value; + } else { + builder.node_name = value; + } + break; + } + case 'Driver': { + builder.driver = value; + break; + } + case 'Endpoint': { + builder.node_endpoint = value; + break; + } + case 'Status': { + builder.node_status = value; + break; + } + case 'Flags': { + builder.node_flags = value; + break; + } + case 'Platforms': { + builder.node_platforms = value.replace(/\s/g, ''); + break itlines; } - break; - } - case 'Driver': { - builder.driver = value; - break; - } - case 'Endpoint': { - builder.node_endpoint = value; - break; - } - case 'Status': { - builder.node_status = value; - break; - } - case 'Flags': { - builder.node_flags = value; - break; - } - case 'Platforms': { - builder.node_platforms = value.replace(/\s/g, ''); - break itlines; } } - } - return builder; - }); + return builder; + }); } export async function install(inputVersion: string, dockerConfigHome: string): Promise { @@ -173,19 +188,29 @@ async function filename(version: string): Promise { } export async function getBuildKitVersion(containerID: string): Promise { - return exec.exec(`docker`, ['inspect', '--format', '{{.Config.Image}}', containerID], true).then(bkitimage => { - if (bkitimage.success && bkitimage.stdout.length > 0) { - return exec.exec(`docker`, ['run', '--rm', bkitimage.stdout, '--version'], true).then(bkitversion => { - if (bkitversion.success && bkitversion.stdout.length > 0) { - return `${bkitimage.stdout} => ${bkitversion.stdout}`; - } else if (bkitversion.stderr.length > 0) { - core.warning(bkitversion.stderr); - } - return bkitversion.stdout; - }); - } else if (bkitimage.stderr.length > 0) { - core.warning(bkitimage.stderr); - } - return bkitimage.stdout; - }); + return exec + .getExecOutput(`docker`, ['inspect', '--format', '{{.Config.Image}}', containerID], { + ignoreReturnCode: true, + silent: true + }) + .then(bkitimage => { + if (bkitimage.exitCode == 0 && bkitimage.stdout.length > 0) { + return exec + .getExecOutput(`docker`, ['run', '--rm', bkitimage.stdout, '--version'], { + ignoreReturnCode: true, + silent: true + }) + .then(bkitversion => { + if (bkitversion.exitCode == 0 && bkitversion.stdout.length > 0) { + return `${bkitimage.stdout} => ${bkitversion.stdout}`; + } else if (bkitversion.stderr.length > 0) { + core.warning(bkitversion.stderr.trim()); + } + return bkitversion.stdout; + }); + } else if (bkitimage.stderr.length > 0) { + core.warning(bkitimage.stderr.trim()); + } + return bkitimage.stdout; + }); } diff --git a/src/docker.ts b/src/docker.ts deleted file mode 100644 index a35b384..0000000 --- a/src/docker.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as exec from './exec'; - -export async function isDaemonRunning(): Promise { - return await exec.exec(`docker`, ['version', '--format', '{{.Server.Os}}'], true).then(res => { - return !res.stdout.includes(' ') && res.success; - }); -} diff --git a/src/exec.ts b/src/exec.ts deleted file mode 100644 index 3d0c4ce..0000000 --- a/src/exec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as aexec from '@actions/exec'; -import {ExecOptions} from '@actions/exec'; - -export interface ExecResult { - success: boolean; - stdout: string; - stderr: string; -} - -export const exec = async (command: string, args: string[] = [], silent: boolean): Promise => { - let stdout: string = ''; - let stderr: string = ''; - - const options: ExecOptions = { - silent: silent, - ignoreReturnCode: true - }; - options.listeners = { - stdout: (data: Buffer) => { - stdout += data.toString(); - }, - stderr: (data: Buffer) => { - stderr += data.toString(); - } - }; - - const returnCode: number = await aexec.exec(command, args, options); - - return { - success: returnCode === 0, - stdout: stdout.trim(), - stderr: stderr.trim() - }; -}; diff --git a/src/main.ts b/src/main.ts index b4f2955..0d5d202 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,11 @@ -import * as core from '@actions/core'; -import * as exec from '@actions/exec'; 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 mexec from './exec'; import * as stateHelper from './state-helper'; +import * as core from '@actions/core'; +import * as exec from '@actions/exec'; async function run(): Promise { try { @@ -94,21 +93,29 @@ async function run(): Promise { async function cleanup(): Promise { if (stateHelper.IsDebug && stateHelper.containerName.length > 0) { core.startGroup(`BuildKit container logs`); - await mexec.exec('docker', ['logs', `${stateHelper.containerName}`], false).then(res => { - if (res.stderr.length > 0 && !res.success) { - core.warning(res.stderr); - } - }); + await exec + .getExecOutput('docker', ['logs', `${stateHelper.containerName}`], { + ignoreReturnCode: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + core.warning(res.stderr.trim()); + } + }); core.endGroup(); } if (stateHelper.builderName.length > 0) { core.startGroup(`Removing builder`); - await mexec.exec('docker', ['buildx', 'rm', `${stateHelper.builderName}`], false).then(res => { - if (res.stderr.length > 0 && !res.success) { - core.warning(res.stderr); - } - }); + await exec + .getExecOutput('docker', ['buildx', 'rm', `${stateHelper.builderName}`], { + ignoreReturnCode: true + }) + .then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + core.warning(res.stderr.trim()); + } + }); core.endGroup(); } }