Upload much more data in status reports

This commit is contained in:
Robert 2020-07-20 16:33:37 +01:00
parent 9769e4a6df
commit 87758a1402
33 changed files with 537 additions and 339 deletions

View file

@ -20,6 +20,7 @@ ava_1.default("emptyPaths", async (t) => {
queries: {}, queries: {},
pathsIgnore: [], pathsIgnore: [],
paths: [], paths: [],
originalUserInput: {},
}; };
analysisPaths.includeAndExcludeAnalysisPaths(config); analysisPaths.includeAndExcludeAnalysisPaths(config);
t.is(process.env['LGTM_INDEX_INCLUDE'], undefined); t.is(process.env['LGTM_INDEX_INCLUDE'], undefined);
@ -32,6 +33,7 @@ ava_1.default("nonEmptyPaths", async (t) => {
queries: {}, queries: {},
paths: ['path1', 'path2', '**/path3'], paths: ['path1', 'path2', '**/path3'],
pathsIgnore: ['path4', 'path5', 'path6/**'], pathsIgnore: ['path4', 'path5', 'path6/**'],
originalUserInput: {},
}; };
analysisPaths.includeAndExcludeAnalysisPaths(config); analysisPaths.includeAndExcludeAnalysisPaths(config);
t.is(process.env['LGTM_INDEX_INCLUDE'], 'path1\npath2'); t.is(process.env['LGTM_INDEX_INCLUDE'], 'path1\npath2');

View file

