Use instance in DownloadSource and pass it on to getApiClient

This commit is contained in:
Michael B. Gale
2026-10-13 18:39:27 +01:00
parent 39358b5a0d
commit 74a138c285
6 changed files with 95 additions and 54 deletions

49
lib/entry-points.js generated
View File

@@ -170602,6 +170602,7 @@ var import_undici = __toESM(require_undici());
// src/github.ts
var GITHUB_DOTCOM_URL = "https://github.com";
var GITHUB_DOTCOM_API_URL = "https://api.github.com";
function parseGitHubUrl(inputUrl) {
const originalUrl = inputUrl;
if (inputUrl.indexOf("://") === -1) {
@@ -170636,6 +170637,12 @@ function getCurrentInstance(env) {
apiURL: env.getRequired("GITHUB_API_URL" /* GITHUB_API_URL */)
};
}
function getDotcomInstance() {
return {
url: GITHUB_DOTCOM_URL,
apiURL: GITHUB_DOTCOM_API_URL
};
}
// src/repository.ts
function isRepositoryNwoStr(val) {
@@ -170728,8 +170735,18 @@ function getApiDetails(env = getEnv()) {
...getCurrentInstance(env)
};
}
function getApiClient(env = getEnv()) {
return createApiClientWithDetails(getApiDetails(env));
function getDotcomApiDetails(env) {
if (env.getRequired("GITHUB_SERVER_URL" /* GITHUB_SERVER_URL */) === GITHUB_DOTCOM_URL) {
return getApiDetails(env);
} else {
return {
auth: void 0,
...getDotcomInstance()
};
}
}
function getApiClient(env = getEnv(), apiDetails) {
return createApiClientWithDetails(apiDetails ?? getApiDetails(env));
}
function getApiClientWithExternalAuth(apiDetails, proxy) {
return createApiClientWithDetails(apiDetails, { allowExternal: true, proxy });
@@ -175732,35 +175749,35 @@ function getCodeQLActionRepository(action) {
`The value of '${"GITHUB_ACTION_REPOSITORY" /* GITHUB_ACTION_REPOSITORY */}' is '${actionRepository}', which does not match the expected 'owner/repo' format.`
);
}
var DEFAULT_DOWNLOAD_SOURCE = [
GITHUB_DOTCOM_URL,
CODEQL_DEFAULT_ACTION_REPOSITORY
];
var isSameDownloadSource = ([srcUrl, srcRepo], [otherUrl, otherRepo]) => {
return srcUrl === otherUrl && srcRepo === otherRepo;
var isSameDownloadSource = ([srcInstance, srcRepo], [otherInstance, otherRepo]) => {
return srcInstance.url === otherInstance.url && srcRepo === otherRepo;
};
async function getCodeQLAssetDownloadURL(action, apiDetails, tagName, assetName, assetKind = "CodeQL bundle") {
const codeQLActionRepository = getCodeQLActionRepository(action);
const defaultDownloadSource = [
getDotcomApiDetails(action.env),
CODEQL_DEFAULT_ACTION_REPOSITORY
];
const potentialDownloadSources = [
// This GitHub instance, and this Action.
[apiDetails.url, codeQLActionRepository],
[apiDetails, codeQLActionRepository],
// This GitHub instance, and the canonical Action.
[apiDetails.url, CODEQL_DEFAULT_ACTION_REPOSITORY],
[apiDetails, CODEQL_DEFAULT_ACTION_REPOSITORY],
// GitHub.com, and the canonical Action.
DEFAULT_DOWNLOAD_SOURCE
defaultDownloadSource
];
const uniqueDownloadSources = potentialDownloadSources.filter(
(source, index2, self2) => {
return !self2.slice(0, index2).some((other) => isSameDownloadSource(source, other));
}
);
for (const [apiURL, repository] of uniqueDownloadSources) {
if (isSameDownloadSource(DEFAULT_DOWNLOAD_SOURCE, [apiURL, repository])) {
for (const [instance8, repository] of uniqueDownloadSources) {
if (isSameDownloadSource(defaultDownloadSource, [instance8, repository])) {
break;
}
const [repositoryOwner, repositoryName] = repository.split("/");
try {
const release2 = await getApiClient().rest.repos.getReleaseByTag({
const release2 = await getApiClient(action.env, instance8).rest.repos.getReleaseByTag({
owner: repositoryOwner,
repo: repositoryName,
tag: tagName
@@ -175768,14 +175785,14 @@ async function getCodeQLAssetDownloadURL(action, apiDetails, tagName, assetName,
for (const asset of release2.data.assets) {
if (asset.name === assetName) {
action.logger.info(
`Found ${assetKind} ${assetName} in ${repository} on ${apiURL} with URL ${asset.url}.`
`Found ${assetKind} ${assetName} in ${repository} on ${instance8.apiURL} with URL ${asset.url}.`
);
return asset.url;
}
}
} catch (e) {
action.logger.info(
`Looked for ${assetKind} ${assetName} in ${repository} on ${apiURL} but got error ${e}.`
`Looked for ${assetKind} ${assetName} in ${repository} on ${instance8.apiURL} but got error ${e}.`
);
}
}

View File

@@ -193,8 +193,11 @@ export function getDotcomApiDetails(env: ReadOnlyEnv): GitHubApiDetails {
}
}
export function getApiClient(env: ReadOnlyEnv = getEnv()) {
return createApiClientWithDetails(getApiDetails(env));
export function getApiClient(
env: ReadOnlyEnv = getEnv(),
apiDetails?: GitHubApiDetails,
) {
return createApiClientWithDetails(apiDetails ?? getApiDetails(env));
}
export function getApiClientWithExternalAuth(

View File

@@ -34,6 +34,7 @@ import {
makeVersionInfo,
createTestConfig,
makeMacro,
mockApiDetails,
} from "./testing-utils";
import { ToolsDownloadStatusReport } from "./tools-download";
import * as util from "./util";
@@ -126,19 +127,6 @@ function mockReleaseApi({
});
}
function mockApiDetails(apiDetails: Required<GitHubApiDetails>) {
// This is a workaround to mock `api.getApiDetails()` since it doesn't seem to be possible to
// mock this directly. The difficulty is that `getApiDetails()` is called locally in
// `api-client.ts`, but `sinon.stub(api, "getApiDetails")` only affects calls to
// `getApiDetails()` via an imported `api` module.
sinon
.stub(actionsUtil, "getRequiredInput")
.withArgs("token")
.returns(apiDetails.auth);
process.env["GITHUB_SERVER_URL"] = apiDetails.url;
process.env["GITHUB_API_URL"] = apiDetails.apiURL || "";
}
async function stubCodeql(): Promise<codeql.CodeQL> {
const codeqlObject = await codeql.getCodeQLForTesting();
sinon.stub(codeqlObject, "getVersion").resolves(makeVersionInfo("2.17.6"));
@@ -290,6 +278,7 @@ for (const { cliVersion, tagName, expectedToolcacheVersion } of [
async (t) => {
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
const result = await installIntoToolcache({
cliVersion,
@@ -470,6 +459,7 @@ test.serial(
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
await installIntoToolcache({
tagName: "codeql-bundle-20200601",
@@ -517,6 +507,7 @@ test.serial(
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
await installIntoToolcache({
tagName: "codeql-bundle-20200601",

View File

@@ -32,6 +32,7 @@ import {
getTestEnv,
initAllState,
makeMacro,
mockApiDetails,
mockBundleDownloadApi,
setupActionsVars,
setupTests,
@@ -221,6 +222,7 @@ for (const {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
const source = await setupCodeql.getCodeQLSource(
"linked",
SAMPLE_DEFAULT_CLI_VERSION,
@@ -255,6 +257,7 @@ test.serial(
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
const source = await setupCodeql.getCodeQLSource(
"latest",
SAMPLE_DEFAULT_CLI_VERSION,
@@ -298,6 +301,7 @@ test.serial(
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
const result = await setupCodeql.setupCodeQLBundle(
"linked",
SAMPLE_DOTCOM_API_DETAILS,
@@ -479,6 +483,7 @@ test.serial(
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir, { GITHUB_EVENT_NAME: "dynamic" });
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
const source = await setupCodeql.getCodeQLSource(
undefined,
@@ -624,6 +629,7 @@ for (const toolsInput of ["nightly", "nightly-latest"]) {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
const source = await setupCodeql.getCodeQLSource(
toolsInput,
SAMPLE_DEFAULT_CLI_VERSION,
@@ -945,6 +951,7 @@ const toolcacheInputFallbackMacro = makeMacro({
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
for (const [k, v] of Object.entries(environment)) {
process.env[k] = v;
@@ -1165,6 +1172,7 @@ test.serial(
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
const source = await setupCodeql.getCodeQLSource(
undefined,
PER_LANGUAGE_CLI_VERSION,
@@ -1207,6 +1215,7 @@ test.serial(
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
for (const { otherLanguagePacksReason, features } of [
// The per-language feature is disabled.
{ otherLanguagePacksReason: undefined, features: createFeatures([]) },
@@ -1286,6 +1295,7 @@ for (const fallback of [false, true]) {
setupActionsVars(tmpDir, tmpDir, {
GITHUB_ACTION_REPOSITORY: "codeql-testing/action-fork",
});
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
const result = await setupCodeql.setupCodeQLBundle(
undefined,
SAMPLE_DOTCOM_API_DETAILS,
@@ -1553,6 +1563,7 @@ for (const error of [
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
await t.throwsAsync(
setupCodeql.setupCodeQLBundle(
undefined,
@@ -2259,6 +2270,7 @@ test.serial(
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
delete process.env[EnvVar.HAS_SET_UP_CODEQL];
stubDownloadAndExtract();
@@ -2294,6 +2306,7 @@ test.serial(
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
mockApiDetails(SAMPLE_DOTCOM_API_DETAILS);
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
delete process.env[EnvVar.HAS_SET_UP_CODEQL];
const staleDirectory = createToolcacheEntry(

View File

@@ -2,7 +2,6 @@ import { ActionState } from "../action-common";
import { isRunningLocalAction } from "../actions-util";
import * as api from "../api-client";
import { ActionsEnvVars } from "../environment";
import { GITHUB_DOTCOM_URL } from "../github";
import { isRepositoryNwoStr, type RepositoryNwoStr } from "../repository";
/** The NWO of the standard CodeQL Action repository. */
@@ -43,20 +42,14 @@ export function getCodeQLActionRepository(
}
/**A download source is a pair of strings. */
export type DownloadSource = [string, RepositoryNwoStr];
/** GitHub.com, and the canonical Action. */
export const DEFAULT_DOWNLOAD_SOURCE: DownloadSource = [
GITHUB_DOTCOM_URL,
CODEQL_DEFAULT_ACTION_REPOSITORY,
];
export type DownloadSource = [api.GitHubApiDetails, RepositoryNwoStr];
/** Decides if the two provided download sources are the same. */
const isSameDownloadSource = (
[srcUrl, srcRepo]: DownloadSource,
[otherUrl, otherRepo]: DownloadSource,
[srcInstance, srcRepo]: DownloadSource,
[otherInstance, otherRepo]: DownloadSource,
) => {
return srcUrl === otherUrl && srcRepo === otherRepo;
return srcInstance.url === otherInstance.url && srcRepo === otherRepo;
};
/** A download URL is represented as a string. */
@@ -88,13 +81,21 @@ export async function getCodeQLAssetDownloadURL(
): Promise<DownloadURL> {
const codeQLActionRepository = getCodeQLActionRepository(action);
// We construct the default GitHub.com source here, because `getDotcomApiDetails`
// may retrieve the `token` input if we are currently running on GitHub.com and
// we want to avoid doing that statically.
const defaultDownloadSource: DownloadSource = [
api.getDotcomApiDetails(action.env),
CODEQL_DEFAULT_ACTION_REPOSITORY,
];
const potentialDownloadSources: DownloadSource[] = [
// This GitHub instance, and this Action.
[apiDetails.url, codeQLActionRepository],
[apiDetails, codeQLActionRepository],
// This GitHub instance, and the canonical Action.
[apiDetails.url, CODEQL_DEFAULT_ACTION_REPOSITORY],
[apiDetails, CODEQL_DEFAULT_ACTION_REPOSITORY],
// GitHub.com, and the canonical Action.
DEFAULT_DOWNLOAD_SOURCE,
defaultDownloadSource,
];
// We now filter out any duplicates.
@@ -107,31 +108,33 @@ export async function getCodeQLAssetDownloadURL(
},
);
for (const [apiURL, repository] of uniqueDownloadSources) {
for (const [instance, repository] of uniqueDownloadSources) {
// If we've reached the final case, short-circuit the API check since we know the asset exists and is public.
if (isSameDownloadSource(DEFAULT_DOWNLOAD_SOURCE, [apiURL, repository])) {
if (isSameDownloadSource(defaultDownloadSource, [instance, repository])) {
break;
}
const [repositoryOwner, repositoryName] = repository.split("/");
try {
const release = await api.getApiClient().rest.repos.getReleaseByTag({
owner: repositoryOwner,
repo: repositoryName,
tag: tagName,
});
const release = await api
.getApiClient(action.env, instance)
.rest.repos.getReleaseByTag({
owner: repositoryOwner,
repo: repositoryName,
tag: tagName,
});
for (const asset of release.data.assets) {
if (asset.name === assetName) {
action.logger.info(
`Found ${assetKind} ${assetName} in ${repository} on ${apiURL} with URL ${asset.url}.`,
`Found ${assetKind} ${assetName} in ${repository} on ${instance.apiURL} with URL ${asset.url}.`,
);
return asset.url;
}
}
} catch (e) {
action.logger.info(
`Looked for ${assetKind} ${assetName} in ${repository} on ${apiURL} but got error ${e}.`,
`Looked for ${assetKind} ${assetName} in ${repository} on ${instance.apiURL} but got error ${e}.`,
);
}
}

View File

@@ -14,6 +14,7 @@ import * as sinon from "sinon";
import { ActionState, StateFeature } from "./action-common";
import { ActionsEnv, getActionVersion } from "./actions-util";
import * as actionsUtil from "./actions-util";
import { AnalysisKind } from "./analyses";
import * as apiClient from "./api-client";
import { GitHubApiDetails } from "./api-client";
@@ -52,6 +53,19 @@ export const SAMPLE_DOTCOM_API_DETAILS = {
apiURL: "https://api.github.com",
};
export function mockApiDetails(apiDetails: Required<GitHubApiDetails>) {
// This is a workaround to mock `api.getApiDetails()` since it doesn't seem to be possible to
// mock this directly. The difficulty is that `getApiDetails()` is called locally in
// `api-client.ts`, but `sinon.stub(api, "getApiDetails")` only affects calls to
// `getApiDetails()` via an imported `api` module.
sinon
.stub(actionsUtil, "getRequiredInput")
.withArgs("token")
.returns(apiDetails.auth);
process.env["GITHUB_SERVER_URL"] = apiDetails.url;
process.env["GITHUB_API_URL"] = apiDetails.apiURL || "";
}
export const LINKED_CLI_VERSION = {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,