mirror of
https://github.com/github/codeql-action.git
synced 2025-12-27 01:30:10 +08:00
181 lines
5.1 KiB
TypeScript
181 lines
5.1 KiB
TypeScript
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 { getCodeQL } from "./codeql";
|
|
import { Config } from "./config-utils";
|
|
import { Language } from "./languages";
|
|
import { Logger } from "./logging";
|
|
import {
|
|
bundleDb,
|
|
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) as any[][],
|
|
).sort())
|
|
suffix += `-${matrixVal}`;
|
|
} catch {
|
|
core.info(
|
|
"Could not parse user-specified `matrix` input into JSON. The debug artifact will not be named with the user's `matrix` input.",
|
|
);
|
|
}
|
|
}
|
|
|
|
try {
|
|
await artifact.create().uploadArtifact(
|
|
sanitizeArifactName(`${artifactName}${suffix}`),
|
|
toUpload.map((file) => path.normalize(file)),
|
|
path.normalize(rootDir),
|
|
{
|
|
continueOnError: true,
|
|
// ensure we don't keep the debug artifacts around for too long since they can be large.
|
|
retentionDays: 7,
|
|
},
|
|
);
|
|
} catch (e) {
|
|
// A failure to upload debug artifacts should not fail the entire action.
|
|
core.warning(`Failed to upload debug artifacts: ${e}`);
|
|
}
|
|
}
|
|
|
|
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 uploadLogsDebugArtifact(config: Config) {
|
|
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));
|
|
}
|
|
}
|
|
|
|
// 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,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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 return the path.
|
|
*/
|
|
async function createPartialDatabaseBundle(
|
|
config: Config,
|
|
language: Language,
|
|
): Promise<string> {
|
|
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);
|
|
return databaseBundlePath;
|
|
}
|
|
|
|
/**
|
|
* Runs `codeql database bundle` command and returns the path.
|
|
*/
|
|
async function createDatabaseBundleCli(
|
|
config: Config,
|
|
language: Language,
|
|
): Promise<string> {
|
|
// Otherwise run `codeql database bundle` command.
|
|
const databaseBundlePath = await bundleDb(
|
|
config,
|
|
language,
|
|
await getCodeQL(config.codeQLCmd),
|
|
`${config.debugDatabaseName}-${language}`,
|
|
);
|
|
return databaseBundlePath;
|
|
}
|
|
|
|
export async function uploadDatabaseBundleDebugArtifact(
|
|
config: Config,
|
|
logger: Logger,
|
|
) {
|
|
for (const language of config.languages) {
|
|
try {
|
|
let databaseBundlePath: string;
|
|
if (!dbIsFinalized(config, language, logger)) {
|
|
databaseBundlePath = await createPartialDatabaseBundle(
|
|
config,
|
|
language,
|
|
);
|
|
} else {
|
|
databaseBundlePath = await createDatabaseBundleCli(config, language);
|
|
}
|
|
await uploadDebugArtifacts(
|
|
[databaseBundlePath],
|
|
config.dbLocation,
|
|
config.debugArtifactName,
|
|
);
|
|
} catch (error) {
|
|
core.info(
|
|
`Failed to upload database debug bundle for ${config.debugDatabaseName}-${language}: ${error}`,
|
|
);
|
|
}
|
|
}
|
|
}
|