From 10aa35afd7f071e70ffaa122e3d7c6ffcb5f7ed1 Mon Sep 17 00:00:00 2001 From: Tobias Date: Tue, 26 Mar 2024 14:56:00 +0100 Subject: [PATCH] feat: fallback to raw endpoint for manifest when rate limit is reached (#766) --- __tests__/install-python.test.ts | 58 ++++++++++++++++++++++++++++++++ dist/setup/index.js | 32 ++++++++++++++++-- src/install-python.ts | 26 +++++++++++++- 3 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 __tests__/install-python.test.ts diff --git a/__tests__/install-python.test.ts b/__tests__/install-python.test.ts new file mode 100644 index 000000000..c3a6e7b46 --- /dev/null +++ b/__tests__/install-python.test.ts @@ -0,0 +1,58 @@ +import { + getManifest, + getManifestFromRepo, + getManifestFromURL +} from '../src/install-python'; +import * as httpm from '@actions/http-client'; +import * as tc from '@actions/tool-cache'; + +jest.mock('@actions/http-client'); +jest.mock('@actions/tool-cache'); + +const mockManifest = [{version: '1.0.0'}]; + +describe('getManifest', () => { + it('should return manifest from repo', async () => { + (tc.getManifestFromRepo as jest.Mock).mockResolvedValue(mockManifest); + const manifest = await getManifest(); + expect(manifest).toEqual(mockManifest); + }); + + it('should return manifest from URL if repo fetch fails', async () => { + (tc.getManifestFromRepo as jest.Mock).mockRejectedValue( + new Error('Fetch failed') + ); + (httpm.HttpClient.prototype.getJson as jest.Mock).mockResolvedValue({ + result: mockManifest + }); + const manifest = await getManifest(); + expect(manifest).toEqual(mockManifest); + }); +}); + +describe('getManifestFromRepo', () => { + it('should return manifest from repo', async () => { + (tc.getManifestFromRepo as jest.Mock).mockResolvedValue(mockManifest); + const manifest = await getManifestFromRepo(); + expect(manifest).toEqual(mockManifest); + }); +}); + +describe('getManifestFromURL', () => { + it('should return manifest from URL', async () => { + (httpm.HttpClient.prototype.getJson as jest.Mock).mockResolvedValue({ + result: mockManifest + }); + const manifest = await getManifestFromURL(); + expect(manifest).toEqual(mockManifest); + }); + + it('should throw error if unable to get manifest from URL', async () => { + (httpm.HttpClient.prototype.getJson as jest.Mock).mockResolvedValue({ + result: null + }); + await expect(getManifestFromURL()).rejects.toThrow( + 'Unable to get manifest from' + ); + }); +}); diff --git a/dist/setup/index.js b/dist/setup/index.js index f3040a80a..d558c611f 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -91388,11 +91388,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.installCpythonFromRelease = exports.getManifest = exports.findReleaseFromManifest = exports.MANIFEST_URL = void 0; +exports.installCpythonFromRelease = exports.getManifestFromURL = exports.getManifestFromRepo = exports.getManifest = exports.findReleaseFromManifest = exports.MANIFEST_URL = void 0; const path = __importStar(__nccwpck_require__(1017)); const core = __importStar(__nccwpck_require__(2186)); const tc = __importStar(__nccwpck_require__(7784)); const exec = __importStar(__nccwpck_require__(1514)); +const httpm = __importStar(__nccwpck_require__(6255)); const utils_1 = __nccwpck_require__(1314); const TOKEN = core.getInput('token'); const AUTH = !TOKEN ? undefined : `token ${TOKEN}`; @@ -91411,10 +91412,37 @@ function findReleaseFromManifest(semanticVersionSpec, architecture, manifest) { } exports.findReleaseFromManifest = findReleaseFromManifest; function getManifest() { + return __awaiter(this, void 0, void 0, function* () { + try { + return yield getManifestFromRepo(); + } + catch (err) { + core.debug('Fetching the manifest via the API failed.'); + if (err instanceof Error) { + core.debug(err.message); + } + } + return yield getManifestFromURL(); + }); +} +exports.getManifest = getManifest; +function getManifestFromRepo() { core.debug(`Getting manifest from ${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}@${MANIFEST_REPO_BRANCH}`); return tc.getManifestFromRepo(MANIFEST_REPO_OWNER, MANIFEST_REPO_NAME, AUTH, MANIFEST_REPO_BRANCH); } -exports.getManifest = getManifest; +exports.getManifestFromRepo = getManifestFromRepo; +function getManifestFromURL() { + return __awaiter(this, void 0, void 0, function* () { + core.debug('Falling back to fetching the manifest using raw URL.'); + const http = new httpm.HttpClient('tool-cache'); + const response = yield http.getJson(exports.MANIFEST_URL); + if (!response.result) { + throw new Error(`Unable to get manifest from ${exports.MANIFEST_URL}`); + } + return response.result; + }); +} +exports.getManifestFromURL = getManifestFromURL; function installPython(workingDirectory) { return __awaiter(this, void 0, void 0, function* () { const options = { diff --git a/src/install-python.ts b/src/install-python.ts index 2af61291d..3abdfde3e 100644 --- a/src/install-python.ts +++ b/src/install-python.ts @@ -2,6 +2,7 @@ import * as path from 'path'; import * as core from '@actions/core'; import * as tc from '@actions/tool-cache'; import * as exec from '@actions/exec'; +import * as httpm from '@actions/http-client'; import {ExecOptions} from '@actions/exec/lib/interfaces'; import {IS_WINDOWS, IS_LINUX} from './utils'; @@ -31,7 +32,19 @@ export async function findReleaseFromManifest( return foundRelease; } -export function getManifest(): Promise { +export async function getManifest(): Promise { + try { + return await getManifestFromRepo(); + } catch (err) { + core.debug('Fetching the manifest via the API failed.'); + if (err instanceof Error) { + core.debug(err.message); + } + } + return await getManifestFromURL(); +} + +export function getManifestFromRepo(): Promise { core.debug( `Getting manifest from ${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}@${MANIFEST_REPO_BRANCH}` ); @@ -43,6 +56,17 @@ export function getManifest(): Promise { ); } +export async function getManifestFromURL(): Promise { + core.debug('Falling back to fetching the manifest using raw URL.'); + + const http: httpm.HttpClient = new httpm.HttpClient('tool-cache'); + const response = await http.getJson(MANIFEST_URL); + if (!response.result) { + throw new Error(`Unable to get manifest from ${MANIFEST_URL}`); + } + return response.result; +} + async function installPython(workingDirectory: string) { const options: ExecOptions = { cwd: workingDirectory,