Allow getCheckoutPathInputOrThrow to get root from config

This commit is contained in:
Michael B. Gale
2026-09-22 12:09:16 +01:00
parent 54a43494ca
commit f5716fadf4
4 changed files with 180 additions and 19 deletions

29
lib/entry-points.js generated
View File

@@ -162185,14 +162185,20 @@ function getUploadInputOrThrow(workflow, jobName, matrixVars) {
matrixVars
);
}
function getCheckoutPathInputOrThrow(workflow, jobName, matrixVars) {
return getInputOrThrow(
workflow,
jobName,
getAnalyzeActionName(),
"checkout_path",
matrixVars
) || getRequiredEnvParam("GITHUB_WORKSPACE");
function getRepositoryRootOrThrow(workflow, jobName, matrixVars, config, env = getEnv()) {
return (
// If the CodeQL Action already has a persisted repository root, then we can just use that.
config.repositoryRoot ?? // Otherwise, try to retrieve it from a `checkout_path` input in the workflow specification.
getInputOrThrow(
workflow,
jobName,
getAnalyzeActionName(),
"checkout_path",
matrixVars
) ?? // Finally, if all of the above fail, just use the value of `GITHUB_WORKSPACE` since that
// is what is used by default.
env.getRequired("GITHUB_WORKSPACE" /* GITHUB_WORKSPACE */)
);
}
async function checkWorkflow(logger, codeql) {
if (!isDynamicWorkflow() && process.env["CODEQL_ACTION_SKIP_WORKFLOW_VALIDATION" /* SKIP_WORKFLOW_VALIDATION */] !== "true") {
@@ -162763,7 +162769,12 @@ async function prepareFailedSarif(logger, features, config) {
});
}
const category = getCategoryInputOrThrow(workflow, jobName, matrix);
const checkoutPath = getCheckoutPathInputOrThrow(workflow, jobName, matrix);
const checkoutPath = getRepositoryRootOrThrow(
workflow,
jobName,
matrix,
config
);
const result = await generateFailedSarif(
logger,
features,

View File

@@ -44,7 +44,7 @@ import {
} from "./util";
import {
getCategoryInputOrThrow,
getCheckoutPathInputOrThrow,
getRepositoryRootOrThrow,
getUploadInputOrThrow,
getWorkflow,
} from "./workflow";
@@ -144,7 +144,15 @@ async function prepareFailedSarif(
});
}
const category = getCategoryInputOrThrow(workflow, jobName, matrix);
const checkoutPath = getCheckoutPathInputOrThrow(workflow, jobName, matrix);
// Try to determine the path at which the repository that we failed to analyse is checked out at.
// We need this to relativise the paths in the SARIF.
const checkoutPath = getRepositoryRootOrThrow(
workflow,
jobName,
matrix,
config,
);
const result = await generateFailedSarif(
logger,

View File

@@ -4,10 +4,12 @@ import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import { createStubCodeQL, getCodeQLForTesting } from "./codeql";
import { EnvVar } from "./environment";
import { ActionsEnvVars, EnvVar } from "./environment";
import {
checkExpectedLogMessages,
createTestConfig,
getRecordingLogger,
getTestEnv,
LoggedMessage,
setupTests,
} from "./testing-utils";
@@ -1002,3 +1004,124 @@ test.serial(
t.is(messages.length, 0);
},
);
test("getRepositoryRootOrThrow - gets root from config", async (t) => {
const expectedRoot = "/path/to/root";
const repositoryRoot = workflow.getRepositoryRootOrThrow(
{},
"testJob",
{},
createTestConfig({ repositoryRoot: expectedRoot }),
getTestEnv(),
);
t.is(repositoryRoot, expectedRoot);
});
test("getRepositoryRootOrThrow - gets root from workflow", async (t) => {
const expectedRoot = "/path/to/root";
const repositoryRoot = workflow.getRepositoryRootOrThrow(
{
jobs: {
testJob: {
steps: [
{
uses: "github/codeql-action/analyze",
with: { checkout_path: expectedRoot },
},
],
},
},
},
"testJob",
{},
createTestConfig({}),
getTestEnv(),
);
t.is(repositoryRoot, expectedRoot);
});
test("getRepositoryRootOrThrow - gets root from environment", async (t) => {
const expectedRoot = "/path/to/root";
const repositoryRoot = workflow.getRepositoryRootOrThrow(
{
jobs: { testJob: { steps: [{ uses: "github/codeql-action/analyze" }] } },
},
"testJob",
{},
createTestConfig({}),
getTestEnv({ [ActionsEnvVars.GITHUB_WORKSPACE]: expectedRoot }),
);
t.is(repositoryRoot, expectedRoot);
});
test("getRepositoryRootOrThrow - throws if there's no matching job", async (t) => {
t.throws(
() =>
workflow.getRepositoryRootOrThrow(
{
jobs: {
otherJob: {
steps: [
{
uses: "github/codeql-action/analyze",
with: { checkout_path: "/some/path" },
},
],
},
},
},
"testJob",
{},
createTestConfig({}),
getTestEnv(),
),
{ message: /since the workflow has no job named testJob./ },
);
});
test("getRepositoryRootOrThrow - throws if there's no analyze step", async (t) => {
t.throws(
() =>
workflow.getRepositoryRootOrThrow(
{
jobs: {
testJob: { steps: [] },
},
},
"testJob",
{},
createTestConfig({}),
getTestEnv(),
),
{ message: /since the testJob job does not call/ },
);
});
test("getRepositoryRootOrThrow - throws if the env var is not set", async (t) => {
t.throws(
() =>
workflow.getRepositoryRootOrThrow(
{
jobs: {
testJob: {
steps: [
{
uses: "github/codeql-action/analyze",
},
],
},
},
},
"testJob",
{},
createTestConfig({}),
getTestEnv(),
),
{
message: `${ActionsEnvVars.GITHUB_WORKSPACE} environment variable must be set`,
},
);
});

