Refactor getCodeQLBundleDownloadURL out of setup-codeql

This commit is contained in:
Michael B. Gale
2026-10-06 17:07:48 +01:00
parent 3f57a00554
commit da07d40a0f
5 changed files with 176 additions and 171 deletions
+70 -102
View File
@@ -42967,11 +42967,11 @@ var require_tracingPolicy = __commonJS({
var import_log = require_log3();
var import_core_util = require_commonjs4();
var import_restError = require_restError3();
var import_util58 = require_internal3();
var import_util59 = require_internal3();
var tracingPolicyName2 = "tracingPolicy";
function tracingPolicy2(options = {}) {
const userAgentPromise = (0, import_userAgent.getUserAgentValue)(options.userAgentPrefix);
const sanitizer = new import_util58.Sanitizer({
const sanitizer = new import_util59.Sanitizer({
additionalAllowedQueryParameters: options.additionalAllowedQueryParameters
});
const tracingClient = tryCreateTracingClient();
@@ -46597,11 +46597,11 @@ var require_response2 = __commonJS({
});
module2.exports = __toCommonJS2(response_exports);
var import_core_rest_pipeline = require_commonjs6();
var import_util58 = require_util9();
var import_util59 = require_util9();
var originalResponse = /* @__PURE__ */ Symbol("Original FullOperationResponse");
function toCompatResponse2(response, options) {
let request3 = (0, import_util58.toWebResourceLike)(response.request);
let headers = (0, import_util58.toHttpHeadersLike)(response.headers);
let request3 = (0, import_util59.toWebResourceLike)(response.request);
let headers = (0, import_util59.toHttpHeadersLike)(response.headers);
if (options?.createProxy) {
return new Proxy(response, {
get(target, prop, receiver) {
@@ -46642,7 +46642,7 @@ var require_response2 = __commonJS({
return {
...compatResponse,
headers,
request: (0, import_util58.toPipelineRequest)(compatResponse.request)
request: (0, import_util59.toPipelineRequest)(compatResponse.request)
};
}
}
@@ -46749,7 +46749,7 @@ var require_requestPolicyFactoryPolicy = __commonJS({
requestPolicyFactoryPolicyName: () => requestPolicyFactoryPolicyName2
});
module2.exports = __toCommonJS2(requestPolicyFactoryPolicy_exports);
var import_util58 = require_util9();
var import_util59 = require_util9();
var import_response = require_response2();
var HttpPipelineLogLevel2 = /* @__PURE__ */ ((HttpPipelineLogLevel22) => {
HttpPipelineLogLevel22[HttpPipelineLogLevel22["ERROR"] = 1] = "ERROR";
@@ -46773,14 +46773,14 @@ var require_requestPolicyFactoryPolicy = __commonJS({
async sendRequest(request3, next) {
let httpPipeline = {
async sendRequest(httpRequest) {
const response2 = await next((0, import_util58.toPipelineRequest)(httpRequest));
const response2 = await next((0, import_util59.toPipelineRequest)(httpRequest));
return (0, import_response.toCompatResponse)(response2, { createProxy: true });
}
};
for (const factory of orderedFactories) {
httpPipeline = factory.create(httpPipeline, mockRequestPolicyOptions);
}
const webResourceLike = (0, import_util58.toWebResourceLike)(request3, { createProxy: true });
const webResourceLike = (0, import_util59.toWebResourceLike)(request3, { createProxy: true });
const response = await httpPipeline.sendRequest(webResourceLike);
return (0, import_response.toPipelineResponse)(response);
}
@@ -46815,12 +46815,12 @@ var require_httpClientAdapter = __commonJS({
});
module2.exports = __toCommonJS2(httpClientAdapter_exports);
var import_response = require_response2();
var import_util58 = require_util9();
var import_util59 = require_util9();
function convertHttpClient2(requestPolicyClient) {
return {
sendRequest: async (request3) => {
const response = await requestPolicyClient.sendRequest(
(0, import_util58.toWebResourceLike)(request3, { createProxy: true })
(0, import_util59.toWebResourceLike)(request3, { createProxy: true })
);
return (0, import_response.toPipelineResponse)(response);
}
@@ -46858,7 +46858,7 @@ var require_commonjs9 = __commonJS({
disableKeepAlivePolicyName: () => import_disableKeepAlivePolicy.disableKeepAlivePolicyName,
requestPolicyFactoryPolicyName: () => import_requestPolicyFactoryPolicy.requestPolicyFactoryPolicyName,
toCompatResponse: () => import_response.toCompatResponse,
toHttpHeadersLike: () => import_util58.toHttpHeadersLike
toHttpHeadersLike: () => import_util59.toHttpHeadersLike
});
module2.exports = __toCommonJS2(src_exports);
var import_extendedClient = require_extendedClient();
@@ -46866,7 +46866,7 @@ var require_commonjs9 = __commonJS({
var import_requestPolicyFactoryPolicy = require_requestPolicyFactoryPolicy();
var import_disableKeepAlivePolicy = require_disableKeepAlivePolicy();
var import_httpClientAdapter = require_httpClientAdapter();
var import_util58 = require_util9();
var import_util59 = require_util9();
}
});
@@ -107096,41 +107096,6 @@ var require_tool_cache = __commonJS({
}
});
// node_modules/fast-deep-equal/index.js
var require_fast_deep_equal = __commonJS({
"node_modules/fast-deep-equal/index.js"(exports2, module2) {
"use strict";
module2.exports = function equal(a, b) {
if (a === b) return true;
if (a && b && typeof a == "object" && typeof b == "object") {
if (a.constructor !== b.constructor) return false;
var length, i, keys;
if (Array.isArray(a)) {
length = a.length;
if (length != b.length) return false;
for (i = length; i-- !== 0; )
if (!equal(a[i], b[i])) return false;
return true;
}
if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
keys = Object.keys(a);
length = keys.length;
if (length !== Object.keys(b).length) return false;
for (i = length; i-- !== 0; )
if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
for (i = length; i-- !== 0; ) {
var key = keys[i];
if (!equal(a[key], b[key])) return false;
}
return true;
}
return a !== a && b !== b;
};
}
});
// node_modules/follow-redirects/debug.js
var require_debug3 = __commonJS({
"node_modules/follow-redirects/debug.js"(exports2, module2) {
@@ -175209,7 +175174,6 @@ var path13 = __toESM(require("path"));
var import_perf_hooks4 = require("perf_hooks");
var core12 = __toESM(require_core());
var toolcache3 = __toESM(require_tool_cache());
var import_fast_deep_equal = __toESM(require_fast_deep_equal());
var semver10 = __toESM(require_semver2());
// src/codeql-bundle.ts
@@ -175645,6 +175609,55 @@ function getCodeQLActionRepository(action) {
}
return action.env.getRequired("GITHUB_ACTION_REPOSITORY" /* GITHUB_ACTION_REPOSITORY */);
}
var DEFAULT_DOWNLOAD_SOURCE = [
GITHUB_DOTCOM_URL,
CODEQL_DEFAULT_ACTION_REPOSITORY
];
var isSameDownloadSource = ([srcUrl, srcRepo], [otherUrl, otherRepo]) => {
return srcUrl === otherUrl && srcRepo === otherRepo;
};
async function getCodeQLAssetDownloadURL(action, apiDetails, tagName, assetName, assetKind = "CodeQL bundle") {
const codeQLActionRepository = getCodeQLActionRepository(action);
const potentialDownloadSources = [
// This GitHub instance, and this Action.
[apiDetails.url, codeQLActionRepository],
// This GitHub instance, and the canonical Action.
[apiDetails.url, CODEQL_DEFAULT_ACTION_REPOSITORY],
// GitHub.com, and the canonical Action.
DEFAULT_DOWNLOAD_SOURCE
];
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])) {
break;
}
const [repositoryOwner, repositoryName] = repository.split("/");
try {
const release2 = await getApiClient().rest.repos.getReleaseByTag({
owner: repositoryOwner,
repo: repositoryName,
tag: tagName
});
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}.`
);
return asset.url;
}
}
} catch (e) {
action.logger.info(
`Looked for ${assetKind} ${assetName} in ${repository} on ${apiURL} but got error ${e}.`
);
}
}
return `https://github.com/${CODEQL_DEFAULT_ACTION_REPOSITORY}/releases/download/${tagName}/${assetName}`;
}
// src/tar.ts
var import_child_process = require("child_process");
@@ -176042,52 +176055,6 @@ var CODEQL_NIGHTLIES_REPOSITORY_NAME = "codeql-cli-nightlies";
var CODEQL_BUNDLE_VERSION_ALIAS = ["linked", "latest"];
var CODEQL_NIGHTLY_TOOLS_INPUTS = ["nightly", "nightly-latest"];
var CODEQL_TOOLCACHE_INPUT = "toolcache";
async function getCodeQLBundleDownloadURL(tagName, apiDetails, codeQLBundleName, logger2) {
const codeQLActionRepository = getCodeQLActionRepository({
logger: logger2,
env: getEnv()
});
const potentialDownloadSources = [
// This GitHub instance, and this Action.
[apiDetails.url, codeQLActionRepository],
// This GitHub instance, and the canonical Action.
[apiDetails.url, CODEQL_DEFAULT_ACTION_REPOSITORY],
// GitHub.com, and the canonical Action.
[GITHUB_DOTCOM_URL, CODEQL_DEFAULT_ACTION_REPOSITORY]
];
const uniqueDownloadSources = potentialDownloadSources.filter(
(source, index2, self2) => {
return !self2.slice(0, index2).some((other) => (0, import_fast_deep_equal.default)(source, other));
}
);
for (const downloadSource of uniqueDownloadSources) {
const [apiURL, repository] = downloadSource;
if (apiURL === GITHUB_DOTCOM_URL && repository === CODEQL_DEFAULT_ACTION_REPOSITORY) {
break;
}
const [repositoryOwner, repositoryName] = repository.split("/");
try {
const release2 = await getApiClient().rest.repos.getReleaseByTag({
owner: repositoryOwner,
repo: repositoryName,
tag: tagName
});
for (const asset of release2.data.assets) {
if (asset.name === codeQLBundleName) {
logger2.info(
`Found CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} with URL ${asset.url}.`
);
return asset.url;
}
}
} catch (e) {
logger2.info(
`Looked for CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} but got error ${e}.`
);
}
}
return `https://github.com/${CODEQL_DEFAULT_ACTION_REPOSITORY}/releases/download/${tagName}/${codeQLBundleName}`;
}
function tryGetBundleVersionFromTagName(tagName, logger2) {
const match2 = tagName.match(/^codeql-bundle-(.+)$/);
if (match2 === null || match2.length < 2) {
@@ -176444,9 +176411,10 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
);
}
compressionMethod = cliVersion2 !== void 0 && await useZstdBundle(cliVersion2, tarSupportsZstd) ? "zstd" : "gzip";
const action = { env: getEnv(), logger: logger2 };
const platform2 = getBundlePlatform();
const perLanguageBundleLanguage = await getPerLanguageBundleLanguage(
{ env: getEnv(), features, logger: logger2 },
{ ...action, features },
{
rawLanguages,
cliVersion: cliVersion2,
@@ -176455,11 +176423,11 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
variant
}
);
const resolveBundleURL = (language) => getCodeQLBundleDownloadURL(
bundleTagName,
const resolveBundleURL = (language) => getCodeQLAssetDownloadURL(
action,
apiDetails,
getCodeQLBundleName(compressionMethod, platform2, language),
logger2
bundleTagName,
getCodeQLBundleName(compressionMethod, platform2, language)
);
const combinedBundleURL = await resolveBundleURL();
if (perLanguageBundleLanguage !== void 0) {
@@ -182926,7 +182894,7 @@ var import_async = __toESM(require_async(), 1);
var import_path7 = require("path");
// node_modules/archiver/lib/error.js
var import_util36 = __toESM(require("util"), 1);
var import_util37 = __toESM(require("util"), 1);
var ERROR_CODES = {
ABORTED: "archive was aborted",
DIRECTORYDIRPATHREQUIRED: "diretory dirpath argument must be a non-empty string value",
@@ -182951,7 +182919,7 @@ function ArchiverError(code, data) {
this.code = code;
this.data = data;
}
import_util36.default.inherits(ArchiverError, Error);
import_util37.default.inherits(ArchiverError, Error);
// node_modules/archiver/lib/core.js
var import_readable_stream2 = __toESM(require_ours(), 1);
+1 -1
View File
@@ -27,7 +27,6 @@
"@octokit/plugin-rest-endpoint-methods": "^18.0.0",
"@octokit/plugin-retry": "^8.1.1",
"archiver": "^8.0.0",
"fast-deep-equal": "^3.1.3",
"follow-redirects": "^1.16.0",
"get-folder-size": "^5.0.0",
"https-proxy-agent": "^7.0.6",
@@ -5817,6 +5816,7 @@
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"dev": true,
"license": "MIT"
},
"node_modules/fast-diff": {
-1
View File
@@ -35,7 +35,6 @@
"@octokit/plugin-rest-endpoint-methods": "^18.0.0",
"@octokit/plugin-retry": "^8.1.1",
"archiver": "^8.0.0",
"fast-deep-equal": "^3.1.3",
"follow-redirects": "^1.16.0",
"get-folder-size": "^5.0.0",
"https-proxy-agent": "^7.0.6",
+6 -67
View File
@@ -5,7 +5,6 @@ import { performance } from "perf_hooks";
import * as core from "@actions/core";
import * as toolcache from "@actions/tool-cache";
import { default as deepEqual } from "fast-deep-equal";
import * as semver from "semver";
import { v4 as uuidV4 } from "uuid";
@@ -44,10 +43,7 @@ import {
logPerLanguageBundleFallback,
} from "./per-language-bundles";
import { getBundlePlatform } from "./platform";
import {
CODEQL_DEFAULT_ACTION_REPOSITORY,
getCodeQLActionRepository,
} from "./setup/repository";
import { getCodeQLAssetDownloadURL } from "./setup/repository";
import * as tar from "./tar";
import {
deleteToolcacheBundles,
@@ -76,64 +72,6 @@ const CODEQL_BUNDLE_VERSION_ALIAS: string[] = ["linked", "latest"];
const CODEQL_NIGHTLY_TOOLS_INPUTS = ["nightly", "nightly-latest"];
const CODEQL_TOOLCACHE_INPUT = "toolcache";
async function getCodeQLBundleDownloadURL(
tagName: string,
apiDetails: api.GitHubApiDetails,
codeQLBundleName: string,
logger: Logger,
): Promise<string> {
const codeQLActionRepository = getCodeQLActionRepository({
logger,
env: getEnv(),
});
const potentialDownloadSources = [
// This GitHub instance, and this Action.
[apiDetails.url, codeQLActionRepository],
// This GitHub instance, and the canonical Action.
[apiDetails.url, CODEQL_DEFAULT_ACTION_REPOSITORY],
// GitHub.com, and the canonical Action.
[util.GITHUB_DOTCOM_URL, CODEQL_DEFAULT_ACTION_REPOSITORY],
];
// We now filter out any duplicates.
// Duplicates will happen either because the GitHub instance is GitHub.com, or because the Action is not a fork.
const uniqueDownloadSources = potentialDownloadSources.filter(
(source, index, self) => {
return !self.slice(0, index).some((other) => deepEqual(source, other));
},
);
for (const downloadSource of uniqueDownloadSources) {
const [apiURL, repository] = downloadSource;
// If we've reached the final case, short-circuit the API check since we know the bundle exists and is public.
if (
apiURL === util.GITHUB_DOTCOM_URL &&
repository === CODEQL_DEFAULT_ACTION_REPOSITORY
) {
break;
}
const [repositoryOwner, repositoryName] = repository.split("/");
try {
const release = await api.getApiClient().rest.repos.getReleaseByTag({
owner: repositoryOwner,
repo: repositoryName,
tag: tagName,
});
for (const asset of release.data.assets) {
if (asset.name === codeQLBundleName) {
logger.info(
`Found CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} with URL ${asset.url}.`,
);
return asset.url;
}
}
} catch (e) {
logger.info(
`Looked for CodeQL bundle ${codeQLBundleName} in ${repository} on ${apiURL} but got error ${e}.`,
);
}
}
return `https://github.com/${CODEQL_DEFAULT_ACTION_REPOSITORY}/releases/download/${tagName}/${codeQLBundleName}`;
}
function tryGetBundleVersionFromTagName(
tagName: string,
logger: Logger,
@@ -707,9 +645,10 @@ export async function getCodeQLSource(
? "zstd"
: "gzip";
const action = { env: getEnv(), logger };
const platform = getBundlePlatform();
const perLanguageBundleLanguage = await getPerLanguageBundleLanguage(
{ env: getEnv(), features, logger },
{ ...action, features },
{
rawLanguages,
cliVersion,
@@ -721,11 +660,11 @@ export async function getCodeQLSource(
// Resolves the combined or per-language bundle URL for the requested release.
const resolveBundleURL = (language?: BuiltInLanguage) =>
getCodeQLBundleDownloadURL(
bundleTagName,
getCodeQLAssetDownloadURL(
action,
apiDetails,
bundleTagName,
getCodeQLBundleName(compressionMethod, platform, language),
logger,
);
const combinedBundleURL = await resolveBundleURL();
+99
View File
@@ -1,6 +1,8 @@
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 "../util";
/** The NWO of the standard CodeQL Action repository. */
export const CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action";
@@ -28,3 +30,100 @@ export function getCodeQLActionRepository(
return action.env.getRequired(ActionsEnvVars.GITHUB_ACTION_REPOSITORY);
}
/**A download source is a pair of strings. */
export type DownloadSource = [string, string];
/** GitHub.com, and the canonical Action. */
export const DEFAULT_DOWNLOAD_SOURCE: DownloadSource = [
GITHUB_DOTCOM_URL,
CODEQL_DEFAULT_ACTION_REPOSITORY,
];
/** Decides if the two provided download sources are the same. */
const isSameDownloadSource = (
[srcUrl, srcRepo]: DownloadSource,
[otherUrl, otherRepo]: DownloadSource,
) => {
return srcUrl === otherUrl && srcRepo === otherRepo;
};
/** A download URL is represented as a string. */
export type DownloadURL = string;
/**
* Tries to find a download URL for `assetName` in a release tagged with `tagName`.
*
* Depending on where and how the CodeQL Action is running, we may be using different `apiDetails` and there may be
* different options for where to source CodeQL releases from.
*
* This function either returns the download URL for the asset for the first release we find, or
* defaults to the assumed download URL for the asset on the default CodeQL Action repository on GitHub.com.
* In the latter case, this function does not guarantee that the asset actually exists.
*
* @param action The Action state.
* @param apiDetails The details of the GitHub API in use.
* @param tagName The name of the release tag we want to obtain the asset from.
* @param assetName The name of the asset we should look for in the release.
* @param [assetKind="CodeQL bundle"] The kind of asset we are looking for to show in log messages.
* @returns A URL that we can use to download the asset.
*/
export async function getCodeQLAssetDownloadURL(
action: ActionState<["ReadOnlyEnv", "Logger"]>,
apiDetails: api.GitHubApiDetails,
tagName: string,
assetName: string,
assetKind: "CodeQL bundle" = "CodeQL bundle",
): Promise<DownloadURL> {
const codeQLActionRepository = getCodeQLActionRepository(action);
const potentialDownloadSources: Array<[string, string]> = [
// This GitHub instance, and this Action.
[apiDetails.url, codeQLActionRepository],
// This GitHub instance, and the canonical Action.
[apiDetails.url, CODEQL_DEFAULT_ACTION_REPOSITORY],
// GitHub.com, and the canonical Action.
DEFAULT_DOWNLOAD_SOURCE,
];
// We now filter out any duplicates.
// Duplicates will happen either because the GitHub instance is GitHub.com, or because the Action is not a fork.
const uniqueDownloadSources = potentialDownloadSources.filter(
(source, index, self) => {
return !self
.slice(0, index)
.some((other) => isSameDownloadSource(source, other));
},
);
for (const [apiURL, repository] of uniqueDownloadSources) {
// If we've reached the final case, short-circuit the API check since we know the bundle exists and is public.
if (isSameDownloadSource(DEFAULT_DOWNLOAD_SOURCE, [apiURL, repository])) {
break;
}
const [repositoryOwner, repositoryName] = repository.split("/");
try {
const release = await api.getApiClient().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}.`,
);
return asset.url;
}
}
} catch (e) {
action.logger.info(
`Looked for ${assetKind} ${assetName} in ${repository} on ${apiURL} but got error ${e}.`,
);
}
}
return `https://github.com/${CODEQL_DEFAULT_ACTION_REPOSITORY}/releases/download/${tagName}/${assetName}`;
}