Skip to content

Commit ccc609b

Browse files
committed
Use github merge-results command when feature flag is enabled
1 parent a12b868 commit ccc609b

File tree

6 files changed

+228
-5
lines changed

6 files changed

+228
-5
lines changed

lib/codeql.js

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/codeql.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/upload-lib.js

Lines changed: 58 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/upload-lib.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/codeql.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ interface ExtraOptions {
5050
extractor?: Options;
5151
queries?: Options;
5252
};
53+
github?: {
54+
"*"?: Options;
55+
"merge-results"?: Options;
56+
};
5357
}
5458

5559
export interface CodeQL {
@@ -191,6 +195,14 @@ export interface CodeQL {
191195
): Promise<void>;
192196
/** Get the location of an extractor for the specified language. */
193197
resolveExtractor(language: Language): Promise<string>;
198+
/**
199+
* Run 'codeql github merge-results'.
200+
*/
201+
mergeResults(
202+
sarifFiles: string[],
203+
outputFile: string,
204+
mergeRunsFromEqualCategory?: boolean,
205+
): Promise<void>;
194206
}
195207

196208
export interface VersionInfo {
@@ -489,6 +501,7 @@ export function setCodeQL(partialCodeql: Partial<CodeQL>): CodeQL {
489501
),
490502
diagnosticsExport: resolveFunction(partialCodeql, "diagnosticsExport"),
491503
resolveExtractor: resolveFunction(partialCodeql, "resolveExtractor"),
504+
mergeResults: resolveFunction(partialCodeql, "mergeResults"),
492505
};
493506
return cachedCodeQL;
494507
}
@@ -1077,6 +1090,29 @@ export async function getCodeQLForCmd(
10771090
).exec();
10781091
return JSON.parse(extractorPath);
10791092
},
1093+
async mergeResults(
1094+
sarifFiles: string[],
1095+
outputFile: string,
1096+
mergeRunsFromEqualCategory = false,
1097+
): Promise<void> {
1098+
const args = [
1099+
"github",
1100+
"merge-results",
1101+
"--output",
1102+
outputFile,
1103+
...getExtraOptionsFromEnv(["github", "merge-results"]),
1104+
];
1105+
1106+
for (const sarifFile of sarifFiles) {
1107+
args.push("--sarif", sarifFile);
1108+
}
1109+
1110+
if (mergeRunsFromEqualCategory) {
1111+
args.push("--sarif-merge-runs-from-equal-category");
1112+
}
1113+
1114+
await new toolrunner.ToolRunner(cmd, args).exec();
1115+
},
10801116
};
10811117
// To ensure that status reports include the CodeQL CLI version wherever
10821118
// possible, we want to call getVersion(), which populates the version value

src/upload-lib.ts

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,29 @@ import fileUrl from "file-url";
88
import * as jsonschema from "jsonschema";
99

1010
import * as actionsUtil from "./actions-util";
11+
import {
12+
getOptionalInput,
13+
getRequiredInput,
14+
getTemporaryDirectory,
15+
} from "./actions-util";
1116
import * as api from "./api-client";
17+
import { getGitHubVersion } from "./api-client";
18+
import { CodeQL, getCodeQL } from "./codeql";
19+
import { getConfig } from "./config-utils";
1220
import { EnvVar } from "./environment";
21+
import { Feature, Features } from "./feature-flags";
1322
import * as fingerprints from "./fingerprints";
23+
import { initCodeQL } from "./init";
1424
import { Logger } from "./logging";
1525
import { parseRepositoryNwo, RepositoryNwo } from "./repository";
1626
import * as util from "./util";
17-
import { SarifFile, ConfigurationError, wrapError } from "./util";
27+
import {
28+
SarifFile,
29+
ConfigurationError,
30+
wrapError,
31+
getRequiredEnvParam,
32+
GitHubVersion,
33+
} from "./util";
1834

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