@ -1 +1 @@
{"version":3,"file":"analysis-paths.test.js","sourceRoot":"","sources":["../src/analysis-paths.test.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,8CAAuB;AAEvB,gEAAkD;AAClD,mDAA2C;AAE3C,0BAAU,CAAC,aAAI,CAAC,CAAC;AAEjB,aAAI,CAAC,YAAY,EAAE,KAAK,EAAC,CAAC,EAAC,EAAE;IAC3B,MAAM,MAAM,GAAG;QACb,SAAS,EAAE,EAAE;QACb,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,EAAE;QACf,KAAK,EAAE,EAAE;KACV,CAAC;IACF,aAAa,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC;IACrD,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,SAAS,CAAC,CAAC;IACnD,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,SAAS,CAAC,CAAC;IACnD,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,SAAS,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC;AAEH,aAAI,CAAC,eAAe,EAAE,KAAK,EAAC,CAAC,EAAC,EAAE;IAC9B,MAAM,MAAM,GAAG;QACb,SAAS,EAAE,EAAE;QACb,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC;QACrC,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC;KAC5C,CAAC;IACF,aAAa,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC;IACrD,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,cAAc,CAAC,CAAC;IACxD,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,cAAc,CAAC,CAAC;IACxD,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,gGAAgG,CAAC,CAAC;AAC5I,CAAC,CAAC,CAAC"} {"version":3,"file":"analysis-paths.test.js","sourceRoot":"","sources":["../src/analysis-paths.test.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,8CAAuB;AAEvB,gEAAkD;AAClD,mDAA2C;AAE3C,0BAAU,CAAC,aAAI,CAAC,CAAC;AAEjB,aAAI,CAAC,YAAY,EAAE,KAAK,EAAC,CAAC,EAAC,EAAE;IAC3B,MAAM,MAAM,GAAG;QACb,SAAS,EAAE,EAAE;QACb,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,EAAE;QACf,KAAK,EAAE,EAAE;QACT,iBAAiB,EAAE,EAAE;KACtB,CAAC;IACF,aAAa,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC;IACrD,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,SAAS,CAAC,CAAC;IACnD,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,SAAS,CAAC,CAAC;IACnD,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,SAAS,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC;AAEH,aAAI,CAAC,eAAe,EAAE,KAAK,EAAC,CAAC,EAAC,EAAE;IAC9B,MAAM,MAAM,GAAG;QACb,SAAS,EAAE,EAAE;QACb,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC;QACrC,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC;QAC3C,iBAAiB,EAAE,EAAE;KACtB,CAAC;IACF,aAAa,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC;IACrD,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,cAAc,CAAC,CAAC;IACxD,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,cAAc,CAAC,CAAC;IACxD,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,gGAAgG,CAAC,CAAC;AAC5I,CAAC,CAAC,CAAC"}

22
lib/autobuild.js generated
View file

@ -11,10 +11,24 @@ const core = __importStar(require("@actions/core"));
const codeql_1 = require("./codeql"); const codeql_1 = require("./codeql");
const sharedEnv = __importStar(require("./shared-environment")); const sharedEnv = __importStar(require("./shared-environment"));
const util = __importStar(require("./util")); const util = __importStar(require("./util"));
async function sendCompletedStatusReport(startedAt, allLanguages, failingLanguage, cause) {
var _a, _b;
const status = failingLanguage !== undefined || cause !== undefined ? 'failure' : 'success';
const statusReportBase = await util.createStatusReportBase('autobuild', status, startedAt, (_a = cause) === null || _a === void 0 ? void 0 : _a.message, (_b = cause) === null || _b === void 0 ? void 0 : _b.stack);
const statusReport = {
...statusReportBase,
autobuild_languages: allLanguages.join(','),
autobuild_failure: failingLanguage,
};
await util.sendStatusReport(statusReport);
}
async function run() { async function run() {
var _a; var _a;
const startedAt = new Date();
let language;
try { try {
if (util.should_abort('autobuild', true) || !await util.reportActionStarting('autobuild')) { if (util.should_abort('autobuild', true) ||
!await util.sendStatusReport(await util.createStatusReportBase('autobuild', 'starting', startedAt), true)) {
return; return;
} }
// Attempt to find a language to autobuild // Attempt to find a language to autobuild
@ -22,7 +36,7 @@ async function run() {
// The languages are sorted in order specified by user or by lines of code if we got // The languages are sorted in order specified by user or by lines of code if we got
// them from the GitHub API, so try to build the first language on the list. // them from the GitHub API, so try to build the first language on the list.
const autobuildLanguages = ((_a = process.env[sharedEnv.CODEQL_ACTION_TRACED_LANGUAGES]) === null || _a === void 0 ? void 0 : _a.split(',')) || []; const autobuildLanguages = ((_a = process.env[sharedEnv.CODEQL_ACTION_TRACED_LANGUAGES]) === null || _a === void 0 ? void 0 : _a.split(',')) || [];
const language = autobuildLanguages[0]; language = autobuildLanguages[0];
if (!language) { if (!language) {
core.info("None of the languages in this project require extra build steps"); core.info("None of the languages in this project require extra build steps");
return; return;
@ -38,10 +52,10 @@ async function run() {
} }
catch (error) { catch (error) {
core.setFailed("We were unable to automatically build your code. Please replace the call to the autobuild action with your custom build steps. " + error.message); core.setFailed("We were unable to automatically build your code. Please replace the call to the autobuild action with your custom build steps. " + error.message);
await util.reportActionFailed('autobuild', error.message, error.stack); await sendCompletedStatusReport(startedAt, [language], language, error);
return; return;
} }
await util.reportActionSucceeded('autobuild'); await sendCompletedStatusReport(startedAt, [language]);
} }
run().catch(e => { run().catch(e => {
core.setFailed("autobuild action failed. " + e); core.setFailed("autobuild action failed. " + e);

View file

@ -1 +1 @@
{"version":3,"file":"autobuild.js","sourceRoot":"","sources":["../src/autobuild.ts"],"names":[],"mappings":";;;;;;;;;AAAA,oDAAsC;AAEtC,qCAAqC;AACrC,gEAAkD;AAClD,6CAA+B;AAE/B,KAAK,UAAU,GAAG;;IAChB,IAAI;QACF,IAAI,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,oBAAoB,CAAC,WAAW,CAAC,EAAE;YACzF,OAAO;SACR;QAED,0CAA0C;QAC1C,mFAAmF;QACnF,oFAAoF;QACpF,4EAA4E;QAC5E,MAAM,kBAAkB,GAAG,OAAA,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,8BAA8B,CAAC,0CAAE,KAAK,CAAC,GAAG,MAAK,EAAE,CAAC;QACnG,MAAM,QAAQ,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC;QAEvC,IAAI,CAAC,QAAQ,EAAE;YACb,IAAI,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;YAC7E,OAAO;SACR;QAED,IAAI,CAAC,KAAK,CAAC,sCAAsC,QAAQ,EAAE,CAAC,CAAC;QAE7D,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE;YACjC,IAAI,CAAC,OAAO,CAAC,oCAAoC,QAAQ,8BAA8B,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,wDAAwD,CAAC,CAAC;SAC3L;QAED,IAAI,CAAC,UAAU,CAAC,qCAAqC,QAAQ,OAAO,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,kBAAS,EAAE,CAAC;QAC3B,MAAM,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAEpC,IAAI,CAAC,QAAQ,EAAE,CAAC;KAEjB;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,CAAC,SAAS,CAAC,kIAAkI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;QACnK,MAAM,IAAI,CAAC,kBAAkB,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO;KACR;IAED,MAAM,IAAI,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC;AAChD,CAAC;AAED,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;IACd,IAAI,CAAC,SAAS,CAAC,4BAA4B,GAAG,CAAC,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC,CAAC,CAAC"} {"version":3,"file":"autobuild.js","sourceRoot":"","sources":["../src/autobuild.ts"],"names":[],"mappings":";;;;;;;;;AAAA,oDAAsC;AAEtC,qCAAqC;AACrC,gEAAkD;AAClD,6CAA+B;AAS/B,KAAK,UAAU,yBAAyB,CACtC,SAAe,EACf,YAAsB,EACtB,eAAwB,EACxB,KAAa;;IAEb,MAAM,MAAM,GAAG,eAAe,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5F,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,sBAAsB,CACxD,WAAW,EACX,MAAM,EACN,SAAS,QACT,KAAK,0CAAE,OAAO,QACd,KAAK,0CAAE,KAAK,CAAC,CAAC;IAChB,MAAM,YAAY,GAA0B;QAC1C,GAAG,gBAAgB;QACnB,mBAAmB,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC;QAC3C,iBAAiB,EAAE,eAAe;KACnC,CAAC;IACF,MAAM,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;AAC5C,CAAC;AAED,KAAK,UAAU,GAAG;;IAChB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;IAC7B,IAAI,QAAQ,CAAC;IACb,IAAI;QACF,IAAI,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,IAAI,CAAC;YACpC,CAAC,MAAM,IAAI,CAAC,gBAAgB,CAAC,MAAM,IAAI,CAAC,sBAAsB,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,EAAE;YAC7G,OAAO;SACR;QAED,0CAA0C;QAC1C,mFAAmF;QACnF,oFAAoF;QACpF,4EAA4E;QAC5E,MAAM,kBAAkB,GAAG,OAAA,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,8BAA8B,CAAC,0CAAE,KAAK,CAAC,GAAG,MAAK,EAAE,CAAC;QACnG,QAAQ,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC;QAEjC,IAAI,CAAC,QAAQ,EAAE;YACb,IAAI,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;YAC7E,OAAO;SACR;QAED,IAAI,CAAC,KAAK,CAAC,sCAAsC,QAAQ,EAAE,CAAC,CAAC;QAE7D,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE;YACjC,IAAI,CAAC,OAAO,CAAC,oCAAoC,QAAQ,8BAA8B,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,wDAAwD,CAAC,CAAC;SAC3L;QAED,IAAI,CAAC,UAAU,CAAC,qCAAqC,QAAQ,OAAO,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,kBAAS,EAAE,CAAC;QAC3B,MAAM,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAEpC,IAAI,CAAC,QAAQ,EAAE,CAAC;KAEjB;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,CAAC,SAAS,CAAC,kIAAkI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;QACnK,MAAM,yBAAyB,CAAC,SAAS,EAAE,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;QACxE,OAAO;KACR;IAED,MAAM,yBAAyB,CAAC,SAAS,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;IACd,IAAI,CAAC,SAAS,CAAC,4BAA4B,GAAG,CAAC,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC,CAAC,CAAC"}

11
lib/config-utils.js generated
View file

@ -354,7 +354,8 @@ async function getDefaultConfig() {
languages: languages, languages: languages,
queries: queries, queries: queries,
pathsIgnore: [], pathsIgnore: [],
paths: [] paths: [],
originalUserInput: {},
}; };
} }
exports.getDefaultConfig = getDefaultConfig; exports.getDefaultConfig = getDefaultConfig;
@ -434,7 +435,13 @@ async function loadConfig(configFile) {
paths.push(validateAndSanitisePath(path, PATHS_PROPERTY, configFile)); paths.push(validateAndSanitisePath(path, PATHS_PROPERTY, configFile));
}); });
} }
return { languages, queries, pathsIgnore, paths }; return {
languages,
queries,
pathsIgnore,
paths,
originalUserInput: parsedYAML
};
} }
/** /**
* Load and return the config. * Load and return the config.

File diff suppressed because one or more lines are too long

View file

@ -169,6 +169,13 @@ ava_1.default("load non-empty input", async (t) => {
queries: { 'javascript': ['/foo/a.ql', '/bar/b.ql'] }, queries: { 'javascript': ['/foo/a.ql', '/bar/b.ql'] },
pathsIgnore: ['a', 'b'], pathsIgnore: ['a', 'b'],
paths: ['c/d'], paths: ['c/d'],
originalUserInput: {
name: 'my config',
'disable-default-queries': true,
queries: [{ uses: './foo' }],
'paths-ignore': ['a', 'b'],
paths: ['c/d'],
},
}; };
fs.writeFileSync(path.join(tmpDir, 'input'), inputFileContents, 'utf8'); fs.writeFileSync(path.join(tmpDir, 'input'), inputFileContents, 'utf8');
setInput('config-file', 'input'); setInput('config-file', 'input');

File diff suppressed because one or more lines are too long

65
lib/finalize-db.js generated
View file

@ -16,6 +16,17 @@ const configUtils = __importStar(require("./config-utils"));
const sharedEnv = __importStar(require("./shared-environment")); const sharedEnv = __importStar(require("./shared-environment"));
const upload_lib = __importStar(require("./upload-lib")); const upload_lib = __importStar(require("./upload-lib"));
const util = __importStar(require("./util")); const util = __importStar(require("./util"));
async function sendStatusReport(startedAt, queriesStats, uploadStats, error) {
var _a, _b, _c;
const status = ((_a = queriesStats) === null || _a === void 0 ? void 0 : _a.analyze_failure_language) !== undefined || error !== undefined ? 'failure' : 'success';
const statusReportBase = await util.createStatusReportBase('finish', status, startedAt, (_b = error) === null || _b === void 0 ? void 0 : _b.message, (_c = error) === null || _c === void 0 ? void 0 : _c.stack);
const statusReport = {
...statusReportBase,
...(queriesStats || {}),
...(uploadStats || {}),
};
await util.sendStatusReport(statusReport);
}
async function createdDBForScannedLanguages(databaseFolder) { async function createdDBForScannedLanguages(databaseFolder) {
const scannedLanguages = process.env[sharedEnv.CODEQL_ACTION_SCANNED_LANGUAGES]; const scannedLanguages = process.env[sharedEnv.CODEQL_ACTION_SCANNED_LANGUAGES];
if (scannedLanguages) { if (scannedLanguages) {
@ -39,27 +50,40 @@ async function finalizeDatabaseCreation(databaseFolder, config) {
// Runs queries and creates sarif files in the given folder // Runs queries and creates sarif files in the given folder
async function runQueries(databaseFolder, sarifFolder, config) { async function runQueries(databaseFolder, sarifFolder, config) {
const codeql = codeql_1.getCodeQL(); const codeql = codeql_1.getCodeQL();
for (let database of fs.readdirSync(databaseFolder)) { for (let language of fs.readdirSync(databaseFolder)) {
core.startGroup('Analyzing ' + database); core.startGroup('Analyzing ' + language);
const queries = config.queries[database] || []; const queries = config.queries[language] || [];
if (queries.length === 0) { if (queries.length === 0) {
throw new Error('Unable to analyse ' + database + ' as no queries were selected for this language'); throw new Error('Unable to analyse ' + language + ' as no queries were selected for this language');
}
try {
// Pass the queries to codeql using a file instead of using the command
// line to avoid command line length restrictions, particularly on windows.
const querySuite = path.join(databaseFolder, language + '-queries.qls');
const querySuiteContents = queries.map(q => '- query: ' + q).join('\n');
fs.writeFileSync(querySuite, querySuiteContents);
core.debug('Query suite file for ' + language + '...\n' + querySuiteContents);
const sarifFile = path.join(sarifFolder, language + '.sarif');
await codeql.databaseAnalyze(path.join(databaseFolder, language), sarifFile, querySuite);
core.debug('SARIF results for database ' + language + ' created at "' + sarifFile + '"');
core.endGroup();
}
catch (e) {
// For now the fields about query performance are not populated
return {
analyze_failure_language: language,
};
} }
// Pass the queries to codeql using a file instead of using the command
// line to avoid command line length restrictions, particularly on windows.
const querySuite = path.join(databaseFolder, database + '-queries.qls');
const querySuiteContents = queries.map(q => '- query: ' + q).join('\n');
fs.writeFileSync(querySuite, querySuiteContents);
core.debug('Query suite file for ' + database + '...\n' + querySuiteContents);
const sarifFile = path.join(sarifFolder, database + '.sarif');
await codeql.databaseAnalyze(path.join(databaseFolder, database), sarifFile, querySuite);
core.debug('SARIF results for database ' + database + ' created at "' + sarifFile + '"');
core.endGroup();
} }
return {};
} }
async function run() { async function run() {
const startedAt = new Date();
let queriesStats = undefined;
let uploadStats = undefined;
try { try {
if (util.should_abort('finish', true) || !await util.reportActionStarting('finish')) { if (util.should_abort('finish', true) ||
!await util.sendStatusReport(await util.createStatusReportBase('finish', 'starting', startedAt), true)) {
return; return;
} }
const config = await configUtils.getConfig(); const config = await configUtils.getConfig();
@ -71,20 +95,17 @@ async function run() {
core.info('Finalizing database creation'); core.info('Finalizing database creation');
await finalizeDatabaseCreation(databaseFolder, config); await finalizeDatabaseCreation(databaseFolder, config);
core.info('Analyzing database'); core.info('Analyzing database');
await runQueries(databaseFolder, sarifFolder, config); queriesStats = await runQueries(databaseFolder, sarifFolder, config);
if ('true' === core.getInput('upload')) { if ('true' === core.getInput('upload')) {
if (!await upload_lib.upload(sarifFolder)) { uploadStats = await upload_lib.upload(sarifFolder);
await util.reportActionFailed('finish', 'upload');
return;
}
} }
} }
catch (error) { catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
await util.reportActionFailed('finish', error.message, error.stack); await sendStatusReport(startedAt, queriesStats, uploadStats, error);
return; return;
} }
await util.reportActionSucceeded('finish'); await sendStatusReport(startedAt, queriesStats, uploadStats);
} }
run().catch(e => { run().catch(e => {
core.setFailed("analyze action failed: " + e); core.setFailed("analyze action failed: " + e);

View file

@ -1 +1 @@
{"version":3,"file":"finalize-db.js","sourceRoot":"","sources":["../src/finalize-db.ts"],"names":[],"mappings":"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"} {"version":3,"file":"finalize-db.js","sourceRoot":"","sources":["../src/finalize-db.ts"],"names":[],"mappings":"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"}

29
lib/setup-tracer.js generated
View file

@ -121,11 +121,32 @@ function concatTracerConfigs(configs) {
fs.writeFileSync(envPath, buffer); fs.writeFileSync(envPath, buffer);
return { env, spec }; return { env, spec };
} }
async function sendSuccessStatusReport(startedAt, config) {
const statusReportBase = await util.createStatusReportBase('init', 'success', startedAt);
const languages = config.languages.join(',');
const workflowLanguages = core.getInput('languages', { required: false });
const paths = (config.originalUserInput.paths || []).join(',');
const pathsIgnore = (config.originalUserInput['paths-ignore'] || []).join(',');
const disableDefaultQueries = config.originalUserInput['disable-default-queries'] ? languages : '';
const queries = (config.originalUserInput.queries || []).map(q => q.uses).join(',');
const statusReport = {
...statusReportBase,
languages: languages,
workflow_languages: workflowLanguages,
paths: paths,
paths_ignore: pathsIgnore,
disable_default_queries: disableDefaultQueries,
queries: queries,
};
await util.sendStatusReport(statusReport);
}
async function run() { async function run() {
const startedAt = new Date();
let config; let config;
let codeql; let codeql;
try { try {
if (util.should_abort('init', false) || !await util.reportActionStarting('init')) { if (util.should_abort('init', false) ||
!await util.sendStatusReport(await util.createStatusReportBase('init', 'starting', startedAt), true)) {
return; return;
} }
core.startGroup('Setup CodeQL tools'); core.startGroup('Setup CodeQL tools');
@ -139,7 +160,7 @@ async function run() {
} }
catch (e) { catch (e) {
core.setFailed(e.message); core.setFailed(e.message);
await util.reportActionAborted('init', e.message); await util.sendStatusReport(await util.createStatusReportBase('init', 'aborted', startedAt, e.message));
return; return;
} }
try { try {
@ -200,10 +221,10 @@ async function run() {
} }
catch (error) { catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
await util.reportActionFailed('init', error.message, error.stack); await util.sendStatusReport(await util.createStatusReportBase('init', 'failure', startedAt, error.message, error.stack));
return; return;
} }
await util.reportActionSucceeded('init'); await sendSuccessStatusReport(startedAt, config);
core.exportVariable(sharedEnv.CODEQL_ACTION_INIT_COMPLETED, 'true'); core.exportVariable(sharedEnv.CODEQL_ACTION_INIT_COMPLETED, 'true');
} }
run().catch(e => { run().catch(e => {

File diff suppressed because one or more lines are too long

View file

@ -10,7 +10,7 @@ exports.CODEQL_ACTION_TRACED_LANGUAGES = 'CODEQL_ACTION_TRACED_LANGUAGES';
// action (i.e. the upload action is being used by a third-party integrator) // action (i.e. the upload action is being used by a third-party integrator)
// then this variable will be assigned the start time of the action invoked // then this variable will be assigned the start time of the action invoked
// rather that the init action. // rather that the init action.
exports.CODEQL_ACTION_STARTED_AT = 'CODEQL_ACTION_STARTED_AT'; exports.CODEQL_WORKFLOW_STARTED_AT = 'CODEQL_WORKFLOW_STARTED_AT';
// Populated when the init action completes successfully // Populated when the init action completes successfully
exports.CODEQL_ACTION_INIT_COMPLETED = 'CODEQL_ACTION_INIT_COMPLETED'; exports.CODEQL_ACTION_INIT_COMPLETED = 'CODEQL_ACTION_INIT_COMPLETED';
//# sourceMappingURL=shared-environment.js.map //# sourceMappingURL=shared-environment.js.map

View file

@ -1 +1 @@
{"version":3,"file":"shared-environment.js","sourceRoot":"","sources":["../src/shared-environment.ts"],"names":[],"mappings":";;AAAa,QAAA,0BAA0B,GAAG,4BAA4B,CAAC;AAC1D,QAAA,0BAA0B,GAAG,4BAA4B,CAAC;AAC1D,QAAA,0BAA0B,GAAG,4BAA4B,CAAC;AAC1D,QAAA,+BAA+B,GAAG,iCAAiC,CAAC;AACpE,QAAA,8BAA8B,GAAG,gCAAgC,CAAC;AAC/E,wEAAwE;AACxE,2EAA2E;AAC3E,4EAA4E;AAC5E,2EAA2E;AAC3E,+BAA+B;AAClB,QAAA,wBAAwB,GAAG,0BAA0B,CAAC;AACnE,wDAAwD;AAC3C,QAAA,4BAA4B,GAAG,8BAA8B,CAAC"} {"version":3,"file":"shared-environment.js","sourceRoot":"","sources":["../src/shared-environment.ts"],"names":[],"mappings":";;AAAa,QAAA,0BAA0B,GAAG,4BAA4B,CAAC;AAC1D,QAAA,0BAA0B,GAAG,4BAA4B,CAAC;AAC1D,QAAA,0BAA0B,GAAG,4BAA4B,CAAC;AAC1D,QAAA,+BAA+B,GAAG,iCAAiC,CAAC;AACpE,QAAA,8BAA8B,GAAG,gCAAgC,CAAC;AAC/E,wEAAwE;AACxE,2EAA2E;AAC3E,4EAA4E;AAC5E,2EAA2E;AAC3E,+BAA+B;AAClB,QAAA,0BAA0B,GAAG,4BAA4B,CAAC;AACvE,wDAAwD;AAC3C,QAAA,4BAA4B,GAAG,8BAA8B,CAAC"}

65
lib/upload-lib.js generated
View file

@ -48,7 +48,7 @@ async function uploadPayload(payload) {
// If in test mode we don't want to upload the results // If in test mode we don't want to upload the results
const testMode = process.env['TEST_MODE'] === 'true' || false; const testMode = process.env['TEST_MODE'] === 'true' || false;
if (testMode) { if (testMode) {
return true; return;
} }
const [owner, repo] = util.getRequiredEnvParam("GITHUB_REPOSITORY").split("/"); const [owner, repo] = util.getRequiredEnvParam("GITHUB_REPOSITORY").split("/");
// Make up to 4 attempts to upload, and sleep for these // Make up to 4 attempts to upload, and sleep for these
@ -66,13 +66,12 @@ async function uploadPayload(payload) {
const statusCode = response.status; const statusCode = response.status;
if (statusCode === 202) { if (statusCode === 202) {
core.info("Successfully uploaded results"); core.info("Successfully uploaded results");
return true; return;
} }
const requestID = response.headers["x-github-request-id"]; const requestID = response.headers["x-github-request-id"];
// On any other status code that's not 5xx mark the upload as failed // On any other status code that's not 5xx mark the upload as failed
if (!statusCode || statusCode < 500 || statusCode >= 600) { if (!statusCode || statusCode < 500 || statusCode >= 600) {
core.setFailed('Upload failed (' + requestID + '): (' + statusCode + ') ' + JSON.stringify(response.data)); throw new Error('Upload failed (' + requestID + '): (' + statusCode + ') ' + JSON.stringify(response.data));
return false;
} }
// On a 5xx status code we may retry the request // On a 5xx status code we may retry the request
if (attempt < backoffPeriods.length) { if (attempt < backoffPeriods.length) {
@ -88,11 +87,12 @@ async function uploadPayload(payload) {
// If the upload fails with 5xx then we assume it is a temporary problem // If the upload fails with 5xx then we assume it is a temporary problem
// and not an error that the user has caused or can fix. // and not an error that the user has caused or can fix.
// We avoid marking the job as failed to avoid breaking CI workflows. // We avoid marking the job as failed to avoid breaking CI workflows.
core.error('Upload failed (' + requestID + '): (' + statusCode + ') ' + JSON.stringify(response.data)); throw new Error('Upload failed (' + requestID + '): (' + statusCode + ') ' + JSON.stringify(response.data));
return false;
} }
} }
return false; // This case shouldn't ever happen as the final iteration of the loop
// will always throw an error instead of exiting to here.
throw new Error('Upload failed');
} }
// Uploads a single sarif file or a directory of sarif files // Uploads a single sarif file or a directory of sarif files
// depending on what the path happens to refer to. // depending on what the path happens to refer to.
@ -103,8 +103,7 @@ async function upload(input) {
.filter(f => f.endsWith(".sarif")) .filter(f => f.endsWith(".sarif"))
.map(f => path.resolve(input, f)); .map(f => path.resolve(input, f));
if (sarifFiles.length === 0) { if (sarifFiles.length === 0) {
core.setFailed("No SARIF files found to upload in \"" + input + "\"."); throw new Error("No SARIF files found to upload in \"" + input + "\".");
return false;
} }
return await uploadFiles(sarifFiles); return await uploadFiles(sarifFiles);
} }
@ -123,27 +122,22 @@ function countResultsInSarif(sarif) {
} }
exports.countResultsInSarif = countResultsInSarif; exports.countResultsInSarif = countResultsInSarif;
// Validates that the given file path refers to a valid SARIF file. // Validates that the given file path refers to a valid SARIF file.
// Returns a non-empty list of error message if the file is invalid, // Throws an error if the file is invalid.
// otherwise returns the empty list if the file is valid.
function validateSarifFileSchema(sarifFilePath) { function validateSarifFileSchema(sarifFilePath) {
const sarif = JSON.parse(fs.readFileSync(sarifFilePath, 'utf8')); const sarif = JSON.parse(fs.readFileSync(sarifFilePath, 'utf8'));
const schema = JSON.parse(fs.readFileSync(__dirname + '/../src/sarif_v2.1.0_schema.json', 'utf8')); const schema = JSON.parse(fs.readFileSync(__dirname + '/../src/sarif_v2.1.0_schema.json', 'utf8'));
const result = new jsonschema.Validator().validate(sarif, schema); const result = new jsonschema.Validator().validate(sarif, schema);
if (result.valid) { if (!result.valid) {
return true; // Output the more verbose error messages in groups as these may be very large.
}
else {
// Set the failure message to the stacks of all the errors.
// This should be of a manageable size and may even give enough to fix the error.
const errorMessages = result.errors.map(e => "- " + e.stack);
core.setFailed("Unable to upload \"" + sarifFilePath + "\" as it is not valid SARIF:\n" + errorMessages.join("\n"));
// Also output the more verbose error messages in groups as these may be very large.
for (const error of result.errors) { for (const error of result.errors) {
core.startGroup("Error details: " + error.stack); core.startGroup("Error details: " + error.stack);
core.info(JSON.stringify(error, null, 2)); core.info(JSON.stringify(error, null, 2));
core.endGroup(); core.endGroup();
} }
return false; // Set the main error message to the stacks of all the errors.
// This should be of a manageable size and may even give enough to fix the error.
const sarifErrors = result.errors.map(e => "- " + e.stack);
throw new Error("Unable to upload \"" + sarifFilePath + "\" as it is not valid SARIF:\n" + sarifErrors.join("\n"));
} }
} }
exports.validateSarifFileSchema = validateSarifFileSchema; exports.validateSarifFileSchema = validateSarifFileSchema;
@ -154,22 +148,19 @@ async function uploadFiles(sarifFiles) {
core.info("Uploading sarif files: " + JSON.stringify(sarifFiles)); core.info("Uploading sarif files: " + JSON.stringify(sarifFiles));
const sentinelEnvVar = "CODEQL_UPLOAD_SARIF"; const sentinelEnvVar = "CODEQL_UPLOAD_SARIF";
if (process.env[sentinelEnvVar]) { if (process.env[sentinelEnvVar]) {
core.error("Aborting upload: only one run of the codeql/analyze or codeql/upload-sarif actions is allowed per job"); throw new Error("Aborting upload: only one run of the codeql/analyze or codeql/upload-sarif actions is allowed per job");
return false;
} }
core.exportVariable(sentinelEnvVar, sentinelEnvVar); core.exportVariable(sentinelEnvVar, sentinelEnvVar);
// Validate that the files we were asked to upload are all valid SARIF files // Validate that the files we were asked to upload are all valid SARIF files
for (const file of sarifFiles) { for (const file of sarifFiles) {
if (!validateSarifFileSchema(file)) { validateSarifFileSchema(file);
return false;
}
} }
const commitOid = await util.getCommitOid(); const commitOid = await util.getCommitOid();
const workflowRunIDStr = util.getRequiredEnvParam('GITHUB_RUN_ID'); const workflowRunIDStr = util.getRequiredEnvParam('GITHUB_RUN_ID');
const ref = util.getRef(); const ref = util.getRef();
const analysisKey = await util.getAnalysisKey(); const analysisKey = await util.getAnalysisKey();
const analysisName = util.getRequiredEnvParam('GITHUB_WORKFLOW'); const analysisName = util.getRequiredEnvParam('GITHUB_WORKFLOW');
const startedAt = process.env[sharedEnv.CODEQL_ACTION_STARTED_AT]; const startedAt = process.env[sharedEnv.CODEQL_WORKFLOW_STARTED_AT];
let sarifPayload = combineSarifFiles(sarifFiles); let sarifPayload = combineSarifFiles(sarifFiles);
sarifPayload = fingerprints.addFingerprints(sarifPayload); sarifPayload = fingerprints.addFingerprints(sarifPayload);
const zipped_sarif = zlib_1.default.gzipSync(sarifPayload).toString('base64'); const zipped_sarif = zlib_1.default.gzipSync(sarifPayload).toString('base64');
@ -177,8 +168,7 @@ async function uploadFiles(sarifFiles) {
let checkoutURI = file_url_1.default(checkoutPath); let checkoutURI = file_url_1.default(checkoutPath);
const workflowRunID = parseInt(workflowRunIDStr, 10); const workflowRunID = parseInt(workflowRunIDStr, 10);
if (Number.isNaN(workflowRunID)) { if (Number.isNaN(workflowRunID)) {
core.setFailed('GITHUB_RUN_ID must define a non NaN workflow run ID'); throw new Error('GITHUB_RUN_ID must define a non NaN workflow run ID');
return false;
} }
let matrix = core.getInput('matrix'); let matrix = core.getInput('matrix');
if (matrix === "null" || matrix === "") { if (matrix === "null" || matrix === "") {
@ -198,12 +188,19 @@ async function uploadFiles(sarifFiles) {
"tool_names": toolNames, "tool_names": toolNames,
}); });
// Log some useful debug info about the info // Log some useful debug info about the info
core.debug("Raw upload size: " + sarifPayload.length + " bytes"); const rawUploadSizeBytes = sarifPayload.length;
core.debug("Base64 zipped upload size: " + zipped_sarif.length + " bytes"); core.debug("Raw upload size: " + rawUploadSizeBytes + " bytes");
core.debug("Number of results in upload: " + countResultsInSarif(sarifPayload)); const zippedUploadSizeBytes = zipped_sarif.length;
core.debug("Base64 zipped upload size: " + zippedUploadSizeBytes + " bytes");
const numResultInSarif = countResultsInSarif(sarifPayload);
core.debug("Number of results in upload: " + numResultInSarif);
// Make the upload // Make the upload
const succeeded = await uploadPayload(payload); await uploadPayload(payload);
core.endGroup(); core.endGroup();
return succeeded; return {
raw_upload_size_bytes: rawUploadSizeBytes,
zipped_upload_size_bytes: zippedUploadSizeBytes,
num_results_in_sarif: numResultInSarif,
};
} }
//# sourceMappingURL=upload-lib.js.map //# sourceMappingURL=upload-lib.js.map

File diff suppressed because one or more lines are too long

View file

@ -16,12 +16,10 @@ const uploadLib = __importStar(require("./upload-lib"));
testing_utils_1.setupTests(ava_1.default); testing_utils_1.setupTests(ava_1.default);
ava_1.default('validateSarifFileSchema - valid', t => { ava_1.default('validateSarifFileSchema - valid', t => {
const inputFile = __dirname + '/../src/testdata/valid-sarif.sarif'; const inputFile = __dirname + '/../src/testdata/valid-sarif.sarif';
t.true(uploadLib.validateSarifFileSchema(inputFile)); t.notThrows(() => uploadLib.validateSarifFileSchema(inputFile));
}); });
ava_1.default('validateSarifFileSchema - invalid', t => { ava_1.default('validateSarifFileSchema - invalid', t => {
const inputFile = __dirname + '/../src/testdata/invalid-sarif.sarif'; const inputFile = __dirname + '/../src/testdata/invalid-sarif.sarif';
t.false(uploadLib.validateSarifFileSchema(inputFile)); t.throws(() => uploadLib.validateSarifFileSchema(inputFile));
// validateSarifFileSchema calls core.setFailed which sets the exit code on error
process.exitCode = 0;
}); });
//# sourceMappingURL=upload-lib.test.js.map //# sourceMappingURL=upload-lib.test.js.map

View file

@ -1 +1 @@
{"version":3,"file":"upload-lib.test.js","sourceRoot":"","sources":["../src/upload-lib.test.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,8CAAuB;AAEvB,mDAA2C;AAC3C,wDAA0C;AAE1C,0BAAU,CAAC,aAAI,CAAC,CAAC;AAEjB,aAAI,CAAC,iCAAiC,EAAE,CAAC,CAAC,EAAE;IAC1C,MAAM,SAAS,GAAG,SAAS,GAAG,oCAAoC,CAAC;IACnE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,SAAS,CAAC,CAAC,CAAC;AACvD,CAAC,CAAC,CAAC;AAEH,aAAI,CAAC,mCAAmC,EAAE,CAAC,CAAC,EAAE;IAC5C,MAAM,SAAS,GAAG,SAAS,GAAG,sCAAsC,CAAC;IACrE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,uBAAuB,CAAC,SAAS,CAAC,CAAC,CAAC;IACtD,iFAAiF;IACjF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"} {"version":3,"file":"upload-lib.test.js","sourceRoot":"","sources":["../src/upload-lib.test.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,8CAAuB;AAEvB,mDAA2C;AAC3C,wDAA0C;AAE1C,0BAAU,CAAC,aAAI,CAAC,CAAC;AAEjB,aAAI,CAAC,iCAAiC,EAAE,CAAC,CAAC,EAAE;IAC1C,MAAM,SAAS,GAAG,SAAS,GAAG,oCAAoC,CAAC;IACnE,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,uBAAuB,CAAC,SAAS,CAAC,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC;AAEH,aAAI,CAAC,mCAAmC,EAAE,CAAC,CAAC,EAAE;IAC5C,MAAM,SAAS,GAAG,SAAS,GAAG,sCAAsC,CAAC;IACrE,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,uBAAuB,CAAC,SAAS,CAAC,CAAC,CAAC;AAC/D,CAAC,CAAC,CAAC"}

22
lib/upload-sarif.js generated
View file

@ -10,21 +10,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core")); const core = __importStar(require("@actions/core"));
const upload_lib = __importStar(require("./upload-lib")); const upload_lib = __importStar(require("./upload-lib"));
const util = __importStar(require("./util")); const util = __importStar(require("./util"));
async function sendSuccessStatusReport(startedAt, uploadStats) {
const statusReportBase = await util.createStatusReportBase('upload-sarif', 'success', startedAt);
const statusReport = {
...statusReportBase,
...uploadStats,
};
await util.sendStatusReport(statusReport);
}
async function run() { async function run() {
if (util.should_abort('upload-sarif', false) || !await util.reportActionStarting('upload-sarif')) { const startedAt = new Date();
if (util.should_abort('upload-sarif', false) ||
!await util.sendStatusReport(await util.createStatusReportBase('upload-sarif', 'starting', startedAt), true)) {
return; return;
} }
try { try {
if (await upload_lib.upload(core.getInput('sarif_file'))) { const uploadStats = await upload_lib.upload(core.getInput('sarif_file'));
await util.reportActionSucceeded('upload-sarif'); await sendSuccessStatusReport(startedAt, uploadStats);
}
else {
await util.reportActionFailed('upload-sarif', 'upload');
}
} }
catch (error) { catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
await util.reportActionFailed('upload-sarif', error.message, error.stack); await util.sendStatusReport(await util.createStatusReportBase('upload-sarif', 'failure', startedAt, error.message, error.stack));
return; return;
} }
} }

View file

@ -1 +1 @@
{"version":3,"file":"upload-sarif.js","sourceRoot":"","sources":["../src/upload-sarif.ts"],"names":[],"mappings":";;;;;;;;;AAAA,oDAAsC;AAEtC,yDAA2C;AAC3C,6CAA+B;AAE/B,KAAK,UAAU,GAAG;IAChB,IAAI,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,oBAAoB,CAAC,cAAc,CAAC,EAAE;QAChG,OAAO;KACR;IAED,IAAI;QACF,IAAI,MAAM,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,EAAE;YACxD,MAAM,IAAI,CAAC,qBAAqB,CAAC,cAAc,CAAC,CAAC;SAClD;aAAM;YACL,MAAM,IAAI,CAAC,kBAAkB,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;SACzD;KACF;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9B,MAAM,IAAI,CAAC,kBAAkB,CAAC,cAAc,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1E,OAAO;KACR;AACH,CAAC;AAED,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;IACd,IAAI,CAAC,SAAS,CAAC,qCAAqC,GAAG,CAAC,CAAC,CAAC;IAC1D,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC,CAAC,CAAC"} {"version":3,"file":"upload-sarif.js","sourceRoot":"","sources":["../src/upload-sarif.ts"],"names":[],"mappings":";;;;;;;;;AAAA,oDAAsC;AAEtC,yDAA2C;AAC3C,6CAA+B;AAI/B,KAAK,UAAU,uBAAuB,CAAC,SAAe,EAAE,WAA0C;IAChG,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,cAAc,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IACjG,MAAM,YAAY,GAA4B;QAC5C,GAAG,gBAAgB;QACnB,GAAI,WAAW;KAChB,CAAC;IACF,MAAM,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;AAC5C,CAAC;AAED,KAAK,UAAU,GAAG;IAChB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;IAC7B,IAAI,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,KAAK,CAAC;QACxC,CAAC,MAAM,IAAI,CAAC,gBAAgB,CAAC,MAAM,IAAI,CAAC,sBAAsB,CAAC,cAAc,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,EAAE;QAChH,OAAO;KACR;IAED,IAAI;QACF,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;QACzE,MAAM,uBAAuB,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;KAEvD;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9B,MAAM,IAAI,CAAC,gBAAgB,CAAC,MAAM,IAAI,CAAC,sBAAsB,CAC3D,cAAc,EACd,SAAS,EACT,SAAS,EACT,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAChB,OAAO;KACR;AACH,CAAC;AAED,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;IACd,IAAI,CAAC,SAAS,CAAC,qCAAqC,GAAG,CAAC,CAAC,CAAC;IAC1D,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC,CAAC,CAAC"}

109
lib/util.js generated
View file

@ -13,7 +13,6 @@ const fs = __importStar(require("fs"));
const os = __importStar(require("os")); const os = __importStar(require("os"));
const path = __importStar(require("path")); const path = __importStar(require("path"));
const api = __importStar(require("./api-client")); const api = __importStar(require("./api-client"));
const configUtils = __importStar(require("./config-utils"));
const sharedEnv = __importStar(require("./shared-environment")); const sharedEnv = __importStar(require("./shared-environment"));
/** /**
* Should the current action be aborted? * Should the current action be aborted?
@ -139,22 +138,11 @@ exports.getRef = getRef;
* *
* @param actionName The name of the action, e.g. 'init', 'finish', 'upload-sarif' * @param actionName The name of the action, e.g. 'init', 'finish', 'upload-sarif'
* @param status The status. Must be 'success', 'failure', or 'starting' * @param status The status. Must be 'success', 'failure', or 'starting'
* @param startedAt The time this action started executing.
* @param cause Cause of failure (only supply if status is 'failure') * @param cause Cause of failure (only supply if status is 'failure')
* @param exception Exception (only supply if status is 'failure') * @param exception Exception (only supply if status is 'failure')
*/ */
async function createStatusReport(actionName, status, cause, exception) { async function createStatusReportBase(actionName, status, actionStartedAt, cause, exception) {
var _a, _b;
// If this is not the init action starting up or aborting then try to load the config.
// If it fails then carry because it's important to still send the status report.
let config = undefined;
if (actionName !== 'init' || (status !== 'starting' && status !== 'aborted')) {
try {
config = await configUtils.getConfig();
}
catch (e) {
core.error('Unable to load config: ' + e);
}
}
const commitOid = process.env['GITHUB_SHA'] || ''; const commitOid = process.env['GITHUB_SHA'] || '';
const ref = getRef(); const ref = getRef();
const workflowRunIDStr = process.env['GITHUB_RUN_ID']; const workflowRunIDStr = process.env['GITHUB_RUN_ID'];
@ -165,20 +153,22 @@ async function createStatusReport(actionName, status, cause, exception) {
const workflowName = process.env['GITHUB_WORKFLOW'] || ''; const workflowName = process.env['GITHUB_WORKFLOW'] || '';
const jobName = process.env['GITHUB_JOB'] || ''; const jobName = process.env['GITHUB_JOB'] || '';
const analysis_key = await getAnalysisKey(); const analysis_key = await getAnalysisKey();
const languages = ((_b = (_a = config) === null || _a === void 0 ? void 0 : _a.languages) === null || _b === void 0 ? void 0 : _b.join(',')) || ""; let workflowStartedAt = process.env[sharedEnv.CODEQL_WORKFLOW_STARTED_AT];
const startedAt = process.env[sharedEnv.CODEQL_ACTION_STARTED_AT] || new Date().toISOString(); if (workflowStartedAt === undefined) {
core.exportVariable(sharedEnv.CODEQL_ACTION_STARTED_AT, startedAt); workflowStartedAt = actionStartedAt.toISOString();
core.exportVariable(sharedEnv.CODEQL_WORKFLOW_STARTED_AT, workflowStartedAt);
}
let statusReport = { let statusReport = {
workflow_run_id: workflowRunID, workflow_run_id: workflowRunID,
workflow_name: workflowName, workflow_name: workflowName,
job_name: jobName, job_name: jobName,
analysis_key: analysis_key, analysis_key: analysis_key,
languages: languages,
commit_oid: commitOid, commit_oid: commitOid,
ref: ref, ref: ref,
action_name: actionName, action_name: actionName,
action_oid: "unknown", action_oid: "unknown",
started_at: startedAt, started_at: workflowStartedAt,
action_started_at: actionStartedAt.toISOString(),
status: status status: status
}; };
// Add optional parameters // Add optional parameters
@ -197,12 +187,17 @@ async function createStatusReport(actionName, status, cause, exception) {
} }
return statusReport; return statusReport;
} }
exports.createStatusReportBase = createStatusReportBase;
/** /**
* Send a status report to the code_scanning/analysis/status endpoint. * Send a status report to the code_scanning/analysis/status endpoint.
* *
* Returns the status code of the response to the status request. * Optionally checks the response from the API endpoint and sets the action
* as failed if the status report failed. This is only expected to be used
* when sending a 'starting' report.
*
* Returns whether sending the status report was successful of not.
*/ */
async function sendStatusReport(statusReport) { async function sendStatusReport(statusReport, ignoreFailures) {
const statusReportJSON = JSON.stringify(statusReport); const statusReportJSON = JSON.stringify(statusReport);
core.debug('Sending status report: ' + statusReportJSON); core.debug('Sending status report: ' + statusReportJSON);
const nwo = getRequiredEnvParam("GITHUB_REPOSITORY"); const nwo = getRequiredEnvParam("GITHUB_REPOSITORY");
@ -212,65 +207,25 @@ async function sendStatusReport(statusReport) {
repo: repo, repo: repo,
data: statusReportJSON, data: statusReportJSON,
}); });
return statusResponse.status; if (!ignoreFailures) {
} // If the status report request fails with a 403 or a 404, then this is a deliberate
/** // message from the endpoint that the SARIF upload can be expected to fail too,
* Send a status report that an action is starting. // so the action should fail to avoid wasting actions minutes.
* //
* If the action is `init` then this also records the start time in the environment, // Other failure responses (or lack thereof) could be transitory and should not
* and ensures that the analysed languages are also recorded in the envirenment. // cause the action to fail.
* if (statusResponse.status === 403) {
* Returns true unless a problem occurred and the action should abort. core.setFailed('The repo on which this action is running is not opted-in to CodeQL code scanning.');
*/ return false;
async function reportActionStarting(action) { }
const statusCode = await sendStatusReport(await createStatusReport(action, 'starting')); if (statusResponse.status === 404) {
// If the status report request fails with a 403 or a 404, then this is a deliberate core.setFailed('Not authorized to used the CodeQL code scanning feature on this repo.');
// message from the endpoint that the SARIF upload can be expected to fail too, return false;
// so the action should fail to avoid wasting actions minutes. }
//
// Other failure responses (or lack thereof) could be transitory and should not
// cause the action to fail.
if (statusCode === 403) {
core.setFailed('The repo on which this action is running is not opted-in to CodeQL code scanning.');
return false;
}
if (statusCode === 404) {
core.setFailed('Not authorized to used the CodeQL code scanning feature on this repo.');
return false;
} }
return true; return true;
} }
exports.reportActionStarting = reportActionStarting; exports.sendStatusReport = sendStatusReport;
/**
* Report that an action has failed.
*
* Note that the started_at date is always that of the `init` action, since
* this is likely to give a more useful duration when inspecting events.
*/
async function reportActionFailed(action, cause, exception) {
await sendStatusReport(await createStatusReport(action, 'failure', cause, exception));
}
exports.reportActionFailed = reportActionFailed;
/**
* Report that an action has succeeded.
*
* Note that the started_at date is always that of the `init` action, since
* this is likely to give a more useful duration when inspecting events.
*/
async function reportActionSucceeded(action) {
await sendStatusReport(await createStatusReport(action, 'success'));
}
exports.reportActionSucceeded = reportActionSucceeded;
/**
* Report that an action has been aborted.
*
* Note that the started_at date is always that of the `init` action, since
* this is likely to give a more useful duration when inspecting events.
*/
async function reportActionAborted(action, cause) {
await sendStatusReport(await createStatusReport(action, 'aborted', cause));
}
exports.reportActionAborted = reportActionAborted;
/** /**
* Get the array of all the tool names contained in the given sarif contents. * Get the array of all the tool names contained in the given sarif contents.
* *

File diff suppressed because one or more lines are too long

View file

@ -11,6 +11,7 @@ test("emptyPaths", async t => {
queries: {}, queries: {},
pathsIgnore: [], pathsIgnore: [],
paths: [], paths: [],
originalUserInput: {},
}; };
analysisPaths.includeAndExcludeAnalysisPaths(config); analysisPaths.includeAndExcludeAnalysisPaths(config);
t.is(process.env['LGTM_INDEX_INCLUDE'], undefined); t.is(process.env['LGTM_INDEX_INCLUDE'], undefined);
@ -24,6 +25,7 @@ test("nonEmptyPaths", async t => {
queries: {}, queries: {},
paths: ['path1', 'path2', '**/path3'], paths: ['path1', 'path2', '**/path3'],
pathsIgnore: ['path4', 'path5', 'path6/**'], pathsIgnore: ['path4', 'path5', 'path6/**'],
originalUserInput: {},
}; };
analysisPaths.includeAndExcludeAnalysisPaths(config); analysisPaths.includeAndExcludeAnalysisPaths(config);
t.is(process.env['LGTM_INDEX_INCLUDE'], 'path1\npath2'); t.is(process.env['LGTM_INDEX_INCLUDE'], 'path1\npath2');

