Use getDefaultCliVersion for start-proxy

This commit is contained in:
Michael B. Gale
2026-02-25 17:43:15 +00:00
parent c0fc915677
commit f657c4e1eb
4 changed files with 138 additions and 50 deletions
+9 -7
View File
@@ -121699,17 +121699,19 @@ function getProxyPackage() {
function getFallbackUrl(proxyPackage) {
return `${UPDATEJOB_PROXY_URL_PREFIX}${proxyPackage}`;
}
async function getLinkedRelease() {
async function getReleaseByVersion(version) {
return getApiClient().rest.repos.getReleaseByTag({
owner: "github",
repo: "codeql-action",
tag: bundleVersion
tag: version
});
}
async function getDownloadUrl(logger) {
async function getDownloadUrl(logger, features) {
const proxyPackage = getProxyPackage();
try {
const cliRelease = await getLinkedRelease();
const gitHubVersion = await getGitHubVersion();
const versionInfo = await features.getDefaultCliVersion(gitHubVersion.type);
const cliRelease = await getReleaseByVersion(versionInfo.tagName);
for (const asset of cliRelease.data.assets) {
if (asset.name === proxyPackage) {
logger.info(
@@ -121781,9 +121783,9 @@ async function cacheProxy(logger, source, filename, version) {
function getProxyFilename() {
return process.platform === "win32" ? `${UPDATEJOB_PROXY}.exe` : UPDATEJOB_PROXY;
}
async function getProxyBinaryPath(logger) {
async function getProxyBinaryPath(logger, features) {
const proxyFileName = getProxyFilename();
const proxyInfo = await getDownloadUrl(logger);
const proxyInfo = await getDownloadUrl(logger, features);
let proxyBin = toolcache.find(proxyFileName, proxyInfo.version);
if (!proxyBin) {
const apiDetails = getApiDetails();
@@ -122141,7 +122143,7 @@ async function run(startedAt) {
all_credentials: credentials,
ca
};
const proxyBin = await getProxyBinaryPath(logger);
const proxyBin = await getProxyBinaryPath(logger, features);
const proxyInfo = await startProxy(
proxyBin,
proxyConfig,
+2 -2
View File
@@ -5,7 +5,7 @@ import * as core from "@actions/core";
import * as actionsUtil from "./actions-util";
import { getGitHubVersion } from "./api-client";
import { Feature, FeatureEnablement, initFeatures } from "./feature-flags";
import { FeatureEnablement, initFeatures } from "./feature-flags";
import { KnownLanguage } from "./languages";
import { getActionsLogger, Logger } from "./logging";
import { getRepositoryNwo } from "./repository";
@@ -98,7 +98,7 @@ async function run(startedAt: Date) {
};
// Start the Proxy
const proxyBin = await getProxyBinaryPath(logger);
const proxyBin = await getProxyBinaryPath(logger, features);
const proxyInfo = await startProxy(
proxyBin,
proxyConfig,
+109 -34
View File
@@ -7,6 +7,7 @@ import sinon from "sinon";
import * as apiClient from "./api-client";
import * as defaults from "./defaults.json";
import { setUpFeatureFlagTests } from "./feature-flags/testing-util";
import { KnownLanguage } from "./languages";
import { getRunnerLogger, Logger } from "./logging";
import * as startProxyExports from "./start-proxy";
@@ -14,12 +15,19 @@ import { parseLanguage } from "./start-proxy";
import * as statusReport from "./status-report";
import {
checkExpectedLogMessages,
createFeatures,
getRecordingLogger,
makeTestToken,
RecordingLogger,
setupTests,
withRecordingLoggerAsync,
} from "./testing-utils";
import { ConfigurationError } from "./util";
import {
ConfigurationError,
GitHubVariant,
GitHubVersion,
withTmpDir,
} from "./util";
setupTests(test);
@@ -347,8 +355,18 @@ test("parseLanguage", async (t) => {
t.deepEqual(parseLanguage(""), undefined);
});
function mockGetReleaseByTag(assets?: Array<{ name: string; url?: string }>) {
const mockClient = sinon.stub(apiClient, "getApiClient");
function mockGetApiClient(endpoints: any) {
return (
sinon
.stub(apiClient, "getApiClient")
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
.returns({ rest: endpoints } as any)
);
}
type ReleaseAssets = Array<{ name: string; url?: string }>;
function mockGetReleaseByTag(assets?: ReleaseAssets) {
const getReleaseByTag =
assets === undefined
? sinon.stub().rejects()
@@ -359,21 +377,28 @@ function mockGetReleaseByTag(assets?: Array<{ name: string; url?: string }>) {
url: "GET /repos/:owner/:repo/releases/tags/:tag",
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
mockClient.returns({
rest: {
repos: {
getReleaseByTag,
},
},
} as any);
return mockClient;
return mockGetApiClient({ repos: { getReleaseByTag } });
}
test("getDownloadUrl returns fallback when `getLinkedRelease` rejects", async (t) => {
function mockOfflineFeatures(tempDir: string, logger: Logger) {
// Using GHES ensures that we are using `OfflineFeatures`.
const gitHubVersion = {
type: GitHubVariant.GHES,
version: "3.0.0",
};
sinon.stub(apiClient, "getGitHubVersion").resolves(gitHubVersion);
return setUpFeatureFlagTests(tempDir, logger, gitHubVersion);
}
test("getDownloadUrl returns fallback when `getReleaseByVersion` rejects", async (t) => {
mockGetReleaseByTag();
const info = await startProxyExports.getDownloadUrl(getRunnerLogger(true));
const features = createFeatures([]);
const info = await startProxyExports.getDownloadUrl(
getRunnerLogger(true),
features,
);
t.is(info.version, startProxyExports.UPDATEJOB_PROXY_VERSION);
t.is(
@@ -383,33 +408,48 @@ test("getDownloadUrl returns fallback when `getLinkedRelease` rejects", async (t
});
test("getDownloadUrl returns fallback when there's no matching release asset", async (t) => {
const logger = new RecordingLogger();
const testAssets = [[], [{ name: "foo" }]];
for (const assets of testAssets) {
const stub = mockGetReleaseByTag(assets);
const info = await startProxyExports.getDownloadUrl(getRunnerLogger(true));
await withTmpDir(async (tempDir) => {
const features = mockOfflineFeatures(tempDir, logger);
t.is(info.version, startProxyExports.UPDATEJOB_PROXY_VERSION);
t.is(
info.url,
startProxyExports.getFallbackUrl(startProxyExports.getProxyPackage()),
);
for (const assets of testAssets) {
const stub = mockGetReleaseByTag(assets);
const info = await startProxyExports.getDownloadUrl(
getRunnerLogger(true),
features,
);
stub.restore();
}
t.is(info.version, startProxyExports.UPDATEJOB_PROXY_VERSION);
t.is(
info.url,
startProxyExports.getFallbackUrl(startProxyExports.getProxyPackage()),
);
stub.restore();
}
});
});
test("getDownloadUrl returns matching release asset", async (t) => {
const logger = new RecordingLogger();
const assets = [
{ name: "foo", url: "other-url" },
{ name: startProxyExports.getProxyPackage(), url: "url-we-want" },
];
mockGetReleaseByTag(assets);
const info = await startProxyExports.getDownloadUrl(getRunnerLogger(true));
await withTmpDir(async (tempDir) => {
const features = mockOfflineFeatures(tempDir, logger);
const info = await startProxyExports.getDownloadUrl(
getRunnerLogger(true),
features,
);
t.is(info.version, defaults.cliVersion);
t.is(info.url, "url-we-want");
t.is(info.version, defaults.cliVersion);
t.is(info.url, "url-we-want");
});
});
test("credentialToStr - hides passwords", (t) => {
@@ -560,13 +600,15 @@ test(
);
test("getProxyBinaryPath - returns path from tool cache if available", async (t) => {
const logger = new RecordingLogger();
mockGetReleaseByTag();
await withRecordingLoggerAsync(async (logger) => {
await withTmpDir(async (tempDir) => {
const toolcachePath = "/path/to/proxy/dir";
sinon.stub(toolcache, "find").returns(toolcachePath);
const path = await startProxyExports.getProxyBinaryPath(logger);
const features = mockOfflineFeatures(tempDir, logger);
const path = await startProxyExports.getProxyBinaryPath(logger, features);
t.assert(path);
t.is(
@@ -577,12 +619,31 @@ test("getProxyBinaryPath - returns path from tool cache if available", async (t)
});
test("getProxyBinaryPath - downloads proxy if not in cache", async (t) => {
const logger = new RecordingLogger();
const expectedTag = "codeql-bundle-v2.20.1";
const expectedParams = {
owner: "github",
repo: "codeql-action",
tag: expectedTag,
};
const downloadUrl = "url-we-want";
mockGetReleaseByTag([
{ name: startProxyExports.getProxyPackage(), url: downloadUrl },
]);
const assets = [
{
name: startProxyExports.getProxyPackage(),
url: downloadUrl,
},
];
await withRecordingLoggerAsync(async (logger) => {
const getReleaseByTag = sinon.stub();
getReleaseByTag.withArgs(sinon.match(expectedParams)).resolves({
status: 200,
data: { assets },
headers: {},
url: "GET /repos/:owner/:repo/releases/tags/:tag",
});
mockGetApiClient({ repos: { getReleaseByTag } });
await withTmpDir(async (tempDir) => {
const toolcachePath = "/path/to/proxy/dir";
const find = sinon.stub(toolcache, "find").returns("");
const getApiDetails = sinon.stub(apiClient, "getApiDetails").returns({
@@ -603,8 +664,22 @@ test("getProxyBinaryPath - downloads proxy if not in cache", async (t) => {
.resolves(extractedPath);
const cacheDir = sinon.stub(toolcache, "cacheDir").resolves(toolcachePath);
const path = await startProxyExports.getProxyBinaryPath(logger);
const gitHubVersion: GitHubVersion = {
type: GitHubVariant.DOTCOM,
};
sinon.stub(apiClient, "getGitHubVersion").resolves(gitHubVersion);
const features = setUpFeatureFlagTests(tempDir, logger, gitHubVersion);
const getDefaultCliVersion = sinon
.stub(features, "getDefaultCliVersion")
.resolves({ cliVersion: "2.20.1", tagName: expectedTag });
const path = await startProxyExports.getProxyBinaryPath(logger, features);
t.assert(getDefaultCliVersion.calledOnce);
sinon.assert.calledOnceWithMatch(
getReleaseByTag,
sinon.match(expectedParams),
);
t.assert(find.calledOnce);
t.assert(getApiDetails.calledOnce);
t.assert(getAuthorizationHeaderFor.calledOnce);
+18 -7
View File
@@ -7,10 +7,12 @@ import {
getApiClient,
getApiDetails,
getAuthorizationHeaderFor,
getGitHubVersion,
} from "./api-client";
import * as artifactScanner from "./artifact-scanner";
import { Config } from "./config-utils";
import * as defaults from "./defaults.json";
import { FeatureEnablement } from "./feature-flags";
import { KnownLanguage } from "./languages";
import { Logger } from "./logging";
import {
@@ -391,15 +393,15 @@ export function getFallbackUrl(proxyPackage: string): string {
/**
* Uses the GitHub API to obtain information about the CodeQL CLI bundle release
* that is pointed at by `defaults.json`.
* that is tagged by `version`.
*
* @returns The response from the GitHub API.
*/
async function getLinkedRelease() {
async function getReleaseByVersion(version: string) {
return getApiClient().rest.repos.getReleaseByTag({
owner: "github",
repo: "codeql-action",
tag: defaults.bundleVersion,
tag: version,
});
}
@@ -412,12 +414,18 @@ async function getLinkedRelease() {
*/
export async function getDownloadUrl(
logger: Logger,
features: FeatureEnablement,
): Promise<{ url: string; version: string }> {
const proxyPackage = getProxyPackage();
try {
// Try to retrieve information about the CLI bundle release pointed at by `defaults.json`.
const cliRelease = await getLinkedRelease();
// Retrieve information about the CLI version we should use. This will be either the linked
// version, or the one enabled by FFs.
const gitHubVersion = await getGitHubVersion();
const versionInfo = await features.getDefaultCliVersion(gitHubVersion.type);
// Try to retrieve information about the CLI bundle release identified by `versionInfo`.
const cliRelease = await getReleaseByVersion(versionInfo.tagName);
// Search the release's assets to find the one we are looking for.
for (const asset of cliRelease.data.assets) {
@@ -548,9 +556,12 @@ export function getProxyFilename() {
* @param logger The logger to use.
* @returns The path to the proxy binary.
*/
export async function getProxyBinaryPath(logger: Logger): Promise<string> {
export async function getProxyBinaryPath(
logger: Logger,
features: FeatureEnablement,
): Promise<string> {
const proxyFileName = getProxyFilename();
const proxyInfo = await getDownloadUrl(logger);
const proxyInfo = await getDownloadUrl(logger, features);
let proxyBin = toolcache.find(proxyFileName, proxyInfo.version);
if (!proxyBin) {