Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,173 changes: 618 additions & 555 deletions lib/entry-points.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/actions-util.ts
Original file line number Diff line number Diff line change
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
13 changes: 6 additions & 7 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,13 +213,11 @@ async function runAutobuildIfLegacyGoWorkflow(config: Config, logger: Logger) {
await runAutobuild(config, BuiltInLanguage.go, logger);
}

async function run({
startedAt,
logger,
actions,
}: ActionState<["Base", "Logger", "Actions"]>) {
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 @@ -311,7 +310,7 @@ async function run({
logger,
);

const checkoutPath = actions.getRequiredInput("checkout_path");
const checkoutPath = await determineCheckoutPath(action, config);

// Setup diff informed analysis if needed (based on whether init created the file)
const diffRangePackDir = await setupDiffInformedQueryRun(
Expand Down Expand Up @@ -407,7 +406,7 @@ async function run({
// Note: Take care with the ordering of this call since databases may be cleaned up
// at the `overlay` or `clear` level.
databaseUploadResults = await cleanupAndUploadDatabases(
{ logger, features },
{ ...action, features },
repositoryNwo,
codeql,
config,
Expand Down
88 changes: 88 additions & 0 deletions src/analyze.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,110 @@ import {
resolveQuerySuiteAlias,
addSarifExtension,
diffRangeExtensionPackContents,
determineCheckoutPath,
} from "./analyze";
import { createStubCodeQL } from "./codeql";
import { Feature } from "./feature-flags";
import * as gitUtils from "./git-utils";
import { BuiltInLanguage } from "./languages";
import { getRunnerLogger } from "./logging";
import {
setupTests,
setupActionsVars,
createFeatures,
createTestConfig,
callee,
} from "./testing-utils";
import * as uploadLib from "./upload-lib";
import * as util from "./util";

setupTests(test);

test.serial(
"determineCheckoutPath - logs when checkout_path is not in a work tree",
async (t) => {
const expectedPath = "/checkout/path";
const target = callee(determineCheckoutPath)
.withActions((actions) => {
sinon
.stub(actions, "getRequiredInput")
.withArgs("checkout_path")
.returns("/checkout/path");
})
.withArgs(createTestConfig({}));
sinon.stub(gitUtils, "getGitRoot").resolves(undefined);

await target
.logs(t, "is not in the work tree of a git repository")
.passes(t.is, expectedPath);
},
);

test.serial(
"determineCheckoutPath - logs when checkout_path is not a repo root",
async (t) => {
const expectedPath = "/checkout/path";
const target = callee(determineCheckoutPath)
.withActions((actions) => {
sinon
.stub(actions, "getRequiredInput")
.withArgs("checkout_path")
.returns("/checkout/path");
})
.withArgs(createTestConfig({}));
sinon.stub(gitUtils, "getGitRoot").resolves("/checkout");

await target
.logs(t, "is not the root of the repository")
.passes(t.is, expectedPath);
},
);

test.serial(
"determineCheckoutPath - logs when checkout_path is not the same as repo root in config",
async (t) => {
const expectedPath = "/checkout/path";
const target = callee(determineCheckoutPath)
.withActions((actions) => {
sinon
.stub(actions, "getRequiredInput")
.withArgs("checkout_path")
.returns("/checkout/path");
})
.withArgs(createTestConfig({ repositoryRoot: "/some/other/path" }));
sinon.stub(gitUtils, "getGitRoot").resolves("/checkout/path");

await target
.logs(t, "does not match that found by the 'codeql-action/init' step")
.passes(t.is, expectedPath);
},
);

test.serial(
"determineCheckoutPath - doesn't log any of the messages when all is as expected",
async (t) => {
const expectedPath = "/checkout/path";
const target = callee(determineCheckoutPath)
.withActions((actions) => {
sinon
.stub(actions, "getRequiredInput")
.withArgs("checkout_path")
.returns("/checkout/path");
})
.withArgs(createTestConfig({ repositoryRoot: "/checkout/path" }));
sinon.stub(gitUtils, "getGitRoot").resolves("/checkout/path");

await target
.notLogs(
t,
"is not in the work tree of a git repository",
"is not the root of the repository",
"does not match that found by the 'codeql-action/init' step",
)
.passes(t.is, expectedPath);
},
);

/**
* Checks the status report produced by the analyze Action.
*
Expand Down
54 changes: 54 additions & 0 deletions src/analyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { performance } from "perf_hooks";
import * as io from "@actions/io";
import * as yaml from "js-yaml";

import type { ActionState } from "./action-common";
import { getTemporaryDirectory } from "./actions-util";
import * as analyses from "./analyses";
import { setupCppAutobuild } from "./autobuild";
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,58 @@ 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 absCheckoutPathInput = path.resolve(checkoutPathInput);
const repositoryRoot = await getGitRoot(absCheckoutPathInput);

if (repositoryRoot === undefined) {
action.logger.warning(
[
`The directory at '${absCheckoutPathInput}' 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 !== absCheckoutPathInput) {
action.logger.warning(
[
`The directory at '${absCheckoutPathInput}' 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 absCheckoutPathInput;
}

async function setupPythonExtractor(logger: Logger) {
const codeqlPython = process.env["CODEQL_PYTHON"];
if (codeqlPython === undefined || codeqlPython.length === 0) {
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