Exec class

Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax 2023-02-20 09:01:01 +01:00
parent 2915834633
commit 35a8193474
No known key found for this signature in database
GPG Key ID: 3248E46B6BB8C7F7
10 changed files with 171 additions and 104 deletions

View File

@ -19,10 +19,10 @@ import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import * as rimraf from 'rimraf'; import * as rimraf from 'rimraf';
import * as semver from 'semver'; import * as semver from 'semver';
import * as exec from '@actions/exec';
import {Buildx} from '../../src/buildx/buildx'; import {Buildx} from '../../src/buildx/buildx';
import {Context} from '../../src/context'; import {Context} from '../../src/context';
import {Exec} from '../../src/exec';
import {Cert} from '../../src/types/buildx'; import {Cert} from '../../src/types/buildx';
@ -91,7 +91,7 @@ describe('certsDir', () => {
describe('isAvailable', () => { describe('isAvailable', () => {
it('docker cli', async () => { it('docker cli', async () => {
const execSpy = jest.spyOn(exec, 'getExecOutput'); const execSpy = jest.spyOn(Exec, 'getExecOutput');
const buildx = new Buildx({ const buildx = new Buildx({
standalone: false standalone: false
}); });
@ -103,7 +103,7 @@ describe('isAvailable', () => {
}); });
}); });
it('standalone', async () => { it('standalone', async () => {
const execSpy = jest.spyOn(exec, 'getExecOutput'); const execSpy = jest.spyOn(Exec, 'getExecOutput');
const buildx = new Buildx({ const buildx = new Buildx({
standalone: true standalone: true
}); });
@ -118,7 +118,7 @@ describe('isAvailable', () => {
describe('printInspect', () => { describe('printInspect', () => {
it('prints builder2 instance', async () => { it('prints builder2 instance', async () => {
const execSpy = jest.spyOn(exec, 'exec'); const execSpy = jest.spyOn(Exec, 'exec');
const buildx = new Buildx({ const buildx = new Buildx({
standalone: true standalone: true
}); });
@ -133,7 +133,7 @@ describe('printInspect', () => {
describe('printVersion', () => { describe('printVersion', () => {
it('docker cli', async () => { it('docker cli', async () => {
const execSpy = jest.spyOn(exec, 'exec'); const execSpy = jest.spyOn(Exec, 'exec');
const buildx = new Buildx({ const buildx = new Buildx({
standalone: false standalone: false
}); });
@ -143,7 +143,7 @@ describe('printVersion', () => {
}); });
}); });
it('standalone', async () => { it('standalone', async () => {
const execSpy = jest.spyOn(exec, 'exec'); const execSpy = jest.spyOn(Exec, 'exec');
const buildx = new Buildx({ const buildx = new Buildx({
standalone: true standalone: true
}); });

View File

@ -15,11 +15,11 @@
*/ */
import {afterEach, beforeEach, describe, expect, it, jest} from '@jest/globals'; import {afterEach, beforeEach, describe, expect, it, jest} from '@jest/globals';
import * as exec from '@actions/exec';
import path from 'path'; import path from 'path';
import osm = require('os'); import osm = require('os');
import {Docker} from '../src/docker'; import {Docker} from '../src/docker';
import {Exec} from '../src/exec';
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
@ -49,7 +49,7 @@ describe('configDir', () => {
describe('isAvailable', () => { describe('isAvailable', () => {
it('cli', async () => { it('cli', async () => {
const execSpy = jest.spyOn(exec, 'getExecOutput'); const execSpy = jest.spyOn(Exec, 'getExecOutput');
await Docker.isAvailable(); await Docker.isAvailable();
// eslint-disable-next-line jest/no-standalone-expect // eslint-disable-next-line jest/no-standalone-expect
expect(execSpy).toHaveBeenCalledWith(`docker`, undefined, { expect(execSpy).toHaveBeenCalledWith(`docker`, undefined, {
@ -61,7 +61,7 @@ describe('isAvailable', () => {
describe('printVersion', () => { describe('printVersion', () => {
it('call docker version', async () => { it('call docker version', async () => {
const execSpy = jest.spyOn(exec, 'exec'); const execSpy = jest.spyOn(Exec, 'exec');
await Docker.printVersion().catch(() => { await Docker.printVersion().catch(() => {
// noop // noop
}); });
@ -71,7 +71,7 @@ describe('printVersion', () => {
describe('printInfo', () => { describe('printInfo', () => {
it('call docker info', async () => { it('call docker info', async () => {
const execSpy = jest.spyOn(exec, 'exec'); const execSpy = jest.spyOn(Exec, 'exec');
await Docker.printInfo().catch(() => { await Docker.printInfo().catch(() => {
// noop // noop
}); });

51
__tests__/exec.test.ts Normal file
View File

@ -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
});
});
});

View File

@ -15,12 +15,12 @@
*/ */
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as exec from '@actions/exec';
import * as semver from 'semver'; import * as semver from 'semver';
import {Buildx} from '../buildx/buildx'; import {Buildx} from '../buildx/buildx';
import {Builder} from '../buildx/builder'; import {Builder} from '../buildx/builder';
import {Config} from './config'; import {Config} from './config';
import {Exec} from '../exec';
import {BuilderInfo, NodeInfo} from '../types/builder'; import {BuilderInfo, NodeInfo} from '../types/builder';
@ -51,32 +51,28 @@ export class BuildKit {
private async getVersionWithinImage(nodeName: string): Promise<string> { private async getVersionWithinImage(nodeName: string): Promise<string> {
core.debug(`BuildKit.getVersionWithinImage nodeName: ${nodeName}`); core.debug(`BuildKit.getVersionWithinImage nodeName: ${nodeName}`);
return exec return Exec.getExecOutput(`docker`, ['inspect', '--format', '{{.Config.Image}}', `${Buildx.containerNamePrefix}${nodeName}`], {
.getExecOutput(`docker`, ['inspect', '--format', '{{.Config.Image}}', `${Buildx.containerNamePrefix}${nodeName}`], { ignoreReturnCode: true,
ignoreReturnCode: true, silent: true
silent: true }).then(bkitimage => {
}) if (bkitimage.exitCode == 0 && bkitimage.stdout.length > 0) {
.then(bkitimage => { core.debug(`BuildKit.getVersionWithinImage image: ${bkitimage.stdout.trim()}`);
if (bkitimage.exitCode == 0 && bkitimage.stdout.length > 0) { return Exec.getExecOutput(`docker`, ['run', '--rm', bkitimage.stdout.trim(), '--version'], {
core.debug(`BuildKit.getVersionWithinImage image: ${bkitimage.stdout.trim()}`); ignoreReturnCode: true,
return exec silent: true
.getExecOutput(`docker`, ['run', '--rm', bkitimage.stdout.trim(), '--version'], { }).then(bkitversion => {
ignoreReturnCode: true, if (bkitversion.exitCode == 0 && bkitversion.stdout.length > 0) {
silent: true return `${bkitimage.stdout.trim()} => ${bkitversion.stdout.trim()}`;
}) } else if (bkitversion.stderr.length > 0) {
.then(bkitversion => { throw new Error(bkitimage.stderr.trim());
if (bkitversion.exitCode == 0 && bkitversion.stdout.length > 0) { }
return `${bkitimage.stdout.trim()} => ${bkitversion.stdout.trim()}`; return bkitversion.stdout.trim();
} else if (bkitversion.stderr.length > 0) { });
throw new Error(bkitimage.stderr.trim()); } else if (bkitimage.stderr.length > 0) {
} throw new Error(bkitimage.stderr.trim());
return bkitversion.stdout.trim(); }
}); return bkitimage.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<boolean> { public async versionSatisfies(builderName: string, range: string, builderInfo?: BuilderInfo): Promise<boolean> {

View File

@ -14,9 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
import * as exec from '@actions/exec';
import {Buildx} from './buildx'; import {Buildx} from './buildx';
import {Exec} from '../exec';
import {BuilderInfo, NodeInfo} from '../types/builder'; import {BuilderInfo, NodeInfo} from '../types/builder';
@ -33,17 +32,15 @@ export class Builder {
public async inspect(name: string): Promise<BuilderInfo> { public async inspect(name: string): Promise<BuilderInfo> {
const cmd = await this.buildx.getCommand(['inspect', name]); const cmd = await this.buildx.getCommand(['inspect', name]);
return await exec return await Exec.getExecOutput(cmd.command, cmd.args, {
.getExecOutput(cmd.command, cmd.args, { ignoreReturnCode: true,
ignoreReturnCode: true, silent: true
silent: true }).then(res => {
}) if (res.stderr.length > 0 && res.exitCode != 0) {
.then(res => { throw new Error(res.stderr.trim());
if (res.stderr.length > 0 && res.exitCode != 0) { }
throw new Error(res.stderr.trim()); return Builder.parseInspect(res.stdout);
} });
return Builder.parseInspect(res.stdout);
});
} }
public static parseInspect(data: string): BuilderInfo { public static parseInspect(data: string): BuilderInfo {

View File

@ -17,10 +17,10 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as exec from '@actions/exec';
import * as semver from 'semver'; import * as semver from 'semver';
import {Docker} from '../docker'; import {Docker} from '../docker';
import {Exec} from '../exec';
import {Inputs} from './inputs'; import {Inputs} from './inputs';
import {Cert} from '../types/buildx'; import {Cert} from '../types/buildx';
@ -67,11 +67,10 @@ export class Buildx {
public async isAvailable(): Promise<boolean> { public async isAvailable(): Promise<boolean> {
const cmd = await this.getCommand([]); const cmd = await this.getCommand([]);
const ok: boolean = await exec const ok: boolean = await Exec.getExecOutput(cmd.command, cmd.args, {
.getExecOutput(cmd.command, cmd.args, { ignoreReturnCode: true,
ignoreReturnCode: true, silent: true
silent: true })
})
.then(res => { .then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) { if (res.stderr.length > 0 && res.exitCode != 0) {
core.debug(`Buildx.isAvailable cmd err: ${res.stderr}`); core.debug(`Buildx.isAvailable cmd err: ${res.stderr}`);
@ -90,7 +89,7 @@ export class Buildx {
public async printInspect(name: string): Promise<void> { public async printInspect(name: string): Promise<void> {
const cmd = await this.getCommand(['inspect', name]); const cmd = await this.getCommand(['inspect', name]);
await exec.exec(cmd.command, cmd.args, { await Exec.exec(cmd.command, cmd.args, {
failOnStdErr: false failOnStdErr: false
}); });
} }
@ -99,17 +98,15 @@ export class Buildx {
return (async () => { return (async () => {
if (!this._version) { if (!this._version) {
const cmd = await this.getCommand(['version']); const cmd = await this.getCommand(['version']);
this._version = await exec this._version = await Exec.getExecOutput(cmd.command, cmd.args, {
.getExecOutput(cmd.command, cmd.args, { ignoreReturnCode: true,
ignoreReturnCode: true, silent: true
silent: true }).then(res => {
}) if (res.stderr.length > 0 && res.exitCode != 0) {
.then(res => { throw new Error(res.stderr.trim());
if (res.stderr.length > 0 && res.exitCode != 0) { }
throw new Error(res.stderr.trim()); return Buildx.parseVersion(res.stdout.trim());
} });
return Buildx.parseVersion(res.stdout.trim());
});
} }
return this._version; return this._version;
})(); })();
@ -117,7 +114,7 @@ export class Buildx {
public async printVersion() { public async printVersion() {
const cmd = await this.getCommand(['version']); const cmd = await this.getCommand(['version']);
await exec.exec(cmd.command, cmd.args, { await Exec.exec(cmd.command, cmd.args, {
failOnStdErr: false failOnStdErr: false
}); });
} }

View File

@ -18,7 +18,6 @@ import fs from 'fs';
import os from 'os'; import os from 'os';
import path from 'path'; import path from 'path';
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as exec from '@actions/exec';
import * as httpm from '@actions/http-client'; import * as httpm from '@actions/http-client';
import * as tc from '@actions/tool-cache'; import * as tc from '@actions/tool-cache';
import * as semver from 'semver'; import * as semver from 'semver';
@ -26,6 +25,7 @@ import * as util from 'util';
import {Buildx} from './buildx'; import {Buildx} from './buildx';
import {Context} from '../context'; import {Context} from '../context';
import {Exec} from '../exec';
import {Docker} from '../docker'; import {Docker} from '../docker';
import {Git} from '../git'; import {Git} from '../git';
@ -82,16 +82,14 @@ export class Install {
if (!toolPath) { if (!toolPath) {
const outputDir = path.join(Context.tmpDir(), 'build-cache'); const outputDir = path.join(Context.tmpDir(), 'build-cache');
const buildCmd = await this.buildCommand(gitContext, outputDir); const buildCmd = await this.buildCommand(gitContext, outputDir);
toolPath = await exec toolPath = await Exec.getExecOutput(buildCmd.command, buildCmd.args, {
.getExecOutput(buildCmd.command, buildCmd.args, { ignoreReturnCode: true
ignoreReturnCode: true }).then(res => {
}) if (res.stderr.length > 0 && res.exitCode != 0) {
.then(res => { core.warning(res.stderr.trim());
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 tc.cacheFile(`${outputDir}/buildx`, os.platform() == 'win32' ? 'docker-buildx.exe' : 'docker-buildx', 'buildx', vspec);
});
} }
return toolPath; return toolPath;

View File

@ -17,7 +17,7 @@
import os from 'os'; import os from 'os';
import path from 'path'; import path from 'path';
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as exec from '@actions/exec'; import {Exec} from './exec';
export class Docker { export class Docker {
static get configDir(): string { static get configDir(): string {
@ -25,11 +25,10 @@ export class Docker {
} }
public static async isAvailable(): Promise<boolean> { public static async isAvailable(): Promise<boolean> {
const ok: boolean = await exec const ok: boolean = await Exec.getExecOutput('docker', undefined, {
.getExecOutput('docker', undefined, { ignoreReturnCode: true,
ignoreReturnCode: true, silent: true
silent: true })
})
.then(res => { .then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) { if (res.stderr.length > 0 && res.exitCode != 0) {
core.debug(`Docker.isAvailable cmd err: ${res.stderr}`); core.debug(`Docker.isAvailable cmd err: ${res.stderr}`);
@ -47,10 +46,10 @@ export class Docker {
} }
public static async printVersion(): Promise<void> { public static async printVersion(): Promise<void> {
await exec.exec('docker', ['version']); await Exec.exec('docker', ['version']);
} }
public static async printInfo(): Promise<void> { public static async printInfo(): Promise<void> {
await exec.exec('docker', ['info']); await Exec.exec('docker', ['info']);
} }
} }

31
src/exec.ts Normal file
View File

@ -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<number> {
core.debug(`Exec.exec: ${commandLine} ${args?.join(' ')}`);
return exec.exec(commandLine, args, options);
}
public static async getExecOutput(commandLine: string, args?: string[], options?: ExecOptions): Promise<ExecOutput> {
core.debug(`Exec.getExecOutput: ${commandLine} ${args?.join(' ')}`);
return exec.getExecOutput(commandLine, args, options);
}
}

View File

@ -14,24 +14,22 @@
* limitations under the License. * limitations under the License.
*/ */
import * as exec from '@actions/exec'; import {Exec} from './exec';
export class Git { export class Git {
public static async getRemoteSha(repo: string, ref: string): Promise<string> { public static async getRemoteSha(repo: string, ref: string): Promise<string> {
return await exec return await Exec.getExecOutput(`git`, ['ls-remote', repo, ref], {
.getExecOutput(`git`, ['ls-remote', repo, ref], { ignoreReturnCode: true,
ignoreReturnCode: true, silent: true
silent: true }).then(res => {
}) if (res.stderr.length > 0 && res.exitCode != 0) {
.then(res => { throw new Error(res.stderr);
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) {
const [rsha] = res.stdout.trim().split(/[\s\t]/); throw new Error(`Cannot find remote ref for ${repo}#${ref}`);
if (rsha.length == 0) { }
throw new Error(`Cannot find remote ref for ${repo}#${ref}`); return rsha;
} });
return rsha;
});
} }
} }