67+
// Takes a list of paths to sarif files and combines them together using the
68+
// CLI `github merge-results` command when all SARIF files are produced by
69+
// CodeQL. Otherwise, it will fall back to combining the files in the action.
70+
// Returns the contents of the combined sarif file.
71+
async function combineSarifFilesUsingCLI(
72+
sarifFiles: string[],
73+
gitHubVersion: GitHubVersion,
74+
features: Features,
75+
logger: Logger,
76+
): Promise<SarifFile> {
77+
// First check if all files are produced by CodeQL.
78+
let allCodeQL = true;
79+
80+
for (const sarifFile of sarifFiles) {
81+
const sarifObject = JSON.parse(
82+
fs.readFileSync(sarifFile, "utf8"),
83+
) as SarifFile;
84+
85+
const allRunsCodeQL = sarifObject.runs?.every(
86+
(run) => run.tool?.driver?.name === "CodeQL",
87+
);
88+
89+
if (!allRunsCodeQL) {
90+
allCodeQL = false;
91+
break;
92+
}
93+
}
94+
95+
if (!allCodeQL) {
96+
logger.warning(
97+
"Not all SARIF files were produced by CodeQL. Merging files in the action.",
98+
);
99+
100+
// If not, use the naive method of combining the files.
101+
return combineSarifFiles(sarifFiles);
102+
}
103+
104+
// Initialize CodeQL, either by using the config file from the 'init' step,
105+
// or by initializing it here.
106+
let codeQL: CodeQL;
107+
let tempDir: string;
108+
109+
const config = await getConfig(actionsUtil.getTemporaryDirectory(), logger);
110+
if (config !== undefined) {
111+
codeQL = await getCodeQL(config.codeQLCmd);
112+
tempDir = config.tempDir;
113+
} else {
114+
logger.warning(
115+
"Initializing CodeQL since the 'init' Action was not called before this step.",
116+
);
117+
118+
const apiDetails = {
119+
auth: getRequiredInput("token"),
120+
externalRepoAuth: getOptionalInput("external-repository-token"),
121+
url: getRequiredEnvParam("GITHUB_SERVER_URL"),
122+
apiURL: getRequiredEnvParam("GITHUB_API_URL"),
123+
};
124+
125+
const codeQLDefaultVersionInfo = await features.getDefaultCliVersion(
126+
gitHubVersion.type,
127+
);
128+
129+
const initCodeQLResult = await initCodeQL(
130+
undefined, // There is no tools input on the upload action
131+
apiDetails,
132+
getTemporaryDirectory(),
133+
gitHubVersion.type,
134+
codeQLDefaultVersionInfo,
135+
logger,
136+
);
137+
138+
codeQL = initCodeQLResult.codeql;
139+
tempDir = getTemporaryDirectory();
140+
}
141+
142+
const outputFile = path.resolve(tempDir, "combined-sarif.sarif");
143+
144+
await codeQL.mergeResults(sarifFiles, outputFile, true);
145+
146+
return JSON.parse(fs.readFileSync(outputFile, "utf8")) as SarifFile;
147+
}
148+
51149
// Populates the run.automationDetails.id field using the analysis_key and environment
52150
// and return an updated sarif file contents.
53151
export function populateRunAutomationDetails(
@@ -363,12 +461,27 @@ async function uploadFiles(
363461
logger.startGroup("Uploading results");
364462
logger.info(`Processing sarif files: ${JSON.stringify(sarifFiles)}`);
365463

464+
const gitHubVersion = await getGitHubVersion();
465+
const features = new Features(
466+
gitHubVersion,
467+
repositoryNwo,
468+
actionsUtil.getTemporaryDirectory(),
469+
logger,
470+
);
471+
366472
// Validate that the files we were asked to upload are all valid SARIF files
367473
for (const file of sarifFiles) {
368474
validateSarifFileSchema(file, logger);
369475
}
370476

371-
let sarif = combineSarifFiles(sarifFiles);
477+
let sarif = (await features.getValue(Feature.CliSarifMerge))
478+
? await combineSarifFilesUsingCLI(
479+
sarifFiles,
480+
gitHubVersion,
481+
features,
482+
logger,
483+
)
484+
: combineSarifFiles(sarifFiles);
372485
sarif = await fingerprints.addFingerprints(sarif, sourceRoot, logger);
373486

374487
sarif = populateRunAutomationDetails(

0 commit comments

Comments
 (0)