View file

@ -4,9 +4,40 @@ import { getCodeQL } from './codeql';
import * as sharedEnv from './shared-environment'; import * as sharedEnv from './shared-environment';
import * as util from './util'; import * as util from './util';
interface AutobuildStatusReport extends util.StatusReportBase {
// Comma-separated set of languages being autobuilt
autobuild_languages: string;
// Language that failed autobuilding (or undefined if all languages succeeded).
autobuild_failure?: string;
}
async function sendCompletedStatusReport(
startedAt: Date,
allLanguages: string[],
failingLanguage?: string,
cause?: Error) {
const status = failingLanguage !== undefined || cause !== undefined ? 'failure' : 'success';
const statusReportBase = await util.createStatusReportBase(
'autobuild',
status,
startedAt,
cause?.message,
cause?.stack);
const statusReport: AutobuildStatusReport = {
...statusReportBase,
autobuild_languages: allLanguages.join(','),
autobuild_failure: failingLanguage,
};
await util.sendStatusReport(statusReport);
}
async function run() { async function run() {
const startedAt = new Date();
let language;
try { try {
if (util.should_abort('autobuild', true) || !await util.reportActionStarting('autobuild')) { if (util.should_abort('autobuild', true) ||
!await util.sendStatusReport(await util.createStatusReportBase('autobuild', 'starting', startedAt), true)) {
return; return;
} }
@ -15,7 +46,7 @@ async function run() {
// The languages are sorted in order specified by user or by lines of code if we got // The languages are sorted in order specified by user or by lines of code if we got
// them from the GitHub API, so try to build the first language on the list. // them from the GitHub API, so try to build the first language on the list.
const autobuildLanguages = process.env[sharedEnv.CODEQL_ACTION_TRACED_LANGUAGES]?.split(',') || []; const autobuildLanguages = process.env[sharedEnv.CODEQL_ACTION_TRACED_LANGUAGES]?.split(',') || [];
const language = autobuildLanguages[0]; language = autobuildLanguages[0];
if (!language) { if (!language) {
core.info("None of the languages in this project require extra build steps"); core.info("None of the languages in this project require extra build steps");
@ -36,11 +67,11 @@ async function run() {
} catch (error) { } catch (error) {
core.setFailed("We were unable to automatically build your code. Please replace the call to the autobuild action with your custom build steps. " + error.message); core.setFailed("We were unable to automatically build your code. Please replace the call to the autobuild action with your custom build steps. " + error.message);
await util.reportActionFailed('autobuild', error.message, error.stack); await sendCompletedStatusReport(startedAt, [language], language, error);
return; return;
} }
await util.reportActionSucceeded('autobuild'); await sendCompletedStatusReport(startedAt, [language]);
} }
run().catch(e => { run().catch(e => {

View file

@ -187,6 +187,13 @@ test("load non-empty input", async t => {
queries: {'javascript': ['/foo/a.ql', '/bar/b.ql']}, queries: {'javascript': ['/foo/a.ql', '/bar/b.ql']},
pathsIgnore: ['a', 'b'], pathsIgnore: ['a', 'b'],
paths: ['c/d'], paths: ['c/d'],
originalUserInput: {
name: 'my config',
'disable-default-queries': true,
queries: [{ uses: './foo' }],
'paths-ignore': ['a', 'b'],
paths: ['c/d'],
},
}; };
fs.writeFileSync(path.join(tmpDir, 'input'), inputFileContents, 'utf8'); fs.writeFileSync(path.join(tmpDir, 'input'), inputFileContents, 'utf8');

View file

@ -17,6 +17,20 @@ const QUERIES_USES_PROPERTY = 'uses';
const PATHS_IGNORE_PROPERTY = 'paths-ignore'; const PATHS_IGNORE_PROPERTY = 'paths-ignore';
const PATHS_PROPERTY = 'paths'; const PATHS_PROPERTY = 'paths';
/**
* Format of the config file supplied by the user.
*/
export interface UserConfig {
name?: string;
'disable-default-queries'?: boolean;
queries?: {
name?: string;
uses: string;
}[];
'paths-ignore'?: string[];
paths?: string[];
}
/** /**
* Format of the parsed config file. * Format of the parsed config file.
*/ */
@ -39,6 +53,14 @@ export interface Config {
* List of paths to include in analysis. * List of paths to include in analysis.
*/ */
paths: string[]; paths: string[];
/**
* A unaltered copy of the original user input.
* Mainly intended to be used for status reporting.
* If any field is useful for the actual processing
* of the action then consider pulling it out to a
* top-level field above.
*/
originalUserInput: UserConfig;
} }
/** /**
@ -460,7 +482,8 @@ export async function getDefaultConfig(): Promise<Config> {
languages: languages, languages: languages,
queries: queries, queries: queries,
pathsIgnore: [], pathsIgnore: [],
paths: [] paths: [],
originalUserInput: {},
}; };
} }
@ -468,7 +491,7 @@ export async function getDefaultConfig(): Promise<Config> {
* Load the config from the given file. * Load the config from the given file.
*/ */
async function loadConfig(configFile: string): Promise<Config> { async function loadConfig(configFile: string): Promise<Config> {
let parsedYAML; let parsedYAML: UserConfig;
if (isLocal(configFile)) { if (isLocal(configFile)) {
// Treat the config file as relative to the workspace // Treat the config file as relative to the workspace
@ -486,7 +509,7 @@ async function loadConfig(configFile: string): Promise<Config> {
if (typeof parsedYAML[NAME_PROPERTY] !== "string") { if (typeof parsedYAML[NAME_PROPERTY] !== "string") {
throw new Error(getNameInvalid(configFile)); throw new Error(getNameInvalid(configFile));
} }
if (parsedYAML[NAME_PROPERTY].length === 0) { if (parsedYAML[NAME_PROPERTY]!.length === 0) {
throw new Error(getNameInvalid(configFile)); throw new Error(getNameInvalid(configFile));
} }
} }
@ -507,7 +530,7 @@ async function loadConfig(configFile: string): Promise<Config> {
if (typeof parsedYAML[DISABLE_DEFAULT_QUERIES_PROPERTY] !== "boolean") { if (typeof parsedYAML[DISABLE_DEFAULT_QUERIES_PROPERTY] !== "boolean") {
throw new Error(getDisableDefaultQueriesInvalid(configFile)); throw new Error(getDisableDefaultQueriesInvalid(configFile));
} }
disableDefaultQueries = parsedYAML[DISABLE_DEFAULT_QUERIES_PROPERTY]; disableDefaultQueries = parsedYAML[DISABLE_DEFAULT_QUERIES_PROPERTY]!;
} }
if (!disableDefaultQueries) { if (!disableDefaultQueries) {
await addDefaultQueries(languages, queries); await addDefaultQueries(languages, queries);
@ -517,7 +540,7 @@ async function loadConfig(configFile: string): Promise<Config> {
if (!(parsedYAML[QUERIES_PROPERTY] instanceof Array)) { if (!(parsedYAML[QUERIES_PROPERTY] instanceof Array)) {
throw new Error(getQueriesInvalid(configFile)); throw new Error(getQueriesInvalid(configFile));
} }
for (const query of parsedYAML[QUERIES_PROPERTY]) { for (const query of parsedYAML[QUERIES_PROPERTY]!) {
if (!(QUERIES_USES_PROPERTY in query) || typeof query[QUERIES_USES_PROPERTY] !== "string") { if (!(QUERIES_USES_PROPERTY in query) || typeof query[QUERIES_USES_PROPERTY] !== "string") {
throw new Error(getQueryUsesInvalid(configFile)); throw new Error(getQueryUsesInvalid(configFile));
} }
@ -529,7 +552,7 @@ async function loadConfig(configFile: string): Promise<Config> {
if (!(parsedYAML[PATHS_IGNORE_PROPERTY] instanceof Array)) { if (!(parsedYAML[PATHS_IGNORE_PROPERTY] instanceof Array)) {
throw new Error(getPathsIgnoreInvalid(configFile)); throw new Error(getPathsIgnoreInvalid(configFile));
} }
parsedYAML[PATHS_IGNORE_PROPERTY].forEach(path => { parsedYAML[PATHS_IGNORE_PROPERTY]!.forEach(path => {
if (typeof path !== "string" || path === '') { if (typeof path !== "string" || path === '') {
throw new Error(getPathsIgnoreInvalid(configFile)); throw new Error(getPathsIgnoreInvalid(configFile));
} }
@ -541,7 +564,7 @@ async function loadConfig(configFile: string): Promise<Config> {
if (!(parsedYAML[PATHS_PROPERTY] instanceof Array)) { if (!(parsedYAML[PATHS_PROPERTY] instanceof Array)) {
throw new Error(getPathsInvalid(configFile)); throw new Error(getPathsInvalid(configFile));
} }
parsedYAML[PATHS_PROPERTY].forEach(path => { parsedYAML[PATHS_PROPERTY]!.forEach(path => {
if (typeof path !== "string" || path === '') { if (typeof path !== "string" || path === '') {
throw new Error(getPathsInvalid(configFile)); throw new Error(getPathsInvalid(configFile));
} }
@ -549,7 +572,13 @@ async function loadConfig(configFile: string): Promise<Config> {
}); });
} }
return {languages, queries, pathsIgnore, paths}; return {
languages,
queries,
pathsIgnore,
paths,
originalUserInput: parsedYAML
};
} }
/** /**
@ -584,7 +613,7 @@ function isLocal(configPath: string): boolean {
return (configPath.indexOf("@") === -1); return (configPath.indexOf("@") === -1);
} }
function getLocalConfig(configFile: string, workspacePath: string): any { function getLocalConfig(configFile: string, workspacePath: string): UserConfig {
// Error if the config file is now outside of the workspace // Error if the config file is now outside of the workspace
if (!(configFile + path.sep).startsWith(workspacePath + path.sep)) { if (!(configFile + path.sep).startsWith(workspacePath + path.sep)) {
throw new Error(getConfigFileOutsideWorkspaceErrorMessage(configFile)); throw new Error(getConfigFileOutsideWorkspaceErrorMessage(configFile));
@ -598,7 +627,7 @@ function getLocalConfig(configFile: string, workspacePath: string): any {
return yaml.safeLoad(fs.readFileSync(configFile, 'utf8')); return yaml.safeLoad(fs.readFileSync(configFile, 'utf8'));
} }
async function getRemoteConfig(configFile: string): Promise<any> { async function getRemoteConfig(configFile: string): Promise<UserConfig> {
// retrieve the various parts of the config location, and ensure they're present // retrieve the various parts of the config location, and ensure they're present
const format = new RegExp('(?<owner>[^/]+)/(?<repo>[^/]+)/(?<path>[^@]+)@(?<ref>.*)'); const format = new RegExp('(?<owner>[^/]+)/(?<repo>[^/]+)/(?<path>[^@]+)@(?<ref>.*)');
const pieces = format.exec(configFile); const pieces = format.exec(configFile);

View file

@ -9,6 +9,53 @@ import * as sharedEnv from './shared-environment';
import * as upload_lib from './upload-lib'; import * as upload_lib from './upload-lib';
import * as util from './util'; import * as util from './util';
interface QueriesStatusReport {
// Time taken in ms to analyze builtin queries for cpp (or undefined if this language was not analyzed)
analyze_builtin_queries_cpp_duration_ms?: number;
// Time taken in ms to analyze builtin queries for csharp (or undefined if this language was not analyzed)
analyze_builtin_queries_csharp_duration_ms?: number;
// Time taken in ms to analyze builtin queries for go (or undefined if this language was not analyzed)
analyze_builtin_queries_go_duration_ms?: number;
// Time taken in ms to analyze builtin queries for java (or undefined if this language was not analyzed)
analyze_builtin_queries_java_duration_ms?: number;
// Time taken in ms to analyze builtin queries for javascript (or undefined if this language was not analyzed)
analyze_builtin_queries_javascript_duration_ms?: number;
// Time taken in ms to analyze builtin queries for python (or undefined if this language was not analyzed)
analyze_builtin_queries_python_duration_ms?: number;
// Time taken in ms to analyze custom queries for cpp (or undefined if this language was not analyzed)
analyze_custom_queries_cpp_duration_ms?: number;
// Time taken in ms to analyze custom queries for csharp (or undefined if this language was not analyzed)
analyze_custom_queries_csharp_duration_ms?: number;
// Time taken in ms to analyze custom queries for go (or undefined if this language was not analyzed)
analyze_custom_queries_go_duration_ms?: number;
// Time taken in ms to analyze custom queries for java (or undefined if this language was not analyzed)
analyze_custom_queries_java_duration_ms?: number;
// Time taken in ms to analyze custom queries for javascript (or undefined if this language was not analyzed)
analyze_custom_queries_javascript_duration_ms?: number;
// Time taken in ms to analyze custom queries for python (or undefined if this language was not analyzed)
analyze_custom_queries_python_duration_ms?: number;
// Name of language that errored during analysis (or undefined if no langauge failed)
analyze_failure_language?: string;
}
interface FinishStatusReport extends util.StatusReportBase, upload_lib.UploadStatusReport, QueriesStatusReport {}
async function sendStatusReport(
startedAt: Date,
queriesStats: QueriesStatusReport | undefined,
uploadStats: upload_lib.UploadStatusReport | undefined,
error?: Error) {
const status = queriesStats?.analyze_failure_language !== undefined || error !== undefined ? 'failure' : 'success';
const statusReportBase = await util.createStatusReportBase('finish', status, startedAt, error?.message, error?.stack);
const statusReport: FinishStatusReport = {
...statusReportBase,
...(queriesStats || {}),
...(uploadStats || {}),
};
await util.sendStatusReport(statusReport);
}
async function createdDBForScannedLanguages(databaseFolder: string) { async function createdDBForScannedLanguages(databaseFolder: string) {
const scannedLanguages = process.env[sharedEnv.CODEQL_ACTION_SCANNED_LANGUAGES]; const scannedLanguages = process.env[sharedEnv.CODEQL_ACTION_SCANNED_LANGUAGES];
if (scannedLanguages) { if (scannedLanguages) {
@ -33,35 +80,53 @@ async function finalizeDatabaseCreation(databaseFolder: string, config: configUt
} }
// Runs queries and creates sarif files in the given folder // Runs queries and creates sarif files in the given folder
async function runQueries(databaseFolder: string, sarifFolder: string, config: configUtils.Config) { async function runQueries(
const codeql = getCodeQL(); databaseFolder: string,
for (let database of fs.readdirSync(databaseFolder)) { sarifFolder: string,
core.startGroup('Analyzing ' + database); config: configUtils.Config): Promise<QueriesStatusReport> {
const queries = config.queries[database] || []; const codeql = getCodeQL();
for (let language of fs.readdirSync(databaseFolder)) {
core.startGroup('Analyzing ' + language);
const queries = config.queries[language] || [];
if (queries.length === 0) { if (queries.length === 0) {
throw new Error('Unable to analyse ' + database + ' as no queries were selected for this language'); throw new Error('Unable to analyse ' + language + ' as no queries were selected for this language');
} }
// Pass the queries to codeql using a file instead of using the command try {
// line to avoid command line length restrictions, particularly on windows. // Pass the queries to codeql using a file instead of using the command
const querySuite = path.join(databaseFolder, database + '-queries.qls'); // line to avoid command line length restrictions, particularly on windows.
const querySuiteContents = queries.map(q => '- query: ' + q).join('\n'); const querySuite = path.join(databaseFolder, language + '-queries.qls');
fs.writeFileSync(querySuite, querySuiteContents); const querySuiteContents = queries.map(q => '- query: ' + q).join('\n');
core.debug('Query suite file for ' + database + '...\n' + querySuiteContents); fs.writeFileSync(querySuite, querySuiteContents);
core.debug('Query suite file for ' + language + '...\n' + querySuiteContents);
const sarifFile = path.join(sarifFolder, database + '.sarif'); const sarifFile = path.join(sarifFolder, language + '.sarif');
await codeql.databaseAnalyze(path.join(databaseFolder, database), sarifFile, querySuite); await codeql.databaseAnalyze(path.join(databaseFolder, language), sarifFile, querySuite);
core.debug('SARIF results for database ' + database + ' created at "' + sarifFile + '"'); core.debug('SARIF results for database ' + language + ' created at "' + sarifFile + '"');
core.endGroup(); core.endGroup();
} catch (e) {
// For now the fields about query performance are not populated
return {
analyze_failure_language: language,
};
}
} }
return {};
} }
async function run() { async function run() {
const startedAt = new Date();
let queriesStats: QueriesStatusReport | undefined = undefined;
let uploadStats: upload_lib.UploadStatusReport | undefined = undefined;
try { try {
if (util.should_abort('finish', true) || !await util.reportActionStarting('finish')) { if (util.should_abort('finish', true) ||
!await util.sendStatusReport(await util.createStatusReportBase('finish', 'starting', startedAt), true)) {
return; return;
} }
const config = await configUtils.getConfig(); const config = await configUtils.getConfig();
@ -78,22 +143,19 @@ async function run() {
await finalizeDatabaseCreation(databaseFolder, config); await finalizeDatabaseCreation(databaseFolder, config);
core.info('Analyzing database'); core.info('Analyzing database');
await runQueries(databaseFolder, sarifFolder, config); queriesStats = await runQueries(databaseFolder, sarifFolder, config);
if ('true' === core.getInput('upload')) { if ('true' === core.getInput('upload')) {
if (!await upload_lib.upload(sarifFolder)) { uploadStats = await upload_lib.upload(sarifFolder);
await util.reportActionFailed('finish', 'upload');
return;
}
} }
} catch (error) { } catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
await util.reportActionFailed('finish', error.message, error.stack); await sendStatusReport(startedAt, queriesStats, uploadStats, error);
return; return;
} }
await util.reportActionSucceeded('finish'); await sendStatusReport(startedAt, queriesStats, uploadStats);
} }
run().catch(e => { run().catch(e => {

View file

@ -131,13 +131,54 @@ function concatTracerConfigs(configs: { [lang: string]: TracerConfig }): TracerC
return { env, spec }; return { env, spec };
} }
interface InitSuccessStatusReport extends util.StatusReportBase {
// Comma-separated list of languages that analysis was run for
// This may be from the workflow file or may be calculated from repository contents
languages: string;
// Comma-separated list of languages specified explicitly in the workflow file
workflow_languages: string;
// Comma-separated list of paths, from the 'paths' config field
paths: string;
// Comma-separated list of paths, from the 'paths-ignore' config field
paths_ignore: string;
// Commas-separated list of languages where the default queries are disabled
disable_default_queries: string;
// Comma-separated list of queries sources, from the 'queries' config field
queries: string;
}
async function sendSuccessStatusReport(startedAt: Date, config: configUtils.Config) {
const statusReportBase = await util.createStatusReportBase('init', 'success', startedAt);
const languages = config.languages.join(',');
const workflowLanguages = core.getInput('languages', { required: false });
const paths = (config.originalUserInput.paths || []).join(',');
const pathsIgnore = (config.originalUserInput['paths-ignore'] || []).join(',');
const disableDefaultQueries = config.originalUserInput['disable-default-queries'] ? languages : '';
const queries = (config.originalUserInput.queries || []).map(q => q.uses).join(',');
const statusReport: InitSuccessStatusReport = {
...statusReportBase,
languages: languages,
workflow_languages: workflowLanguages,
paths: paths,
paths_ignore: pathsIgnore,
disable_default_queries: disableDefaultQueries,
queries: queries,
};
await util.sendStatusReport(statusReport);
}
async function run() { async function run() {
const startedAt = new Date();
let config: configUtils.Config; let config: configUtils.Config;
let codeql: CodeQL; let codeql: CodeQL;
try { try {
if (util.should_abort('init', false) || !await util.reportActionStarting('init')) { if (util.should_abort('init', false) ||
!await util.sendStatusReport(await util.createStatusReportBase('init', 'starting', startedAt), true)) {
return; return;
} }
@ -153,7 +194,7 @@ async function run() {
} catch (e) { } catch (e) {
core.setFailed(e.message); core.setFailed(e.message);
await util.reportActionAborted('init', e.message); await util.sendStatusReport(await util.createStatusReportBase('init', 'aborted', startedAt, e.message));
return; return;
} }
@ -226,10 +267,15 @@ async function run() {
} catch (error) { } catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
await util.reportActionFailed('init', error.message, error.stack); await util.sendStatusReport(await util.createStatusReportBase(
'init',
'failure',
startedAt,
error.message,
error.stack));
return; return;
} }
await util.reportActionSucceeded('init'); await sendSuccessStatusReport(startedAt, config);
core.exportVariable(sharedEnv.CODEQL_ACTION_INIT_COMPLETED, 'true'); core.exportVariable(sharedEnv.CODEQL_ACTION_INIT_COMPLETED, 'true');
} }

View file

@ -8,6 +8,6 @@ export const CODEQL_ACTION_TRACED_LANGUAGES = 'CODEQL_ACTION_TRACED_LANGUAGES';
// action (i.e. the upload action is being used by a third-party integrator) // action (i.e. the upload action is being used by a third-party integrator)
// then this variable will be assigned the start time of the action invoked // then this variable will be assigned the start time of the action invoked
// rather that the init action. // rather that the init action.
export const CODEQL_ACTION_STARTED_AT = 'CODEQL_ACTION_STARTED_AT'; export const CODEQL_WORKFLOW_STARTED_AT = 'CODEQL_WORKFLOW_STARTED_AT';
// Populated when the init action completes successfully // Populated when the init action completes successfully
export const CODEQL_ACTION_INIT_COMPLETED = 'CODEQL_ACTION_INIT_COMPLETED'; export const CODEQL_ACTION_INIT_COMPLETED = 'CODEQL_ACTION_INIT_COMPLETED';

View file

@ -7,12 +7,10 @@ setupTests(test);
test('validateSarifFileSchema - valid', t => { test('validateSarifFileSchema - valid', t => {
const inputFile = __dirname + '/../src/testdata/valid-sarif.sarif'; const inputFile = __dirname + '/../src/testdata/valid-sarif.sarif';
t.true(uploadLib.validateSarifFileSchema(inputFile)); t.notThrows(() => uploadLib.validateSarifFileSchema(inputFile));
}); });
test('validateSarifFileSchema - invalid', t => { test('validateSarifFileSchema - invalid', t => {
const inputFile = __dirname + '/../src/testdata/invalid-sarif.sarif'; const inputFile = __dirname + '/../src/testdata/invalid-sarif.sarif';
t.false(uploadLib.validateSarifFileSchema(inputFile)); t.throws(() => uploadLib.validateSarifFileSchema(inputFile));
// validateSarifFileSchema calls core.setFailed which sets the exit code on error
process.exitCode = 0;
}); });

View file

@ -35,13 +35,13 @@ export function combineSarifFiles(sarifFiles: string[]): string {
// Upload the given payload. // Upload the given payload.
// If the request fails then this will retry a small number of times. // If the request fails then this will retry a small number of times.
async function uploadPayload(payload): Promise<boolean> { async function uploadPayload(payload) {
core.info('Uploading results'); core.info('Uploading results');
// If in test mode we don't want to upload the results // If in test mode we don't want to upload the results
const testMode = process.env['TEST_MODE'] === 'true' || false; const testMode = process.env['TEST_MODE'] === 'true' || false;
if (testMode) { if (testMode) {
return true; return;
} }
const [owner, repo] = util.getRequiredEnvParam("GITHUB_REPOSITORY").split("/"); const [owner, repo] = util.getRequiredEnvParam("GITHUB_REPOSITORY").split("/");
@ -64,15 +64,14 @@ async function uploadPayload(payload): Promise<boolean> {
const statusCode = response.status; const statusCode = response.status;
if (statusCode === 202) { if (statusCode === 202) {
core.info("Successfully uploaded results"); core.info("Successfully uploaded results");
return true; return;
} }
const requestID = response.headers["x-github-request-id"]; const requestID = response.headers["x-github-request-id"];
// On any other status code that's not 5xx mark the upload as failed // On any other status code that's not 5xx mark the upload as failed
if (!statusCode || statusCode < 500 || statusCode >= 600) { if (!statusCode || statusCode < 500 || statusCode >= 600) {
core.setFailed('Upload failed (' + requestID + '): (' + statusCode + ') ' + JSON.stringify(response.data)); throw new Error('Upload failed (' + requestID + '): (' + statusCode + ') ' + JSON.stringify(response.data));
return false;
} }
// On a 5xx status code we may retry the request // On a 5xx status code we may retry the request
@ -89,25 +88,34 @@ async function uploadPayload(payload): Promise<boolean> {
// If the upload fails with 5xx then we assume it is a temporary problem // If the upload fails with 5xx then we assume it is a temporary problem
// and not an error that the user has caused or can fix. // and not an error that the user has caused or can fix.
// We avoid marking the job as failed to avoid breaking CI workflows. // We avoid marking the job as failed to avoid breaking CI workflows.
core.error('Upload failed (' + requestID + '): (' + statusCode + ') ' + JSON.stringify(response.data)); throw new Error('Upload failed (' + requestID + '): (' + statusCode + ') ' + JSON.stringify(response.data));
return false;
} }
} }
return false; // This case shouldn't ever happen as the final iteration of the loop
// will always throw an error instead of exiting to here.
throw new Error('Upload failed');
}
export interface UploadStatusReport {
// Size in bytes of unzipped SARIF upload
raw_upload_size_bytes?: number;
// Size in bytes of actual SARIF upload
zipped_upload_size_bytes?: number;
// Number of results in the SARIF upload
num_results_in_sarif?: number;
} }
// Uploads a single sarif file or a directory of sarif files // Uploads a single sarif file or a directory of sarif files
// depending on what the path happens to refer to. // depending on what the path happens to refer to.
// Returns true iff the upload occurred and succeeded // Returns true iff the upload occurred and succeeded
export async function upload(input: string): Promise<boolean> { export async function upload(input: string): Promise<UploadStatusReport> {
if (fs.lstatSync(input).isDirectory()) { if (fs.lstatSync(input).isDirectory()) {
const sarifFiles = fs.readdirSync(input) const sarifFiles = fs.readdirSync(input)
.filter(f => f.endsWith(".sarif")) .filter(f => f.endsWith(".sarif"))
.map(f => path.resolve(input, f)); .map(f => path.resolve(input, f));
if (sarifFiles.length === 0) { if (sarifFiles.length === 0) {
core.setFailed("No SARIF files found to upload in \"" + input + "\"."); throw new Error("No SARIF files found to upload in \"" + input + "\".");
return false;
} }
return await uploadFiles(sarifFiles); return await uploadFiles(sarifFiles);
} else { } else {
@ -125,50 +133,42 @@ export function countResultsInSarif(sarif: string): number {
} }
// Validates that the given file path refers to a valid SARIF file. // Validates that the given file path refers to a valid SARIF file.
// Returns a non-empty list of error message if the file is invalid, // Throws an error if the file is invalid.
// otherwise returns the empty list if the file is valid. export function validateSarifFileSchema(sarifFilePath: string) {
export function validateSarifFileSchema(sarifFilePath: string): boolean {
const sarif = JSON.parse(fs.readFileSync(sarifFilePath, 'utf8')); const sarif = JSON.parse(fs.readFileSync(sarifFilePath, 'utf8'));
const schema = JSON.parse(fs.readFileSync(__dirname + '/../src/sarif_v2.1.0_schema.json', 'utf8')); const schema = JSON.parse(fs.readFileSync(__dirname + '/../src/sarif_v2.1.0_schema.json', 'utf8'));
const result = new jsonschema.Validator().validate(sarif, schema); const result = new jsonschema.Validator().validate(sarif, schema);
if (result.valid) { if (!result.valid) {
return true; // Output the more verbose error messages in groups as these may be very large.
} else {
// Set the failure message to the stacks of all the errors.
// This should be of a manageable size and may even give enough to fix the error.
const errorMessages = result.errors.map(e => "- " + e.stack);
core.setFailed("Unable to upload \"" + sarifFilePath + "\" as it is not valid SARIF:\n" + errorMessages.join("\n"));
// Also output the more verbose error messages in groups as these may be very large.
for (const error of result.errors) { for (const error of result.errors) {
core.startGroup("Error details: " + error.stack); core.startGroup("Error details: " + error.stack);
core.info(JSON.stringify(error, null, 2)); core.info(JSON.stringify(error, null, 2));
core.endGroup(); core.endGroup();
} }
return false; // Set the main error message to the stacks of all the errors.
// This should be of a manageable size and may even give enough to fix the error.
const sarifErrors = result.errors.map(e => "- " + e.stack);
throw new Error("Unable to upload \"" + sarifFilePath + "\" as it is not valid SARIF:\n" + sarifErrors.join("\n"));
} }
} }
// Uploads the given set of sarif files. // Uploads the given set of sarif files.
// Returns true iff the upload occurred and succeeded // Returns true iff the upload occurred and succeeded
async function uploadFiles(sarifFiles: string[]): Promise<boolean> { async function uploadFiles(sarifFiles: string[]): Promise<UploadStatusReport> {
core.startGroup("Uploading results"); core.startGroup("Uploading results");
core.info("Uploading sarif files: " + JSON.stringify(sarifFiles)); core.info("Uploading sarif files: " + JSON.stringify(sarifFiles));
const sentinelEnvVar = "CODEQL_UPLOAD_SARIF"; const sentinelEnvVar = "CODEQL_UPLOAD_SARIF";
if (process.env[sentinelEnvVar]) { if (process.env[sentinelEnvVar]) {
core.error("Aborting upload: only one run of the codeql/analyze or codeql/upload-sarif actions is allowed per job"); throw new Error("Aborting upload: only one run of the codeql/analyze or codeql/upload-sarif actions is allowed per job");
return false;
} }
core.exportVariable(sentinelEnvVar, sentinelEnvVar); core.exportVariable(sentinelEnvVar, sentinelEnvVar);
// Validate that the files we were asked to upload are all valid SARIF files // Validate that the files we were asked to upload are all valid SARIF files
for (const file of sarifFiles) { for (const file of sarifFiles) {
if (!validateSarifFileSchema(file)) { validateSarifFileSchema(file);
return false;
}
} }
const commitOid = await util.getCommitOid(); const commitOid = await util.getCommitOid();
@ -176,7 +176,7 @@ async function uploadFiles(sarifFiles: string[]): Promise<boolean> {
const ref = util.getRef(); const ref = util.getRef();
const analysisKey = await util.getAnalysisKey(); const analysisKey = await util.getAnalysisKey();
const analysisName = util.getRequiredEnvParam('GITHUB_WORKFLOW'); const analysisName = util.getRequiredEnvParam('GITHUB_WORKFLOW');
const startedAt = process.env[sharedEnv.CODEQL_ACTION_STARTED_AT]; const startedAt = process.env[sharedEnv.CODEQL_WORKFLOW_STARTED_AT];
let sarifPayload = combineSarifFiles(sarifFiles); let sarifPayload = combineSarifFiles(sarifFiles);
sarifPayload = fingerprints.addFingerprints(sarifPayload); sarifPayload = fingerprints.addFingerprints(sarifPayload);
@ -187,8 +187,7 @@ async function uploadFiles(sarifFiles: string[]): Promise<boolean> {
const workflowRunID = parseInt(workflowRunIDStr, 10); const workflowRunID = parseInt(workflowRunIDStr, 10);
if (Number.isNaN(workflowRunID)) { if (Number.isNaN(workflowRunID)) {
core.setFailed('GITHUB_RUN_ID must define a non NaN workflow run ID'); throw new Error('GITHUB_RUN_ID must define a non NaN workflow run ID');
return false;
} }
let matrix: string | undefined = core.getInput('matrix'); let matrix: string | undefined = core.getInput('matrix');
@ -212,14 +211,21 @@ async function uploadFiles(sarifFiles: string[]): Promise<boolean> {
}); });
// Log some useful debug info about the info // Log some useful debug info about the info
core.debug("Raw upload size: " + sarifPayload.length + " bytes"); const rawUploadSizeBytes = sarifPayload.length;
core.debug("Base64 zipped upload size: " + zipped_sarif.length + " bytes"); core.debug("Raw upload size: " + rawUploadSizeBytes + " bytes");
core.debug("Number of results in upload: " + countResultsInSarif(sarifPayload)); const zippedUploadSizeBytes = zipped_sarif.length;
core.debug("Base64 zipped upload size: " + zippedUploadSizeBytes + " bytes");
const numResultInSarif = countResultsInSarif(sarifPayload);
core.debug("Number of results in upload: " + numResultInSarif);
// Make the upload // Make the upload
const succeeded = await uploadPayload(payload); await uploadPayload(payload);
core.endGroup(); core.endGroup();
return succeeded; return {
raw_upload_size_bytes: rawUploadSizeBytes,
zipped_upload_size_bytes: zippedUploadSizeBytes,
num_results_in_sarif: numResultInSarif,
};
} }

View file

@ -3,20 +3,36 @@ import * as core from '@actions/core';
import * as upload_lib from './upload-lib'; import * as upload_lib from './upload-lib';
import * as util from './util'; import * as util from './util';
interface UploadSarifStatusReport extends util.StatusReportBase, upload_lib.UploadStatusReport {}
async function sendSuccessStatusReport(startedAt: Date, uploadStats: upload_lib.UploadStatusReport) {
const statusReportBase = await util.createStatusReportBase('upload-sarif', 'success', startedAt);
const statusReport: UploadSarifStatusReport = {
...statusReportBase,
... uploadStats,
};
await util.sendStatusReport(statusReport);
}
async function run() { async function run() {
if (util.should_abort('upload-sarif', false) || !await util.reportActionStarting('upload-sarif')) { const startedAt = new Date();
if (util.should_abort('upload-sarif', false) ||
!await util.sendStatusReport(await util.createStatusReportBase('upload-sarif', 'starting', startedAt), true)) {
return; return;
} }
try { try {
if (await upload_lib.upload(core.getInput('sarif_file'))) { const uploadStats = await upload_lib.upload(core.getInput('sarif_file'));
await util.reportActionSucceeded('upload-sarif'); await sendSuccessStatusReport(startedAt, uploadStats);
} else {
await util.reportActionFailed('upload-sarif', 'upload');
}
} catch (error) { } catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
await util.reportActionFailed('upload-sarif', error.message, error.stack); await util.sendStatusReport(await util.createStatusReportBase(
'upload-sarif',
'failure',
startedAt,
error.message,
error.stack));
return; return;
} }
} }

View file

@ -5,7 +5,6 @@ import * as os from 'os';
import * as path from 'path'; import * as path from 'path';
import * as api from './api-client'; import * as api from './api-client';
import * as configUtils from './config-utils';
import * as sharedEnv from './shared-environment'; import * as sharedEnv from './shared-environment';
/** /**
@ -138,21 +137,36 @@ export function getRef(): string {
type ActionName = 'init' | 'autobuild' | 'finish' | 'upload-sarif'; type ActionName = 'init' | 'autobuild' | 'finish' | 'upload-sarif';
type ActionStatus = 'starting' | 'aborted' | 'success' | 'failure'; type ActionStatus = 'starting' | 'aborted' | 'success' | 'failure';
interface StatusReport { export interface StatusReportBase {
// ID of the workflow run containing the action run
"workflow_run_id": number; "workflow_run_id": number;
// Workflow name. Converted to analysis_name further down the pipeline.
"workflow_name": string; "workflow_name": string;
// Job name from the workflow
"job_name": string; "job_name": string;
// Analysis key, normally composed from the workflow path and job name
"analysis_key": string; "analysis_key": string;
// Value of the matrix for this instantiation of the job
"matrix_vars"?: string; "matrix_vars"?: string;
"languages": string; // Commit oid that the workflow was triggered on
"commit_oid": string; "commit_oid": string;
// Ref that the workflow was triggered on
"ref": string; "ref": string;
// Name of the action being executed
"action_name": ActionName; "action_name": ActionName;
// Version if the action being executed, as a commit oid
"action_oid": string; "action_oid": string;
// Time the first action started. Normally the init action
"started_at": string; "started_at": string;
// Time this action started
"action_started_at": string;
// Time this action completed, or undefined if not yet completed
"completed_at"?: string; "completed_at"?: string;
// State this action is currently in
"status": ActionStatus; "status": ActionStatus;
// Cause of the failure (or undefined if status is not failure)
"cause"?: string; "cause"?: string;
// Stack trace of the failure (or undefined if status is not failure)
"exception"?: string; "exception"?: string;
} }
@ -161,26 +175,17 @@ interface StatusReport {
* *
* @param actionName The name of the action, e.g. 'init', 'finish', 'upload-sarif' * @param actionName The name of the action, e.g. 'init', 'finish', 'upload-sarif'
* @param status The status. Must be 'success', 'failure', or 'starting' * @param status The status. Must be 'success', 'failure', or 'starting'
* @param startedAt The time this action started executing.
* @param cause Cause of failure (only supply if status is 'failure') * @param cause Cause of failure (only supply if status is 'failure')
* @param exception Exception (only supply if status is 'failure') * @param exception Exception (only supply if status is 'failure')
*/ */
async function createStatusReport( export async function createStatusReportBase(
actionName: ActionName, actionName: ActionName,
status: ActionStatus, status: ActionStatus,
actionStartedAt: Date,
cause?: string, cause?: string,
exception?: string): exception?: string):
Promise<StatusReport> { Promise<StatusReportBase> {
// If this is not the init action starting up or aborting then try to load the config.
// If it fails then carry because it's important to still send the status report.
let config: configUtils.Config | undefined = undefined;
if (actionName !== 'init' || (status !== 'starting' && status !== 'aborted')) {
try {
config = await configUtils.getConfig();
} catch (e) {
core.error('Unable to load config: ' + e);
}
}
const commitOid = process.env['GITHUB_SHA'] || ''; const commitOid = process.env['GITHUB_SHA'] || '';
const ref = getRef(); const ref = getRef();
@ -192,21 +197,23 @@ async function createStatusReport(
const workflowName = process.env['GITHUB_WORKFLOW'] || ''; const workflowName = process.env['GITHUB_WORKFLOW'] || '';
const jobName = process.env['GITHUB_JOB'] || ''; const jobName = process.env['GITHUB_JOB'] || '';
const analysis_key = await getAnalysisKey(); const analysis_key = await getAnalysisKey();
const languages = config?.languages?.join(',') || ""; let workflowStartedAt = process.env[sharedEnv.CODEQL_WORKFLOW_STARTED_AT];
const startedAt = process.env[sharedEnv.CODEQL_ACTION_STARTED_AT] || new Date().toISOString(); if (workflowStartedAt === undefined) {
core.exportVariable(sharedEnv.CODEQL_ACTION_STARTED_AT, startedAt); workflowStartedAt = actionStartedAt.toISOString();
core.exportVariable(sharedEnv.CODEQL_WORKFLOW_STARTED_AT, workflowStartedAt);
}
let statusReport: StatusReport = { let statusReport: StatusReportBase = {
workflow_run_id: workflowRunID, workflow_run_id: workflowRunID,
workflow_name: workflowName, workflow_name: workflowName,
job_name: jobName, job_name: jobName,
analysis_key: analysis_key, analysis_key: analysis_key,
languages: languages,
commit_oid: commitOid, commit_oid: commitOid,
ref: ref, ref: ref,
action_name: actionName, action_name: actionName,
action_oid: "unknown", // TODO decide if it's possible to fill this in action_oid: "unknown", // TODO decide if it's possible to fill this in
started_at: startedAt, started_at: workflowStartedAt,
action_started_at: actionStartedAt.toISOString(),
status: status status: status
}; };
@ -231,9 +238,16 @@ async function createStatusReport(
/** /**
* Send a status report to the code_scanning/analysis/status endpoint. * Send a status report to the code_scanning/analysis/status endpoint.
* *
* Returns the status code of the response to the status request. * Optionally checks the response from the API endpoint and sets the action
* as failed if the status report failed. This is only expected to be used
* when sending a 'starting' report.
*
* Returns whether sending the status report was successful of not.
*/ */
async function sendStatusReport(statusReport: StatusReport): Promise<number> { export async function sendStatusReport<S extends StatusReportBase>(
statusReport: S,
ignoreFailures?: boolean): Promise<boolean> {
const statusReportJSON = JSON.stringify(statusReport); const statusReportJSON = JSON.stringify(statusReport);
core.debug('Sending status report: ' + statusReportJSON); core.debug('Sending status report: ' + statusReportJSON);
@ -245,68 +259,27 @@ async function sendStatusReport(statusReport: StatusReport): Promise<number> {
repo: repo, repo: repo,
data: statusReportJSON, data: statusReportJSON,
}); });
return statusResponse.status;
}
/** if (!ignoreFailures) {
* Send a status report that an action is starting. // If the status report request fails with a 403 or a 404, then this is a deliberate
* // message from the endpoint that the SARIF upload can be expected to fail too,
* If the action is `init` then this also records the start time in the environment, // so the action should fail to avoid wasting actions minutes.
* and ensures that the analysed languages are also recorded in the envirenment. //
* // Other failure responses (or lack thereof) could be transitory and should not
* Returns true unless a problem occurred and the action should abort. // cause the action to fail.
*/ if (statusResponse.status === 403) {
export async function reportActionStarting(action: ActionName): Promise<boolean> { core.setFailed('The repo on which this action is running is not opted-in to CodeQL code scanning.');
const statusCode = await sendStatusReport(await createStatusReport(action, 'starting')); return false;
}
// If the status report request fails with a 403 or a 404, then this is a deliberate if (statusResponse.status === 404) {
// message from the endpoint that the SARIF upload can be expected to fail too, core.setFailed('Not authorized to used the CodeQL code scanning feature on this repo.');
// so the action should fail to avoid wasting actions minutes. return false;
// }
// Other failure responses (or lack thereof) could be transitory and should not
// cause the action to fail.
if (statusCode === 403) {
core.setFailed('The repo on which this action is running is not opted-in to CodeQL code scanning.');
return false;
}
if (statusCode === 404) {
core.setFailed('Not authorized to used the CodeQL code scanning feature on this repo.');
return false;
} }
return true; return true;
} }
/**
* Report that an action has failed.
*
* Note that the started_at date is always that of the `init` action, since
* this is likely to give a more useful duration when inspecting events.
*/
export async function reportActionFailed(action: ActionName, cause?: string, exception?: string) {
await sendStatusReport(await createStatusReport(action, 'failure', cause, exception));
}
/**
* Report that an action has succeeded.
*
* Note that the started_at date is always that of the `init` action, since
* this is likely to give a more useful duration when inspecting events.
*/
export async function reportActionSucceeded(action: ActionName) {
await sendStatusReport(await createStatusReport(action, 'success'));
}
/**
* Report that an action has been aborted.
*
* Note that the started_at date is always that of the `init` action, since
* this is likely to give a more useful duration when inspecting events.
*/
export async function reportActionAborted(action: ActionName, cause?: string) {
await sendStatusReport(await createStatusReport(action, 'aborted', cause));
}
/** /**
* Get the array of all the tool names contained in the given sarif contents. * Get the array of all the tool names contained in the given sarif contents.
* *