From e73ffbcdec6e32336d408b2ff7c61488032f3ec4 Mon Sep 17 00:00:00 2001
From: eric sciple <ericsciple@users.noreply.github.com>
Date: Mon, 18 May 2020 12:29:21 -0400
Subject: [PATCH] support ghes (#157)

---
 dist/index.js     | 444 ++++++++++++++++++++++++++++++++++++++++++----
 package-lock.json |  13 +-
 package.json      |   2 +-
 src/installer.ts  |  83 ++++++---
 src/main.ts       |  11 +-
 5 files changed, 481 insertions(+), 72 deletions(-)

diff --git a/dist/index.js b/dist/index.js
index e6aa40b..87ce6f0 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1338,7 +1338,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
 };
 Object.defineProperty(exports, "__esModule", { value: true });
 const semver = __importStar(__webpack_require__(280));
-const core_1 = __webpack_require__(470);
+const core_1 = __webpack_require__(902);
 // needs to be require for core node modules to be mocked
 /* eslint @typescript-eslint/no-require-imports: 0 */
 const os = __webpack_require__(87);
@@ -4631,6 +4631,7 @@ const core = __importStar(__webpack_require__(470));
 const installer = __importStar(__webpack_require__(749));
 const auth = __importStar(__webpack_require__(202));
 const path = __importStar(__webpack_require__(622));