View File

@@ -8,7 +8,8 @@ import * as yaml from "js-yaml";
import { isDynamicWorkflow } from "./actions-util";
import * as api from "./api-client";
import { CodeQL } from "./codeql";
import { EnvVar } from "./environment";
import type { Config } from "./config-utils";
import { ActionsEnvVars, EnvVar, getEnv, ReadOnlyEnv } from "./environment";
import { Logger } from "./logging";
import {
getRequiredEnvParam,
@@ -441,27 +442,45 @@ export function getUploadInputOrThrow(
}
/**
* Makes a best effort attempt to retrieve the checkout_path input for the
* particular job, given a set of matrix variables.
* Makes a best effort attempt to determine the root path of the repository that the analysis
* relates to. We need that to make paths in SARIF files relative.
*
* - If available, we take the `repositoryRoot` from the `config`.
* - If it isn't, we fall back to trying to extract a `checkout_path` input from the `workflow`.
* - Finally, we fall back to the value of `GITHUB_WORKSPACE`.
*
* Typically you'll want to wrap this function in a try/catch block and handle the error.
*
* @returns the checkout_path input
* @throws an error if the checkout_path input could not be determined
* @param workflow The workflow specification of the currently running workflow.
* @param jobName The name of the job that is currently running.
* @param matrixVars The matrix variables, if any.
* @param config The CodeQL Action configuration state.
* @param env The environment variables.
*
* @returns The repository root path, or its best approximation.
* @throws `Error` if the repository root could not be determined.
*/
export function getCheckoutPathInputOrThrow(
export function getRepositoryRootOrThrow(
workflow: Workflow,
jobName: string,
matrixVars: { [key: string]: string } | undefined,
config: Config,
env: ReadOnlyEnv = getEnv(),
): string {
return (
// If the CodeQL Action already has a persisted repository root, then we can just use that.
config.repositoryRoot ??
// Otherwise, try to retrieve it from a `checkout_path` input in the workflow specification.
getInputOrThrow(
workflow,
jobName,
getAnalyzeActionName(),
"checkout_path",
matrixVars,
) || getRequiredEnvParam("GITHUB_WORKSPACE") // if unspecified, checkout_path defaults to ${{ github.workspace }}
) ??
// Finally, if all of the above fail, just use the value of `GITHUB_WORKSPACE` since that
// is what is used by default.
env.getRequired(ActionsEnvVars.GITHUB_WORKSPACE)
);
}