From 0b66095a848d5fcfa12981fab71344e96921d0f8 Mon Sep 17 00:00:00 2001
From: Aparna Jyothi <aparnajyothi-y@github.com>
Date: Wed, 29 Jan 2025 18:41:57 +0530
Subject: [PATCH] mirrorurl code

---
 action.yml                                    |   3 +
 dist/setup/index.js                           | 184 ++++++++++++------
 src/distributions/base-distribution.ts        |  26 +++
 src/distributions/base-models.ts              |   2 +
 .../official_builds/official_builds.ts        |  51 +++++
 src/main.ts                                   |   6 +-
 6 files changed, 210 insertions(+), 62 deletions(-)

diff --git a/action.yml b/action.yml
index 99db5869..e2e7697a 100644
--- a/action.yml
+++ b/action.yml
@@ -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:
diff --git a/dist/setup/index.js b/dist/setup/index.js
index cdca1dbf..ab183ef2 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -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}'`);
-                }
-                else {
-                    core.info(`Failed to resolve version ${this.nodeInfo.versionSpec} from manifest`);
-                }
-            }
-            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);
+            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 = yield this.extractArchive(downloadPath, versionInfo, false);
+                        toolPath = downloadPath;
                     }
                 }
-                else {
-                    core.info('Not found in manifest. Falling back to download directly from Node');
-                }
-            }
-            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 {
+                catch (err) {
                     core.info(err.message);
+                    core.debug((_a = err.stack) !== null && _a !== void 0 ? _a : 'empty stack');
                 }
-                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();
+            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`);
+                    }
+                }
+                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 {
+                        core.info('Not found in manifest. Falling back to download directly from Node');
+                    }
+                }
+                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');
+                }
+                if (!toolPath) {
+                    toolPath = yield this.downloadDirectlyFromNode();
+                }
+                if (this.osPlat != 'win32') {
+                    toolPath = path_1.default.join(toolPath, 'bin');
+                }
+                core.addPath(toolPath);
             }
-            if (this.osPlat != 'win32') {
-                toolPath = path_1.default.join(toolPath, 'bin');
-            }
-            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();
diff --git a/src/distributions/base-distribution.ts b/src/distributions/base-distribution.ts
index 70b4b572..ac13ccc7 100644
--- a/src/distributions/base-distribution.ts
+++ b/src/distributions/base-distribution.ts
@@ -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(
diff --git a/src/distributions/base-models.ts b/src/distributions/base-models.ts
index 0be93b63..d3dbee15 100644
--- a/src/distributions/base-models.ts
+++ b/src/distributions/base-models.ts
@@ -4,6 +4,7 @@ export interface NodeInputs {
   auth?: string;
   checkLatest: boolean;
   stable: boolean;
+  mirrorURL: string;
 }
 
 export interface INodeVersionInfo {
@@ -11,6 +12,7 @@ export interface INodeVersionInfo {
   resolvedVersion: string;
   arch: string;
   fileName: string;
+  
 }
 
 export interface INodeVersion {
diff --git a/src/distributions/official_builds/official_builds.ts b/src/distributions/official_builds/official_builds.ts
index e56eaf81..7f8b65b7 100644
--- a/src/distributions/official_builds/official_builds.ts
+++ b/src/distributions/official_builds/official_builds.ts
@@ -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;
+    }
+  }
+
 }
diff --git a/src/main.ts b/src/main.ts
index c55c3b00..a3856ace 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -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();