Merge branch 'main' into robertbrignull/meta
This commit is contained in:
commit
d0d858c809
14 changed files with 508 additions and 68 deletions
22
.github/workflows/check-expected-release-files.yml
vendored
Normal file
22
.github/workflows/check-expected-release-files.yml
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
name: Check Expected Release Files
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/check-expected-release-files.yml
|
||||
- src/defaults.json
|
||||
|
||||
jobs:
|
||||
check-expected-release-files:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout CodeQL Action
|
||||
uses: actions/checkout@v2
|
||||
- name: Check Expected Release Files
|
||||
run: |
|
||||
bundle_version="$(cat "./src/defaults.json" | jq -r ".bundleVersion")"
|
||||
set -x
|
||||
for expected_file in "codeql-bundle.tar.gz" "codeql-bundle-linux64.tar.gz" "codeql-bundle-osx64.tar.gz" "codeql-bundle-win64.tar.gz" "codeql-runner-linux" "codeql-runner-macos" "codeql-runner-win.exe"; do
|
||||
curl --location --fail --head --request GET "https://github.com/github/codeql-action/releases/download/$bundle_version/$expected_file" > /dev/null
|
||||
done
|
||||
1
.github/workflows/codeql.yml
vendored
1
.github/workflows/codeql.yml
vendored
|
|
@ -4,6 +4,7 @@ on:
|
|||
push:
|
||||
branches: [main, v1]
|
||||
pull_request:
|
||||
branches: [main, v1]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
|
|||
23
.github/workflows/integration-testing.yml
vendored
23
.github/workflows/integration-testing.yml
vendored
|
|
@ -129,6 +129,29 @@ jobs:
|
|||
env:
|
||||
TEST_MODE: true
|
||||
|
||||
go-custom-tracing-autobuild:
|
||||
# No need to test Go autobuild on multiple OSes since
|
||||
# we're testing Go custom tracing with a manual build on all OSes.
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CODEQL_EXTRACTOR_GO_BUILD_TRACING: "on"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Move codeql-action
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir ../action
|
||||
mv * .github ../action/
|
||||
mv ../action/tests/multi-language-repo/{*,.github} .
|
||||
- uses: ./../action/init
|
||||
with:
|
||||
languages: go
|
||||
- uses: ./../action/autobuild
|
||||
- uses: ./../action/analyze
|
||||
env:
|
||||
TEST_MODE: true
|
||||
|
||||
multi-language-repo_rubocop:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
|
|
|||
98
lib/actions-util.js
generated
98
lib/actions-util.js
generated
|
|
@ -101,6 +101,45 @@ exports.getCommitOid = async function () {
|
|||
function isObject(o) {
|
||||
return o !== null && typeof o === "object";
|
||||
}
|
||||
const GLOB_PATTERN = new RegExp("(\\*\\*?)");
|
||||
function escapeRegExp(string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
||||
}
|
||||
function patternToRegExp(value) {
|
||||
return new RegExp(`^${value
|
||||
.split(GLOB_PATTERN)
|
||||
.reduce(function (arr, cur) {
|
||||
if (cur === "**") {
|
||||
arr.push(".*?");
|
||||
}
|
||||
else if (cur === "*") {
|
||||
arr.push("[^/]*?");
|
||||
}
|
||||
else if (cur) {
|
||||
arr.push(escapeRegExp(cur));
|
||||
}
|
||||
return arr;
|
||||
}, [])
|
||||
.join("")}$`);
|
||||
}
|
||||
// this function should return true if patternA is a superset of patternB
|
||||
// e.g: * is a superset of main-* but main-* is not a superset of *.
|
||||
function patternIsSuperset(patternA, patternB) {
|
||||
return patternToRegExp(patternA).test(patternB);
|
||||
}
|
||||
exports.patternIsSuperset = patternIsSuperset;
|
||||
function branchesToArray(branches) {
|
||||
if (typeof branches === "string") {
|
||||
return [branches];
|
||||
}
|
||||
if (Array.isArray(branches)) {
|
||||
if (branches.length === 0) {
|
||||
return "**";
|
||||
}
|
||||
return branches;
|
||||
}
|
||||
return "**";
|
||||
}
|
||||
var MissingTriggers;
|
||||
(function (MissingTriggers) {
|
||||
MissingTriggers[MissingTriggers["None"] = 0] = "None";
|
||||
|
|
@ -116,25 +155,28 @@ function toCodedErrors(errors) {
|
|||
exports.WorkflowErrors = toCodedErrors({
|
||||
MismatchedBranches: `Please make sure that every branch in on.pull_request is also in on.push so that Code Scanning can compare pull requests against the state of the base branch.`,
|
||||
MissingHooks: `Please specify on.push and on.pull_request hooks so that Code Scanning can compare pull requests against the state of the base branch.`,
|
||||
MissingPullRequestHook: `Please specify an on.pull_request hook so that Code Scanning is run against pull requests.`,
|
||||
MissingPullRequestHook: `Please specify an on.pull_request hook so that Code Scanning is explicitly run against pull requests. This will be required to see results on pull requests from January 31 2021.`,
|
||||
MissingPushHook: `Please specify an on.push hook so that Code Scanning can compare pull requests against the state of the base branch.`,
|
||||
PathsSpecified: `Using on.push.paths can prevent Code Scanning annotating new alerts in your pull requests.`,
|
||||
PathsIgnoreSpecified: `Using on.push.paths-ignore can prevent Code Scanning annotating new alerts in your pull requests.`,
|
||||
CheckoutWrongHead: `git checkout HEAD^2 is no longer necessary. Please remove this step as Code Scanning recommends analyzing the merge commit for best results.`,
|
||||
LintFailed: `Unable to lint workflow for CodeQL.`,
|
||||
});
|
||||
function validateWorkflow(doc) {
|
||||
var _a, _b, _c, _d, _e;
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h;
|
||||
const errors = [];
|
||||
// .jobs[key].steps[].run
|
||||
for (const job of Object.values(((_a = doc) === null || _a === void 0 ? void 0 : _a.jobs) || {})) {
|
||||
for (const step of ((_b = job) === null || _b === void 0 ? void 0 : _b.steps) || []) {
|
||||
// this was advice that we used to give in the README
|
||||
// we actually want to run the analysis on the merge commit
|
||||
// to produce results that are more inline with expectations
|
||||
// (i.e: this is what will happen if you merge this PR)
|
||||
// and avoid some race conditions
|
||||
if (((_c = step) === null || _c === void 0 ? void 0 : _c.run) === "git checkout HEAD^2") {
|
||||
errors.push(exports.WorkflowErrors.CheckoutWrongHead);
|
||||
if (Array.isArray((_b = job) === null || _b === void 0 ? void 0 : _b.steps)) {
|
||||
for (const step of (_c = job) === null || _c === void 0 ? void 0 : _c.steps) {
|
||||
// this was advice that we used to give in the README
|
||||
// we actually want to run the analysis on the merge commit
|
||||
// to produce results that are more inline with expectations
|
||||
// (i.e: this is what will happen if you merge this PR)
|
||||
// and avoid some race conditions
|
||||
if (((_d = step) === null || _d === void 0 ? void 0 : _d.run) === "git checkout HEAD^2") {
|
||||
errors.push(exports.WorkflowErrors.CheckoutWrongHead);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -171,23 +213,23 @@ function validateWorkflow(doc) {
|
|||
missing = missing | MissingTriggers.Push;
|
||||
}
|
||||
else {
|
||||
const paths = (_d = doc.on.push) === null || _d === void 0 ? void 0 : _d.paths;
|
||||
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
|
||||
// currently we cannot go back through the history and find the most recent baseline
|
||||
if (Array.isArray(paths) && paths.length > 0) {
|
||||
errors.push(exports.WorkflowErrors.PathsSpecified);
|
||||
}
|
||||
const pathsIgnore = (_e = doc.on.push) === null || _e === void 0 ? void 0 : _e["paths-ignore"];
|
||||
const pathsIgnore = (_f = doc.on.push) === null || _f === void 0 ? void 0 : _f["paths-ignore"];
|
||||
if (Array.isArray(pathsIgnore) && pathsIgnore.length > 0) {
|
||||
errors.push(exports.WorkflowErrors.PathsIgnoreSpecified);
|
||||
}
|
||||
}
|
||||
if (doc.on.push) {
|
||||
const push = doc.on.push.branches || [];
|
||||
if (doc.on.pull_request) {
|
||||
const pull_request = doc.on.pull_request.branches || [];
|
||||
const difference = pull_request.filter((value) => !push.includes(value));
|
||||
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
|
||||
|
|
@ -201,6 +243,11 @@ function validateWorkflow(doc) {
|
|||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// on is not a known type
|
||||
// this workflow is likely malformed
|
||||
missing = MissingTriggers.Push | MissingTriggers.PullRequest;
|
||||
}
|
||||
switch (missing) {
|
||||
case MissingTriggers.PullRequest | MissingTriggers.Push:
|
||||
errors.push(exports.WorkflowErrors.MissingHooks);
|
||||
|
|
@ -216,11 +263,16 @@ function validateWorkflow(doc) {
|
|||
}
|
||||
exports.validateWorkflow = validateWorkflow;
|
||||
async function getWorkflowErrors() {
|
||||
const workflow = await getWorkflow();
|
||||
if (workflow === undefined) {
|
||||
return [];
|
||||
try {
|
||||
const workflow = await getWorkflow();
|
||||
if (workflow === undefined) {
|
||||
return [];
|
||||
}
|
||||
return validateWorkflow(workflow);
|
||||
}
|
||||
catch (e) {
|
||||
return [exports.WorkflowErrors.LintFailed];
|
||||
}
|
||||
return validateWorkflow(workflow);
|
||||
}
|
||||
exports.getWorkflowErrors = getWorkflowErrors;
|
||||
function formatWorkflowErrors(errors) {
|
||||
|
|
@ -428,10 +480,10 @@ async function sendStatusReport(statusReport) {
|
|||
// this means that this action version is no longer compatible with the API
|
||||
// we still want to continue as it is likely the analysis endpoint will work
|
||||
if (getRequiredEnvParam("GITHUB_SERVER_URL") !== util_1.GITHUB_DOTCOM_URL) {
|
||||
core.warning("CodeQL Action version is incompatible with the code scanning endpoint. Please update to a compatible version of codeql-action.");
|
||||
core.debug("CodeQL Action version is incompatible with the code scanning endpoint. Please update to a compatible version of codeql-action.");
|
||||
}
|
||||
else {
|
||||
core.warning("CodeQL Action is out-of-date. Please upgrade to the latest version of codeql-action.");
|
||||
core.debug("CodeQL Action is out-of-date. Please upgrade to the latest version of codeql-action.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
119
lib/actions-util.test.js
generated
119
lib/actions-util.test.js
generated
|
|
@ -89,13 +89,13 @@ ava_1.default("validateWorkflow() when on.push is valid", (t) => {
|
|||
const errors = actionsutil.validateWorkflow({
|
||||
on: ["push", "pull_request"],
|
||||
});
|
||||
t.deepEqual(errors.length, 0);
|
||||
t.deepEqual(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.length, 0);
|
||||
t.deepEqual(errors, []);
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push should not have a path", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
|
|
@ -110,14 +110,25 @@ 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.length, 0);
|
||||
t.deepEqual(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]);
|
||||
});
|
||||
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, []);
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push is correct with empty objects", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: { push: undefined, pull_request: undefined },
|
||||
});
|
||||
console.log(errors);
|
||||
t.deepEqual(errors.length, 0);
|
||||
t.deepEqual(errors, []);
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push is mismatched", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
|
|
@ -135,7 +146,7 @@ ava_1.default("validateWorkflow() when on.push is not mismatched", (t) => {
|
|||
pull_request: { branches: ["main"] },
|
||||
},
|
||||
});
|
||||
t.deepEqual(errors.length, 0);
|
||||
t.deepEqual(errors, []);
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.push is mismatched for pull_request", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
|
|
@ -146,6 +157,62 @@ ava_1.default("validateWorkflow() when on.push is mismatched for pull_request",
|
|||
});
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
});
|
||||
ava_1.default("validateWorkflow() for a range of malformed workflows", (t) => {
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: 1,
|
||||
pull_request: 1,
|
||||
},
|
||||
}), []);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: 1,
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: [1],
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { 1: 1 },
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: 1 },
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: [1] },
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: { steps: 1 } },
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: { steps: [{ notrun: "git checkout HEAD^2" }] } },
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
on: 1,
|
||||
jobs: { test: [undefined] },
|
||||
}), [actionsutil.WorkflowErrors.MissingHooks]);
|
||||
t.deepEqual(actionsutil.validateWorkflow(1), [
|
||||
actionsutil.WorkflowErrors.MissingHooks,
|
||||
]);
|
||||
t.deepEqual(actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: {
|
||||
branches: 1,
|
||||
},
|
||||
pull_request: {
|
||||
branches: 1,
|
||||
},
|
||||
},
|
||||
}), []);
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.pull_request for every branch but push specifies branches", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: {
|
||||
|
|
@ -155,6 +222,24 @@ ava_1.default("validateWorkflow() when on.pull_request for every branch but push
|
|||
});
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.pull_request for wildcard branches", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: { branches: ["feature/*"] },
|
||||
pull_request: { branches: "feature/moose" },
|
||||
},
|
||||
});
|
||||
t.deepEqual(errors, []);
|
||||
});
|
||||
ava_1.default("validateWorkflow() when on.pull_request for mismatched wildcard branches", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: { branches: ["feature/moose"] },
|
||||
pull_request: { branches: "feature/*" },
|
||||
},
|
||||
});
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
});
|
||||
ava_1.default("validateWorkflow() when HEAD^2 is checked out", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: ["push", "pull_request"],
|
||||
|
|
@ -183,4 +268,26 @@ ava_1.default("formatWorkflowCause()", (t) => {
|
|||
t.deepEqual(message, "CheckoutWrongHead,PathsSpecified");
|
||||
t.deepEqual(actionsutil.formatWorkflowCause([]), undefined);
|
||||
});
|
||||
ava_1.default("patternIsSuperset()", (t) => {
|
||||
t.false(actionsutil.patternIsSuperset("main-*", "main"));
|
||||
t.true(actionsutil.patternIsSuperset("*", "*"));
|
||||
t.true(actionsutil.patternIsSuperset("*", "main-*"));
|
||||
t.false(actionsutil.patternIsSuperset("main-*", "*"));
|
||||
t.false(actionsutil.patternIsSuperset("main-*", "main"));
|
||||
t.true(actionsutil.patternIsSuperset("main", "main"));
|
||||
t.false(actionsutil.patternIsSuperset("*", "feature/*"));
|
||||
t.true(actionsutil.patternIsSuperset("**", "feature/*"));
|
||||
t.false(actionsutil.patternIsSuperset("feature-*", "**"));
|
||||
t.false(actionsutil.patternIsSuperset("a/**/c", "a/**/d"));
|
||||
t.false(actionsutil.patternIsSuperset("a/**/c", "a/**"));
|
||||
t.true(actionsutil.patternIsSuperset("a/**", "a/**/c"));
|
||||
t.true(actionsutil.patternIsSuperset("a/**/c", "a/main-**/c"));
|
||||
t.false(actionsutil.patternIsSuperset("a/**/b/**/c", "a/**/d/**/c"));
|
||||
t.true(actionsutil.patternIsSuperset("a/**/b/**/c", "a/**/b/c/**/c"));
|
||||
t.true(actionsutil.patternIsSuperset("a/**/b/**/c", "a/**/b/d/**/c"));
|
||||
t.false(actionsutil.patternIsSuperset("a/**/c/d/**/c", "a/**/b/**/c"));
|
||||
t.false(actionsutil.patternIsSuperset("a/main-**/c", "a/**/c"));
|
||||
t.true(actionsutil.patternIsSuperset("/robin/*/release/*", "/robin/moose/release/goose"));
|
||||
t.false(actionsutil.patternIsSuperset("/robin/moose/release/goose", "/robin/*/release/*"));
|
||||
});
|
||||
//# sourceMappingURL=actions-util.test.js.map
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"bundleVersion": "codeql-bundle-20201106"
|
||||
"bundleVersion": "codeql-bundle-20201127"
|
||||
}
|
||||
|
|
|
|||
8
lib/init-action.js
generated
8
lib/init-action.js
generated
|
|
@ -65,8 +65,12 @@ async function run() {
|
|||
try {
|
||||
actionsUtil.prepareLocalRunEnvironment();
|
||||
const workflowErrors = await actionsUtil.getWorkflowErrors();
|
||||
if (workflowErrors.length > 0) {
|
||||
core.warning(actionsUtil.formatWorkflowErrors(workflowErrors));
|
||||
// we do not want to worry users if linting is failing
|
||||
// but we do want to send a status report containing this error code
|
||||
// below
|
||||
const userWorkflowErrors = workflowErrors.filter((o) => o.code !== "LintFailed");
|
||||
if (userWorkflowErrors.length > 0) {
|
||||
core.warning(actionsUtil.formatWorkflowErrors(userWorkflowErrors));
|
||||
}
|
||||
if (!(await actionsUtil.sendStatusReport(await actionsUtil.createStatusReportBase("init", "starting", startedAt, actionsUtil.formatWorkflowCause(workflowErrors))))) {
|
||||
return;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -113,7 +113,7 @@ test("validateWorkflow() when on.push is valid", (t) => {
|
|||
on: ["push", "pull_request"],
|
||||
});
|
||||
|
||||
t.deepEqual(errors.length, 0);
|
||||
t.deepEqual(errors, []);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is a valid superset", (t) => {
|
||||
|
|
@ -121,7 +121,7 @@ test("validateWorkflow() when on.push is a valid superset", (t) => {
|
|||
on: ["push", "pull_request", "schedule"],
|
||||
});
|
||||
|
||||
t.deepEqual(errors.length, 0);
|
||||
t.deepEqual(errors, []);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push should not have a path", (t) => {
|
||||
|
|
@ -140,7 +140,23 @@ test("validateWorkflow() when on.push is a correct object", (t) => {
|
|||
on: { push: { branches: ["main"] }, pull_request: { branches: ["main"] } },
|
||||
});
|
||||
|
||||
t.deepEqual(errors.length, 0);
|
||||
t.deepEqual(errors, []);
|
||||
});
|
||||
|
||||
test("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]);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.pull_requests is a string and correct", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: { push: { branches: "*" }, pull_request: { branches: "*" } },
|
||||
});
|
||||
|
||||
t.deepEqual(errors, []);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is correct with empty objects", (t) => {
|
||||
|
|
@ -148,9 +164,7 @@ test("validateWorkflow() when on.push is correct with empty objects", (t) => {
|
|||
on: { push: undefined, pull_request: undefined },
|
||||
});
|
||||
|
||||
console.log(errors);
|
||||
|
||||
t.deepEqual(errors.length, 0);
|
||||
t.deepEqual(errors, []);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is mismatched", (t) => {
|
||||
|
|
@ -172,7 +186,7 @@ test("validateWorkflow() when on.push is not mismatched", (t) => {
|
|||
},
|
||||
});
|
||||
|
||||
t.deepEqual(errors.length, 0);
|
||||
t.deepEqual(errors, []);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.push is mismatched for pull_request", (t) => {
|
||||
|
|
@ -186,6 +200,107 @@ test("validateWorkflow() when on.push is mismatched for pull_request", (t) => {
|
|||
t.deepEqual(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,
|
||||
},
|
||||
pull_request: {
|
||||
branches: 1,
|
||||
},
|
||||
},
|
||||
} as any),
|
||||
[]
|
||||
);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.pull_request for every branch but push specifies branches", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: {
|
||||
|
|
@ -197,6 +312,28 @@ test("validateWorkflow() when on.pull_request for every branch but push specifie
|
|||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.pull_request for wildcard branches", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: { branches: ["feature/*"] },
|
||||
pull_request: { branches: "feature/moose" },
|
||||
},
|
||||
});
|
||||
|
||||
t.deepEqual(errors, []);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when on.pull_request for mismatched wildcard branches", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: {
|
||||
push: { branches: ["feature/moose"] },
|
||||
pull_request: { branches: "feature/*" },
|
||||
},
|
||||
});
|
||||
|
||||
t.deepEqual(errors, [actionsutil.WorkflowErrors.MismatchedBranches]);
|
||||
});
|
||||
|
||||
test("validateWorkflow() when HEAD^2 is checked out", (t) => {
|
||||
const errors = actionsutil.validateWorkflow({
|
||||
on: ["push", "pull_request"],
|
||||
|
|
@ -230,3 +367,36 @@ test("formatWorkflowCause()", (t) => {
|
|||
t.deepEqual(message, "CheckoutWrongHead,PathsSpecified");
|
||||
t.deepEqual(actionsutil.formatWorkflowCause([]), undefined);
|
||||
});
|
||||
|
||||
test("patternIsSuperset()", (t) => {
|
||||
t.false(actionsutil.patternIsSuperset("main-*", "main"));
|
||||
t.true(actionsutil.patternIsSuperset("*", "*"));
|
||||
t.true(actionsutil.patternIsSuperset("*", "main-*"));
|
||||
t.false(actionsutil.patternIsSuperset("main-*", "*"));
|
||||
t.false(actionsutil.patternIsSuperset("main-*", "main"));
|
||||
t.true(actionsutil.patternIsSuperset("main", "main"));
|
||||
t.false(actionsutil.patternIsSuperset("*", "feature/*"));
|
||||
t.true(actionsutil.patternIsSuperset("**", "feature/*"));
|
||||
t.false(actionsutil.patternIsSuperset("feature-*", "**"));
|
||||
t.false(actionsutil.patternIsSuperset("a/**/c", "a/**/d"));
|
||||
t.false(actionsutil.patternIsSuperset("a/**/c", "a/**"));
|
||||
t.true(actionsutil.patternIsSuperset("a/**", "a/**/c"));
|
||||
t.true(actionsutil.patternIsSuperset("a/**/c", "a/main-**/c"));
|
||||
t.false(actionsutil.patternIsSuperset("a/**/b/**/c", "a/**/d/**/c"));
|
||||
t.true(actionsutil.patternIsSuperset("a/**/b/**/c", "a/**/b/c/**/c"));
|
||||
t.true(actionsutil.patternIsSuperset("a/**/b/**/c", "a/**/b/d/**/c"));
|
||||
t.false(actionsutil.patternIsSuperset("a/**/c/d/**/c", "a/**/b/**/c"));
|
||||
t.false(actionsutil.patternIsSuperset("a/main-**/c", "a/**/c"));
|
||||
t.true(
|
||||
actionsutil.patternIsSuperset(
|
||||
"/robin/*/release/*",
|
||||
"/robin/moose/release/goose"
|
||||
)
|
||||
);
|
||||
t.false(
|
||||
actionsutil.patternIsSuperset(
|
||||
"/robin/moose/release/goose",
|
||||
"/robin/*/release/*"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ interface WorkflowJob {
|
|||
}
|
||||
|
||||
interface WorkflowTrigger {
|
||||
branches?: string[];
|
||||
branches?: string[] | string;
|
||||
paths?: string[];
|
||||
}
|
||||
|
||||
|
|
@ -134,6 +134,49 @@ function isObject(o: unknown): o is object {
|
|||
return o !== null && typeof o === "object";
|
||||
}
|
||||
|
||||
const GLOB_PATTERN = new RegExp("(\\*\\*?)");
|
||||
|
||||
function escapeRegExp(string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
||||
}
|
||||
|
||||
function patternToRegExp(value) {
|
||||
return new RegExp(
|
||||
`^${value
|
||||
.split(GLOB_PATTERN)
|
||||
.reduce(function (arr, cur) {
|
||||
if (cur === "**") {
|
||||
arr.push(".*?");
|
||||
} else if (cur === "*") {
|
||||
arr.push("[^/]*?");
|
||||
} else if (cur) {
|
||||
arr.push(escapeRegExp(cur));
|
||||
}
|
||||
return arr;
|
||||
}, [])
|
||||
.join("")}$`
|
||||
);
|
||||
}
|
||||
|
||||
// this function should return true if patternA is a superset of patternB
|
||||
// e.g: * is a superset of main-* but main-* is not a superset of *.
|
||||
export function patternIsSuperset(patternA: string, patternB: string): boolean {
|
||||
return patternToRegExp(patternA).test(patternB);
|
||||
}
|
||||
|
||||
function branchesToArray(branches?: string | null | string[]): string[] | "**" {
|
||||
if (typeof branches === "string") {
|
||||
return [branches];
|
||||
}
|
||||
if (Array.isArray(branches)) {
|
||||
if (branches.length === 0) {
|
||||
return "**";
|
||||
}
|
||||
return branches;
|
||||
}
|
||||
return "**";
|
||||
}
|
||||
|
||||
enum MissingTriggers {
|
||||
None = 0,
|
||||
Push = 1,
|
||||
|
|
@ -144,7 +187,6 @@ interface CodedError {
|
|||
message: string;
|
||||
code: string;
|
||||
}
|
||||
|
||||
function toCodedErrors(errors: {
|
||||
[key: string]: string;
|
||||
}): { [key: string]: CodedError } {
|
||||
|
|
@ -157,11 +199,12 @@ function toCodedErrors(errors: {
|
|||
export const WorkflowErrors = toCodedErrors({
|
||||
MismatchedBranches: `Please make sure that every branch in on.pull_request is also in on.push so that Code Scanning can compare pull requests against the state of the base branch.`,
|
||||
MissingHooks: `Please specify on.push and on.pull_request hooks so that Code Scanning can compare pull requests against the state of the base branch.`,
|
||||
MissingPullRequestHook: `Please specify an on.pull_request hook so that Code Scanning is run against pull requests.`,
|
||||
MissingPullRequestHook: `Please specify an on.pull_request hook so that Code Scanning is explicitly run against pull requests. This will be required to see results on pull requests from January 31 2021.`,
|
||||
MissingPushHook: `Please specify an on.push hook so that Code Scanning can compare pull requests against the state of the base branch.`,
|
||||
PathsSpecified: `Using on.push.paths can prevent Code Scanning annotating new alerts in your pull requests.`,
|
||||
PathsIgnoreSpecified: `Using on.push.paths-ignore can prevent Code Scanning annotating new alerts in your pull requests.`,
|
||||
CheckoutWrongHead: `git checkout HEAD^2 is no longer necessary. Please remove this step as Code Scanning recommends analyzing the merge commit for best results.`,
|
||||
LintFailed: `Unable to lint workflow for CodeQL.`,
|
||||
});
|
||||
|
||||
export function validateWorkflow(doc: Workflow): CodedError[] {
|
||||
|
|
@ -169,14 +212,16 @@ export function validateWorkflow(doc: Workflow): CodedError[] {
|
|||
|
||||
// .jobs[key].steps[].run
|
||||
for (const job of Object.values(doc?.jobs || {})) {
|
||||
for (const step of job?.steps || []) {
|
||||
// this was advice that we used to give in the README
|
||||
// we actually want to run the analysis on the merge commit
|
||||
// to produce results that are more inline with expectations
|
||||
// (i.e: this is what will happen if you merge this PR)
|
||||
// and avoid some race conditions
|
||||
if (step?.run === "git checkout HEAD^2") {
|
||||
errors.push(WorkflowErrors.CheckoutWrongHead);
|
||||
if (Array.isArray(job?.steps)) {
|
||||
for (const step of job?.steps) {
|
||||
// this was advice that we used to give in the README
|
||||
// we actually want to run the analysis on the merge commit
|
||||
// to produce results that are more inline with expectations
|
||||
// (i.e: this is what will happen if you merge this PR)
|
||||
// and avoid some race conditions
|
||||
if (step?.run === "git checkout HEAD^2") {
|
||||
errors.push(WorkflowErrors.CheckoutWrongHead);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -224,13 +269,14 @@ export function validateWorkflow(doc: Workflow): CodedError[] {
|
|||
}
|
||||
}
|
||||
|
||||
if (doc.on.push) {
|
||||
const push = doc.on.push.branches || [];
|
||||
const push = branchesToArray(doc.on.push?.branches);
|
||||
|
||||
if (doc.on.pull_request) {
|
||||
const pull_request = 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.includes(value)
|
||||
(value) => !push.some((o) => patternIsSuperset(o, value))
|
||||
);
|
||||
if (difference.length > 0) {
|
||||
// there are branches in pull_request that may not have a baseline
|
||||
|
|
@ -243,6 +289,10 @@ export function validateWorkflow(doc: Workflow): CodedError[] {
|
|||
errors.push(WorkflowErrors.MismatchedBranches);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// on is not a known type
|
||||
// this workflow is likely malformed
|
||||
missing = MissingTriggers.Push | MissingTriggers.PullRequest;
|
||||
}
|
||||
|
||||
switch (missing) {
|
||||
|
|
@ -261,13 +311,17 @@ export function validateWorkflow(doc: Workflow): CodedError[] {
|
|||
}
|
||||
|
||||
export async function getWorkflowErrors(): Promise<CodedError[]> {
|
||||
const workflow = await getWorkflow();
|
||||
try {
|
||||
const workflow = await getWorkflow();
|
||||
|
||||
if (workflow === undefined) {
|
||||
return [];
|
||||
if (workflow === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return validateWorkflow(workflow);
|
||||
} catch (e) {
|
||||
return [WorkflowErrors.LintFailed];
|
||||
}
|
||||
|
||||
return validateWorkflow(workflow);
|
||||
}
|
||||
|
||||
export function formatWorkflowErrors(errors: CodedError[]): string {
|
||||
|
|
@ -560,11 +614,11 @@ export async function sendStatusReport<S extends StatusReportBase>(
|
|||
// this means that this action version is no longer compatible with the API
|
||||
// we still want to continue as it is likely the analysis endpoint will work
|
||||
if (getRequiredEnvParam("GITHUB_SERVER_URL") !== GITHUB_DOTCOM_URL) {
|
||||
core.warning(
|
||||
core.debug(
|
||||
"CodeQL Action version is incompatible with the code scanning endpoint. Please update to a compatible version of codeql-action."
|
||||
);
|
||||
} else {
|
||||
core.warning(
|
||||
core.debug(
|
||||
"CodeQL Action is out-of-date. Please upgrade to the latest version of codeql-action."
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"bundleVersion": "codeql-bundle-20201106"
|
||||
"bundleVersion": "codeql-bundle-20201127"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,8 +109,15 @@ async function run() {
|
|||
|
||||
const workflowErrors = await actionsUtil.getWorkflowErrors();
|
||||
|
||||
if (workflowErrors.length > 0) {
|
||||
core.warning(actionsUtil.formatWorkflowErrors(workflowErrors));
|
||||
// we do not want to worry users if linting is failing
|
||||
// but we do want to send a status report containing this error code
|
||||
// below
|
||||
const userWorkflowErrors = workflowErrors.filter(
|
||||
(o) => o.code !== "LintFailed"
|
||||
);
|
||||
|
||||
if (userWorkflowErrors.length > 0) {
|
||||
core.warning(actionsUtil.formatWorkflowErrors(userWorkflowErrors));
|
||||
}
|
||||
|
||||
if (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue