Move debug artifact methods into separate file

This commit is contained in:
Angela P Wen
2022-08-02 12:27:52 +02:00
parent 5895ab0c0b
commit eeee462f05
18 changed files with 434 additions and 359 deletions

View File

@@ -753,15 +753,3 @@ test("isAnalyzingDefaultBranch()", async (t) => {
t.deepEqual(await actionsutil.isAnalyzingDefaultBranch(), false);
});
});
test("sanitizeArifactName", (t) => {
t.deepEqual(actionsutil.sanitizeArifactName("hello-world_"), "hello-world_");
t.deepEqual(actionsutil.sanitizeArifactName("hello`world`"), "helloworld");
t.deepEqual(actionsutil.sanitizeArifactName("hello===123"), "hello123");
t.deepEqual(
actionsutil.sanitizeArifactName("*m)a&n^y%i££n+v!a:l[i]d"),
"manyinvalid"
);
});
// TODO(angelapwen): Test uploadDebugArtifacts if toUpload is empty

View File

@@ -2,35 +2,22 @@ import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import * as artifact from "@actions/artifact";
import * as core from "@actions/core";
import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as safeWhich from "@chrisgavin/safe-which";
import AdmZip from "adm-zip";
import del from "del";
import * as yaml from "js-yaml";
import { dbIsFinalized } from "./analyze";
import * as api from "./api-client";
import { CODEQL_VERSION_NEW_TRACING, getCodeQL } from "./codeql";
import { Config } from "./config-utils";
import { Logger } from "./logging";
import * as sharedEnv from "./shared-environment";
import {
bundleDb,
codeQlVersionAbove,
doesDirectoryExist,
getCachedCodeQlVersion,
getCodeQLDatabasePath,
getRequiredEnvParam,
GITHUB_DOTCOM_URL,
isGitHubGhesVersionBelow,
isHTTPError,
isInTestMode,
listFolder,
UserError,
} from "./util";
import { Language } from "./languages";
// eslint-disable-next-line import/no-commonjs
const pkg = require("../package.json");
@@ -883,184 +870,4 @@ export async function isAnalyzingDefaultBranch(): Promise<boolean> {
const defaultBranch = event?.repository?.default_branch;
return currentRef === defaultBranch;
}
export function sanitizeArifactName(name: string): string {
return name.replace(/[^a-zA-Z0-9_\\-]+/g, "");
}
export async function uploadDebugArtifacts(
toUpload: string[],
rootDir: string,
artifactName: string
) {
if (toUpload.length === 0) {
return;
}
let suffix = "";
const matrix = getRequiredInput("matrix");
if (matrix) {
try {
for (const [, matrixVal] of Object.entries(JSON.parse(matrix)).sort())
suffix += `-${matrixVal}`;
} catch (e) {
core.info(
"Could not parse user-specified `matrix` input into JSON. The debug artifact will not be named with the user's `matrix` input."
);
}
}
await artifact.create().uploadArtifact(
sanitizeArifactName(`${artifactName}${suffix}`),
toUpload.map((file) => path.normalize(file)),
path.normalize(rootDir)
);
}
export async function uploadSarifDebugArtifact(
config: Config,
outputDir: string
) {
if (!doesDirectoryExist(outputDir)) {
return;
}
let toUpload: string[] = [];
for (const lang of config.languages) {
const sarifFile = path.resolve(outputDir, `${lang}.sarif`);
if (fs.existsSync(sarifFile)) {
toUpload = toUpload.concat(sarifFile);
}
}
await uploadDebugArtifacts(toUpload, outputDir, config.debugArtifactName);
}
export async function uploadFinalLogsDebugArtifact(config: Config) {
core.info("Debug mode is on. Printing CodeQL debug logs...");
for (const language of config.languages) {
const databaseDirectory = getCodeQLDatabasePath(config, language);
const logsDirectory = path.join(databaseDirectory, "log");
if (!doesDirectoryExist(logsDirectory)) {
core.info(`Directory ${logsDirectory} does not exist.`);
continue; // Skip this language database.
}
const walkLogFiles = (dir: string) => {
const entries = fs.readdirSync(dir, { withFileTypes: true });
if (entries.length === 0) {
core.info(`No debug logs found at directory ${logsDirectory}.`);
}
for (const entry of entries) {
if (entry.isFile()) {
core.startGroup(`CodeQL Debug Logs - ${language} - ${entry.name}`);
process.stdout.write(fs.readFileSync(path.resolve(dir, entry.name)));
core.endGroup();
} else if (entry.isDirectory()) {
walkLogFiles(path.resolve(dir, entry.name));
}
}
};
walkLogFiles(logsDirectory);
}
}
export async function uploadLogsDebugArtifact(config: Config) {
const codeql = await getCodeQL(config.codeQLCmd);
let toUpload: string[] = [];
for (const language of config.languages) {
const databaseDirectory = getCodeQLDatabasePath(config, language);
const logsDirectory = path.resolve(databaseDirectory, "log");
if (doesDirectoryExist(logsDirectory)) {
toUpload = toUpload.concat(listFolder(logsDirectory));
}
}
if (await codeQlVersionAbove(codeql, CODEQL_VERSION_NEW_TRACING)) {
// Multilanguage tracing: there are additional logs in the root of the cluster
const multiLanguageTracingLogsDirectory = path.resolve(
config.dbLocation,
"log"
);
if (doesDirectoryExist(multiLanguageTracingLogsDirectory)) {
toUpload = toUpload.concat(listFolder(multiLanguageTracingLogsDirectory));
}
}
await uploadDebugArtifacts(
toUpload,
config.dbLocation,
config.debugArtifactName
);
// Before multi-language tracing, we wrote a compound-build-tracer.log in the temp dir
if (!(await codeQlVersionAbove(codeql, CODEQL_VERSION_NEW_TRACING))) {
const compoundBuildTracerLogDirectory = path.resolve(
config.tempDir,
"compound-build-tracer.log"
);
if (doesDirectoryExist(compoundBuildTracerLogDirectory)) {
await uploadDebugArtifacts(
[compoundBuildTracerLogDirectory],
config.tempDir,
config.debugArtifactName
);
}
}
}
/**
* If a database has not been finalized, we cannot run the `codeql database bundle`
* command in the CLI because it will return an error. Instead we directly zip
* all files in the database folder and upload it as an artifact.
*/
async function uploadPartialDatabaseBundle(config: Config, language: Language) {
const databasePath = getCodeQLDatabasePath(config, language);
const databaseBundlePath = path.resolve(
config.dbLocation,
`${config.debugDatabaseName}-${language}-partial.zip`
);
core.info(
`${config.debugDatabaseName}-${language} is not finalized. Uploading partial database bundle at ${databaseBundlePath}...`
);
// See `bundleDb` for explanation behind deleting existing db bundle.
if (fs.existsSync(databaseBundlePath)) {
await del(databaseBundlePath, { force: true });
}
const zip = new AdmZip();
zip.addLocalFolder(databasePath);
zip.writeZip(databaseBundlePath);
await uploadDebugArtifacts(
[databaseBundlePath],
config.dbLocation,
config.debugArtifactName
);
}
export async function uploadDatabaseBundleDebugArtifact(
config: Config,
logger: Logger
) {
for (const language of config.languages) {
if (!dbIsFinalized(config, language, logger)) {
await uploadPartialDatabaseBundle(config, language);
continue;
}
try {
// Otherwise run `codeql database bundle` command.
const bundlePath = await bundleDb(
config,
language,
await getCodeQL(config.codeQLCmd),
`${config.debugDatabaseName}-${language}`
);
await uploadDebugArtifacts(
[bundlePath],
config.dbLocation,
config.debugArtifactName
);
} catch (error) {
core.info(
`Failed to upload database debug bundles for ${config.debugDatabaseName}-${language}: ${error}`
);
}
}
}
}

