Skip to content
Draft
186 changes: 124 additions & 62 deletions lib/entry-points.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions src/actions-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as github from "@actions/github";
import * as io from "@actions/io";

import type { Config } from "./config-utils";
import { Env, EnvVar, ActionsEnvVars } from "./environment";
import { Env, EnvVar, ActionsEnvVars, ReadOnlyEnv } from "./environment";
import { Logger } from "./logging";
import {
doesDirectoryExist,
Expand Down Expand Up @@ -94,7 +94,7 @@ export function getActionVersion(): string {
*
* This will be "dynamic" for default setup workflow runs.
*/
export function getWorkflowEventName(env: Env = getEnv()) {
export function getWorkflowEventName(env: ReadOnlyEnv = getEnv()) {
return env.getRequired(ActionsEnvVars.GITHUB_EVENT_NAME);
}

Expand All @@ -121,7 +121,7 @@ function getRelativeScriptPath(env: Env): string {
}

/** Returns the contents of `GITHUB_EVENT_PATH` as a JSON object. */
export function getWorkflowEvent(env: Env = getEnv()): any {
export function getWorkflowEvent(env: ReadOnlyEnv = getEnv()): any {
const eventJsonFile = env.getRequired(ActionsEnvVars.GITHUB_EVENT_PATH);
try {
return JSON.parse(fs.readFileSync(eventJsonFile, "utf-8"));
Expand Down
2 changes: 2 additions & 0 deletions src/analyze-action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ test.serial(
requiredInputStub.withArgs("token").returns("fake-token");
requiredInputStub.withArgs("upload-database").returns("false");
requiredInputStub.withArgs("output").returns("out");
requiredInputStub.withArgs("checkout_path").returns("");
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
optionalInputStub.withArgs("expect-error").returns("false");
sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion);
Expand Down Expand Up @@ -104,6 +105,7 @@ test.serial(
requiredInputStub.withArgs("token").returns("fake-token");
requiredInputStub.withArgs("upload-database").returns("false");
requiredInputStub.withArgs("output").returns("out");
requiredInputStub.withArgs("checkout_path").returns("");
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
optionalInputStub.withArgs("expect-error").returns("false");
sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion);
Expand Down
24 changes: 18 additions & 6 deletions src/analyze-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as analyses from "./analyses";
import {
CodeQLAnalysisError,
dbIsFinalized,
determineCheckoutPath,
QueriesStatusReport,
runFinalize,
runQueries,
Expand Down Expand Up @@ -212,9 +213,11 @@ async function runAutobuildIfLegacyGoWorkflow(config: Config, logger: Logger) {
await runAutobuild(config, BuiltInLanguage.go, logger);
}

async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.
const startedAt = action.startedAt;
const logger = action.logger;

let uploadResults:
| Partial<Record<analyses.AnalysisKind, UploadResult>>
Expand Down Expand Up @@ -307,8 +310,13 @@ async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
logger,
);

const checkoutPath = await determineCheckoutPath(action, config);

// Setup diff informed analysis if needed (based on whether init created the file)
const diffRangePackDir = await setupDiffInformedQueryRun(logger);
const diffRangePackDir = await setupDiffInformedQueryRun(
logger,
checkoutPath,
);

await warnIfGoInstalledAfterInit(config, logger);
await runAutobuildIfLegacyGoWorkflow(config, logger);
Expand Down Expand Up @@ -354,7 +362,6 @@ async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
actionsUtil.getOptionalInput("upload"),
);
if (runStats) {
const checkoutPath = actionsUtil.getRequiredInput("checkout_path");
const category = actionsUtil.getOptionalInput("category");

uploadResults = await postProcessAndUploadSarif(
Expand Down Expand Up @@ -388,18 +395,23 @@ async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
// Possibly upload the overlay-base database to actions cache.
// Note: Take care with the ordering of this call since databases may be cleaned up
// at the `overlay` level.
await cleanupAndUploadOverlayBaseDatabaseToCache(codeql, config, logger);
await cleanupAndUploadOverlayBaseDatabaseToCache(
codeql,
config,
logger,
checkoutPath,
);

// Possibly upload the database bundles for remote queries.
// Note: Take care with the ordering of this call since databases may be cleaned up
// at the `overlay` or `clear` level.
databaseUploadResults = await cleanupAndUploadDatabases(
{ ...action, features },
repositoryNwo,
codeql,
config,
apiDetails,
features,
logger,
checkoutPath,
);

// Possibly upload the TRAP caches for later re-use
Expand Down
57 changes: 55 additions & 2 deletions src/analyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { performance } from "perf_hooks";
import * as io from "@actions/io";
import * as yaml from "js-yaml";

import { getTemporaryDirectory, getRequiredInput } from "./actions-util";
import type { ActionState } from "./action-common";
import { getTemporaryDirectory } from "./actions-util";
import * as analyses from "./analyses";
import { setupCppAutobuild } from "./autobuild";
import { type CodeQL } from "./codeql";
Expand All @@ -21,6 +22,7 @@ import {
} from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment";
import { FeatureEnablement, Feature } from "./feature-flags";
import { getGitRoot } from "./git-utils";
import { BuiltInLanguage, Language } from "./languages";
import { Logger, withGroupAsync } from "./logging";
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
Expand Down Expand Up @@ -85,6 +87,57 @@ export interface QueriesStatusReport
event_reports?: EventReport[];
}

/**
* Determines the path at which the repository being analysed is checked out at.
* Returns the value of the required `checkout_path` input and validates that it
* refers to the root of a repository.
*
* @param action The action state.
* @param config The CodeQL Action configuration state.
*/
export async function determineCheckoutPath(
action: ActionState<["Logger", "Actions"]>,
config: configUtils.Config,
) {
const checkoutPathInput = action.actions.getRequiredInput("checkout_path");

// Try to obtain the root path of the repository and validate that it matches the input.
const repositoryRoot = await getGitRoot(checkoutPathInput);

if (repositoryRoot === undefined) {
action.logger.warning(
[
`The directory at '${checkoutPathInput}' is not in the work tree of a git repository.`,
"If the repository being analyzed is checked out elsewhere,",
"you must explicitly set the 'checkout_path' input for the 'codeql-action/analyze' step to",
"the checkout path.",
].join(" "),
);
} else if (repositoryRoot !== path.resolve(checkoutPathInput)) {
action.logger.warning(
[
`The directory at '${checkoutPathInput}' is not the root of the repository ('${repositoryRoot}').`,
"Set the 'checkout_path' input for the 'codeql-action/analyze' step to the root path of the checkout.",
].join(" "),
);
} else if (
config.repositoryRoot !== undefined &&
repositoryRoot !== config.repositoryRoot
) {
// The repository root that was persisted by the `init` step doesn't match the one we have found here.
action.logger.warning(
[
`The repository path at '${repositoryRoot}' does not match that found by the 'codeql-action/init' step: '${config.repositoryRoot}'.`,
"Ensure that the 'checkout_path' input for the 'codeql-action/analyze' step is set to the path of the same repository that",
"the 'codeql-action/init' step determined. This is either the GitHub Actions workspace or the repository root corresponding to",
"the 'source-root' input if that was provided.",
].join(" "),
);
}

return checkoutPathInput;
}

async function setupPythonExtractor(logger: Logger) {
const codeqlPython = process.env["CODEQL_PYTHON"];
if (codeqlPython === undefined || codeqlPython.length === 0) {
Expand Down Expand Up @@ -233,6 +286,7 @@ async function finalizeDatabaseCreation(
*/
export async function setupDiffInformedQueryRun(
logger: Logger,
checkoutPath: string,
): Promise<string | undefined> {
return await withGroupAsync(
"Generating diff range extension pack",
Expand All @@ -245,7 +299,6 @@ export async function setupDiffInformedQueryRun(
return undefined;
}

const checkoutPath = getRequiredInput("checkout_path");
const packDir = writeDiffRangeDataExtensionPack(
logger,
diffRanges,
Expand Down
2 changes: 1 addition & 1 deletion src/codeql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ export async function getTrapCachingExtractorConfigArgsForLang(
): Promise<string[]> {
const cacheDir = config.trapCaches[language];
if (cacheDir === undefined) return [];
const write = await isAnalyzingDefaultBranch();
const write = await isAnalyzingDefaultBranch(getEnv(), config.repositoryRoot);
return [
`-O=${language}.trap.cache.dir=${cacheDir}`,
`-O=${language}.trap.cache.bound=${TRAP_CACHE_SIZE_MB}`,
Expand Down
12 changes: 7 additions & 5 deletions src/config-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ test.serial("load empty config", async (t) => {
createTestInitConfigInputs({
languagesInput: languages,
repository: { owner: "github", repo: "example" },
sourceRoot: tempDir,
tempDir,
codeql,
logger,
Expand All @@ -186,6 +187,7 @@ test.serial("load empty config", async (t) => {
logger,
}),
{},
undefined,
);

t.deepEqual(config, expectedConfig);
Expand Down Expand Up @@ -216,6 +218,7 @@ test.serial("load code quality config", async (t) => {
analysisKinds: [AnalysisKind.CodeQuality],
languagesInput: languages,
repository: { owner: "github", repo: "example" },
sourceRoot: tempDir,
tempDir,
codeql,
logger,
Expand Down Expand Up @@ -296,6 +299,7 @@ test.serial(
analysisKinds: [AnalysisKind.CodeQuality],
languagesInput: languages,
repository: { owner: "github", repo: "example" },
sourceRoot: tempDir,
tempDir,
codeql,
repositoryProperties,
Expand Down Expand Up @@ -512,6 +516,7 @@ test.serial("load non-empty input", async (t) => {
// And the config we expect it to parse to
const expectedConfig = createTestConfig({
languages: [BuiltInLanguage.javascript],
repositoryRoot: undefined,
buildMode: BuildMode.None,
originalUserInput: userConfig,
computedConfig: userConfig,
Expand All @@ -532,6 +537,7 @@ test.serial("load non-empty input", async (t) => {
state,
createTestInitConfigInputs({
languagesInput,
sourceRoot: tempDir,
buildModeInput: "none",
configFile: configFilePath,
debugArtifactName: "my-artifact",
Expand Down Expand Up @@ -1092,11 +1098,6 @@ const checkOverlayEnablementMacro = makeMacro({
return lang === BuiltInLanguage.java;
});

// Mock git root detection
if (setup.gitRoot !== undefined) {
sinon.stub(gitUtils, "getGitRoot").resolves(setup.gitRoot);
}

// Mock submodule detection
sinon.stub(gitUtils, "hasSubmodules").returns(setup.hasSubmodules);

Expand All @@ -1109,6 +1110,7 @@ const checkOverlayEnablementMacro = makeMacro({
codeql,
features,
setup.languages,
setup.gitRoot, // repositoryRoot
tempDir, // sourceRoot
setup.buildMode,
undefined,
Expand Down
Loading
Loading