From 7538fa2081b0390132157106cb1de472d1a192d9 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Tue, 13 Oct 2026 14:42:12 +0100 Subject: [PATCH] Move some definitions out of `util.ts` into a new `github.ts` module --- lib/entry-points.js | 90 +++++++++++++++++++++--------------------- src/api-client.ts | 3 +- src/database-upload.ts | 3 +- src/github.test.ts | 69 ++++++++++++++++++++++++++++++++ src/github.ts | 62 +++++++++++++++++++++++++++++ src/setup-codeql.ts | 5 ++- src/status-report.ts | 2 +- src/util.test.ts | 60 ---------------------------- src/util.ts | 48 ---------------------- 9 files changed, 184 insertions(+), 158 deletions(-) create mode 100644 src/github.test.ts create mode 100644 src/github.ts diff --git a/lib/entry-points.js b/lib/entry-points.js index 4bda95685..da8986fc5 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -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(); } }); @@ -169587,7 +169587,6 @@ function checkSchema(schema, obj, options = {}, path30 = "") { // src/util.ts var BASE_DATABASE_OIDS_FILE_NAME = "base-database-oids.json"; var BROKEN_VERSIONS = ["0.0.0-20211207"]; -var GITHUB_DOTCOM_URL = "https://github.com"; var DEFAULT_DEBUG_ARTIFACT_NAME = "debug-artifacts"; var DEFAULT_DEBUG_DATABASE_NAME = "db"; var DEFAULT_RESERVED_RAM_SCALING_FACTOR = 0.05; @@ -169803,34 +169802,6 @@ function getGeneratedSuitePath(config, language) { "config-queries.qls" ); } -function parseGitHubUrl(inputUrl) { - const originalUrl = inputUrl; - if (inputUrl.indexOf("://") === -1) { - inputUrl = `https://${inputUrl}`; - } - if (!inputUrl.startsWith("http://") && !inputUrl.startsWith("https://")) { - throw new ConfigurationError(`"${originalUrl}" is not a http or https URL`); - } - let url2; - try { - url2 = new URL(inputUrl); - } catch { - throw new ConfigurationError(`"${originalUrl}" is not a valid URL`); - } - if (url2.hostname === "github.com" || url2.hostname === "api.github.com") { - return GITHUB_DOTCOM_URL; - } - if (url2.pathname.indexOf("/api/v3") !== -1) { - url2.pathname = url2.pathname.substring(0, url2.pathname.indexOf("/api/v3")); - } - if (url2.hostname.startsWith("api.")) { - url2.hostname = url2.hostname.substring(4); - } - if (!url2.pathname.endsWith("/")) { - url2.pathname = `${url2.pathname}/`; - } - return url2.toString(); -} var CODEQL_ACTION_WARNED_ABOUT_VERSION_ENV_VAR = "CODEQL_ACTION_WARNED_ABOUT_VERSION"; var hasBeenWarnedAboutVersion = false; function checkGitHubVersionInRange(version, logger2) { @@ -170688,6 +170659,37 @@ retry.VERSION = VERSION7; // src/api-client.ts var import_undici = __toESM(require_undici()); +// src/github.ts +var GITHUB_DOTCOM_URL = "https://github.com"; +function parseGitHubUrl(inputUrl) { + const originalUrl = inputUrl; + if (inputUrl.indexOf("://") === -1) { + inputUrl = `https://${inputUrl}`; + } + if (!inputUrl.startsWith("http://") && !inputUrl.startsWith("https://")) { + throw new ConfigurationError(`"${originalUrl}" is not a http or https URL`); + } + let url2; + try { + url2 = new URL(inputUrl); + } catch { + throw new ConfigurationError(`"${originalUrl}" is not a valid URL`); + } + if (url2.hostname === "github.com" || url2.hostname === "api.github.com") { + return GITHUB_DOTCOM_URL; + } + if (url2.pathname.indexOf("/api/v3") !== -1) { + url2.pathname = url2.pathname.substring(0, url2.pathname.indexOf("/api/v3")); + } + if (url2.hostname.startsWith("api.")) { + url2.hostname = url2.hostname.substring(4); + } + if (!url2.pathname.endsWith("/")) { + url2.pathname = `${url2.pathname}/`; + } + return url2.toString(); +} + // src/repository.ts function getRepositoryNwo() { return getRepositoryNwoFromEnv("GITHUB_REPOSITORY"); @@ -183046,7 +183048,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", @@ -183071,7 +183073,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); diff --git a/src/api-client.ts b/src/api-client.ts index 80872c31e..8f35d7771 100644 --- a/src/api-client.ts +++ b/src/api-client.ts @@ -19,16 +19,15 @@ import { getEnv, exportEnvVar, } from "./environment"; +import { GITHUB_DOTCOM_URL, parseGitHubUrl } from "./github"; import { Logger } from "./logging"; import { getRepositoryNwo, RepositoryNwo } from "./repository"; import { asHTTPError, ConfigurationError, getRequiredEnvParam, - GITHUB_DOTCOM_URL, GitHubVariant, GitHubVersion, - parseGitHubUrl, parseMatrixInput, } from "./util"; diff --git a/src/database-upload.ts b/src/database-upload.ts index 9e4339fd4..a79669436 100644 --- a/src/database-upload.ts +++ b/src/database-upload.ts @@ -12,11 +12,12 @@ import { type CodeQL } from "./codeql"; import { Config } from "./config-utils"; import { Feature } from "./feature-flags"; import * as gitUtils from "./git-utils"; +import { parseGitHubUrl } from "./github"; import { Logger, withGroupAsync } from "./logging"; import { OverlayDatabaseMode } from "./overlay/overlay-database-mode"; import { RepositoryNwo } from "./repository"; import * as util from "./util"; -import { asHTTPError, bundleDb, CleanupLevel, parseGitHubUrl } from "./util"; +import { asHTTPError, bundleDb, CleanupLevel } from "./util"; /** Information about a database upload. */ export interface DatabaseUploadResult { diff --git a/src/github.test.ts b/src/github.test.ts new file mode 100644 index 000000000..8645f493b --- /dev/null +++ b/src/github.test.ts @@ -0,0 +1,69 @@ +import test from "ava"; + +import * as github from "./github"; +import { setupTests } from "./testing-utils"; + +setupTests(test); + +test("parseGitHubUrl", (t) => { + t.deepEqual(github.parseGitHubUrl("github.com"), "https://github.com"); + t.deepEqual( + github.parseGitHubUrl("https://github.com"), + "https://github.com", + ); + t.deepEqual( + github.parseGitHubUrl("https://api.github.com"), + "https://github.com", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.com/foo/bar"), + "https://github.com", + ); + + t.deepEqual( + github.parseGitHubUrl("github.example.com"), + "https://github.example.com/", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.example.com"), + "https://github.example.com/", + ); + t.deepEqual( + github.parseGitHubUrl("https://api.github.example.com"), + "https://github.example.com/", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.example.com/api/v3"), + "https://github.example.com/", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.example.com:1234"), + "https://github.example.com:1234/", + ); + t.deepEqual( + github.parseGitHubUrl("https://api.github.example.com:1234"), + "https://github.example.com:1234/", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.example.com:1234/api/v3"), + "https://github.example.com:1234/", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.example.com/base/path"), + "https://github.example.com/base/path/", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.example.com/base/path/api/v3"), + "https://github.example.com/base/path/", + ); + + t.throws(() => github.parseGitHubUrl(""), { + message: '"" is not a valid URL', + }); + t.throws(() => github.parseGitHubUrl("ssh://github.com"), { + message: '"ssh://github.com" is not a http or https URL', + }); + t.throws(() => github.parseGitHubUrl("http:///::::433"), { + message: '"http:///::::433" is not a valid URL', + }); +}); diff --git a/src/github.ts b/src/github.ts new file mode 100644 index 000000000..00d2f3435 --- /dev/null +++ b/src/github.ts @@ -0,0 +1,62 @@ +import type { GitHubInstance } from "./api-client"; +import { ActionsEnvVars, type ReadOnlyEnv } from "./environment"; +import { ConfigurationError } from "./util"; + +/** A string representing a GitHub instance address. */ +export type InstanceURL = string; + +/** + * The URL for github.com. + */ +export const GITHUB_DOTCOM_URL: InstanceURL = "https://github.com"; + +/** A string representing a GitHub API instance address. */ +export type InstanceApiURL = string; + +/** + * The URL for api.github.com. + */ +export const GITHUB_DOTCOM_API_URL: InstanceApiURL = "https://api.github.com"; + +/** + * Parses user input of a github.com or GHES URL to a canonical form. + * Removes any API prefix or suffix if one is present. + */ +export function parseGitHubUrl(inputUrl: string): string { + const originalUrl = inputUrl; + if (inputUrl.indexOf("://") === -1) { + inputUrl = `https://${inputUrl}`; + } + if (!inputUrl.startsWith("http://") && !inputUrl.startsWith("https://")) { + throw new ConfigurationError(`"${originalUrl}" is not a http or https URL`); + } + + let url: URL; + try { + url = new URL(inputUrl); + } catch { + throw new ConfigurationError(`"${originalUrl}" is not a valid URL`); + } + + // If we detect this is trying to be to github.com + // then return with a fixed canonical URL. + if (url.hostname === "github.com" || url.hostname === "api.github.com") { + return GITHUB_DOTCOM_URL; + } + + // Remove the API prefix if it's present + if (url.pathname.indexOf("/api/v3") !== -1) { + url.pathname = url.pathname.substring(0, url.pathname.indexOf("/api/v3")); + } + // Also consider subdomain isolation on GHES + if (url.hostname.startsWith("api.")) { + url.hostname = url.hostname.substring(4); + } + + // Normalise path to having a trailing slash for consistency + if (!url.pathname.endsWith("/")) { + url.pathname = `${url.pathname}/`; + } + + return url.toString(); +} diff --git a/src/setup-codeql.ts b/src/setup-codeql.ts index daaba174c..c6504f779 100644 --- a/src/setup-codeql.ts +++ b/src/setup-codeql.ts @@ -36,6 +36,7 @@ import { Feature, FeatureEnablement, } from "./feature-flags"; +import { GITHUB_DOTCOM_URL } from "./github"; import { BuiltInLanguage } from "./languages"; import { Logger } from "./logging"; import { getCodeQlVersionsForOverlayBaseDatabases } from "./overlay/caching"; @@ -100,7 +101,7 @@ async function getCodeQLBundleDownloadURL( // 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], + [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. @@ -113,7 +114,7 @@ async function getCodeQLBundleDownloadURL( 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 && + apiURL === GITHUB_DOTCOM_URL && repository === CODEQL_DEFAULT_ACTION_REPOSITORY ) { break; diff --git a/src/status-report.ts b/src/status-report.ts index fc45e82d6..32418ed92 100644 --- a/src/status-report.ts +++ b/src/status-report.ts @@ -31,6 +31,7 @@ import { exportEnvVar, } from "./environment"; import { getRef } from "./git-utils"; +import { GITHUB_DOTCOM_URL } from "./github"; import * as json from "./json"; import type { Logger } from "./logging"; import type { OverlayBaseDatabaseDownloadStats } from "./overlay/caching"; @@ -41,7 +42,6 @@ import { ConfigurationError, getRequiredEnvParam, isInTestMode, - GITHUB_DOTCOM_URL, DiskUsage, assertNever, BuildMode, diff --git a/src/util.test.ts b/src/util.test.ts index 074310279..bca2b452e 100644 --- a/src/util.test.ts +++ b/src/util.test.ts @@ -164,66 +164,6 @@ test.serial("getExtraOptionsEnvParam() fails on invalid JSON", (t) => { process.env.CODEQL_ACTION_EXTRA_OPTIONS = origExtraOptions; }); -test("parseGitHubUrl", (t) => { - t.deepEqual(util.parseGitHubUrl("github.com"), "https://github.com"); - t.deepEqual(util.parseGitHubUrl("https://github.com"), "https://github.com"); - t.deepEqual( - util.parseGitHubUrl("https://api.github.com"), - "https://github.com", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.com/foo/bar"), - "https://github.com", - ); - - t.deepEqual( - util.parseGitHubUrl("github.example.com"), - "https://github.example.com/", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.example.com"), - "https://github.example.com/", - ); - t.deepEqual( - util.parseGitHubUrl("https://api.github.example.com"), - "https://github.example.com/", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.example.com/api/v3"), - "https://github.example.com/", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.example.com:1234"), - "https://github.example.com:1234/", - ); - t.deepEqual( - util.parseGitHubUrl("https://api.github.example.com:1234"), - "https://github.example.com:1234/", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.example.com:1234/api/v3"), - "https://github.example.com:1234/", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.example.com/base/path"), - "https://github.example.com/base/path/", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.example.com/base/path/api/v3"), - "https://github.example.com/base/path/", - ); - - t.throws(() => util.parseGitHubUrl(""), { - message: '"" is not a valid URL', - }); - t.throws(() => util.parseGitHubUrl("ssh://github.com"), { - message: '"ssh://github.com" is not a http or https URL', - }); - t.throws(() => util.parseGitHubUrl("http:///::::433"), { - message: '"http:///::::433" is not a valid URL', - }); -}); - test("allowed API versions", async (t) => { t.is(util.apiVersionInRange("1.33.0", "1.33", "2.0"), undefined); t.is(util.apiVersionInRange("1.33.1", "1.33", "2.0"), undefined); diff --git a/src/util.ts b/src/util.ts index 566386de6..dc6ed1872 100644 --- a/src/util.ts +++ b/src/util.ts @@ -45,11 +45,6 @@ const BASE_DATABASE_OIDS_FILE_NAME = "base-database-oids.json"; */ const BROKEN_VERSIONS = ["0.0.0-20211207"]; -/** - * The URL for github.com. - */ -export const GITHUB_DOTCOM_URL = "https://github.com"; - /** * Default name of the debugging artifact. */ @@ -465,49 +460,6 @@ export function getGeneratedSuitePath(config: Config, language: Language) { ); } -/** - * Parses user input of a github.com or GHES URL to a canonical form. - * Removes any API prefix or suffix if one is present. - */ -export function parseGitHubUrl(inputUrl: string): string { - const originalUrl = inputUrl; - if (inputUrl.indexOf("://") === -1) { - inputUrl = `https://${inputUrl}`; - } - if (!inputUrl.startsWith("http://") && !inputUrl.startsWith("https://")) { - throw new ConfigurationError(`"${originalUrl}" is not a http or https URL`); - } - - let url: URL; - try { - url = new URL(inputUrl); - } catch { - throw new ConfigurationError(`"${originalUrl}" is not a valid URL`); - } - - // If we detect this is trying to be to github.com - // then return with a fixed canonical URL. - if (url.hostname === "github.com" || url.hostname === "api.github.com") { - return GITHUB_DOTCOM_URL; - } - - // Remove the API prefix if it's present - if (url.pathname.indexOf("/api/v3") !== -1) { - url.pathname = url.pathname.substring(0, url.pathname.indexOf("/api/v3")); - } - // Also consider subdomain isolation on GHES - if (url.hostname.startsWith("api.")) { - url.hostname = url.hostname.substring(4); - } - - // Normalise path to having a trailing slash for consistency - if (!url.pathname.endsWith("/")) { - url.pathname = `${url.pathname}/`; - } - - return url.toString(); -} - const CODEQL_ACTION_WARNED_ABOUT_VERSION_ENV_VAR = "CODEQL_ACTION_WARNED_ABOUT_VERSION";