View File

@@ -7,6 +7,7 @@ import * as core from "@actions/core";
import * as actionsUtil from "./actions-util";
import { getConfig } from "./config-utils";
import * as debugArtifacts from "./debug-artifacts";
import { getActionsLogger } from "./logging";
async function run(uploadSarifDebugArtifact: Function) {
@@ -28,7 +29,7 @@ async function run(uploadSarifDebugArtifact: Function) {
async function runWrapper() {
try {
await run(actionsUtil.uploadSarifDebugArtifact);
await run(debugArtifacts.uploadSarifDebugArtifact);
} catch (error) {
core.setFailed(`analyze action cleanup failed: ${error}`);
console.log(error);

View File

@@ -0,0 +1,18 @@
import test from "ava";
import * as debugArtifacts from "./debug-artifacts";
test("sanitizeArifactName", (t) => {
t.deepEqual(
debugArtifacts.sanitizeArifactName("hello-world_"),
"hello-world_"
);
t.deepEqual(debugArtifacts.sanitizeArifactName("hello`world`"), "helloworld");
t.deepEqual(debugArtifacts.sanitizeArifactName("hello===123"), "hello123");
t.deepEqual(
debugArtifacts.sanitizeArifactName("*m)a&n^y%i££n+v!a:l[i]d"),
"manyinvalid"
);
});
// TODO(angelapwen): Test uploadDebugArtifacts if toUpload is empty

201
src/debug-artifacts.ts Normal file
View File

@@ -0,0 +1,201 @@
import * as fs from "fs";
import * as path from "path";
import * as artifact from "@actions/artifact";
import * as core from "@actions/core";
import AdmZip from "adm-zip";
import del from "del";
import { getRequiredInput } from "./actions-util";
import { dbIsFinalized } from "./analyze";
import { CODEQL_VERSION_NEW_TRACING, getCodeQL } from "./codeql";
import { Config } from "./config-utils";
import { Language } from "./languages";
import { Logger } from "./logging";
import {
bundleDb,
codeQlVersionAbove,
doesDirectoryExist,
getCodeQLDatabasePath,
listFolder,
} from "./util";
export function sanitizeArifactName(name: string): string {
return name.replace(/[^a-zA-Z0-9_\\-]+/g, "");
}
export async function uploadDebugArtifacts(
toUpload: string[],
rootDir: string,
artifactName: string
) {
if (toUpload.length === 0) {
return;
}
let suffix = "";
const matrix = getRequiredInput("matrix");
if (matrix) {
try {
for (const [, matrixVal] of Object.entries(JSON.parse(matrix)).sort())
suffix += `-${matrixVal}`;
} catch (e) {
core.info(
"Could not parse user-specified `matrix` input into JSON. The debug artifact will not be named with the user's `matrix` input."
);
}
}
await artifact.create().uploadArtifact(
sanitizeArifactName(`${artifactName}${suffix}`),
toUpload.map((file) => path.normalize(file)),
path.normalize(rootDir)
);
}
export async function uploadSarifDebugArtifact(
config: Config,
outputDir: string
) {
if (!doesDirectoryExist(outputDir)) {
return;
}
let toUpload: string[] = [];
for (const lang of config.languages) {
const sarifFile = path.resolve(outputDir, `${lang}.sarif`);
if (fs.existsSync(sarifFile)) {
toUpload = toUpload.concat(sarifFile);
}
}
await uploadDebugArtifacts(toUpload, outputDir, config.debugArtifactName);
}
export async function uploadFinalLogsDebugArtifact(config: Config) {
core.info("Debug mode is on. Printing CodeQL debug logs...");
for (const language of config.languages) {
const databaseDirectory = getCodeQLDatabasePath(config, language);
const logsDirectory = path.join(databaseDirectory, "log");
if (!doesDirectoryExist(logsDirectory)) {
core.info(`Directory ${logsDirectory} does not exist.`);
continue; // Skip this language database.
}
const walkLogFiles = (dir: string) => {
const entries = fs.readdirSync(dir, { withFileTypes: true });
if (entries.length === 0) {
core.info(`No debug logs found at directory ${logsDirectory}.`);
}
for (const entry of entries) {
if (entry.isFile()) {
core.startGroup(`CodeQL Debug Logs - ${language} - ${entry.name}`);
process.stdout.write(fs.readFileSync(path.resolve(dir, entry.name)));
core.endGroup();
} else if (entry.isDirectory()) {
walkLogFiles(path.resolve(dir, entry.name));
}
}
};
walkLogFiles(logsDirectory);
}
}
export async function uploadLogsDebugArtifact(config: Config) {
const codeql = await getCodeQL(config.codeQLCmd);
let toUpload: string[] = [];
for (const language of config.languages) {
const databaseDirectory = getCodeQLDatabasePath(config, language);
const logsDirectory = path.resolve(databaseDirectory, "log");
if (doesDirectoryExist(logsDirectory)) {
toUpload = toUpload.concat(listFolder(logsDirectory));
}
}
if (await codeQlVersionAbove(codeql, CODEQL_VERSION_NEW_TRACING)) {
// Multilanguage tracing: there are additional logs in the root of the cluster
const multiLanguageTracingLogsDirectory = path.resolve(
config.dbLocation,
"log"
);
if (doesDirectoryExist(multiLanguageTracingLogsDirectory)) {
toUpload = toUpload.concat(listFolder(multiLanguageTracingLogsDirectory));
}
}
await uploadDebugArtifacts(
toUpload,
config.dbLocation,
config.debugArtifactName
);
// Before multi-language tracing, we wrote a compound-build-tracer.log in the temp dir
if (!(await codeQlVersionAbove(codeql, CODEQL_VERSION_NEW_TRACING))) {
const compoundBuildTracerLogDirectory = path.resolve(
config.tempDir,
"compound-build-tracer.log"
);
if (doesDirectoryExist(compoundBuildTracerLogDirectory)) {
await uploadDebugArtifacts(
[compoundBuildTracerLogDirectory],
config.tempDir,
config.debugArtifactName
);
}
}
}
/**
* If a database has not been finalized, we cannot run the `codeql database bundle`
* command in the CLI because it will return an error. Instead we directly zip
* all files in the database folder and upload it as an artifact.
*/
async function uploadPartialDatabaseBundle(config: Config, language: Language) {
const databasePath = getCodeQLDatabasePath(config, language);
const databaseBundlePath = path.resolve(
config.dbLocation,
`${config.debugDatabaseName}-${language}-partial.zip`
);
core.info(
`${config.debugDatabaseName}-${language} is not finalized. Uploading partial database bundle at ${databaseBundlePath}...`
);
// See `bundleDb` for explanation behind deleting existing db bundle.
if (fs.existsSync(databaseBundlePath)) {
await del(databaseBundlePath, { force: true });
}
const zip = new AdmZip();
zip.addLocalFolder(databasePath);
zip.writeZip(databaseBundlePath);
await uploadDebugArtifacts(
[databaseBundlePath],
config.dbLocation,
config.debugArtifactName
);
}
export async function uploadDatabaseBundleDebugArtifact(
config: Config,
logger: Logger
) {
for (const language of config.languages) {
if (!dbIsFinalized(config, language, logger)) {
await uploadPartialDatabaseBundle(config, language);
continue;
}
try {
// Otherwise run `codeql database bundle` command.
const bundlePath = await bundleDb(
config,
language,
await getCodeQL(config.codeQLCmd),
`${config.debugDatabaseName}-${language}`
);
await uploadDebugArtifacts(
[bundlePath],
config.dbLocation,
config.debugArtifactName
);
} catch (error) {
core.info(
`Failed to upload database debug bundles for ${config.debugDatabaseName}-${language}: ${error}`
);
}
}
}

View File

@@ -8,6 +8,7 @@ import * as core from "@actions/core";
import * as actionsUtil from "./actions-util";
import { getConfig } from "./config-utils";
import * as debugArtifacts from "./debug-artifacts";
import { getActionsLogger } from "./logging";
async function run(
@@ -35,9 +36,9 @@ async function run(
async function runWrapper() {
try {
await run(
actionsUtil.uploadDatabaseBundleDebugArtifact,
actionsUtil.uploadLogsDebugArtifact,
actionsUtil.uploadFinalLogsDebugArtifact
debugArtifacts.uploadDatabaseBundleDebugArtifact,
debugArtifacts.uploadLogsDebugArtifact,
debugArtifacts.uploadFinalLogsDebugArtifact
);
} catch (error) {
core.setFailed(`init action cleanup failed: ${error}`);