mirror of https://github.com/actions/toolkit
Implement new logic for the command wrapper and add tests
parent
2a75b5e2b3
commit
72a2612e0c
|
@ -1,53 +1,83 @@
|
||||||
import {Command} from '../src/exec-command-wrapper'
|
import CommandHelper from '../src/exec-command-wrapper'
|
||||||
import * as io from '@actions/io'
|
import * as io from '@actions/io'
|
||||||
|
|
||||||
const IS_LINUX = process.platform === 'linux'
|
const IS_WINDOWS = process.platform === 'win32'
|
||||||
|
|
||||||
describe('Command', () => {
|
describe('Command', () => {
|
||||||
it('creates a command object', async () => {
|
it('creates a command object', async () => {
|
||||||
if (IS_LINUX) {
|
let toolpath: string
|
||||||
const toolpath = await io.which('echo', true)
|
let args: string[]
|
||||||
const command = new Command(`"${toolpath}"`, ['hello'])
|
if (IS_WINDOWS) {
|
||||||
expect(command).toBeDefined()
|
toolpath = await io.which('cmd', true)
|
||||||
expect(command).toBeInstanceOf(Command)
|
args = ['/c', 'echo', 'hello']
|
||||||
|
} else {
|
||||||
|
toolpath = await io.which('echo', true)
|
||||||
|
args = ['hello']
|
||||||
}
|
}
|
||||||
|
const command = new CommandHelper(`"${toolpath}"`, args)
|
||||||
|
expect(command).toBeDefined()
|
||||||
|
expect(command).toBeInstanceOf(CommandHelper)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('runs a command with non-zero exit code', async () => {
|
it('runs a command with non-zero exit code', async () => {
|
||||||
if (IS_LINUX) {
|
let toolpath: string
|
||||||
const nonExistentDir = 'non-existent-dir'
|
let args: string[]
|
||||||
const toolpath = await io.which('ls', true)
|
if (IS_WINDOWS) {
|
||||||
const args = ['-l', nonExistentDir]
|
toolpath = await io.which('cmd', true)
|
||||||
const command = new Command(`"${toolpath}"`, args)
|
args = ['/c', 'dir', 'non-existent-dir']
|
||||||
|
} else {
|
||||||
let failed = false
|
toolpath = await io.which('ls', true)
|
||||||
|
args = ['-l', 'non-existent-dir']
|
||||||
await command.execute().catch(err => {
|
}
|
||||||
failed = true
|
const command = new CommandHelper(`"${toolpath}"`, args, undefined, {
|
||||||
expect(err.message).toContain(
|
throwOnEmptyOutput: true
|
||||||
`The process '${toolpath}' failed with exit code `
|
})
|
||||||
)
|
try {
|
||||||
})
|
const result = await command.execute()
|
||||||
|
expect(result.exitCode).not.toEqual(0)
|
||||||
expect(failed).toBe(true)
|
} catch (err) {
|
||||||
|
expect(err.message).toContain(
|
||||||
|
`The process '${toolpath}' failed with exit code `
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('runs a command with zero exit code', async () => {
|
it('runs a command with zero exit code', async () => {
|
||||||
if (IS_LINUX) {
|
let toolpath: string
|
||||||
const toolpath = await io.which('echo', true)
|
let args: string[]
|
||||||
const command = new Command(`"${toolpath}"`, ['hello'])
|
if (IS_WINDOWS) {
|
||||||
const result = await command.execute()
|
toolpath = await io.which('cmd', true)
|
||||||
expect(result).toEqual('hello')
|
args = ['/c', 'echo', 'hello']
|
||||||
|
} else {
|
||||||
|
toolpath = await io.which('echo', true)
|
||||||
|
args = ['hello']
|
||||||
}
|
}
|
||||||
|
const command = new CommandHelper(`"${toolpath}"`, args)
|
||||||
|
const result = await command.execute()
|
||||||
|
|
||||||
|
expect(result.stdout).toContain('hello')
|
||||||
|
expect(result.exitCode).toEqual(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('runs a command with empty output', async () => {
|
it('runs a command with empty output', async () => {
|
||||||
if (IS_LINUX) {
|
let toolpath: string
|
||||||
const toolpath = await io.which('echo', true)
|
let args: string[]
|
||||||
const command = new Command(`"${toolpath}"`, [''])
|
if (IS_WINDOWS) {
|
||||||
|
toolpath = await io.which('cmd', true)
|
||||||
|
args = ['/c', 'echo.']
|
||||||
|
} else {
|
||||||
|
toolpath = await io.which('echo', true)
|
||||||
|
args = ['']
|
||||||
|
}
|
||||||
|
|
||||||
|
const command = new CommandHelper(`"${toolpath}"`, args, undefined, {
|
||||||
|
throwOnEmptyOutput: true
|
||||||
|
})
|
||||||
|
try {
|
||||||
const result = await command.execute()
|
const result = await command.execute()
|
||||||
expect(result).toEqual('')
|
expect(result.stdout).toBe('')
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toContain('Command produced empty output.')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,123 +1,79 @@
|
||||||
import * as core from '@actions/core'
|
|
||||||
import * as exec from '@actions/exec'
|
import * as exec from '@actions/exec'
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
export class Command {
|
export default class CommandHelper {
|
||||||
private readonly commandText: string
|
private commandText: string
|
||||||
private readonly args: string[]
|
private args: string[]
|
||||||
private readonly options: exec.ExecOptions | undefined
|
private options: exec.ExecOptions | undefined
|
||||||
|
|
||||||
private failOnError = false
|
private throwOnError: boolean
|
||||||
private throwOnError = false
|
private throwOnEmptyOutput: boolean
|
||||||
|
private failOnError: boolean
|
||||||
private failOnEmptyOutput = false
|
private failOnEmptyOutput: boolean
|
||||||
private throwOnEmptyOutput = false
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
commandText: string,
|
commandText: string,
|
||||||
args: string[],
|
args: string[] = [],
|
||||||
options: exec.ExecOptions | undefined = undefined
|
options: exec.ExecOptions | undefined = {},
|
||||||
|
config: {
|
||||||
|
throwOnError?: boolean
|
||||||
|
throwOnEmptyOutput?: boolean
|
||||||
|
failOnError?: boolean
|
||||||
|
failOnEmptyOutput?: boolean
|
||||||
|
} = {}
|
||||||
) {
|
) {
|
||||||
this.commandText = commandText
|
this.commandText = commandText
|
||||||
this.args = args
|
this.args = args
|
||||||
this.options = options
|
this.options = options
|
||||||
|
this.throwOnError = config.throwOnError ?? false
|
||||||
|
this.throwOnEmptyOutput = config.throwOnEmptyOutput ?? false
|
||||||
|
this.failOnError = config.failOnError ?? false
|
||||||
|
this.failOnEmptyOutput = config.failOnEmptyOutput ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
get failOn(): {error: () => Command; empty: () => Command} {
|
async execute(): Promise<exec.ExecOutput> {
|
||||||
return {
|
try {
|
||||||
error: this.setFailOnError,
|
const output = await exec.getExecOutput(
|
||||||
empty: this.setFailOnEmptyOutput
|
this.commandText,
|
||||||
}
|
this.args,
|
||||||
}
|
this.options
|
||||||
|
|
||||||
get throwOn(): {error: () => Command; empty: () => Command} {
|
|
||||||
return {
|
|
||||||
error: this.setThrowOnError,
|
|
||||||
empty: this.setThrowOnEmptyOutput
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private setFailOnError = (): Command => {
|
|
||||||
this.failOnError = true
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
private setThrowOnError = (): Command => {
|
|
||||||
this.throwOnError = true
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
private setFailOnEmptyOutput = (): Command => {
|
|
||||||
this.failOnEmptyOutput = true
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
private setThrowOnEmptyOutput = (): Command => {
|
|
||||||
this.throwOnEmptyOutput = true
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
private setFailedOnNonZeroExitCode(
|
|
||||||
command: string,
|
|
||||||
exitCode: number,
|
|
||||||
error: string
|
|
||||||
): void {
|
|
||||||
if (exitCode !== 0) {
|
|
||||||
error = !error.trim()
|
|
||||||
? `The '${command}' command failed with exit code: ${exitCode}`
|
|
||||||
: error
|
|
||||||
core.setFailed(error)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
private throwErrorOnNonZeroExitCode(
|
|
||||||
command: string,
|
|
||||||
exitCode: number,
|
|
||||||
error: string
|
|
||||||
): void {
|
|
||||||
if (exitCode !== 0) {
|
|
||||||
error = !error.trim()
|
|
||||||
? `The '${command}' command failed with exit code: ${exitCode}`
|
|
||||||
: error
|
|
||||||
throw new Error(error)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
async execute(): Promise<string> {
|
|
||||||
const {stdout, stderr, exitCode} = await exec.getExecOutput(
|
|
||||||
this.commandText,
|
|
||||||
this.args,
|
|
||||||
this.options
|
|
||||||
)
|
|
||||||
|
|
||||||
if (this.failOnError) {
|
|
||||||
this.setFailedOnNonZeroExitCode(this.commandText, exitCode, stderr)
|
|
||||||
return stdout.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.throwOnError) {
|
|
||||||
this.throwErrorOnNonZeroExitCode(this.commandText, exitCode, stderr)
|
|
||||||
return stdout.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.failOnEmptyOutput && !stdout.trim()) {
|
|
||||||
core.setFailed(
|
|
||||||
`The '${this.commandText}' command failed with empty output`
|
|
||||||
)
|
)
|
||||||
return stdout.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.throwOnEmptyOutput && !stdout.trim()) {
|
if (this.throwOnError && output.stderr) {
|
||||||
throw new Error(
|
this.onError(output.stderr).throw()
|
||||||
`The '${this.commandText}' command failed with empty output`
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return stdout.trim()
|
if (this.throwOnEmptyOutput && output.stdout.trim() === '') {
|
||||||
|
this.onError('Command produced empty output.').throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.failOnError && output.stderr) {
|
||||||
|
this.onError(output.stderr).fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.failOnEmptyOutput && output.stdout.trim() === '') {
|
||||||
|
this.onError('Command produced empty output.').fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error((error as Error).message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onError(errorMessage: string): {
|
||||||
|
throw: () => never
|
||||||
|
fail: () => void
|
||||||
|
} {
|
||||||
|
core.error(`Error occurred: ${errorMessage}`)
|
||||||
|
|
||||||
|
return {
|
||||||
|
throw: () => {
|
||||||
|
throw new Error(errorMessage)
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
core.setFailed(errorMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// new Command('echo', ['hello', 'world'])
|
|
||||||
// .failOn.error()
|
|
||||||
// .execute()
|
|
||||||
|
|
Loading…
Reference in New Issue