Merge pull request #1242 from github/henrymercer/go-more-backwards-compat
Go extraction reconciliation: Ensure backwards compatibility for multi-language builds
This commit is contained in:
commit
34aa5a554b
15 changed files with 188 additions and 59 deletions
|
|
@ -133,7 +133,7 @@ function doesGoExtractionOutputExist(config: Config): boolean {
|
|||
* steps.
|
||||
*
|
||||
* - We detect whether an autobuild step is present by checking the
|
||||
* `CODEQL_ACTION_DID_AUTOBUILD_GOLANG` environment variable, which is set
|
||||
* `util.DID_AUTOBUILD_GO_ENV_VAR_NAME` environment variable, which is set
|
||||
* when the autobuilder is invoked.
|
||||
* - We approximate whether manual build steps are present by looking at
|
||||
* whether any extraction output already exists for Go.
|
||||
|
|
@ -152,7 +152,7 @@ async function runAutobuildIfLegacyGoWorkflow(
|
|||
);
|
||||
return;
|
||||
}
|
||||
if (process.env["CODEQL_ACTION_DID_AUTOBUILD_GOLANG"] === "true") {
|
||||
if (process.env[util.DID_AUTOBUILD_GO_ENV_VAR_NAME] === "true") {
|
||||
// This log line is info level while Go extraction reconciliation is in beta.
|
||||
// We will make it debug level once Go extraction reconciliation is GA.
|
||||
logger.info("Won't run Go autobuild since it has already been run.");
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@ import {
|
|||
StatusReportBase,
|
||||
} from "./actions-util";
|
||||
import { getApiDetails, getGitHubVersionActionsOnly } from "./api-client";
|
||||
import { determineAutobuildLanguage, runAutobuild } from "./autobuild";
|
||||
import { determineAutobuildLanguages, runAutobuild } from "./autobuild";
|
||||
import * as configUtils from "./config-utils";
|
||||
import { GitHubFeatureFlags } from "./feature-flags";
|
||||
import { Language } from "./languages";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import {
|
||||
DID_AUTOBUILD_GO_ENV_VAR_NAME,
|
||||
checkActionVersion,
|
||||
checkGitHubVersionInRange,
|
||||
getRequiredEnvParam,
|
||||
|
|
@ -61,7 +62,8 @@ async function run() {
|
|||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
await checkActionVersion(pkg.version);
|
||||
let language: Language | undefined = undefined;
|
||||
let currentLanguage: Language | undefined = undefined;
|
||||
let languages: Language[] | undefined = undefined;
|
||||
try {
|
||||
if (
|
||||
!(await sendStatusReport(
|
||||
|
|
@ -88,8 +90,8 @@ async function run() {
|
|||
);
|
||||
}
|
||||
|
||||
language = await determineAutobuildLanguage(config, featureFlags, logger);
|
||||
if (language !== undefined) {
|
||||
languages = await determineAutobuildLanguages(config, featureFlags, logger);
|
||||
if (languages !== undefined) {
|
||||
const workingDirectory = getOptionalInput("working-directory");
|
||||
if (workingDirectory) {
|
||||
logger.info(
|
||||
|
|
@ -97,9 +99,12 @@ async function run() {
|
|||
);
|
||||
process.chdir(workingDirectory);
|
||||
}
|
||||
await runAutobuild(language, config, logger);
|
||||
if (language === Language.go) {
|
||||
core.exportVariable("CODEQL_ACTION_DID_AUTOBUILD_GOLANG", "true");
|
||||
for (const language of languages) {
|
||||
currentLanguage = language;
|
||||
await runAutobuild(language, config, logger);
|
||||
if (language === Language.go) {
|
||||
core.exportVariable(DID_AUTOBUILD_GO_ENV_VAR_NAME, "true");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -111,14 +116,14 @@ async function run() {
|
|||
console.log(error);
|
||||
await sendCompletedStatusReport(
|
||||
startedAt,
|
||||
language ? [language] : [],
|
||||
language,
|
||||
languages ?? [],
|
||||
currentLanguage,
|
||||
error instanceof Error ? error : new Error(String(error))
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await sendCompletedStatusReport(startedAt, language ? [language] : []);
|
||||
await sendCompletedStatusReport(startedAt, languages ?? []);
|
||||
}
|
||||
|
||||
async function runWrapper() {
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ import { Language, isTracedLanguage } from "./languages";
|
|||
import { Logger } from "./logging";
|
||||
import * as util from "./util";
|
||||
|
||||
export async function determineAutobuildLanguage(
|
||||
export async function determineAutobuildLanguages(
|
||||
config: configUtils.Config,
|
||||
featureFlags: FeatureFlags,
|
||||
logger: Logger
|
||||
): Promise<Language | undefined> {
|
||||
): Promise<Language[] | undefined> {
|
||||
const isGoExtractionReconciliationEnabled =
|
||||
await util.isGoExtractionReconciliationEnabled(featureFlags);
|
||||
// Attempt to find a language to autobuild
|
||||
|
|
@ -19,26 +19,81 @@ export async function determineAutobuildLanguage(
|
|||
const autobuildLanguages = config.languages.filter((l) =>
|
||||
isTracedLanguage(l, isGoExtractionReconciliationEnabled, logger)
|
||||
);
|
||||
const language = autobuildLanguages[0];
|
||||
|
||||
if (!language) {
|
||||
if (!autobuildLanguages) {
|
||||
logger.info(
|
||||
"None of the languages in this project require extra build steps"
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
logger.debug(`Detected dominant traced language: ${language}`);
|
||||
/**
|
||||
* Additionally autobuild Go in the autobuild Action to ensure backwards
|
||||
* compatibility for users performing a multi-language build within a single
|
||||
* job.
|
||||
*
|
||||
* For example, consider a user with the following workflow file:
|
||||
*
|
||||
* ```yml
|
||||
* - uses: github/codeql-action/init@v2
|
||||
* with:
|
||||
* languages: go, java
|
||||
* - uses: github/codeql-action/autobuild@v2
|
||||
* - uses: github/codeql-action/analyze@v2
|
||||
* ```
|
||||
*
|
||||
* - With Go extraction disabled, we will run the Java autobuilder in the
|
||||
* autobuild Action, ensuring we extract both Java and Go code.
|
||||
* - With Go extraction enabled, taking the previous behavior we'd run the Go
|
||||
* autobuilder, since Go is first on the list of languages. We wouldn't run
|
||||
* the Java autobuilder at all and so we'd only extract Go code.
|
||||
*
|
||||
* We therefore introduce a special case here such that we'll autobuild Go
|
||||
* in addition to the primary non-Go traced language in the autobuild Action.
|
||||
*
|
||||
* This special case behavior should be removed as part of the next major
|
||||
* version of the CodeQL Action.
|
||||
*/
|
||||
const autobuildLanguagesWithoutGo = autobuildLanguages.filter(
|
||||
(l) => l !== Language.go
|
||||
);
|
||||
|
||||
if (autobuildLanguages.length > 1) {
|
||||
const languages: Language[] = [];
|
||||
// First run the autobuilder for the first non-Go traced language, if one
|
||||
// exists.
|
||||
if (autobuildLanguagesWithoutGo[0] !== undefined) {
|
||||
languages.push(autobuildLanguagesWithoutGo[0]);
|
||||
}
|
||||
// If Go is requested, run the Go autobuilder last to ensure it doesn't
|
||||
// interfere with the other autobuilder.
|
||||
if (autobuildLanguages.length !== autobuildLanguagesWithoutGo.length) {
|
||||
languages.push(Language.go);
|
||||
}
|
||||
|
||||
logger.debug(`Will autobuild ${languages.join(" and ")}.`);
|
||||
|
||||
// In general the autobuilders for other traced languages may conflict with
|
||||
// each other. Therefore if a user has requested more than one non-Go traced
|
||||
// language, we ask for manual build steps.
|
||||
// Matrixing the build would also work, but that would change the SARIF
|
||||
// categories, potentially leading to a "stale tips" situation where alerts
|
||||
// that should be fixed remain on a repo since they are linked to SARIF
|
||||
// categories that are no longer updated.
|
||||
if (autobuildLanguagesWithoutGo.length > 1) {
|
||||
logger.warning(
|
||||
`We will only automatically build ${language} code. If you wish to scan ${autobuildLanguages
|
||||
`We will only automatically build ${languages.join(
|
||||
" and "
|
||||
)} code. If you wish to scan ${autobuildLanguagesWithoutGo
|
||||
.slice(1)
|
||||
.join(" and ")}, you must replace this call with custom build steps.`
|
||||
.join(
|
||||
" and "
|
||||
)}, you must replace the autobuild step of your workflow with custom build steps. ` +
|
||||
"For more information, see " +
|
||||
"https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages#adding-build-steps-for-a-compiled-language"
|
||||
);
|
||||
}
|
||||
|
||||
return language;
|
||||
return languages;
|
||||
}
|
||||
|
||||
export async function runAutobuild(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Command } from "commander";
|
|||
import del from "del";
|
||||
|
||||
import { runFinalize, runQueries } from "./analyze";
|
||||
import { determineAutobuildLanguage, runAutobuild } from "./autobuild";
|
||||
import { determineAutobuildLanguages, runAutobuild } from "./autobuild";
|
||||
import { CodeQL, CODEQL_VERSION_NEW_TRACING, getCodeQL } from "./codeql";
|
||||
import { Config, getConfig } from "./config-utils";
|
||||
import { createFeatureFlags } from "./feature-flags";
|
||||
|
|
@ -362,9 +362,9 @@ program
|
|||
}
|
||||
await enrichEnvironment(Mode.runner, await getCodeQL(config.codeQLCmd));
|
||||
importTracerEnvironment(config);
|
||||
let language: Language | undefined = undefined;
|
||||
let languages: Language[] | undefined = undefined;
|
||||
if (cmd.language !== undefined) {
|
||||
language = parseLanguage(cmd.language);
|
||||
const language = parseLanguage(cmd.language);
|
||||
if (language === undefined || !config.languages.includes(language)) {
|
||||
throw new Error(
|
||||
`"${cmd.language}" is not a recognised language. ` +
|
||||
|
|
@ -373,15 +373,18 @@ program
|
|||
)}.`
|
||||
);
|
||||
}
|
||||
languages = [language];
|
||||
} else {
|
||||
language = await determineAutobuildLanguage(
|
||||
languages = await determineAutobuildLanguages(
|
||||
config,
|
||||
createFeatureFlags([]),
|
||||
logger
|
||||
);
|
||||
}
|
||||
if (language !== undefined) {
|
||||
await runAutobuild(language, config, logger);
|
||||
if (languages !== undefined) {
|
||||
for (const language of languages) {
|
||||
await runAutobuild(language, config, logger);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error("Autobuild failed");
|
||||
|
|
|
|||
|
|
@ -45,6 +45,13 @@ export const DEFAULT_DEBUG_ARTIFACT_NAME = "debug-artifacts";
|
|||
*/
|
||||
export const DEFAULT_DEBUG_DATABASE_NAME = "db";
|
||||
|
||||
/**
|
||||
* Environment variable that is set to "true" when the CodeQL Action has invoked
|
||||
* the Go autobuilder.
|
||||
*/
|
||||
export const DID_AUTOBUILD_GO_ENV_VAR_NAME =
|
||||
"CODEQL_ACTION_DID_AUTOBUILD_GOLANG";
|
||||
|
||||
export interface SarifFile {
|
||||
version?: string | null;
|
||||
runs: Array<{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue