Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mirror-url parameter to allow downloading Node.js from a custom URL #1211

Closed
wants to merge 26 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
mirrorurl code
  • Loading branch information
aparnajyothi-y committed Jan 29, 2025
commit 0b66095a848d5fcfa12981fab71344e96921d0f8
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -14,6 +14,9 @@ inputs:
check-latest:
description: 'Set this option if you want the action to check for the latest available version that satisfies the version spec.'
default: false
mirrorURL:
description: 'Custom mirror URL to download Node.js from (optional)'
required: false
registry-url:
description: 'Optional registry to set up for auth. Will set the registry in a project level .npmrc and .yarnrc file, and set up auth to read in from env.NODE_AUTH_TOKEN.'
scope:
180 changes: 121 additions & 59 deletions dist/setup/index.js
Original file line number Diff line number Diff line change
@@ -100174,6 +100174,26 @@ class BaseDistribution {
fileName: fileName
};
}
getNodejsMirrorURLInfo(version) {
const mirrorURL = this.nodeInfo.mirrorURL;
const osArch = this.translateArchToDistUrl(this.nodeInfo.arch);
version = semver_1.default.clean(version) || '';
const fileName = this.osPlat == 'win32'
? `node-v${version}-win-${osArch}`
: `node-v${version}-${this.osPlat}-${osArch}`;
const urlFileName = this.osPlat == 'win32'
? this.nodeInfo.arch === 'arm64'
? `${fileName}.zip`
: `${fileName}.7z`
: `${fileName}.tar.gz`;
const url = `${mirrorURL}/v${version}/${urlFileName}`;
return {
downloadUrl: url,
resolvedVersion: version,
arch: osArch,
fileName: fileName
};
}
downloadNodejs(info) {
return __awaiter(this, void 0, void 0, function* () {
let downloadPath = '';
@@ -100451,73 +100471,90 @@ class OfficialBuilds extends base_distribution_1.default {
}
setupNodeJs() {
return __awaiter(this, void 0, void 0, function* () {
var _a;
let manifest;
let nodeJsVersions;
const osArch = this.translateArchToDistUrl(this.nodeInfo.arch);
if (this.isLtsAlias(this.nodeInfo.versionSpec)) {
core.info('Attempt to resolve LTS alias from manifest...');
// No try-catch since it's not possible to resolve LTS alias without manifest
manifest = yield this.getManifest();
this.nodeInfo.versionSpec = this.resolveLtsAliasFromManifest(this.nodeInfo.versionSpec, this.nodeInfo.stable, manifest);
}
if (this.isLatestSyntax(this.nodeInfo.versionSpec)) {
nodeJsVersions = yield this.getNodeJsVersions();
const versions = this.filterVersions(nodeJsVersions);
this.nodeInfo.versionSpec = this.evaluateVersions(versions);
core.info('getting latest node version...');
}
if (this.nodeInfo.checkLatest) {
core.info('Attempt to resolve the latest version from manifest...');
const resolvedVersion = yield this.resolveVersionFromManifest(this.nodeInfo.versionSpec, this.nodeInfo.stable, osArch, manifest);
if (resolvedVersion) {
this.nodeInfo.versionSpec = resolvedVersion;
core.info(`Resolved as '${resolvedVersion}'`);
var _a, _b;
if (this.nodeInfo.mirrorURL) {
let downloadPath = '';
let toolPath = '';
try {
core.info(`Attempting to download using mirror URL...`);
downloadPath = yield this.downloadFromMirrorURL(); // Attempt to download from the mirror
if (downloadPath) {
toolPath = downloadPath;
}
}
else {
core.info(`Failed to resolve version ${this.nodeInfo.versionSpec} from manifest`);
catch (err) {
core.info(err.message);
core.debug((_a = err.stack) !== null && _a !== void 0 ? _a : 'empty stack');
}
}
let toolPath = this.findVersionInHostedToolCacheDirectory();
if (toolPath) {
core.info(`Found in cache @ ${toolPath}`);
this.addToolPath(toolPath);
return;
}
let downloadPath = '';
try {
core.info(`Attempting to download ${this.nodeInfo.versionSpec}...`);
const versionInfo = yield this.getInfoFromManifest(this.nodeInfo.versionSpec, this.nodeInfo.stable, osArch, manifest);
if (versionInfo) {
core.info(`Acquiring ${versionInfo.resolvedVersion} - ${versionInfo.arch} from ${versionInfo.downloadUrl}`);
downloadPath = yield tc.downloadTool(versionInfo.downloadUrl, undefined, this.nodeInfo.auth);
if (downloadPath) {
toolPath = yield this.extractArchive(downloadPath, versionInfo, false);
else {
let manifest;
let nodeJsVersions;
const osArch = this.translateArchToDistUrl(this.nodeInfo.arch);
if (this.isLtsAlias(this.nodeInfo.versionSpec)) {
core.info('Attempt to resolve LTS alias from manifest...');
// No try-catch since it's not possible to resolve LTS alias without manifest
manifest = yield this.getManifest();
this.nodeInfo.versionSpec = this.resolveLtsAliasFromManifest(this.nodeInfo.versionSpec, this.nodeInfo.stable, manifest);
}
if (this.isLatestSyntax(this.nodeInfo.versionSpec)) {
nodeJsVersions = yield this.getNodeJsVersions();
const versions = this.filterVersions(nodeJsVersions);
this.nodeInfo.versionSpec = this.evaluateVersions(versions);
core.info('getting latest node version...');
}
if (this.nodeInfo.checkLatest) {
core.info('Attempt to resolve the latest version from manifest...');
const resolvedVersion = yield this.resolveVersionFromManifest(this.nodeInfo.versionSpec, this.nodeInfo.stable, osArch, manifest);
if (resolvedVersion) {
this.nodeInfo.versionSpec = resolvedVersion;
core.info(`Resolved as '${resolvedVersion}'`);
}
else {
core.info(`Failed to resolve version ${this.nodeInfo.versionSpec} from manifest`);
}
}
else {
core.info('Not found in manifest. Falling back to download directly from Node');
let toolPath = this.findVersionInHostedToolCacheDirectory();
if (toolPath) {
core.info(`Found in cache @ ${toolPath}`);
this.addToolPath(toolPath);
return;
}
}
catch (err) {
// Rate limit?
if (err instanceof tc.HTTPError &&
(err.httpStatusCode === 403 || err.httpStatusCode === 429)) {
core.info(`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`);
let downloadPath = '';
try {
core.info(`Attempting to download ${this.nodeInfo.versionSpec}...`);
const versionInfo = yield this.getInfoFromManifest(this.nodeInfo.versionSpec, this.nodeInfo.stable, osArch, manifest);
if (versionInfo) {
core.info(`Acquiring ${versionInfo.resolvedVersion} - ${versionInfo.arch} from ${versionInfo.downloadUrl}`);
downloadPath = yield tc.downloadTool(versionInfo.downloadUrl, undefined, this.nodeInfo.auth);
if (downloadPath) {
toolPath = yield this.extractArchive(downloadPath, versionInfo, false);
}
}
else {
core.info('Not found in manifest. Falling back to download directly from Node');
}
}
else {
core.info(err.message);
catch (err) {
// Rate limit?
if (err instanceof tc.HTTPError &&
(err.httpStatusCode === 403 || err.httpStatusCode === 429)) {
core.info(`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`);
}
else {
core.info(err.message);
}
core.debug((_b = err.stack) !== null && _b !== void 0 ? _b : 'empty stack');
core.info('Falling back to download directly from Node');
}
core.debug((_a = err.stack) !== null && _a !== void 0 ? _a : 'empty stack');
core.info('Falling back to download directly from Node');
}
if (!toolPath) {
toolPath = yield this.downloadDirectlyFromNode();
}
if (this.osPlat != 'win32') {
toolPath = path_1.default.join(toolPath, 'bin');
if (!toolPath) {
toolPath = yield this.downloadDirectlyFromNode();
}
if (this.osPlat != 'win32') {
toolPath = path_1.default.join(toolPath, 'bin');
}
core.addPath(toolPath);
}
core.addPath(toolPath);
});
}
addToolPath(toolPath) {
@@ -100626,6 +100663,29 @@ class OfficialBuilds extends base_distribution_1.default {
isLatestSyntax(versionSpec) {
return ['current', 'latest', 'node'].includes(versionSpec);
}
downloadFromMirrorURL() {
return __awaiter(this, void 0, void 0, function* () {
const nodeJsVersions = yield this.getNodeJsVersions();
const versions = this.filterVersions(nodeJsVersions);
const evaluatedVersion = this.evaluateVersions(versions);
if (!evaluatedVersion) {
throw new Error(`Unable to find Node version '${this.nodeInfo.versionSpec}' for platform ${this.osPlat} and architecture ${this.nodeInfo.arch}.`);
}
const toolName = this.getNodejsMirrorURLInfo(evaluatedVersion);
try {
const toolPath = yield this.downloadNodejs(toolName);
return toolPath;
}
catch (error) {
if (error instanceof tc.HTTPError && error.httpStatusCode === 404) {
core.warning(`Node version ${this.nodeInfo.versionSpec} for platform ${this.osPlat} and architecture ${this.nodeInfo.arch} was found but failed to download. ` +
'This usually happens when downloadable binaries are not fully updated at https://nodejs.org/. ' +
'To resolve this issue you may either fall back to the older version or try again later.');
}
throw error;
}
});
}
}
exports["default"] = OfficialBuilds;

@@ -100748,6 +100808,7 @@ function run() {
if (!arch) {
arch = os_1.default.arch();
}
const mirrorURL = core.getInput('mirrorURL').trim(); // .trim() to remove any accidental spaces
if (version) {
const token = core.getInput('token');
const auth = !token ? undefined : `token ${token}`;
@@ -100758,7 +100819,8 @@ function run() {
checkLatest,
auth,
stable,
arch
arch,
mirrorURL
};
const nodeDistribution = (0, installer_factory_1.getNodejsDistribution)(nodejsInfo);
yield nodeDistribution.setupNodeJs();
26 changes: 26 additions & 0 deletions src/distributions/base-distribution.ts
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@ export default abstract class BaseDistribution {
}

protected abstract getDistributionUrl(): string;


public async setupNodeJs() {
let nodeJsVersions: INodeVersion[] | undefined;
@@ -128,6 +129,31 @@ export default abstract class BaseDistribution {
};
}

protected getNodejsMirrorURLInfo(version: string) {
const mirrorURL = this.nodeInfo.mirrorURL;
const osArch: string = this.translateArchToDistUrl(this.nodeInfo.arch);
version = semver.clean(version) || '';
const fileName: string =
this.osPlat == 'win32'
? `node-v${version}-win-${osArch}`
: `node-v${version}-${this.osPlat}-${osArch}`;
const urlFileName: string =
this.osPlat == 'win32'
? this.nodeInfo.arch === 'arm64'
? `${fileName}.zip`
: `${fileName}.7z`
: `${fileName}.tar.gz`;

const url = `${mirrorURL}/v${version}/${urlFileName}`;

return <INodeVersionInfo>{
downloadUrl: url,
resolvedVersion: version,
arch: osArch,
fileName: fileName
};
}

protected async downloadNodejs(info: INodeVersionInfo) {
let downloadPath = '';
core.info(
2 changes: 2 additions & 0 deletions src/distributions/base-models.ts
Original file line number Diff line number Diff line change
@@ -4,13 +4,15 @@ export interface NodeInputs {
auth?: string;
checkLatest: boolean;
stable: boolean;
mirrorURL: string;
}

export interface INodeVersionInfo {
downloadUrl: string;
resolvedVersion: string;
arch: string;
fileName: string;

}

export interface INodeVersion {
51 changes: 51 additions & 0 deletions src/distributions/official_builds/official_builds.ts
Original file line number Diff line number Diff line change
@@ -12,9 +12,27 @@ interface INodeRelease extends tc.IToolRelease {
export default class OfficialBuilds extends BaseDistribution {
constructor(nodeInfo: NodeInputs) {
super(nodeInfo);

}


public async setupNodeJs() {
if(this.nodeInfo.mirrorURL){

let downloadPath = '';
let toolPath = '';
try {
core.info(`Attempting to download using mirror URL...`);
downloadPath = await this.downloadFromMirrorURL(); // Attempt to download from the mirror
if (downloadPath) {
toolPath = downloadPath;
}
} catch (err) {
core.info((err as Error).message);
core.debug((err as Error).stack ?? 'empty stack');
}

}else{
let manifest: tc.IToolRelease[] | undefined;
let nodeJsVersions: INodeVersion[] | undefined;
const osArch = this.translateArchToDistUrl(this.nodeInfo.arch);
@@ -125,6 +143,8 @@ export default class OfficialBuilds extends BaseDistribution {

core.addPath(toolPath);
}
}


protected addToolPath(toolPath: string) {
if (this.osPlat != 'win32') {
@@ -180,6 +200,7 @@ export default class OfficialBuilds extends BaseDistribution {
return `https://nodejs.org/dist`;
}


private getManifest(): Promise<tc.IToolRelease[]> {
core.debug('Getting manifest from actions/node-versions@main');
return tc.getManifestFromRepo(
@@ -291,4 +312,34 @@ export default class OfficialBuilds extends BaseDistribution {
private isLatestSyntax(versionSpec): boolean {
return ['current', 'latest', 'node'].includes(versionSpec);
}

protected async downloadFromMirrorURL() {
const nodeJsVersions = await this.getNodeJsVersions();
const versions = this.filterVersions(nodeJsVersions);
const evaluatedVersion = this.evaluateVersions(versions);

if (!evaluatedVersion) {
throw new Error(
`Unable to find Node version '${this.nodeInfo.versionSpec}' for platform ${this.osPlat} and architecture ${this.nodeInfo.arch}.`
);
}

const toolName = this.getNodejsMirrorURLInfo(evaluatedVersion);

try {
const toolPath = await this.downloadNodejs(toolName);
return toolPath;
} catch (error) {
if (error instanceof tc.HTTPError && error.httpStatusCode === 404) {
core.warning(
`Node version ${this.nodeInfo.versionSpec} for platform ${this.osPlat} and architecture ${this.nodeInfo.arch} was found but failed to download. ` +
'This usually happens when downloadable binaries are not fully updated at https://nodejs.org/. ' +
'To resolve this issue you may either fall back to the older version or try again later.'
);
}

throw error;
}
}

}
6 changes: 5 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
@@ -33,6 +33,9 @@ export async function run() {
arch = os.arch();
}

const mirrorURL = core.getInput('mirrorURL').trim(); // .trim() to remove any accidental spaces


if (version) {
const token = core.getInput('token');
const auth = !token ? undefined : `token ${token}`;
@@ -45,7 +48,8 @@ export async function run() {
checkLatest,
auth,
stable,
arch
arch,
mirrorURL
};
const nodeDistribution = getNodejsDistribution(nodejsInfo);
await nodeDistribution.setupNodeJs();