Type that registries must have either an url or a host

This commit is contained in:
Michael B. Gale
2026-02-10 17:05:44 +00:00
parent 700fc11b44
commit 4d44b570d2
6 changed files with 126 additions and 47 deletions

View File

@@ -80948,7 +80948,7 @@ var require_Credential = __commonJS({
"use strict";
Object.defineProperty(exports2, "__esModule", { value: true });
exports2.Credential = void 0;
var Credential2 = class {
var Credential = class {
/**
* Creates a RequestPolicy object.
*
@@ -80959,7 +80959,7 @@ var require_Credential = __commonJS({
throw new Error("Method should be implemented in children classes.");
}
};
exports2.Credential = Credential2;
exports2.Credential = Credential;
}
});
@@ -82088,7 +82088,7 @@ var require_Credential2 = __commonJS({
"use strict";
Object.defineProperty(exports2, "__esModule", { value: true });
exports2.Credential = void 0;
var Credential2 = class {
var Credential = class {
/**
* Creates a RequestPolicy object.
*
@@ -82099,7 +82099,7 @@ var require_Credential2 = __commonJS({
throw new Error("Method should be implemented in children classes.");
}
};
exports2.Credential = Credential2;
exports2.Credential = Credential;
}
});
@@ -121412,6 +121412,15 @@ async function sendUnhandledErrorStatusReport(actionName, actionStartedAt, error
}
}
// src/start-proxy/types.ts
function getAddressString(address) {
if (address.url === void 0) {
return address.host;
} else {
return address.url;
}
}
// src/start-proxy.ts
function getStartProxyErrorMessage(errorType) {
switch (errorType) {
@@ -121509,6 +121518,23 @@ var LANGUAGE_TO_REGISTRY_TYPE = {
rust: ["cargo_registry"],
go: ["goproxy_server", "git_source"]
};
function getRegistryAddress(registry) {
if (isDefined2(registry.url)) {
return {
url: registry.url,
host: registry.host
};
} else if (isDefined2(registry.host)) {
return {
url: void 0,
host: registry.host
};
} else {
throw new ConfigurationError(
"Invalid credentials - must specify host or url"
);
}
}
function getCredentials(logger, registrySecrets, registriesCredentials, language) {
const registryTypeForLanguage = language ? LANGUAGE_TO_REGISTRY_TYPE[language] : void 0;
let credentialsStr;
@@ -121545,11 +121571,7 @@ function getCredentials(logger, registrySecrets, registriesCredentials, language
if (isDefined2(e.token)) {
core10.setSecret(e.token);
}
if (!isDefined2(e.url) && !isDefined2(e.host)) {
throw new ConfigurationError(
"Invalid credentials - must specify host or url"
);
}
const address = getRegistryAddress(e);
if (registryTypeForLanguage && !registryTypeForLanguage.some((t) => t === e.type)) {
continue;
}
@@ -121568,11 +121590,10 @@ function getCredentials(logger, registrySecrets, registriesCredentials, language
}
out.push({
type: e.type,
host: e.host,
url: e.url,
username: e.username,
password: e.password,
token: e.token
token: e.token,
...address
});
}
return out;
@@ -121714,7 +121735,7 @@ var NetworkReachabilityBackend = class {
async checkConnection(registry) {
return new Promise((resolve2, reject) => {
const req = https.request(
registry.url,
getAddressString(registry),
{ agent: this.agent, method: "HEAD", ca: this.proxy.cert },
(res) => {
res.destroy();
@@ -121741,21 +121762,22 @@ async function checkConnections(logger, proxy, backend) {
backend = new NetworkReachabilityBackend(logger, proxy);
}
for (const registry of proxy.registries) {
const address = getAddressString(registry);
try {
logger.debug(`Testing connection to ${registry.url}...`);
logger.debug(`Testing connection to ${address}...`);
const statusCode = await backend.checkConnection(registry);
logger.info(
`Successfully tested connection to ${registry.url} (${statusCode})`
`Successfully tested connection to ${address} (${statusCode})`
);
result.add(registry);
} catch (e) {
if (e instanceof ReachabilityError && e.statusCode !== void 0) {
logger.error(
`Connection test to ${registry.url} failed. (${e.statusCode})`
`Connection test to ${address} failed. (${e.statusCode})`
);
} else {
logger.error(
`Connection test to ${registry.url} failed: ${getErrorMessage(e)}`
`Connection test to ${address} failed: ${getErrorMessage(e)}`
);
}
}

View File

@@ -11,16 +11,16 @@ import { KnownLanguage } from "./languages";
import { getActionsLogger, Logger } from "./logging";
import { getRepositoryNwo } from "./repository";
import {
Credential,
credentialToStr,
getCredentials,
getProxyBinaryPath,
getSafeErrorMessage,
parseLanguage,
ProxyInfo,
Registry,
sendFailedStatusReport,
sendSuccessStatusReport,
ValidCredential,
ValidRegistry,
} from "./start-proxy";
import { checkConnections } from "./start-proxy/reachability";
import { ActionName, sendUnhandledErrorStatusReport } from "./status-report";
@@ -40,7 +40,7 @@ type BasicAuthCredentials = {
};
type ProxyConfig = {
all_credentials: Credential[];
all_credentials: ValidCredential[];
ca: CertificateAuthority;
proxy_auth?: BasicAuthCredentials;
};
@@ -243,7 +243,7 @@ async function startProxy(
core.setOutput("proxy_port", port.toString());
core.setOutput("proxy_ca_certificate", config.ca.cert);
const registry_urls: Registry[] = config.all_credentials
const registry_urls: ValidRegistry[] = config.all_credentials
.filter((credential) => credential.url !== undefined)
.map((credential) => ({
type: credential.type,

View File

@@ -13,7 +13,12 @@ import { Config } from "./config-utils";
import * as defaults from "./defaults.json";
import { KnownLanguage } from "./languages";
import { Logger } from "./logging";
import { Credential } from "./start-proxy/types";
import {
Address,
Credential,
Registry,
ValidCredential,
} from "./start-proxy/types";
import {
ActionName,
createStatusReportBase,
@@ -223,6 +228,31 @@ const LANGUAGE_TO_REGISTRY_TYPE: Partial<Record<KnownLanguage, string[]>> = {
go: ["goproxy_server", "git_source"],
} as const;
/**
* Extracts an `Address` value from the given `Registry` value by determining whether it has
* a `url` value, or no `url` value but a `host` value.
*
* @throws A `ConfigurationError` if the `Registry` value contains neither a `url` or `host` field.
*/
function getRegistryAddress(registry: Registry): Address {
if (isDefined(registry.url)) {
return {
url: registry.url,
host: registry.host,
};
} else if (isDefined(registry.host)) {
return {
url: undefined,
host: registry.host,
};
} else {
// The proxy needs one of these to work. If both are defined, the url has the precedence.
throw new ConfigurationError(
"Invalid credentials - must specify host or url",
);
}
}
// getCredentials returns registry credentials from action inputs.
// It prefers `registries_credentials` over `registry_secrets`.
// If neither is set, it returns an empty array.
@@ -231,7 +261,7 @@ export function getCredentials(
registrySecrets: string | undefined,
registriesCredentials: string | undefined,
language: KnownLanguage | undefined,
): Credential[] {
): ValidCredential[] {
const registryTypeForLanguage = language
? LANGUAGE_TO_REGISTRY_TYPE[language]
: undefined;
@@ -265,7 +295,7 @@ export function getCredentials(
);
}
const out: Credential[] = [];
const out: ValidCredential[] = [];
for (const e of parsed) {
if (e === null || typeof e !== "object") {
throw new ConfigurationError("Invalid credentials - must be an object");
@@ -279,12 +309,7 @@ export function getCredentials(
core.setSecret(e.token);
}
if (!isDefined(e.url) && !isDefined(e.host)) {
// The proxy needs one of these to work. If both are defined, the url has the precedence.
throw new ConfigurationError(
"Invalid credentials - must specify host or url",
);
}
const address = getRegistryAddress(e);
// Filter credentials based on language if specified. `type` is the registry type.
// E.g., "maven_feed" for Java/Kotlin, "nuget_repository" for C#.
@@ -327,11 +352,10 @@ export function getCredentials(
out.push({
type: e.type,
host: e.host,
url: e.url,
username: e.username,
password: e.password,
token: e.token,
...address,
});
}
return out;

View File

@@ -11,22 +11,22 @@ import {
ReachabilityBackend,
ReachabilityError,
} from "./reachability";
import { ProxyInfo, Registry } from "./types";
import { ProxyInfo, ValidRegistry } from "./types";
setupTests(test);
class MockReachabilityBackend implements ReachabilityBackend {
public async checkConnection(_registry: Registry): Promise<number> {
public async checkConnection(_registry: ValidRegistry): Promise<number> {
return 200;
}
}
const mavenRegistry: Registry = {
const mavenRegistry: ValidRegistry = {
type: "maven_registry",
url: "https://repo.maven.apache.org/maven2/",
};
const nugetFeed: Registry = {
const nugetFeed: ValidRegistry = {
type: "nuget_feed",
url: "https://api.nuget.org/v3/index.json",
};

View File

@@ -5,7 +5,7 @@ import { HttpsProxyAgent } from "https-proxy-agent";
import { Logger } from "../logging";
import { getErrorMessage } from "../util";
import { ProxyInfo, Registry } from "./types";
import { getAddressString, ProxyInfo, Registry, ValidRegistry } from "./types";
export class ReachabilityError extends Error {
constructor(
@@ -34,7 +34,7 @@ export interface ReachabilityBackend {
* @param registry The registry to try and reach.
* @returns The successful status code (in the `<400` range).
*/
checkConnection: (registry: Registry) => Promise<number>;
checkConnection: (registry: ValidRegistry) => Promise<number>;
}
class NetworkReachabilityBackend implements ReachabilityBackend {
@@ -47,10 +47,10 @@ class NetworkReachabilityBackend implements ReachabilityBackend {
this.agent = new HttpsProxyAgent(`http://${proxy.host}:${proxy.port}`);
}
public async checkConnection(registry: Registry): Promise<number> {
public async checkConnection(registry: ValidRegistry): Promise<number> {
return new Promise((resolve, reject) => {
const req = https.request(
registry.url as string,
getAddressString(registry),
{ agent: this.agent, method: "HEAD", ca: this.proxy.cert },
(res) => {
res.destroy();
@@ -83,8 +83,8 @@ export async function checkConnections(
logger: Logger,
proxy: ProxyInfo,
backend?: ReachabilityBackend,
): Promise<Set<Registry>> {
const result: Set<Registry> = new Set();
): Promise<Set<ValidRegistry>> {
const result: Set<ValidRegistry> = new Set();
// Don't do anything if there are no registries.
if (proxy.registries.length === 0) return result;
@@ -96,22 +96,23 @@ export async function checkConnections(
}
for (const registry of proxy.registries) {
const address = getAddressString(registry);
try {
logger.debug(`Testing connection to ${registry.url}...`);
logger.debug(`Testing connection to ${address}...`);
const statusCode = await backend.checkConnection(registry);
logger.info(
`Successfully tested connection to ${registry.url} (${statusCode})`,
`Successfully tested connection to ${address} (${statusCode})`,
);
result.add(registry);
} catch (e) {
if (e instanceof ReachabilityError && e.statusCode !== undefined) {
logger.error(
`Connection test to ${registry.url} failed. (${e.statusCode})`,
`Connection test to ${address} failed. (${e.statusCode})`,
);
} else {
logger.error(
`Connection test to ${registry.url} failed: ${getErrorMessage(e)}`,
`Connection test to ${address} failed: ${getErrorMessage(e)}`,
);
}
}

View File

@@ -10,9 +10,41 @@ export interface Registry {
url?: string;
}
// If a registry has an `url`, then that takes precedence over the `host` which may or may
// not be defined.
interface HasUrl {
url: string;
host?: string;
}
// If a registry does not have an `url`, then it must have a `host`.
interface WithoutUrl {
url: undefined;
host: string;
}
/**
* A valid `Registry` value must either have a `url` or a `host` value. If it has a `url` value,
* then that takes precedence over the `host` value. If there is no `url` value, then it must
* have a `host` value.
*/
export type Address = HasUrl | WithoutUrl;
/** Gets the address as a string. This will either be the `url` if present, or the `host` if not. */
export function getAddressString(address: Address): string {
if (address.url === undefined) {
return address.host;
} else {
return address.url;
}
}
export type ValidRegistry<T extends Registry = Registry> = T & Address;
export type ValidCredential = ValidRegistry<Credential>;
export interface ProxyInfo {
host: string;
port: number;
cert: string;
registries: Registry[];
registries: ValidRegistry[];
}