From 35a8193474a026cf897b291052d561559ed3e6fa Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Mon, 20 Feb 2023 09:01:01 +0100 Subject: [PATCH] Exec class Signed-off-by: CrazyMax --- __tests__/buildx/buildx.test.ts | 12 ++++---- __tests__/docker.test.ts | 8 +++--- __tests__/exec.test.ts | 51 +++++++++++++++++++++++++++++++++ src/buildkit/buildkit.ts | 50 +++++++++++++++----------------- src/buildx/builder.ts | 23 +++++++-------- src/buildx/buildx.ts | 35 +++++++++++----------- src/buildx/install.ts | 20 ++++++------- src/docker.ts | 15 +++++----- src/exec.ts | 31 ++++++++++++++++++++ src/git.ts | 30 +++++++++---------- 10 files changed, 171 insertions(+), 104 deletions(-) create mode 100644 __tests__/exec.test.ts create mode 100644 src/exec.ts diff --git a/__tests__/buildx/buildx.test.ts b/__tests__/buildx/buildx.test.ts index 0417427..b5c53f6 100644 --- a/__tests__/buildx/buildx.test.ts +++ b/__tests__/buildx/buildx.test.ts @@ -19,10 +19,10 @@ import * as fs from 'fs'; import * as path from 'path'; import * as rimraf from 'rimraf'; import * as semver from 'semver'; -import * as exec from '@actions/exec'; import {Buildx} from '../../src/buildx/buildx'; import {Context} from '../../src/context'; +import {Exec} from '../../src/exec'; import {Cert} from '../../src/types/buildx'; @@ -91,7 +91,7 @@ describe('certsDir', () => { describe('isAvailable', () => { it('docker cli', async () => { - const execSpy = jest.spyOn(exec, 'getExecOutput'); + const execSpy = jest.spyOn(Exec, 'getExecOutput'); const buildx = new Buildx({ standalone: false }); @@ -103,7 +103,7 @@ describe('isAvailable', () => { }); }); it('standalone', async () => { - const execSpy = jest.spyOn(exec, 'getExecOutput'); + const execSpy = jest.spyOn(Exec, 'getExecOutput'); const buildx = new Buildx({ standalone: true }); @@ -118,7 +118,7 @@ describe('isAvailable', () => { describe('printInspect', () => { it('prints builder2 instance', async () => { - const execSpy = jest.spyOn(exec, 'exec'); + const execSpy = jest.spyOn(Exec, 'exec'); const buildx = new Buildx({ standalone: true }); @@ -133,7 +133,7 @@ describe('printInspect', () => { describe('printVersion', () => { it('docker cli', async () => { - const execSpy = jest.spyOn(exec, 'exec'); + const execSpy = jest.spyOn(Exec, 'exec'); const buildx = new Buildx({ standalone: false }); @@ -143,7 +143,7 @@ describe('printVersion', () => { }); }); it('standalone', async () => { - const execSpy = jest.spyOn(exec, 'exec'); + const execSpy = jest.spyOn(Exec, 'exec'); const buildx = new Buildx({ standalone: true }); diff --git a/__tests__/docker.test.ts b/__tests__/docker.test.ts index 140b399..c8a7415 100644 --- a/__tests__/docker.test.ts +++ b/__tests__/docker.test.ts @@ -15,11 +15,11 @@ */ import {afterEach, beforeEach, describe, expect, it, jest} from '@jest/globals'; -import * as exec from '@actions/exec'; import path from 'path'; import osm = require('os'); import {Docker} from '../src/docker'; +import {Exec} from '../src/exec'; beforeEach(() => { jest.clearAllMocks(); @@ -49,7 +49,7 @@ describe('configDir', () => { describe('isAvailable', () => { it('cli', async () => { - const execSpy = jest.spyOn(exec, 'getExecOutput'); + const execSpy = jest.spyOn(Exec, 'getExecOutput'); await Docker.isAvailable(); // eslint-disable-next-line jest/no-standalone-expect expect(execSpy).toHaveBeenCalledWith(`docker`, undefined, { @@ -61,7 +61,7 @@ describe('isAvailable', () => { describe('printVersion', () => { it('call docker version', async () => { - const execSpy = jest.spyOn(exec, 'exec'); + const execSpy = jest.spyOn(Exec, 'exec'); await Docker.printVersion().catch(() => { // noop }); @@ -71,7 +71,7 @@ describe('printVersion', () => { describe('printInfo', () => { it('call docker info', async () => { - const execSpy = jest.spyOn(exec, 'exec'); + const execSpy = jest.spyOn(Exec, 'exec'); await Docker.printInfo().catch(() => { // noop }); diff --git a/__tests__/exec.test.ts b/__tests__/exec.test.ts new file mode 100644 index 0000000..0880af0 --- /dev/null +++ b/__tests__/exec.test.ts @@ -0,0 +1,51 @@ +/** + * Copyright 2023 actions-toolkit authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {beforeEach, describe, expect, it, jest} from '@jest/globals'; + +import {Exec} from '../src/exec'; + +beforeEach(() => { + jest.clearAllMocks(); +}); + +describe('exec', () => { + it('returns docker version', async () => { + const execSpy = jest.spyOn(Exec, 'exec'); + await Exec.exec('docker', ['version'], { + ignoreReturnCode: true, + silent: true + }); + expect(execSpy).toHaveBeenCalledWith(`docker`, ['version'], { + ignoreReturnCode: true, + silent: true + }); + }); +}); + +describe('getExecOutput', () => { + it('returns docker version', async () => { + const execSpy = jest.spyOn(Exec, 'getExecOutput'); + await Exec.getExecOutput('docker', ['version'], { + ignoreReturnCode: true, + silent: true + }); + expect(execSpy).toHaveBeenCalledWith(`docker`, ['version'], { + ignoreReturnCode: true, + silent: true + }); + }); +}); diff --git a/src/buildkit/buildkit.ts b/src/buildkit/buildkit.ts index ddbe96b..30bd8b8 100644 --- a/src/buildkit/buildkit.ts +++ b/src/buildkit/buildkit.ts @@ -15,12 +15,12 @@ */ import * as core from '@actions/core'; -import * as exec from '@actions/exec'; import * as semver from 'semver'; import {Buildx} from '../buildx/buildx'; import {Builder} from '../buildx/builder'; import {Config} from './config'; +import {Exec} from '../exec'; import {BuilderInfo, NodeInfo} from '../types/builder'; @@ -51,32 +51,28 @@ export class BuildKit { private async getVersionWithinImage(nodeName: string): Promise { core.debug(`BuildKit.getVersionWithinImage nodeName: ${nodeName}`); - return exec - .getExecOutput(`docker`, ['inspect', '--format', '{{.Config.Image}}', `${Buildx.containerNamePrefix}${nodeName}`], { - ignoreReturnCode: true, - silent: true - }) - .then(bkitimage => { - if (bkitimage.exitCode == 0 && bkitimage.stdout.length > 0) { - core.debug(`BuildKit.getVersionWithinImage image: ${bkitimage.stdout.trim()}`); - return exec - .getExecOutput(`docker`, ['run', '--rm', bkitimage.stdout.trim(), '--version'], { - ignoreReturnCode: true, - silent: true - }) - .then(bkitversion => { - if (bkitversion.exitCode == 0 && bkitversion.stdout.length > 0) { - return `${bkitimage.stdout.trim()} => ${bkitversion.stdout.trim()}`; - } else if (bkitversion.stderr.length > 0) { - throw new Error(bkitimage.stderr.trim()); - } - return bkitversion.stdout.trim(); - }); - } else if (bkitimage.stderr.length > 0) { - throw new Error(bkitimage.stderr.trim()); - } - return bkitimage.stdout.trim(); - }); + return Exec.getExecOutput(`docker`, ['inspect', '--format', '{{.Config.Image}}', `${Buildx.containerNamePrefix}${nodeName}`], { + ignoreReturnCode: true, + silent: true + }).then(bkitimage => { + if (bkitimage.exitCode == 0 && bkitimage.stdout.length > 0) { + core.debug(`BuildKit.getVersionWithinImage image: ${bkitimage.stdout.trim()}`); + return Exec.getExecOutput(`docker`, ['run', '--rm', bkitimage.stdout.trim(), '--version'], { + ignoreReturnCode: true, + silent: true + }).then(bkitversion => { + if (bkitversion.exitCode == 0 && bkitversion.stdout.length > 0) { + return `${bkitimage.stdout.trim()} => ${bkitversion.stdout.trim()}`; + } else if (bkitversion.stderr.length > 0) { + throw new Error(bkitimage.stderr.trim()); + } + return bkitversion.stdout.trim(); + }); + } else if (bkitimage.stderr.length > 0) { + throw new Error(bkitimage.stderr.trim()); + } + return bkitimage.stdout.trim(); + }); } public async versionSatisfies(builderName: string, range: string, builderInfo?: BuilderInfo): Promise { diff --git a/src/buildx/builder.ts b/src/buildx/builder.ts index 18d5fde..eab4159 100644 --- a/src/buildx/builder.ts +++ b/src/buildx/builder.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import * as exec from '@actions/exec'; - import {Buildx} from './buildx'; +import {Exec} from '../exec'; import {BuilderInfo, NodeInfo} from '../types/builder'; @@ -33,17 +32,15 @@ export class Builder { public async inspect(name: string): Promise { const cmd = await this.buildx.getCommand(['inspect', name]); - return await exec - .getExecOutput(cmd.command, cmd.args, { - ignoreReturnCode: true, - silent: true - }) - .then(res => { - if (res.stderr.length > 0 && res.exitCode != 0) { - throw new Error(res.stderr.trim()); - } - return Builder.parseInspect(res.stdout); - }); + return await Exec.getExecOutput(cmd.command, cmd.args, { + ignoreReturnCode: true, + silent: true + }).then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + throw new Error(res.stderr.trim()); + } + return Builder.parseInspect(res.stdout); + }); } public static parseInspect(data: string): BuilderInfo { diff --git a/src/buildx/buildx.ts b/src/buildx/buildx.ts index 5435491..fbf3d64 100644 --- a/src/buildx/buildx.ts +++ b/src/buildx/buildx.ts @@ -17,10 +17,10 @@ import fs from 'fs'; import path from 'path'; import * as core from '@actions/core'; -import * as exec from '@actions/exec'; import * as semver from 'semver'; import {Docker} from '../docker'; +import {Exec} from '../exec'; import {Inputs} from './inputs'; import {Cert} from '../types/buildx'; @@ -67,11 +67,10 @@ export class Buildx { public async isAvailable(): Promise { const cmd = await this.getCommand([]); - const ok: boolean = await exec - .getExecOutput(cmd.command, cmd.args, { - ignoreReturnCode: true, - silent: true - }) + const ok: boolean = await Exec.getExecOutput(cmd.command, cmd.args, { + ignoreReturnCode: true, + silent: true + }) .then(res => { if (res.stderr.length > 0 && res.exitCode != 0) { core.debug(`Buildx.isAvailable cmd err: ${res.stderr}`); @@ -90,7 +89,7 @@ export class Buildx { public async printInspect(name: string): Promise { const cmd = await this.getCommand(['inspect', name]); - await exec.exec(cmd.command, cmd.args, { + await Exec.exec(cmd.command, cmd.args, { failOnStdErr: false }); } @@ -99,17 +98,15 @@ export class Buildx { return (async () => { if (!this._version) { const cmd = await this.getCommand(['version']); - this._version = await exec - .getExecOutput(cmd.command, cmd.args, { - ignoreReturnCode: true, - silent: true - }) - .then(res => { - if (res.stderr.length > 0 && res.exitCode != 0) { - throw new Error(res.stderr.trim()); - } - return Buildx.parseVersion(res.stdout.trim()); - }); + this._version = await Exec.getExecOutput(cmd.command, cmd.args, { + ignoreReturnCode: true, + silent: true + }).then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + throw new Error(res.stderr.trim()); + } + return Buildx.parseVersion(res.stdout.trim()); + }); } return this._version; })(); @@ -117,7 +114,7 @@ export class Buildx { public async printVersion() { const cmd = await this.getCommand(['version']); - await exec.exec(cmd.command, cmd.args, { + await Exec.exec(cmd.command, cmd.args, { failOnStdErr: false }); } diff --git a/src/buildx/install.ts b/src/buildx/install.ts index 7161b23..30425ab 100644 --- a/src/buildx/install.ts +++ b/src/buildx/install.ts @@ -18,7 +18,6 @@ import fs from 'fs'; import os from 'os'; import path from 'path'; import * as core from '@actions/core'; -import * as exec from '@actions/exec'; import * as httpm from '@actions/http-client'; import * as tc from '@actions/tool-cache'; import * as semver from 'semver'; @@ -26,6 +25,7 @@ import * as util from 'util'; import {Buildx} from './buildx'; import {Context} from '../context'; +import {Exec} from '../exec'; import {Docker} from '../docker'; import {Git} from '../git'; @@ -82,16 +82,14 @@ export class Install { if (!toolPath) { const outputDir = path.join(Context.tmpDir(), 'build-cache'); const buildCmd = await this.buildCommand(gitContext, outputDir); - toolPath = await exec - .getExecOutput(buildCmd.command, buildCmd.args, { - ignoreReturnCode: true - }) - .then(res => { - if (res.stderr.length > 0 && res.exitCode != 0) { - core.warning(res.stderr.trim()); - } - return tc.cacheFile(`${outputDir}/buildx`, os.platform() == 'win32' ? 'docker-buildx.exe' : 'docker-buildx', 'buildx', vspec); - }); + toolPath = await Exec.getExecOutput(buildCmd.command, buildCmd.args, { + ignoreReturnCode: true + }).then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + core.warning(res.stderr.trim()); + } + return tc.cacheFile(`${outputDir}/buildx`, os.platform() == 'win32' ? 'docker-buildx.exe' : 'docker-buildx', 'buildx', vspec); + }); } return toolPath; diff --git a/src/docker.ts b/src/docker.ts index 5743fea..e959ac9 100644 --- a/src/docker.ts +++ b/src/docker.ts @@ -17,7 +17,7 @@ import os from 'os'; import path from 'path'; import * as core from '@actions/core'; -import * as exec from '@actions/exec'; +import {Exec} from './exec'; export class Docker { static get configDir(): string { @@ -25,11 +25,10 @@ export class Docker { } public static async isAvailable(): Promise { - const ok: boolean = await exec - .getExecOutput('docker', undefined, { - ignoreReturnCode: true, - silent: true - }) + const ok: boolean = await Exec.getExecOutput('docker', undefined, { + ignoreReturnCode: true, + silent: true + }) .then(res => { if (res.stderr.length > 0 && res.exitCode != 0) { core.debug(`Docker.isAvailable cmd err: ${res.stderr}`); @@ -47,10 +46,10 @@ export class Docker { } public static async printVersion(): Promise { - await exec.exec('docker', ['version']); + await Exec.exec('docker', ['version']); } public static async printInfo(): Promise { - await exec.exec('docker', ['info']); + await Exec.exec('docker', ['info']); } } diff --git a/src/exec.ts b/src/exec.ts new file mode 100644 index 0000000..c697dd7 --- /dev/null +++ b/src/exec.ts @@ -0,0 +1,31 @@ +/** + * Copyright 2023 actions-toolkit authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as core from '@actions/core'; +import * as exec from '@actions/exec'; +import {ExecOptions, ExecOutput} from '@actions/exec'; + +export class Exec { + public static async exec(commandLine: string, args?: string[], options?: ExecOptions): Promise { + core.debug(`Exec.exec: ${commandLine} ${args?.join(' ')}`); + return exec.exec(commandLine, args, options); + } + + public static async getExecOutput(commandLine: string, args?: string[], options?: ExecOptions): Promise { + core.debug(`Exec.getExecOutput: ${commandLine} ${args?.join(' ')}`); + return exec.getExecOutput(commandLine, args, options); + } +} diff --git a/src/git.ts b/src/git.ts index 459eae0..dcb3f43 100644 --- a/src/git.ts +++ b/src/git.ts @@ -14,24 +14,22 @@ * limitations under the License. */ -import * as exec from '@actions/exec'; +import {Exec} from './exec'; export class Git { public static async 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] = res.stdout.trim().split(/[\s\t]/); - if (rsha.length == 0) { - throw new Error(`Cannot find remote ref for ${repo}#${ref}`); - } - return rsha; - }); + 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] = res.stdout.trim().split(/[\s\t]/); + if (rsha.length == 0) { + throw new Error(`Cannot find remote ref for ${repo}#${ref}`); + } + return rsha; + }); } }