Skip to content
Prev Previous commit
Next Next commit
Persist repository root from init action in CodeQL Action state
  • Loading branch information
mbg committed Sep 22, 2026
commit 5dab0ccf518dc8b6da0d5a182150ae1b5db7253a
43 changes: 33 additions & 10 deletions lib/entry-points.js

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

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
2 changes: 1 addition & 1 deletion src/analyze-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) {
logger,
);

const checkoutPath = determineCheckoutPath(action);
const checkoutPath = await determineCheckoutPath(action);

// Setup diff informed analysis if needed (based on whether init created the file)
const diffRangePackDir = await setupDiffInformedQueryRun(
Expand Down
30 changes: 28 additions & 2 deletions src/analyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,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 @@ -93,8 +94,33 @@ export interface QueriesStatusReport
*
* @param action The action state.
*/
export function determineCheckoutPath(action: ActionState<["Actions"]>) {
return action.actions.getRequiredInput("checkout_path");
export async function determineCheckoutPath(
action: ActionState<["Logger", "Actions"]>,
) {
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);
Comment thread
mbg marked this conversation as resolved.
Outdated

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)) {
Comment thread
mbg marked this conversation as resolved.
Outdated
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(" "),
);
}

return checkoutPathInput;
}

async function setupPythonExtractor(logger: Logger) {
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
16 changes: 12 additions & 4 deletions src/config-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ export async function initActionState(
enableFileCoverageInformation,
}: InitConfigInputs,
userConfig: UserConfig,
repositoryRoot: string | undefined,
): Promise<Config> {
const languages = await getLanguages(
codeql,
Expand Down Expand Up @@ -436,6 +437,7 @@ export async function initActionState(

return {
version: getActionVersion(),
repositoryRoot,
analysisKinds,
languages,
buildMode,
Expand Down Expand Up @@ -718,6 +720,7 @@ export async function checkOverlayEnablement(
codeql: CodeQL,
features: FeatureEnablement,
languages: Language[],
repositoryRoot: string | undefined,
sourceRoot: string,
buildMode: BuildMode | undefined,
ramInput: string | undefined,
Expand Down Expand Up @@ -747,6 +750,7 @@ export async function checkOverlayEnablement(
true,
codeql,
languages,
repositoryRoot,
sourceRoot,
buildMode,
gitVersion,
Expand Down Expand Up @@ -836,6 +840,7 @@ export async function checkOverlayEnablement(
false,
codeql,
languages,
repositoryRoot,
sourceRoot,
buildMode,
gitVersion,
Expand All @@ -855,6 +860,7 @@ async function validateOverlayDatabaseMode(
overlayModeSetExplicitly: boolean,
codeql: CodeQL,
languages: Language[],
repositoryRoot: string | undefined,
sourceRoot: string,
buildMode: BuildMode | undefined,
gitVersion: GitVersionInfo | undefined,
Expand Down Expand Up @@ -890,16 +896,15 @@ async function validateOverlayDatabaseMode(
);
return new Failure(OverlayDisabledReason.IncompatibleCodeQl);
}
const gitRoot = await getGitRoot(sourceRoot);
if (gitRoot === undefined) {
if (repositoryRoot === undefined) {
logger.warning(
`Cannot build an ${overlayDatabaseMode} database because ` +
`the source root "${sourceRoot}" is not inside a git repository. ` +
"Falling back to creating a normal full database instead.",
);
return new Failure(OverlayDisabledReason.NoGitRoot);
}
if (hasSubmodules(gitRoot)) {
if (hasSubmodules(repositoryRoot)) {
if (gitVersion === undefined) {
logger.warning(
`Cannot build an ${overlayDatabaseMode} database because ` +
Expand Down Expand Up @@ -1160,9 +1165,11 @@ export async function initConfig(
const { logger, features } = actionState;
const { tempDir } = inputs;

const repositoryRoot = await getGitRoot(inputs.sourceRoot);

const userConfig = await determineUserConfig(actionState, tempDir, inputs);

const config = await initActionState(inputs, userConfig);
const config = await initActionState(inputs, userConfig, repositoryRoot);

// If Code Quality analysis is the only enabled analysis kind, then we will initialise
// the database for Code Quality. That entails disabling the default queries and only
Expand Down Expand Up @@ -1244,6 +1251,7 @@ export async function initConfig(
inputs.codeql,
inputs.features,
config.languages,
repositoryRoot,
inputs.sourceRoot,
config.buildMode,
inputs.ramInput,
Expand Down
5 changes: 5 additions & 0 deletions src/config/action-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export interface Config {
* The version of the CodeQL Action that the configuration is for.
*/
version: string;
/**
* The path at which the repository being analysed is checked out at, if available.
* Persisted in the CodeQL Action configuration state, so that we can consult it in later workflow steps.
*/
repositoryRoot: string | undefined;
/**
* Set of analysis kinds that are enabled.
*/
Expand Down
1 change: 1 addition & 0 deletions src/testing-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,7 @@ export function createTestConfig(overrides: Partial<Config>): Config {
{},
{
version: getActionVersion(),
repositoryRoot: undefined,
analysisKinds: [AnalysisKind.CodeScanning],
languages: [],
buildMode: undefined,
Expand Down