Skip to content

Commit

Permalink
Fix exit code handling (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
amckinney authored Apr 6, 2021
1 parent 55cad16 commit 231a240
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 25 deletions.
2 changes: 2 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ inputs:
github_token:
description: 'The Github authentication token.'
required: true
buf_token:
description: 'The buf authentication token used for private inputs.'
runs:
using: 'node12'
main: './dist/main.js'
11 changes: 6 additions & 5 deletions dist/main.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions dist/main.js.map

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"@actions/github": "^4.0.0",
"@actions/io": "^1.0.2",
"@types/node": "^14.14.35",
"@types/semver": "^7.3.4",
"child_process": "^1.0.2"
},
"devDependencies": {
Expand Down
35 changes: 20 additions & 15 deletions src/buf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import * as child from 'child_process';
import { Error, isError } from './error';

// breakingExitCode is the exit code used to signal that buf
// successfully found incompatible changes.
const breakingExitCode = 100

// BreakingResult includes both the raw and formatted FileAnnotation
// output of a 'buf breaking` command execution. We include both so
// that we preserve the same content users would see on the command line.
Expand All @@ -26,6 +30,7 @@ export interface FileAnnotation {

// ExecException is a subset of the child.ExecException interface.
interface ExecException {
status: number;
stdout: Buffer | string;
stderr: Buffer | string;
}
Expand All @@ -40,11 +45,11 @@ export function breaking(
input: string,
against: string,
): BreakingResult | Error {
const rawOutput = runCommand(`${binaryPath} breaking ${input} --against ${against}`);
const rawOutput = runBreakingCommand(`${binaryPath} breaking ${input} --against ${against}`);
if (isError(rawOutput)) {
return rawOutput
}
const jsonOutput = runCommand(`${binaryPath} breaking ${input} --against ${against} --error-format=json`);
const jsonOutput = runBreakingCommand(`${binaryPath} breaking ${input} --against ${against} --error-format=json`);
if (isError(jsonOutput)) {
return jsonOutput
}
Expand All @@ -60,27 +65,26 @@ export function breaking(
};
}

// runCommand runs the given command and maps its output into an
// array of FileAnnotations.
function runCommand(cmd: string): string | Error {
let output = '';
// runBreakingCommand runs the given command. Note that this function assumes
// the given command is 'buf breaking', and handles its exit code as such.
function runBreakingCommand(cmd: string): string | Error {
try {
child.execSync(cmd);
} catch (error) {
let commandError = '';
if (isExecException(error)) {
output = error.stdout.toString();
commandError = error.stderr.toString();
} else {
commandError = `failed to run command: ${cmd}`
}
if (commandError !== '') {
if (error.status == breakingExitCode) {
// The command found warnings to report.
return error.stdout.toString();
}
return {
message: commandError,
message: error.stderr.toString(),
};
}
return {
message: `failed to run command: ${cmd}`
};
}
return output
return ''
}

// parseLines parses the given output lines into an array
Expand Down Expand Up @@ -123,6 +127,7 @@ function isFileAnnotation(o: any): o is FileAnnotation {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isExecException(o: any): o is ExecException {
return (
'status' in o &&
'stdout' in o &&
'stderr' in o
);
Expand Down
45 changes: 43 additions & 2 deletions src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,28 @@
import * as core from '@actions/core';
import * as github from '@actions/github'
import * as io from '@actions/io';
import * as child from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import * as semver from 'semver';
import { breaking } from './buf';
import { Error, isError } from './error';
import { postComments } from './github';

// minimumBufVersion is the minimum buf version required to
// run this action. At least this version is required because
// the implementation uses the FileAnnotation exit code introduced
// in the following release:
// https://github.com/bufbuild/buf/releases/tag/v0.41.0
const minimumBufVersion = '0.41.0'

// runnerTempEnvKey is the environment variable key
// used to access a temporary directory. Although
// undocumented in the Github Actions documentation,
// this can be found in the @actions/tools-cache module.
// https://github.com/actions/toolkit/blob/4bf916289e5e32bb7d1bd7f21842c3afeab3b25a/packages/tool-cache/src/tool-cache.ts#L701
const runnerTempEnvKey = 'RUNNER_TEMP'

export async function run(): Promise<void> {
try {
const result = await runBreaking();
Expand Down Expand Up @@ -63,12 +81,35 @@ async function runBreaking(): Promise<null|Error> {
}
const binaryPath = await io.which('buf', true);
if (binaryPath === '') {
// TODO: Update this reference to a link once it's available.
return {
message: 'buf is not installed; please add the "bufbuild/setup-buf" step to your job'
message: 'buf is not installed; please add the "bufbuild/buf-setup-action" step to your job found at https://github.com/bufbuild/buf-setup-action'
};
}
const version = child.execSync(`${binaryPath} --version 2>&1`).toString();
if (semver.lt(version, minimumBufVersion)) {
return {
message: `buf must be at least version ${minimumBufVersion}, but found ${version}`
};
}

const bufToken = core.getInput('buf_token');
if (bufToken !== '') {
// If the BUF_TOKEN is set, add it to the runner's .netrc.
const tempDir = process.env[runnerTempEnvKey] ?? '';
if (tempDir === '') {
return {
message: `expected ${runnerTempEnvKey} to be defined`
};
}

// TODO: For now, we hard-code the 'buf.build' remote. This will
// need to be refactored once we support federation between other
// BSR remotes.
const netrcPath = path.join(tempDir, '.netrc');
fs.writeFileSync(netrcPath, `machine buf.build\npassword ${bufToken}`, { flag: 'w' });
process.env.NETRC = netrcPath;
}

const result = breaking(binaryPath, input, against);
if (isError(result)) {
return result
Expand Down

0 comments on commit 231a240

Please sign in to comment.