+const url_1 = __webpack_require__(835);
 function run() {
     return __awaiter(this, void 0, void 0, function* () {
         try {
@@ -4645,8 +4646,9 @@ function run() {
             console.log(`version: ${version}`);
             if (version) {
                 let token = core.getInput('token');
+                let auth = !token || isGhes() ? undefined : `token ${token}`;
                 let stable = (core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
-                yield installer.getNode(version, stable, token);
+                yield installer.getNode(version, stable, auth);
             }
             const registryUrl = core.getInput('registry-url');
             const alwaysAuth = core.getInput('always-auth');
@@ -4664,6 +4666,10 @@ function run() {
     });
 }
 exports.run = run;
+function isGhes() {
+    const ghUrl = new url_1.URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com');
+    return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
+}
 //# sourceMappingURL=main.js.map
 
 /***/ }),
@@ -10883,7 +10889,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
     return (mod && mod.__esModule) ? mod : { "default": mod };
 };
 Object.defineProperty(exports, "__esModule", { value: true });
-const core = __importStar(__webpack_require__(470));
+const core = __importStar(__webpack_require__(902));
 const io = __importStar(__webpack_require__(1));
 const fs = __importStar(__webpack_require__(747));
 const mm = __importStar(__webpack_require__(31));
@@ -10912,9 +10918,10 @@ const userAgent = 'actions/tool-cache';
  *
  * @param url       url of tool to download
  * @param dest      path to download tool
+ * @param auth      authorization header
  * @returns         path to downloaded tool
  */
-function downloadTool(url, dest, token) {
+function downloadTool(url, dest, auth) {
     return __awaiter(this, void 0, void 0, function* () {
         dest = dest || path.join(_getTempDirectory(), v4_1.default());
         yield io.mkdirP(path.dirname(dest));
@@ -10925,7 +10932,7 @@ function downloadTool(url, dest, token) {
         const maxSeconds = _getGlobal('TEST_DOWNLOAD_TOOL_RETRY_MAX_SECONDS', 20);
         const retryHelper = new retry_helper_1.RetryHelper(maxAttempts, minSeconds, maxSeconds);
         return yield retryHelper.execute(() => __awaiter(this, void 0, void 0, function* () {
-            return yield downloadToolAttempt(url, dest || '', token);
+            return yield downloadToolAttempt(url, dest || '', auth);
         }), (err) => {
             if (err instanceof HTTPError && err.httpStatusCode) {
                 // Don't retry anything less than 500, except 408 Request Timeout and 429 Too Many Requests
@@ -10941,7 +10948,7 @@ function downloadTool(url, dest, token) {
     });
 }
 exports.downloadTool = downloadTool;
-function downloadToolAttempt(url, dest, token) {
+function downloadToolAttempt(url, dest, auth) {
     return __awaiter(this, void 0, void 0, function* () {
         if (fs.existsSync(dest)) {
             throw new Error(`Destination file path ${dest} already exists`);
@@ -10951,9 +10958,10 @@ function downloadToolAttempt(url, dest, token) {
             allowRetries: false
         });
         let headers;
-        if (token) {
+        if (auth) {
+            core.debug('set auth');
             headers = {
-                authorization: `token ${token}`
+                authorization: auth
             };
         }
         const response = yield http.get(url, headers);
@@ -11011,9 +11019,10 @@ function extract7z(file, dest, _7zPath) {
         process.chdir(dest);
         if (_7zPath) {
             try {
+                const logLevel = core.isDebug() ? '-bb1' : '-bb0';
                 const args = [
                     'x',
-                    '-bb1',
+                    logLevel,
                     '-bd',
                     '-sccUTF-8',
                     file
@@ -11096,6 +11105,9 @@ function extractTar(file, dest, flags = 'xz') {
         else {
             args = [flags];
         }
+        if (core.isDebug() && !flags.includes('v')) {
+            args.push('-v');
+        }
         let destArg = dest;
         let fileArg = file;
         if (IS_WINDOWS && isGnuTar) {
@@ -11145,7 +11157,7 @@ function extractZipWin(file, dest) {
         const escapedDest = dest.replace(/'/g, "''").replace(/"|\n|\r/g, '');
         const command = `$ErrorActionPreference = 'Stop' ; try { Add-Type -AssemblyName System.IO.Compression.FileSystem } catch { } ; [System.IO.Compression.ZipFile]::ExtractToDirectory('${escapedFile}', '${escapedDest}')`;
         // run powershell
-        const powershellPath = yield io.which('powershell');
+        const powershellPath = yield io.which('powershell', true);
         const args = [
             '-NoLogo',
             '-Sta',
@@ -11161,8 +11173,12 @@ function extractZipWin(file, dest) {
 }
 function extractZipNix(file, dest) {
     return __awaiter(this, void 0, void 0, function* () {
-        const unzipPath = yield io.which('unzip');
-        yield exec_1.exec(`"${unzipPath}"`, [file], { cwd: dest });
+        const unzipPath = yield io.which('unzip', true);
+        const args = [file];
+        if (!core.isDebug()) {
+            args.unshift('-q');
+        }
+        yield exec_1.exec(`"${unzipPath}"`, args, { cwd: dest });
     });
 }
 /**
@@ -11290,14 +11306,16 @@ function findAllVersions(toolName, arch) {
     return versions;
 }
 exports.findAllVersions = findAllVersions;
-function getManifestFromRepo(owner, repo, token, branch = 'master') {
+function getManifestFromRepo(owner, repo, auth, branch = 'master') {
     return __awaiter(this, void 0, void 0, function* () {
         let releases = [];
         const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${branch}`;
         const http = new httpm.HttpClient('tool-cache');
-        const headers = {
-            authorization: `token ${token}`
-        };
+        const headers = {};
+        if (auth) {
+            core.debug('set auth');
+            headers.authorization = auth;
+        }
         const response = yield http.getJson(treeUrl, headers);
         if (!response.result) {
             return releases;
@@ -12976,12 +12994,11 @@ const tc = __importStar(__webpack_require__(533));
 const path = __importStar(__webpack_require__(622));
 const semver = __importStar(__webpack_require__(280));
 const fs = __webpack_require__(747);
-function getNode(versionSpec, stable, token) {
+function getNode(versionSpec, stable, auth) {
     return __awaiter(this, void 0, void 0, function* () {
         let osPlat = os.platform();
         let osArch = translateArchToDistUrl(os.arch());
         // check cache
-        let info = null;
         let toolPath;
         toolPath = tc.find('node', versionSpec);
         // If not found in cache, download
@@ -12990,31 +13007,58 @@ function getNode(versionSpec, stable, token) {
         }
         else {
             console.log(`Attempting to download ${versionSpec}...`);
-            let info = yield getInfoFromManifest(versionSpec, stable, token);
-            if (!info) {
-                console.log('Not found in manifest.  Falling back to download directly from Node');
-                info = yield getInfoFromDist(versionSpec);
-            }
-            if (!info) {
-                throw new Error(`Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`);
-            }
-            console.log(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
             let downloadPath = '';
+            let info = null;
+            //
+            // Try download from internal distribution (popular versions only)
+            //
             try {
-                downloadPath = yield tc.downloadTool(info.downloadUrl, undefined, token);
+                info = yield getInfoFromManifest(versionSpec, stable, auth);
+                if (info) {
+                    console.log(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
+                    downloadPath = yield tc.downloadTool(info.downloadUrl, undefined, auth);
+                }
+                else {
+                    console.log('Not found in manifest.  Falling back to download directly from Node');
+                }
             }
             catch (err) {
-                if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
-                    yield acquireNodeFromFallbackLocation(info.resolvedVersion);
-                    return;
+                // Rate limit?
+                if (err instanceof tc.HTTPError &&
+                    (err.httpStatusCode === 403 || err.httpStatusCode === 429)) {
+                    console.log(`Received HTTP status code ${err.httpStatusCode}.  This usually indicates the rate limit has been exceeded`);
+                }
+                else {
+                    console.log(err.message);
+                }
+                core.debug(err.stack);
+                console.log('Falling back to download directly from Node');
+            }
+            //
+            // Download from nodejs.org
+            //
+            if (!downloadPath) {
+                info = yield getInfoFromDist(versionSpec);
+                if (!info) {
+                    throw new Error(`Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`);
+                }
+                console.log(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
+                try {
+                    downloadPath = yield tc.downloadTool(info.downloadUrl);
+                }
+                catch (err) {
+                    if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
+                        return yield acquireNodeFromFallbackLocation(info.resolvedVersion);
+                    }
+                    throw err;
                 }
-                throw err;
             }
             //
             // Extract
             //
             console.log('Extracting ...');
             let extPath;
+            info = info || {}; // satisfy compiler, never null when reaches here
             if (osPlat == 'win32') {
                 let _7zPath = path.join(__dirname, '..', 'externals', '7zr.exe');
                 extPath = yield tc.extract7z(downloadPath, undefined, _7zPath);
@@ -13052,10 +13096,10 @@ function getNode(versionSpec, stable, token) {
     });
 }
 exports.getNode = getNode;
-function getInfoFromManifest(versionSpec, stable, token) {
+function getInfoFromManifest(versionSpec, stable, auth) {
     return __awaiter(this, void 0, void 0, function* () {
         let info = null;
-        const releases = yield tc.getManifestFromRepo('actions', 'node-versions', token);
+        const releases = yield tc.getManifestFromRepo('actions', 'node-versions', auth);
         console.log(`matching ${versionSpec}...`);
         const rel = yield tc.findFromManifest(versionSpec, stable, releases);
         if (rel && rel.files.length > 0) {
@@ -13063,7 +13107,6 @@ function getInfoFromManifest(versionSpec, stable, token) {
             info.resolvedVersion = rel.version;
             info.downloadUrl = rel.files[0].download_url;
             info.fileName = rel.files[0].filename;
-            info.token = token;
         }
         return info;
     });
@@ -13072,7 +13115,6 @@ function getInfoFromDist(versionSpec) {
     return __awaiter(this, void 0, void 0, function* () {
         let osPlat = os.platform();
         let osArch = translateArchToDistUrl(os.arch());
-        let info = null;
         let version;
         version = yield queryDistForMatch(versionSpec);
         if (!version) {
@@ -15956,6 +15998,105 @@ function set(object, path, value) {
 module.exports = set;
 
 
+/***/ }),
+
+/***/ 888:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
+    result["default"] = mod;
+    return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const os = __importStar(__webpack_require__(87));
+/**
+ * Commands
+ *
+ * Command Format:
+ *   ::name key=value,key=value::message
+ *
+ * Examples:
+ *   ::warning::This is the message
+ *   ::set-env name=MY_VAR::some value
+ */
+function issueCommand(command, properties, message) {
+    const cmd = new Command(command, properties, message);
+    process.stdout.write(cmd.toString() + os.EOL);
+}
+exports.issueCommand = issueCommand;
+function issue(name, message = '') {
+    issueCommand(name, {}, message);
+}
+exports.issue = issue;
+const CMD_STRING = '::';
+class Command {
+    constructor(command, properties, message) {
+        if (!command) {
+            command = 'missing.command';
+        }
+        this.command = command;
+        this.properties = properties;
+        this.message = message;
+    }
+    toString() {
+        let cmdStr = CMD_STRING + this.command;
+        if (this.properties && Object.keys(this.properties).length > 0) {
+            cmdStr += ' ';
+            let first = true;
+            for (const key in this.properties) {
+                if (this.properties.hasOwnProperty(key)) {
+                    const val = this.properties[key];
+                    if (val) {
+                        if (first) {
+                            first = false;
+                        }
+                        else {
+                            cmdStr += ',';
+                        }
+                        cmdStr += `${key}=${escapeProperty(val)}`;
+                    }
+                }
+            }
+        }
+        cmdStr += `${CMD_STRING}${escapeData(this.message)}`;
+        return cmdStr;
+    }
+}
+/**
+ * Sanitizes an input into a string so it can be passed into issueCommand safely
+ * @param input input to sanitize into a string
+ */
+function toCommandValue(input) {
+    if (input === null || input === undefined) {
+        return '';
+    }
+    else if (typeof input === 'string' || input instanceof String) {
+        return input;
+    }
+    return JSON.stringify(input);
+}
+exports.toCommandValue = toCommandValue;
+function escapeData(s) {
+    return toCommandValue(s)
+        .replace(/%/g, '%25')
+        .replace(/\r/g, '%0D')
+        .replace(/\n/g, '%0A');
+}
+function escapeProperty(s) {
+    return toCommandValue(s)
+        .replace(/%/g, '%25')
+        .replace(/\r/g, '%0D')
+        .replace(/\n/g, '%0A')
+        .replace(/:/g, '%3A')
+        .replace(/,/g, '%2C');
+}
+//# sourceMappingURL=command.js.map
+
 /***/ }),
 
 /***/ 899:
@@ -16061,6 +16202,235 @@ function patchForDeprecation(octokit, apiOptions, method, methodName) {
 }
 
 
+/***/ }),
+
+/***/ 902:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
+    result["default"] = mod;
+    return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const command_1 = __webpack_require__(888);
+const os = __importStar(__webpack_require__(87));
+const path = __importStar(__webpack_require__(622));
+/**
+ * The code to exit an action
+ */
+var ExitCode;
+(function (ExitCode) {
+    /**
+     * A code indicating that the action was successful
+     */
+    ExitCode[ExitCode["Success"] = 0] = "Success";
+    /**
+     * A code indicating that the action was a failure
+     */
+    ExitCode[ExitCode["Failure"] = 1] = "Failure";
+})(ExitCode = exports.ExitCode || (exports.ExitCode = {}));
+//-----------------------------------------------------------------------
+// Variables
+//-----------------------------------------------------------------------
+/**
+ * Sets env variable for this action and future actions in the job
+ * @param name the name of the variable to set
+ * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function exportVariable(name, val) {
+    const convertedVal = command_1.toCommandValue(val);
+    process.env[name] = convertedVal;
+    command_1.issueCommand('set-env', { name }, convertedVal);
+}
+exports.exportVariable = exportVariable;
+/**
+ * Registers a secret which will get masked from logs
+ * @param secret value of the secret
+ */
+function setSecret(secret) {
+    command_1.issueCommand('add-mask', {}, secret);
+}
+exports.setSecret = setSecret;
+/**
+ * Prepends inputPath to the PATH (for this action and future actions)
+ * @param inputPath
+ */
+function addPath(inputPath) {
+    command_1.issueCommand('add-path', {}, inputPath);
+    process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`;
+}
+exports.addPath = addPath;
+/**
+ * Gets the value of an input.  The value is also trimmed.
+ *
+ * @param     name     name of the input to get
+ * @param     options  optional. See InputOptions.
+ * @returns   string
+ */
+function getInput(name, options) {
+    const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
+    if (options && options.required && !val) {
+        throw new Error(`Input required and not supplied: ${name}`);
+    }
+    return val.trim();
+}
+exports.getInput = getInput;
+/**
+ * Sets the value of an output.
+ *
+ * @param     name     name of the output to set
+ * @param     value    value to store. Non-string values will be converted to a string via JSON.stringify
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function setOutput(name, value) {
+    command_1.issueCommand('set-output', { name }, value);
+}
+exports.setOutput = setOutput;
+/**
+ * Enables or disables the echoing of commands into stdout for the rest of the step.
+ * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set.
+ *
+ */
+function setCommandEcho(enabled) {
+    command_1.issue('echo', enabled ? 'on' : 'off');
+}
+exports.setCommandEcho = setCommandEcho;
+//-----------------------------------------------------------------------
+// Results
+//-----------------------------------------------------------------------
+/**
+ * Sets the action status to failed.
+ * When the action exits it will be with an exit code of 1
+ * @param message add error issue message
+ */
+function setFailed(message) {
+    process.exitCode = ExitCode.Failure;
+    error(message);
+}
+exports.setFailed = setFailed;
+//-----------------------------------------------------------------------
+// Logging Commands
+//-----------------------------------------------------------------------
+/**
+ * Gets whether Actions Step Debug is on or not
+ */
+function isDebug() {
+    return process.env['RUNNER_DEBUG'] === '1';
+}
+exports.isDebug = isDebug;
+/**
+ * Writes debug message to user log
+ * @param message debug message
+ */
+function debug(message) {
+    command_1.issueCommand('debug', {}, message);
+}
+exports.debug = debug;
+/**
+ * Adds an error issue
+ * @param message error issue message. Errors will be converted to string via toString()
+ */
+function error(message) {
+    command_1.issue('error', message instanceof Error ? message.toString() : message);
+}
+exports.error = error;
+/**
+ * Adds an warning issue
+ * @param message warning issue message. Errors will be converted to string via toString()
+ */
+function warning(message) {
+    command_1.issue('warning', message instanceof Error ? message.toString() : message);
+}
+exports.warning = warning;
+/**
+ * Writes info to log with console.log.
+ * @param message info message
+ */
+function info(message) {
+    process.stdout.write(message + os.EOL);
+}
+exports.info = info;
+/**
+ * Begin an output group.
+ *
+ * Output until the next `groupEnd` will be foldable in this group
+ *
+ * @param name The name of the output group
+ */
+function startGroup(name) {
+    command_1.issue('group', name);
+}
+exports.startGroup = startGroup;
+/**
+ * End an output group.
+ */
+function endGroup() {
+    command_1.issue('endgroup');
+}
+exports.endGroup = endGroup;
+/**
+ * Wrap an asynchronous function call in a group.
+ *
+ * Returns the same type as the function itself.
+ *
+ * @param name The name of the group
+ * @param fn The function to wrap in the group
+ */
+function group(name, fn) {
+    return __awaiter(this, void 0, void 0, function* () {
+        startGroup(name);
+        let result;
+        try {
+            result = yield fn();
+        }
+        finally {
+            endGroup();
+        }
+        return result;
+    });
+}
+exports.group = group;
+//-----------------------------------------------------------------------
+// Wrapper action state
+//-----------------------------------------------------------------------
+/**
+ * Saves state for current action, the state can only be retrieved by this action's post job execution.
+ *
+ * @param     name     name of the state to store
+ * @param     value    value to store. Non-string values will be converted to a string via JSON.stringify
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function saveState(name, value) {
+    command_1.issueCommand('save-state', { name }, value);
+}
+exports.saveState = saveState;
+/**
+ * Gets the value of an state set by this action's main execution.
+ *
+ * @param     name     name of the state to get
+ * @returns   string
+ */
+function getState(name) {
+    return process.env[`STATE_${name}`] || '';
+}
+exports.getState = getState;
+//# sourceMappingURL=core.js.map
+
 /***/ }),
 
 /***/ 929:
@@ -16702,7 +17072,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
     return result;
 };
 Object.defineProperty(exports, "__esModule", { value: true });
-const core = __importStar(__webpack_require__(470));
+const core = __importStar(__webpack_require__(902));
 /**
  * Internal class for retries
  */
diff --git a/package-lock.json b/package-lock.json
index 26be5a5..be7fec2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -40,11 +40,11 @@
       "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg=="
     },
     "@actions/tool-cache": {
-      "version": "1.5.3",
-      "resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.5.3.tgz",
-      "integrity": "sha512-G6OMdGvKVkApJv+nRURpi1nZUKonqWq37fqK8rdJLJr5PuWAEo/cqD/ibano7Da/LRx1yYFEMwO6RXkB2VaIqQ==",
+      "version": "1.5.4",
+      "resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.5.4.tgz",
+      "integrity": "sha512-72ijIBM0s/dx2D0eYYxaxaeKWeVatOK8OHPNctJ5cyKjZp1j12egX+nW/N+tnQRNMVxTp9WjudZO5wizUBxC/w==",
       "requires": {
-        "@actions/core": "^1.2.0",
+        "@actions/core": "^1.2.3",
         "@actions/exec": "^1.0.0",
         "@actions/http-client": "^1.0.8",
         "@actions/io": "^1.0.1",
@@ -52,6 +52,11 @@
         "uuid": "^3.3.2"
       },
       "dependencies": {
+        "@actions/core": {
+          "version": "1.2.4",
+          "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.4.tgz",
+          "integrity": "sha512-YJCEq8BE3CdN8+7HPZ/4DxJjk/OkZV2FFIf+DlZTC/4iBlzYCD5yjRR6eiOS5llO11zbRltIRuKAjMKaWTE6cg=="
+        },
         "@actions/http-client": {
           "version": "1.0.8",
           "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz",
diff --git a/package.json b/package.json
index a6c45d6..9a3f1c5 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
     "@actions/github": "^1.1.0",
     "@actions/http-client": "^1.0.6",
     "@actions/io": "^1.0.2",
-    "@actions/tool-cache": "^1.5.3",
+    "@actions/tool-cache": "^1.5.4",
     "semver": "^6.1.1"
   },
   "devDependencies": {
diff --git a/src/installer.ts b/src/installer.ts
index 1229482..ee96bda 100644
--- a/src/installer.ts
+++ b/src/installer.ts
@@ -6,7 +6,6 @@ import * as io from '@actions/io';
 import * as tc from '@actions/tool-cache';
 import * as path from 'path';
 import * as semver from 'semver';
-import {Url} from 'url';
 import fs = require('fs');
 
 //
@@ -20,7 +19,6 @@ export interface INodeVersion {
 
 interface INodeVersionInfo {
   downloadUrl: string;
-  token: string | null;
   resolvedVersion: string;
   fileName: string;
 }
@@ -28,13 +26,12 @@ interface INodeVersionInfo {
 export async function getNode(
   versionSpec: string,
   stable: boolean,
-  token: string
+  auth: string | undefined
 ) {
   let osPlat: string = os.platform();
   let osArch: string = translateArchToDistUrl(os.arch());
 
   // check cache
-  let info: INodeVersionInfo | null = null;
   let toolPath: string;
   toolPath = tc.find('node', versionSpec);
 
@@ -43,32 +40,61 @@ export async function getNode(
     console.log(`Found in cache @ ${toolPath}`);
   } else {
     console.log(`Attempting to download ${versionSpec}...`);
-    let info = await getInfoFromManifest(versionSpec, stable, token);
-    if (!info) {
-      console.log(
-        'Not found in manifest.  Falling back to download directly from Node'
-      );
-      info = await getInfoFromDist(versionSpec);
-    }
-
-    if (!info) {
-      throw new Error(
-        `Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`
-      );
-    }
-
-    console.log(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
-
     let downloadPath = '';
+    let info: INodeVersionInfo | null = null;
+
+    //
+    // Try download from internal distribution (popular versions only)
+    //
     try {
-      downloadPath = await tc.downloadTool(info.downloadUrl, undefined, token);
+      info = await getInfoFromManifest(versionSpec, stable, auth);
+      if (info) {
+        console.log(
+          `Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`
+        );
+        downloadPath = await tc.downloadTool(info.downloadUrl, undefined, auth);
+      } else {
+        console.log(
+          'Not found in manifest.  Falling back to download directly from Node'
+        );
+      }
     } catch (err) {
-      if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
-        await acquireNodeFromFallbackLocation(info.resolvedVersion);
-        return;
+      // Rate limit?
+      if (
+        err instanceof tc.HTTPError &&
+        (err.httpStatusCode === 403 || err.httpStatusCode === 429)
+      ) {
+        console.log(
+          `Received HTTP status code ${err.httpStatusCode}.  This usually indicates the rate limit has been exceeded`
+        );
+      } else {
+        console.log(err.message);
+      }
+      core.debug(err.stack);
+      console.log('Falling back to download directly from Node');
+    }
+
+    //
+    // Download from nodejs.org
+    //
+    if (!downloadPath) {
+      info = await getInfoFromDist(versionSpec);
+      if (!info) {
+        throw new Error(
+          `Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`
+        );
       }
 
-      throw err;
+      console.log(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
+      try {
+        downloadPath = await tc.downloadTool(info.downloadUrl);
+      } catch (err) {
+        if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
+          return await acquireNodeFromFallbackLocation(info.resolvedVersion);
+        }
+
+        throw err;
+      }
     }
 
     //
@@ -76,6 +102,7 @@ export async function getNode(
     //
     console.log('Extracting ...');
     let extPath: string;
+    info = info || ({} as INodeVersionInfo); // satisfy compiler, never null when reaches here
     if (osPlat == 'win32') {
       let _7zPath = path.join(__dirname, '..', 'externals', '7zr.exe');
       extPath = await tc.extract7z(downloadPath, undefined, _7zPath);
@@ -117,13 +144,13 @@ export async function getNode(
 async function getInfoFromManifest(
   versionSpec: string,
   stable: boolean,
-  token: string
+  auth: string | undefined
 ): Promise<INodeVersionInfo | null> {
   let info: INodeVersionInfo | null = null;
   const releases = await tc.getManifestFromRepo(
     'actions',
     'node-versions',
-    token
+    auth
   );
   console.log(`matching ${versionSpec}...`);
   const rel = await tc.findFromManifest(versionSpec, stable, releases);
@@ -133,7 +160,6 @@ async function getInfoFromManifest(
     info.resolvedVersion = rel.version;
     info.downloadUrl = rel.files[0].download_url;
     info.fileName = rel.files[0].filename;
-    info.token = token;
   }
 
   return info;
@@ -145,7 +171,6 @@ async function getInfoFromDist(
   let osPlat: string = os.platform();
   let osArch: string = translateArchToDistUrl(os.arch());
 
-  let info: INodeVersionInfo | null = null;
   let version: string;
 
   version = await queryDistForMatch(versionSpec);
diff --git a/src/main.ts b/src/main.ts
index 6a3486c..ab89421 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -2,6 +2,7 @@ import * as core from '@actions/core';
 import * as installer from './installer';
 import * as auth from './authutil';
 import * as path from 'path';
+import {URL} from 'url';
 
 export async function run() {
   try {
@@ -17,8 +18,9 @@ export async function run() {
     console.log(`version: ${version}`);
     if (version) {
       let token = core.getInput('token');
+      let auth = !token || isGhes() ? undefined : `token ${token}`;
       let stable = (core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
-      await installer.getNode(version, stable, token);
+      await installer.getNode(version, stable, auth);
     }
 
     const registryUrl: string = core.getInput('registry-url');
@@ -39,3 +41,10 @@ export async function run() {
     core.setFailed(error.message);
   }
 }
+
+function isGhes(): boolean {
+  const ghUrl = new URL(
+    process.env['GITHUB_SERVER_URL'] || 'https://github.com'
+  );
+  return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
+}