From 9e956a555c2c3d534ac93f57280063bfa7c63468 Mon Sep 17 00:00:00 2001
From: Nikolai Laevskii <nikolai.laevskii@akvelon.com>
Date: Thu, 19 Oct 2023 13:43:56 +0200
Subject: [PATCH 1/4] Add notice about binaries not being updated yet

---
 __tests__/official-installer.test.ts          |  35 +++++
 .../official_builds/official_builds.ts        | 134 +++++++++++-------
 2 files changed, 118 insertions(+), 51 deletions(-)

diff --git a/__tests__/official-installer.test.ts b/__tests__/official-installer.test.ts
index 474fb5b9..f86ef7b0 100644
--- a/__tests__/official-installer.test.ts
+++ b/__tests__/official-installer.test.ts
@@ -357,6 +357,41 @@ describe('setup-node', () => {
     expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
   });
 
+  it('reports when download failed but version exists', async () => {
+    os.platform = 'linux';
+    os.arch = 'x64';
+
+    // a version which is not in the manifest but is in node dist
+    const versionSpec = '11.15.0';
+
+    inputs['node-version'] = versionSpec;
+    inputs['always-auth'] = false;
+    inputs['token'] = 'faketoken';
+
+    // ... but not in the local cache
+    findSpy.mockImplementation(() => '');
+
+    dlSpy.mockImplementationOnce(async () => {
+      throw new tc.HTTPError(404);
+    });
+
+    await main.run();
+
+    expect(getManifestSpy).toHaveBeenCalled();
+    expect(logSpy).toHaveBeenCalledWith(
+      `Attempting to download ${versionSpec}...`
+    );
+    expect(logSpy).toHaveBeenCalledWith(
+      'Not found in manifest. Falling back to download directly from Node'
+    );
+    expect(dlSpy).toHaveBeenCalled();
+    expect(logSpy).toHaveBeenCalledWith(
+      `Node version ${versionSpec} for platform ${os.platform} and architecture ${os.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.'
+    );
+  });
+
   it('acquires specified architecture of node', async () => {
     for (const {arch, version, osSpec} of [
       {arch: 'x86', version: '12.16.2', osSpec: 'win32'},
diff --git a/src/distributions/official_builds/official_builds.ts b/src/distributions/official_builds/official_builds.ts
index 222d341e..68827880 100644
--- a/src/distributions/official_builds/official_builds.ts
+++ b/src/distributions/official_builds/official_builds.ts
@@ -18,6 +18,7 @@ export default class OfficialBuilds extends BaseDistribution {
     let manifest: tc.IToolRelease[] | undefined;
     let nodeJsVersions: INodeVersion[] | undefined;
     const osArch = this.translateArchToDistUrl(this.nodeInfo.arch);
+
     if (this.isLtsAlias(this.nodeInfo.versionSpec)) {
       core.info('Attempt to resolve LTS alias from manifest...');
 
@@ -61,63 +62,57 @@ export default class OfficialBuilds extends BaseDistribution {
 
     if (toolPath) {
       core.info(`Found in cache @ ${toolPath}`);
-    } else {
-      let downloadPath = '';
-      try {
-        core.info(`Attempting to download ${this.nodeInfo.versionSpec}...`);
+      this.addToolPath(toolPath);
+      return;
+    }
 
-        const versionInfo = await this.getInfoFromManifest(
-          this.nodeInfo.versionSpec,
-          this.nodeInfo.stable,
-          osArch,
-          manifest
+    let downloadPath = '';
+    try {
+      core.info(`Attempting to download ${this.nodeInfo.versionSpec}...`);
+
+      const versionInfo = await this.getInfoFromManifest(
+        this.nodeInfo.versionSpec,
+        this.nodeInfo.stable,
+        osArch,
+        manifest
+      );
+
+      if (versionInfo) {
+        core.info(
+          `Acquiring ${versionInfo.resolvedVersion} - ${versionInfo.arch} from ${versionInfo.downloadUrl}`
+        );
+        downloadPath = await tc.downloadTool(
+          versionInfo.downloadUrl,
+          undefined,
+          this.nodeInfo.auth
         );
-        if (versionInfo) {
-          core.info(
-            `Acquiring ${versionInfo.resolvedVersion} - ${versionInfo.arch} from ${versionInfo.downloadUrl}`
-          );
-          downloadPath = await tc.downloadTool(
-            versionInfo.downloadUrl,
-            undefined,
-            this.nodeInfo.auth
-          );
 
-          if (downloadPath) {
-            toolPath = await this.extractArchive(downloadPath, versionInfo);
-          }
-        } else {
-          core.info(
-            'Not found in manifest. Falling back to download directly from Node'
-          );
+        if (downloadPath) {
+          toolPath = await this.extractArchive(downloadPath, versionInfo);
         }
-      } 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 as Error).message);
-        }
-        core.debug((err as Error).stack ?? 'empty stack');
-        core.info('Falling back to download directly from Node');
+      } 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 as Error).message);
+      }
+      core.debug((err as Error).stack ?? 'empty stack');
+      core.info('Falling back to download directly from Node');
+    }
 
-      if (!toolPath) {
-        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.getNodejsDistInfo(evaluatedVersion);
-        toolPath = await this.downloadNodejs(toolName);
-      }
+    if (!toolPath) {
+      toolPath = await this.downloadDirectlyFromNode();
     }
 
     if (this.osPlat != 'win32') {
@@ -127,6 +122,43 @@ export default class OfficialBuilds extends BaseDistribution {
     core.addPath(toolPath);
   }
 
+  protected addToolPath(toolPath: string) {
+    if (this.osPlat != 'win32') {
+      toolPath = path.join(toolPath, 'bin');
+    }
+
+    core.addPath(toolPath);
+  }
+
+  protected async downloadDirectlyFromNode() {
+    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.getNodejsDistInfo(evaluatedVersion);
+
+    try {
+      const toolPath = await this.downloadNodejs(toolName);
+      return toolPath;
+    } catch (error) {
+      if (error instanceof tc.HTTPError && error.httpStatusCode === 404) {
+        core.info(
+          `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;
+    }
+  }
+
   protected evaluateVersions(versions: string[]): string {
     let version = '';
 

From 5a8d9111e32e049e80167427e34aad324f306b77 Mon Sep 17 00:00:00 2001
From: Nikolai Laevskii <nikolai.laevskii@akvelon.com>
Date: Thu, 19 Oct 2023 14:31:08 +0200
Subject: [PATCH 2/4] Update build

---
 dist/setup/index.js | 90 ++++++++++++++++++++++++++++-----------------
 1 file changed, 56 insertions(+), 34 deletions(-)

diff --git a/dist/setup/index.js b/dist/setup/index.js
index 5196ada9..8c777f08 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -72783,45 +72783,38 @@ class OfficialBuilds extends base_distribution_1.default {
             let toolPath = this.findVersionInHostedToolCacheDirectory();
             if (toolPath) {
                 core.info(`Found in cache @ ${toolPath}`);
+                this.addToolPath(toolPath);
+                return;
             }
-            else {
-                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);
-                        }
-                    }
-                    else {
-                        core.info('Not found in manifest. Falling back to download directly from Node');
+            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);
                     }
                 }
-                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((_a = err.stack) !== null && _a !== void 0 ? _a : 'empty stack');
-                    core.info('Falling back to download directly from Node');
+                else {
+                    core.info('Not found in manifest. Falling back to download directly from Node');
                 }
-                if (!toolPath) {
-                    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.getNodejsDistInfo(evaluatedVersion);
-                    toolPath = yield this.downloadNodejs(toolName);
+            }
+            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((_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');
@@ -72829,6 +72822,35 @@ class OfficialBuilds extends base_distribution_1.default {
             core.addPath(toolPath);
         });
     }
+    addToolPath(toolPath) {
+        if (this.osPlat != 'win32') {
+            toolPath = path_1.default.join(toolPath, 'bin');
+        }
+        core.addPath(toolPath);
+    }
+    downloadDirectlyFromNode() {
+        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.getNodejsDistInfo(evaluatedVersion);
+            try {
+                const toolPath = yield this.downloadNodejs(toolName);
+                return toolPath;
+            }
+            catch (error) {
+                if (error instanceof tc.HTTPError && error.httpStatusCode === 404) {
+                    core.info(`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;
+            }
+        });
+    }
     evaluateVersions(versions) {
         let version = '';
         if (this.isLatestSyntax(this.nodeInfo.versionSpec)) {

From ac16ae42d727f28d8231c7ce87ef399a68ce8d80 Mon Sep 17 00:00:00 2001
From: Nikolai Laevskii <nikolai.laevskii@akvelon.com>
Date: Thu, 19 Oct 2023 16:59:10 +0200
Subject: [PATCH 3/4] Update message to use waning instead of info

---
 dist/setup/index.js                                  | 2 +-
 src/distributions/official_builds/official_builds.ts | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/dist/setup/index.js b/dist/setup/index.js
index 8c777f08..eb589b77 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -72843,7 +72843,7 @@ class OfficialBuilds extends base_distribution_1.default {
             }
             catch (error) {
                 if (error instanceof tc.HTTPError && error.httpStatusCode === 404) {
-                    core.info(`Node version ${this.nodeInfo.versionSpec} for platform ${this.osPlat} and architecture ${this.nodeInfo.arch} was found but failed to download. ` +
+                    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.');
                 }
diff --git a/src/distributions/official_builds/official_builds.ts b/src/distributions/official_builds/official_builds.ts
index 68827880..4e368b00 100644
--- a/src/distributions/official_builds/official_builds.ts
+++ b/src/distributions/official_builds/official_builds.ts
@@ -148,7 +148,7 @@ export default class OfficialBuilds extends BaseDistribution {
       return toolPath;
     } catch (error) {
       if (error instanceof tc.HTTPError && error.httpStatusCode === 404) {
-        core.info(
+        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.'

From e52912ef25bc38da27c83c00c3c61bbb5b7bddc3 Mon Sep 17 00:00:00 2001
From: Nikolai Laevskii <nikolai.laevskii@akvelon.com>
Date: Thu, 19 Oct 2023 17:12:39 +0200
Subject: [PATCH 4/4] Update tests

---
 __tests__/official-installer.test.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/__tests__/official-installer.test.ts b/__tests__/official-installer.test.ts
index f86ef7b0..2d36c19c 100644
--- a/__tests__/official-installer.test.ts
+++ b/__tests__/official-installer.test.ts
@@ -385,7 +385,7 @@ describe('setup-node', () => {
       'Not found in manifest. Falling back to download directly from Node'
     );
     expect(dlSpy).toHaveBeenCalled();
-    expect(logSpy).toHaveBeenCalledWith(
+    expect(warningSpy).toHaveBeenCalledWith(
       `Node version ${versionSpec} for platform ${os.platform} and architecture ${os.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.'