Skip to content
Draft
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
34 changes: 31 additions & 3 deletions lib/entry-points.js

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

17 changes: 13 additions & 4 deletions src/config-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2544,6 +2544,7 @@ test("loadUserConfig - loads local configuration files", async (t) => {
) =>
configUtils.loadUserConfig(
actionState,
[AnalysisKind.CodeScanning],
filePath,
workspaceDir,
SAMPLE_DOTCOM_API_DETAILS,
Expand Down Expand Up @@ -2587,12 +2588,19 @@ test.serial("loadUserConfig - loads remote configuration files", async (t) => {

const remoteAddress = "owner/repo/file@ref";
await callee(configUtils.loadUserConfig)
.withArgs(remoteAddress, tmpDir, SAMPLE_DOTCOM_API_DETAILS, tmpDir)
.withArgs(
[AnalysisKind.CodeScanning],
remoteAddress,
tmpDir,
SAMPLE_DOTCOM_API_DETAILS,
tmpDir,
)
.passes(t.deepEqual, {});

t.true(
getRemoteConfig.calledOnceWithExactly(
sinon.match.any,
[AnalysisKind.CodeScanning],
remoteAddress,
SAMPLE_DOTCOM_API_DETAILS,
),
Expand Down Expand Up @@ -2626,9 +2634,9 @@ test.serial(
// match our expectations. We break it down like this to get
// more useful test output.
const args = getRemoteConfig.getCalls()[0].args;
t.is(args.length, 3);
t.deepEqual(args[1], address);
t.deepEqual(args[2], SAMPLE_DOTCOM_API_DETAILS);
t.is(args.length, 4);
t.deepEqual(args[2], address);
t.deepEqual(args[3], SAMPLE_DOTCOM_API_DETAILS);
};

// Utility function to assert that `targetWithArgs` has not identified
Expand Down Expand Up @@ -2665,6 +2673,7 @@ test.serial(

// Prepare the test call to `loadUserConfig`.
const targetWithArgs = target.withArgs(
[AnalysisKind.CodeScanning],
address,
tmpDir,
SAMPLE_DOTCOM_API_DETAILS,
Expand Down
10 changes: 9 additions & 1 deletion src/config-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ async function downloadCacheWithTime(
*/
export async function loadUserConfig(
actionState: ActionState<["Logger", "Env", "FeatureFlags"]>,
analysisKinds: AnalysisKind[],
configFile: string,
workspacePath: string,
apiDetails: api.GitHubApiCombinedDetails,
Expand Down Expand Up @@ -511,7 +512,12 @@ export async function loadUserConfig(
if (isExplicitRemotePath(configFile)) {
configFile = configFile.substring(REMOTE_PATH_PREFIX.length);
}
return await getRemoteConfig(actionState, configFile, apiDetails);
return await getRemoteConfig(
actionState,
analysisKinds,
configFile,
apiDetails,
);
}
}

Expand Down Expand Up @@ -1071,6 +1077,7 @@ export async function determineUserConfig(
);
const fromConfigFile = await loadUserConfig(
action,
inputs.analysisKinds,
inputs.configFile,
inputs.workspacePath,
inputs.apiDetails,
Expand Down Expand Up @@ -1118,6 +1125,7 @@ export async function determineUserConfig(
action.logger.debug(`Using configuration file: ${inputs.configFile}`);
return await loadUserConfig(
action,
inputs.analysisKinds,
inputs.configFile,
inputs.workspacePath,
inputs.apiDetails,
Expand Down
72 changes: 71 additions & 1 deletion src/config/file.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
setupTests,
} from "../testing-utils";

import type { UserConfig } from "./db-config";
import { getConfigFileInput, getRemoteConfig } from "./file";

setupTests(test);
Expand Down Expand Up @@ -137,7 +138,11 @@ test.serial("getRemoteConfig uses proxy when it is supposed to", async (t) => {

const target = callee(getRemoteConfig)
.withDefaultActionsEnv()
.withArgs("file.yml", SAMPLE_DOTCOM_API_DETAILS);
.withArgs(
[AnalysisKind.CodeScanning],
"file.yml",
SAMPLE_DOTCOM_API_DETAILS,
);

// Should use it when the FF is enabled and the environment variables are set.
await target
Expand All @@ -164,3 +169,68 @@ test.serial("getRemoteConfig uses proxy when it is supposed to", async (t) => {
.notLogs(t, "Using private registry proxy at 'http://localhost:1234'")
.throws(t, { message: errorMessage });
});

test.serial("getRemoteConfig replaces meta variables", async (t) => {
const client = github.getOctokit("123");
const response = {
data: {
content: Buffer.from("disable-default-queries: false").toString("base64"),
},
};
sinon.stub(client.rest.repos, "getContent").callsFake((params) => {
if (params?.path.endsWith("$kind.yml")) {
throw new Error(`Unexpected request path: ${params.path}`);
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return response as any;
});

sinon
.stub(api, "getApiClientWithExternalAuth")
.callsFake((_details, _proxy) => {
return client;
});

const target = callee(getRemoteConfig)
.withDefaultActionsEnv()
.withArgs(
[AnalysisKind.CodeScanning],
"owner/repo:file-$kind.yml",
SAMPLE_DOTCOM_API_DETAILS,
);

// Should replace the meta variable if the FF is enabled.
await target
.withFeatures([Feature.RemoteAddressAnalysisMetaVar])
.logs(
t,
"Remote file address after replacing meta variables: owner/repo:file-code-scanning.yml",
)
.passes(t.deepEqual, {
"disable-default-queries": false,
} satisfies UserConfig);

// But not if the FF is off.
await target.throws(t, {
instanceOf: Error,
message: "Unexpected request path: file-$kind.yml",
});

// Or if there are multiple analysis kinds.
await callee(getRemoteConfig)
.withDefaultActionsEnv()
.withArgs(
[AnalysisKind.CodeScanning, AnalysisKind.CodeQuality],
"owner/repo:file-$kind.yml",
SAMPLE_DOTCOM_API_DETAILS,
)
.withFeatures([Feature.RemoteAddressAnalysisMetaVar])
.logs(
t,
`Ignoring '${Feature.RemoteAddressAnalysisMetaVar}' feature, because multiple analysis kinds are enabled.`,
)
.throws(t, {
instanceOf: Error,
message: "Unexpected request path: file-$kind.yml",
});
});
24 changes: 24 additions & 0 deletions src/config/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ export async function getConfigFileInput(
return undefined;
}

/** Replaces supported meta variables in `configFileAddress`. */
export function replaceMetaVars(
configFileAddress: string,
analysisKind: AnalysisKind,
): string {
return configFileAddress.replaceAll("$kind", analysisKind);
}

/**
* Attempts to fetch a `UserConfig` from a remote `address`.
*
Expand All @@ -91,9 +99,25 @@ export async function getConfigFileInput(
*/
export async function getRemoteConfig(
actionState: ActionState<["Logger", "Env", "FeatureFlags"]>,
analysisKinds: AnalysisKind[],
configFile: string,
apiDetails: api.GitHubApiCombinedDetails,
): Promise<UserConfig> {
const supportMetaVar = await actionState.features.getValue(
Feature.RemoteAddressAnalysisMetaVar,
);

if (supportMetaVar && analysisKinds.length === 1) {
configFile = replaceMetaVars(configFile, analysisKinds[0]);
actionState.logger.debug(
`Remote file address after replacing meta variables: ${configFile}`,
);
} else if (supportMetaVar) {
actionState.logger.warning(
`Ignoring '${Feature.RemoteAddressAnalysisMetaVar}' feature, because multiple analysis kinds are enabled.`,
);
}

const address = await parseRemoteFileAddress(actionState, configFile);

const shouldProxyRequest = await actionState.features.getValue(
Expand Down
7 changes: 7 additions & 0 deletions src/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ export enum Feature {
QaTelemetryEnabled = "qa_telemetry_enabled",
/** Routes (some) API requests through the registry proxy. */
ProxyApiRequests = "proxy_api_requests",
/** Adds support for an analysis kind meta variable in remote addresses. */
RemoteAddressAnalysisMetaVar = "remote_address_analysis_meta_var",
/** Note that this currently only disables baseline file coverage information. */
SkipFileCoverageOnPrs = "skip_file_coverage_on_prs",
StartProxyUseFeaturesRelease = "start_proxy_use_features_release",
Expand Down Expand Up @@ -385,6 +387,11 @@ export const featureConfig = {
envVar: "CODEQL_ACTION_PROXY_API_REQUESTS",
minimumVersion: undefined,
},
[Feature.RemoteAddressAnalysisMetaVar]: {
defaultValue: false,
envVar: "CODEQL_ACTION_REMOTE_ADDRESS_ANALYSIS_META_VAR",
minimumVersion: undefined,
},
[Feature.SkipFileCoverageOnPrs]: {
defaultValue: false,
envVar: "CODEQL_ACTION_SKIP_FILE_COVERAGE_ON_PRS",
Expand Down
Loading