Merge pull request #363 from github/simon-engledew/fix-incorrect-branch-warning
Fix overzealous warnings when PR scanning is not required
This commit is contained in:
commit
4bdcd08344
6 changed files with 412 additions and 264 deletions
61
lib/actions-util.js
generated
61
lib/actions-util.js
generated
|
|
@ -186,37 +186,27 @@ function validateWorkflow(doc) {
|
|||
}
|
||||
let missing = MissingTriggers.None;
|
||||
if (doc.on === undefined) {
|
||||
missing = MissingTriggers.Push | MissingTriggers.PullRequest;
|
||||
// this is not a valid config
|
||||
}
|
||||
else if (typeof doc.on === "string") {
|
||||
switch (doc.on) {
|
||||
case "push":
|
||||
missing = MissingTriggers.PullRequest;
|
||||
break;
|
||||
case "pull_request":
|
||||
missing = MissingTriggers.Push;
|
||||
break;
|
||||
default:
|
||||
missing = MissingTriggers.Push | MissingTriggers.PullRequest;
|
||||
break;
|
||||
if (doc.on === "pull_request") {
|
||||
missing = MissingTriggers.Push;
|
||||
}
|
||||
}
|
||||
else if (Array.isArray(doc.on)) {
|
||||
if (!doc.on.includes("push")) {
|
||||
const hasPush = doc.on.includes("push");
|
||||
const hasPullRequest = doc.on.includes("pull_request");
|
||||
if (hasPullRequest && !hasPush) {
|
||||
missing = missing | MissingTriggers.Push;
|
||||
}
|
||||
if (!doc.on.includes("pull_request")) {
|
||||
missing = missing | MissingTriggers.PullRequest;
|
||||
}
|
||||
}
|
||||
else if (isObject(doc.on)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(doc.on, "pull_request")) {
|
||||
missing = missing | MissingTriggers.PullRequest;
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(doc.on, "push")) {
|
||||
const hasPush = Object.prototype.hasOwnProperty.call(doc.on, "push");
|
||||
const hasPullRequest = Object.prototype.hasOwnProperty.call(doc.on, "pull_request");
|
||||
if (!hasPush && hasPullRequest) {
|
||||
missing = missing | MissingTriggers.Push;
|
||||
}
|
||||
else {
|
||||
if (hasPush && hasPullRequest) {
|
||||
const paths = (_e = doc.on.push) === null || _e === void 0 ? void 0 : _e.paths;
|
||||
// if you specify paths or paths-ignore you can end up with commits that have no baseline
|
||||
// if they didn't change any files
|
||||
|
|
@ -229,22 +219,27 @@ function validateWorkflow(doc) {
|
|||
errors.push(exports.WorkflowErrors.PathsIgnoreSpecified);
|
||||
}
|
||||
}
|
||||
const push = branchesToArray((_g = doc.on.push) === null || _g === void 0 ? void 0 : _g.branches);
|
||||
if (push !== "**") {
|
||||
const pull_request = branchesToArray((_h = doc.on.pull_request) === null || _h === void 0 ? void 0 : _h.branches);
|
||||
if (pull_request !== "**") {
|
||||
const difference = pull_request.filter((value) => !push.some((o) => patternIsSuperset(o, value)));
|
||||
if (difference.length > 0) {
|
||||
// there are branches in pull_request that may not have a baseline
|
||||
// because we are not building them on push
|
||||
// if doc.on.pull_request is null that means 'all branches'
|
||||
// if doc.on.pull_request is undefined that means 'off'
|
||||
// we only want to check for mismatched branches if pull_request is on.
|
||||
if (doc.on.pull_request !== undefined) {
|
||||
const push = branchesToArray((_g = doc.on.push) === null || _g === void 0 ? void 0 : _g.branches);
|
||||
if (push !== "**") {
|
||||
const pull_request = branchesToArray((_h = doc.on.pull_request) === null || _h === void 0 ? void 0 : _h.branches);
|
||||
if (pull_request !== "**") {
|
||||
const difference = pull_request.filter((value) => !push.some((o) => patternIsSuperset(o, value)));
|
||||
if (difference.length > 0) {
|
||||
// there are branches in pull_request that may not have a baseline
|
||||
// because we are not building them on push
|
||||
errors.push(exports.WorkflowErrors.MismatchedBranches);
|
||||
}
|
||||
}
|
||||
else if (push.length > 0) {
|
||||
// push is set up to run on a subset of branches
|
||||
// and you could open a PR against a branch with no baseline
|
||||
errors.push(exports.WorkflowErrors.MismatchedBranches);
|
||||
}
|
||||
}
|
||||
else if (push.length > 0) {
|
||||
// push is set up to run on a subset of branches
|
||||
// and you could open a PR against a branch with no baseline
|
||||
errors.push(exports.WorkflowErrors.MismatchedBranches);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
154
lib/actions-util.test.js
generated
154
lib/actions-util.test.js
generated
|
|
@ -15,6 +15,9 @@ const yaml = __importStar(require("js-yaml"));
|
|||
const sinon_1 = __importDefault(require("sinon"));
|
||||
const actionsutil = __importStar(require("./actions-util"));
|
||||
const testing_utils_1 = require("./testing-utils");
|
||||
function errorCodes(actual, expected) {
|
||||
return [actual.map(({ code }) => code), expected.map(({ code }) => code)];
|
||||
}
|
||||
testing_utils_1.setupTests(ava_1.default);
|
||||
ava_1.default("getRef() throws on the empty string", async (t) => {
|
||||
process.env["GITHUB_REF"] = "";
|
||||
|
|
@ -69,34 +72,29 @@ ava_1.default("prepareEnvironment() when a local run", (t) => {
|
|||
t.deepEqual(process.env.GITHUB_JOB, "UNKNOWN-JOB");
|
||||
t.deepEqual(process.env.CODEQL_ACTION_ANALYSIS_KEY, "LOCAL-RUN:UNKNOWN-JOB");
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on is missing", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({});
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push is missing", (t) => {
|
||||
ava_1.default("validateWorkflow() when on is empty", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({ on: {} });
|
||||
console.log(errors);
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push is an array missing pull_request", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({ on: ["push"] });
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MissingPullRequestHook]);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push is an array missing push", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({ on: ["pull_request"] });
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MissingPushHook]);
|
||||
t.deepEqual(...errorCodes(errors, [actionsutil.WorkflowErrors.MissingPushHook]));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push is valid", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: ["push", "pull_request"],
|
||||
});
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push is a valid superset", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: ["push", "pull_request", "schedule"],
|
||||
});
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push should not have a path", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
|
|
@ -105,31 +103,33 @@ ava_1.default("validateWorkflow() when on.push should not have a path", (t) => {
|
|||
pull_request: { branches: ["main"] },
|
||||
},
|
||||
});
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.PathsSpecified]);
|
||||
t.deepEqual(...errorCodes(errors, [actionsutil.WorkflowErrors.PathsSpecified]));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push is a correct object", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: { push: { branches: ["main"] }, pull_request: { branches: ["main"] } },
|
||||
});
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.pull_requests is a string", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: { push: { branches: ["main"] }, pull_request: { branches: "*" } },
|
||||
});
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
t.deepEqual(...errorCodes(errors, [actionsutil.WorkflowErrors.MismatchedBranches]));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.pull_requests is a string and correct", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: { push: { branches: "*" }, pull_request: { branches: "*" } },
|
||||
});
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push is correct with empty objects", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: { push: undefined, pull_request: undefined },
|
||||
});
|
||||
t.deepEqual(errors, []);
|
||||
const errors = actionsutil.validateWorkflow(yaml.safeLoad(`
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
`));
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push is mismatched", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
|
|
@ -138,7 +138,7 @@ ava_1.default("validateWorkflow() when on.push is mismatched", (t) => {
|
|||
pull_request: { branches: ["feature"] },
|
||||
},
|
||||
});
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
t.deepEqual(...errorCodes(errors, [actionsutil.WorkflowErrors.MismatchedBranches]));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push is not mismatched", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
|
|
@ -147,7 +147,7 @@ ava_1.default("validateWorkflow() when on.push is not mismatched", (t) => {
|
|||
pull_request: { branches: ["main"] },
|
||||
},
|
||||
});
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push is mismatched for pull_request", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
|
|
@ -156,54 +156,52 @@ ava_1.default("validateWorkflow() when on.push is mismatched for pull_request",
|
|||
pull_request: { branches: ["main", "feature"] },
|
||||
},
|
||||
});
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
t.deepEqual(...errorCodes(errors, [actionsutil.WorkflowErrors.MismatchedBranches]));
|
||||
});
|
||||
ava_1.default("validateWorkflow() for a range of malformed workflows", (t) => {
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: 1,
|
||||
pull_request: 1,
|
||||
},
|
||||
}), []);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
}), []));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: 1,
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: [1],
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { 1: 1 },
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: 1 },
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: [1] },
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: { steps: 1 } },
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: { steps: [{ notrun: "git checkout HEAD^2" }] } },
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: [undefined] },
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow(1), [
|
||||
actionsutil.WorkflowErrors.MissingHooks,
|
||||
]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow(1), []));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: {
|
||||
branches: 1,
|
||||
|
|
@ -212,16 +210,17 @@ ava_1.default("validateWorkflow() for a range of malformed workflows", (t) => {
|
|||
branches: 1,
|
||||
},
|
||||
},
|
||||
}), []);
|
||||
}), []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.pull_request for every branch but push specifies branches", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: { branches: ["main"] },
|
||||
pull_request: null,
|
||||
},
|
||||
});
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
const errors = actionsutil.validateWorkflow(yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
`));
|
||||
t.deepEqual(...errorCodes(errors, [actionsutil.WorkflowErrors.MismatchedBranches]));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.pull_request for wildcard branches", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
|
|
@ -230,7 +229,7 @@ ava_1.default("validateWorkflow() when on.pull_request for wildcard branches", (
|
|||
pull_request: { branches: "feature/moose" },
|
||||
},
|
||||
});
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.pull_request for mismatched wildcard branches", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
|
|
@ -239,7 +238,7 @@ ava_1.default("validateWorkflow() when on.pull_request for mismatched wildcard b
|
|||
pull_request: { branches: "feature/*" },
|
||||
},
|
||||
});
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
t.deepEqual(...errorCodes(errors, [actionsutil.WorkflowErrors.MismatchedBranches]));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when HEAD^2 is checked out", (t) => {
|
||||
process.env.GITHUB_JOB = "test";
|
||||
|
|
@ -247,7 +246,7 @@ ava_1.default("validateWorkflow() when HEAD^2 is checked out", (t) => {
|
|||
on: ["push", "pull_request"],
|
||||
jobs: { test: { steps: [{ run: "git checkout HEAD^2" }] } },
|
||||
});
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.CheckoutWrongHead]);
|
||||
t.deepEqual(...errorCodes(errors, [actionsutil.WorkflowErrors.CheckoutWrongHead]));
|
||||
});
|
||||
ava_1.default("formatWorkflowErrors() when there is one error", (t) => {
|
||||
const message = actionsutil.formatWorkflowErrors([
|
||||
|
|
@ -301,7 +300,7 @@ ava_1.default("validateWorkflow() when branches contain dots", (t) => {
|
|||
# The branches below must be a subset of the branches above
|
||||
branches: [4.1, master]
|
||||
`));
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push has a trailing comma", (t) => {
|
||||
const errors = actionsutil.validateWorkflow(yaml.safeLoad(`
|
||||
|
|
@ -313,7 +312,7 @@ on:
|
|||
# The branches below must be a subset of the branches above
|
||||
branches: [master]
|
||||
`));
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() should only report the current job's CheckoutWrongHead", (t) => {
|
||||
process.env.GITHUB_JOB = "test";
|
||||
|
|
@ -337,7 +336,7 @@ jobs:
|
|||
test3:
|
||||
steps: []
|
||||
`));
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.CheckoutWrongHead]);
|
||||
t.deepEqual(...errorCodes(errors, [actionsutil.WorkflowErrors.CheckoutWrongHead]));
|
||||
});
|
||||
ava_1.default("validateWorkflow() should not report a different job's CheckoutWrongHead", (t) => {
|
||||
process.env.GITHUB_JOB = "test3";
|
||||
|
|
@ -361,6 +360,39 @@ jobs:
|
|||
test3:
|
||||
steps: []
|
||||
`));
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on is missing", (t) => {
|
||||
const errors = actionsutil.validateWorkflow(yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
`));
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() with a different on setup", (t) => {
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow(yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
on: "workflow_dispatch"
|
||||
`)), []));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow(yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
on: [workflow_dispatch]
|
||||
`)), []));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow(yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
`)), []));
|
||||
});
|
||||
ava_1.default("validateWorkflow() should not report an error if PRs are totally unconfigured", (t) => {
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow(yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
`)), []));
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow(yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
on: ["push"]
|
||||
`)), []));
|
||||
});
|
||||
//# sourceMappingURL=actions-util.test.js.map
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -5,6 +5,13 @@ import sinon from "sinon";
|
|||
import * as actionsutil from "./actions-util";
|
||||
import { setupTests } from "./testing-utils";
|
||||
|
||||
function errorCodes(
|
||||
actual: actionsutil.CodedError[],
|
||||
expected: actionsutil.CodedError[]
|
||||
): [string[], string[]] {
|
||||
return [actual.map(({ code }) => code), expected.map(({ code }) => code)];
|
||||
}
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test("getRef() throws on the empty string", async (t) => {
|
||||
|
|
@ -83,30 +90,24 @@ test("prepareEnvironment() when a local run", (t) => {
|
|||
t.deepEqual(process.env.CODEQL_ACTION_ANALYSIS_KEY, "LOCAL-RUN:UNKNOWN-JOB");
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on is missing", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({});
|
||||
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is missing", (t) => {
|
||||
test("validateWorkflow() when on is empty", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({ on: {} });
|
||||
|
||||
console.log(errors);
|
||||
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is an array missing pull_request", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({ on: ["push"] });
|
||||
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MissingPullRequestHook]);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is an array missing push", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({ on: ["pull_request"] });
|
||||
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MissingPushHook]);
|
||||
t.deepEqual(
|
||||
...errorCodes(errors, [actionsutil.WorkflowErrors.MissingPushHook])
|
||||
);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is valid", (t) => {
|
||||
|
|
@ -114,7 +115,7 @@ test("validateWorkflow() when on.push is valid", (t) => {
|
|||
on: ["push", "pull_request"],
|
||||
});
|
||||
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is a valid superset", (t) => {
|
||||
|
|
@ -122,7 +123,7 @@ test("validateWorkflow() when on.push is a valid superset", (t) => {
|
|||
on: ["push", "pull_request", "schedule"],
|
||||
});
|
||||
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push should not have a path", (t) => {
|
||||
|
|
@ -133,7 +134,9 @@ test("validateWorkflow() when on.push should not have a path", (t) => {
|
|||
},
|
||||
});
|
||||
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.PathsSpecified]);
|
||||
t.deepEqual(
|
||||
...errorCodes(errors, [actionsutil.WorkflowErrors.PathsSpecified])
|
||||
);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is a correct object", (t) => {
|
||||
|
|
@ -141,7 +144,7 @@ test("validateWorkflow() when on.push is a correct object", (t) => {
|
|||
on: { push: { branches: ["main"] }, pull_request: { branches: ["main"] } },
|
||||
});
|
||||
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.pull_requests is a string", (t) => {
|
||||
|
|
@ -149,7 +152,9 @@ test("validateWorkflow() when on.pull_requests is a string", (t) => {
|
|||
on: { push: { branches: ["main"] }, pull_request: { branches: "*" } },
|
||||
});
|
||||
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
t.deepEqual(
|
||||
...errorCodes(errors, [actionsutil.WorkflowErrors.MismatchedBranches])
|
||||
);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.pull_requests is a string and correct", (t) => {
|
||||
|
|
@ -157,15 +162,19 @@ test("validateWorkflow() when on.pull_requests is a string and correct", (t) =>
|
|||
on: { push: { branches: "*" }, pull_request: { branches: "*" } },
|
||||
});
|
||||
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is correct with empty objects", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: { push: undefined, pull_request: undefined },
|
||||
});
|
||||
const errors = actionsutil.validateWorkflow(
|
||||
yaml.safeLoad(`
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
`)
|
||||
);
|
||||
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is mismatched", (t) => {
|
||||
|
|
@ -176,7 +185,9 @@ test("validateWorkflow() when on.push is mismatched", (t) => {
|
|||
},
|
||||
});
|
||||
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
t.deepEqual(
|
||||
...errorCodes(errors, [actionsutil.WorkflowErrors.MismatchedBranches])
|
||||
);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is not mismatched", (t) => {
|
||||
|
|
@ -187,7 +198,7 @@ test("validateWorkflow() when on.push is not mismatched", (t) => {
|
|||
},
|
||||
});
|
||||
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is mismatched for pull_request", (t) => {
|
||||
|
|
@ -198,119 +209,146 @@ test("validateWorkflow() when on.push is mismatched for pull_request", (t) => {
|
|||
},
|
||||
});
|
||||
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
t.deepEqual(
|
||||
...errorCodes(errors, [actionsutil.WorkflowErrors.MismatchedBranches])
|
||||
);
|
||||
});
|
||||
|
||||
test("validateWorkflow() for a range of malformed workflows", (t) => {
|
||||
t.deepEqual(
|
||||
actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: 1,
|
||||
pull_request: 1,
|
||||
},
|
||||
} as any),
|
||||
[]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: 1,
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: [1],
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { 1: 1 },
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: 1 },
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: [1] },
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: { steps: 1 } },
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: { steps: [{ notrun: "git checkout HEAD^2" }] } },
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: [undefined] },
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
);
|
||||
|
||||
t.deepEqual(actionsutil.validateWorkflow(1 as any), [
|
||||
actionsutil.WorkflowErrors.MissingHooks,
|
||||
]);
|
||||
|
||||
t.deepEqual(
|
||||
actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: {
|
||||
branches: 1,
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: 1,
|
||||
pull_request: 1,
|
||||
},
|
||||
pull_request: {
|
||||
branches: 1,
|
||||
} as any),
|
||||
[]
|
||||
)
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
)
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: 1,
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
)
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: [1],
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
)
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { 1: 1 },
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
)
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: 1 },
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
)
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: [1] },
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
)
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: { steps: 1 } },
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
)
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: { steps: [{ notrun: "git checkout HEAD^2" }] } },
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
)
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: [undefined] },
|
||||
} as any),
|
||||
[actionsutil.WorkflowErrors.MissingHooks]
|
||||
)
|
||||
);
|
||||
|
||||
t.deepEqual(...errorCodes(actionsutil.validateWorkflow(1 as any), []));
|
||||
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: {
|
||||
branches: 1,
|
||||
},
|
||||
pull_request: {
|
||||
branches: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as any),
|
||||
[]
|
||||
} as any),
|
||||
[]
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.pull_request for every branch but push specifies branches", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: { branches: ["main"] },
|
||||
pull_request: null,
|
||||
},
|
||||
});
|
||||
const errors = actionsutil.validateWorkflow(
|
||||
yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
`)
|
||||
);
|
||||
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
t.deepEqual(
|
||||
...errorCodes(errors, [actionsutil.WorkflowErrors.MismatchedBranches])
|
||||
);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.pull_request for wildcard branches", (t) => {
|
||||
|
|
@ -321,7 +359,7 @@ test("validateWorkflow() when on.pull_request for wildcard branches", (t) => {
|
|||
},
|
||||
});
|
||||
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.pull_request for mismatched wildcard branches", (t) => {
|
||||
|
|
@ -332,7 +370,9 @@ test("validateWorkflow() when on.pull_request for mismatched wildcard branches",
|
|||
},
|
||||
});
|
||||
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
t.deepEqual(
|
||||
...errorCodes(errors, [actionsutil.WorkflowErrors.MismatchedBranches])
|
||||
);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when HEAD^2 is checked out", (t) => {
|
||||
|
|
@ -343,7 +383,9 @@ test("validateWorkflow() when HEAD^2 is checked out", (t) => {
|
|||
jobs: { test: { steps: [{ run: "git checkout HEAD^2" }] } },
|
||||
});
|
||||
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.CheckoutWrongHead]);
|
||||
t.deepEqual(
|
||||
...errorCodes(errors, [actionsutil.WorkflowErrors.CheckoutWrongHead])
|
||||
);
|
||||
});
|
||||
|
||||
test("formatWorkflowErrors() when there is one error", (t) => {
|
||||
|
|
@ -416,7 +458,7 @@ test("validateWorkflow() when branches contain dots", (t) => {
|
|||
`)
|
||||
);
|
||||
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push has a trailing comma", (t) => {
|
||||
|
|
@ -432,7 +474,7 @@ on:
|
|||
`)
|
||||
);
|
||||
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
|
||||
test("validateWorkflow() should only report the current job's CheckoutWrongHead", (t) => {
|
||||
|
|
@ -461,7 +503,9 @@ jobs:
|
|||
`)
|
||||
);
|
||||
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.CheckoutWrongHead]);
|
||||
t.deepEqual(
|
||||
...errorCodes(errors, [actionsutil.WorkflowErrors.CheckoutWrongHead])
|
||||
);
|
||||
});
|
||||
|
||||
test("validateWorkflow() should not report a different job's CheckoutWrongHead", (t) => {
|
||||
|
|
@ -490,5 +534,82 @@ jobs:
|
|||
`)
|
||||
);
|
||||
|
||||
t.deepEqual(errors, []);
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on is missing", (t) => {
|
||||
const errors = actionsutil.validateWorkflow(
|
||||
yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
`)
|
||||
);
|
||||
|
||||
t.deepEqual(...errorCodes(errors, []));
|
||||
});
|
||||
|
||||
test("validateWorkflow() with a different on setup", (t) => {
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow(
|
||||
yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
on: "workflow_dispatch"
|
||||
`)
|
||||
),
|
||||
[]
|
||||
)
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow(
|
||||
yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
on: [workflow_dispatch]
|
||||
`)
|
||||
),
|
||||
[]
|
||||
)
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow(
|
||||
yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
`)
|
||||
),
|
||||
[]
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test("validateWorkflow() should not report an error if PRs are totally unconfigured", (t) => {
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow(
|
||||
yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
`)
|
||||
),
|
||||
[]
|
||||
)
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
...errorCodes(
|
||||
actionsutil.validateWorkflow(
|
||||
yaml.safeLoad(`
|
||||
name: "CodeQL"
|
||||
on: ["push"]
|
||||
`)
|
||||
),
|
||||
[]
|
||||
)
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ enum MissingTriggers {
|
|||
PullRequest = 2,
|
||||
}
|
||||
|
||||
interface CodedError {
|
||||
export interface CodedError {
|
||||
message: string;
|
||||
code: string;
|
||||
}
|
||||
|
|
@ -236,33 +236,28 @@ export function validateWorkflow(doc: Workflow): CodedError[] {
|
|||
let missing = MissingTriggers.None;
|
||||
|
||||
if (doc.on === undefined) {
|
||||
missing = MissingTriggers.Push | MissingTriggers.PullRequest;
|
||||
// this is not a valid config
|
||||
} else if (typeof doc.on === "string") {
|
||||
switch (doc.on) {
|
||||
case "push":
|
||||
missing = MissingTriggers.PullRequest;
|
||||
break;
|
||||
case "pull_request":
|
||||
missing = MissingTriggers.Push;
|
||||
break;
|
||||
default:
|
||||
missing = MissingTriggers.Push | MissingTriggers.PullRequest;
|
||||
break;
|
||||
if (doc.on === "pull_request") {
|
||||
missing = MissingTriggers.Push;
|
||||
}
|
||||
} else if (Array.isArray(doc.on)) {
|
||||
if (!doc.on.includes("push")) {
|
||||
const hasPush = doc.on.includes("push");
|
||||
const hasPullRequest = doc.on.includes("pull_request");
|
||||
if (hasPullRequest && !hasPush) {
|
||||
missing = missing | MissingTriggers.Push;
|
||||
}
|
||||
if (!doc.on.includes("pull_request")) {
|
||||
missing = missing | MissingTriggers.PullRequest;
|
||||
}
|
||||
} else if (isObject(doc.on)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(doc.on, "pull_request")) {
|
||||
missing = missing | MissingTriggers.PullRequest;
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(doc.on, "push")) {
|
||||
const hasPush = Object.prototype.hasOwnProperty.call(doc.on, "push");
|
||||
const hasPullRequest = Object.prototype.hasOwnProperty.call(
|
||||
doc.on,
|
||||
"pull_request"
|
||||
);
|
||||
|
||||
if (!hasPush && hasPullRequest) {
|
||||
missing = missing | MissingTriggers.Push;
|
||||
} else {
|
||||
}
|
||||
if (hasPush && hasPullRequest) {
|
||||
const paths = doc.on.push?.paths;
|
||||
// if you specify paths or paths-ignore you can end up with commits that have no baseline
|
||||
// if they didn't change any files
|
||||
|
|
@ -276,24 +271,29 @@ export function validateWorkflow(doc: Workflow): CodedError[] {
|
|||
}
|
||||
}
|
||||
|
||||
const push = branchesToArray(doc.on.push?.branches);
|
||||
// if doc.on.pull_request is null that means 'all branches'
|
||||
// if doc.on.pull_request is undefined that means 'off'
|
||||
// we only want to check for mismatched branches if pull_request is on.
|
||||
if (doc.on.pull_request !== undefined) {
|
||||
const push = branchesToArray(doc.on.push?.branches);
|
||||
|
||||
if (push !== "**") {
|
||||
const pull_request = branchesToArray(doc.on.pull_request?.branches);
|
||||
if (push !== "**") {
|
||||
const pull_request = branchesToArray(doc.on.pull_request?.branches);
|
||||
|
||||
if (pull_request !== "**") {
|
||||
const difference = pull_request.filter(
|
||||
(value) => !push.some((o) => patternIsSuperset(o, value))
|
||||
);
|
||||
if (difference.length > 0) {
|
||||
// there are branches in pull_request that may not have a baseline
|
||||
// because we are not building them on push
|
||||
if (pull_request !== "**") {
|
||||
const difference = pull_request.filter(
|
||||
(value) => !push.some((o) => patternIsSuperset(o, value))
|
||||
);
|
||||
if (difference.length > 0) {
|
||||
// there are branches in pull_request that may not have a baseline
|
||||
// because we are not building them on push
|
||||
errors.push(WorkflowErrors.MismatchedBranches);
|
||||
}
|
||||
} else if (push.length > 0) {
|
||||
// push is set up to run on a subset of branches
|
||||
// and you could open a PR against a branch with no baseline
|
||||
errors.push(WorkflowErrors.MismatchedBranches);
|
||||
}
|
||||
} else if (push.length > 0) {
|
||||
// push is set up to run on a subset of branches
|
||||
// and you could open a PR against a branch with no baseline
|
||||
errors.push(WorkflowErrors.MismatchedBranches);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue