Skip to content

Commit

Permalink
Use github merge-results command when feature flag is enabled
Browse files Browse the repository at this point in the history
  • Loading branch information
Koen Vlaswinkel committed Mar 22, 2024
1 parent a12b868 commit ccc609b
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 5 deletions.
17 changes: 17 additions & 0 deletions lib/codeql.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/codeql.js.map

Large diffs are not rendered by default.

59 changes: 58 additions & 1 deletion lib/upload-lib.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/upload-lib.js.map

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions src/codeql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ interface ExtraOptions {
extractor?: Options;
queries?: Options;
};
github?: {
"*"?: Options;
"merge-results"?: Options;
};
}

export interface CodeQL {
Expand Down Expand Up @@ -191,6 +195,14 @@ export interface CodeQL {
): Promise<void>;
/** Get the location of an extractor for the specified language. */
resolveExtractor(language: Language): Promise<string>;
/**
* Run 'codeql github merge-results'.
*/
mergeResults(
sarifFiles: string[],
outputFile: string,
mergeRunsFromEqualCategory?: boolean,
): Promise<void>;
}

export interface VersionInfo {
Expand Down Expand Up @@ -489,6 +501,7 @@ export function setCodeQL(partialCodeql: Partial<CodeQL>): CodeQL {
),
diagnosticsExport: resolveFunction(partialCodeql, "diagnosticsExport"),
resolveExtractor: resolveFunction(partialCodeql, "resolveExtractor"),
mergeResults: resolveFunction(partialCodeql, "mergeResults"),
};
return cachedCodeQL;
}
Expand Down Expand Up @@ -1077,6 +1090,29 @@ export async function getCodeQLForCmd(
).exec();
return JSON.parse(extractorPath);
},
async mergeResults(
sarifFiles: string[],
outputFile: string,
mergeRunsFromEqualCategory = false,
): Promise<void> {
const args = [
"github",
"merge-results",
"--output",
outputFile,
...getExtraOptionsFromEnv(["github", "merge-results"]),
];

for (const sarifFile of sarifFiles) {
args.push("--sarif", sarifFile);
}

if (mergeRunsFromEqualCategory) {
args.push("--sarif-merge-runs-from-equal-category");
}

await new toolrunner.ToolRunner(cmd, args).exec();
},
};
// To ensure that status reports include the CodeQL CLI version wherever
// possible, we want to call getVersion(), which populates the version value
Expand Down
117 changes: 115 additions & 2 deletions src/upload-lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,29 @@ import fileUrl from "file-url";
import * as jsonschema from "jsonschema";

import * as actionsUtil from "./actions-util";
import {
getOptionalInput,
getRequiredInput,
getTemporaryDirectory,
} from "./actions-util";
import * as api from "./api-client";
import { getGitHubVersion } from "./api-client";
import { CodeQL, getCodeQL } from "./codeql";
import { getConfig } from "./config-utils";
import { EnvVar } from "./environment";
import { Feature, Features } from "./feature-flags";
import * as fingerprints from "./fingerprints";
import { initCodeQL } from "./init";
import { Logger } from "./logging";
import { parseRepositoryNwo, RepositoryNwo } from "./repository";
import * as util from "./util";
import { SarifFile, ConfigurationError, wrapError } from "./util";
import {
SarifFile,
ConfigurationError,
wrapError,
getRequiredEnvParam,
GitHubVersion,
} from "./util";

const GENERIC_403_MSG =
"The repo on which this action is running has not opted-in to CodeQL code scanning.";
Expand Down Expand Up @@ -48,6 +64,88 @@ function combineSarifFiles(sarifFiles: string[]): SarifFile {
return combinedSarif;
}

// Takes a list of paths to sarif files and combines them together using the
// CLI `github merge-results` command when all SARIF files are produced by
// CodeQL. Otherwise, it will fall back to combining the files in the action.
// Returns the contents of the combined sarif file.
async function combineSarifFilesUsingCLI(
sarifFiles: string[],
gitHubVersion: GitHubVersion,
features: Features,
logger: Logger,
): Promise<SarifFile> {
// First check if all files are produced by CodeQL.
let allCodeQL = true;

for (const sarifFile of sarifFiles) {
const sarifObject = JSON.parse(
fs.readFileSync(sarifFile, "utf8"),
) as SarifFile;

const allRunsCodeQL = sarifObject.runs?.every(
(run) => run.tool?.driver?.name === "CodeQL",
);

if (!allRunsCodeQL) {
allCodeQL = false;
break;
}
}

if (!allCodeQL) {
logger.warning(
"Not all SARIF files were produced by CodeQL. Merging files in the action.",
);

// If not, use the naive method of combining the files.
return combineSarifFiles(sarifFiles);
}

// Initialize CodeQL, either by using the config file from the 'init' step,
// or by initializing it here.
let codeQL: CodeQL;
let tempDir: string;

const config = await getConfig(actionsUtil.getTemporaryDirectory(), logger);
if (config !== undefined) {
codeQL = await getCodeQL(config.codeQLCmd);
tempDir = config.tempDir;
} else {
logger.warning(
"Initializing CodeQL since the 'init' Action was not called before this step.",
);

const apiDetails = {
auth: getRequiredInput("token"),
externalRepoAuth: getOptionalInput("external-repository-token"),
url: getRequiredEnvParam("GITHUB_SERVER_URL"),
apiURL: getRequiredEnvParam("GITHUB_API_URL"),
};

const codeQLDefaultVersionInfo = await features.getDefaultCliVersion(
gitHubVersion.type,
);

const initCodeQLResult = await initCodeQL(
undefined, // There is no tools input on the upload action
apiDetails,
getTemporaryDirectory(),
gitHubVersion.type,
codeQLDefaultVersionInfo,
logger,
);

codeQL = initCodeQLResult.codeql;
tempDir = getTemporaryDirectory();
}

const outputFile = path.resolve(tempDir, "combined-sarif.sarif");

await codeQL.mergeResults(sarifFiles, outputFile, true);

return JSON.parse(fs.readFileSync(outputFile, "utf8")) as SarifFile;
}

// Populates the run.automationDetails.id field using the analysis_key and environment
// and return an updated sarif file contents.
export function populateRunAutomationDetails(
Expand Down Expand Up @@ -363,12 +461,27 @@ async function uploadFiles(
logger.startGroup("Uploading results");
logger.info(`Processing sarif files: ${JSON.stringify(sarifFiles)}`);

const gitHubVersion = await getGitHubVersion();
const features = new Features(
gitHubVersion,
repositoryNwo,
actionsUtil.getTemporaryDirectory(),
logger,
);

// Validate that the files we were asked to upload are all valid SARIF files
for (const file of sarifFiles) {
validateSarifFileSchema(file, logger);
}

let sarif = combineSarifFiles(sarifFiles);
let sarif = (await features.getValue(Feature.CliSarifMerge))
? await combineSarifFilesUsingCLI(
sarifFiles,
gitHubVersion,
features,
logger,
)
: combineSarifFiles(sarifFiles);
sarif = await fingerprints.addFingerprints(sarif, sourceRoot, logger);

sarif = populateRunAutomationDetails(
Expand Down

0 comments on commit ccc609b

Please sign in to comment.