@@ -97291,6 +97291,33 @@ const fs_1 = __importDefault(__nccwpck_require__(9896));
9729197291const utils_1 = __nccwpck_require__(1798);
9729297292const TOKEN = core.getInput('token');
9729397293const AUTH = !TOKEN ? undefined : `token ${TOKEN}`;
97294+ /*
97295+ * Generic retry wrapper
97296+ */
97297+ async function retry(fn, retries = 3, delayMs = 2000) {
97298+ let lastErr;
97299+ for (let attempt = 1; attempt <= retries; attempt++) {
97300+ try {
97301+ return await fn();
97302+ }
97303+ catch (err) {
97304+ lastErr = err;
97305+ const status = err?.statusCode ||
97306+ err?.httpStatusCode ||
97307+ err?.response?.message?.statusCode;
97308+ const retryable = !status || status >= 500 || status === 429 || status === 403;
97309+ core.warning(`Attempt ${attempt} failed: ${err.message}. ` +
97310+ (retryable && attempt < retries
97311+ ? `Retrying in ${delayMs}ms...`
97312+ : `No more retries.`));
97313+ if (!retryable || attempt === retries)
97314+ break;
97315+ await new Promise(res => setTimeout(res, delayMs));
97316+ delayMs *= 2; // exponential backoff
97317+ }
97318+ }
97319+ throw lastErr;
97320+ }
9729497321async function installGraalPy(graalpyVersion, architecture, allowPreReleases, releases) {
9729597322 let downloadDir;
9729697323 releases = releases ?? (await getAvailableGraalPyVersions());
@@ -97312,15 +97339,15 @@ async function installGraalPy(graalpyVersion, architecture, allowPreReleases, re
9731297339 const downloadUrl = `${foundAsset.browser_download_url}`;
9731397340 core.info(`Downloading GraalPy from "${downloadUrl}" ...`);
9731497341 try {
97315- const graalpyPath = await tc.downloadTool(downloadUrl, undefined, AUTH);
97342+ // ⭐ Wrapped in retry
97343+ const graalpyPath = await retry(() => tc.downloadTool(downloadUrl, undefined, AUTH), 4, 2000);
9731697344 core.info('Extracting downloaded archive...');
9731797345 if (utils_1.IS_WINDOWS) {
9731897346 downloadDir = await tc.extractZip(graalpyPath);
9731997347 }
9732097348 else {
9732197349 downloadDir = await tc.extractTar(graalpyPath);
9732297350 }
97323- // folder name in archive is unpredictable
9732497351 const archiveName = fs_1.default.readdirSync(downloadDir)[0];
9732597352 const toolDir = path.join(downloadDir, archiveName);
9732697353 let installDir = toolDir;
@@ -97334,54 +97361,16 @@ async function installGraalPy(graalpyVersion, architecture, allowPreReleases, re
9733497361 }
9733597362 catch (err) {
9733697363 if (err instanceof Error) {
97337- const isRateLimit = err instanceof tc.HTTPError &&
97338- (err.httpStatusCode === 403 || err.httpStatusCode === 429);
97339- if (isRateLimit) {
97340- core.warning(`Rate limit or restricted access response received: HTTP ${err.httpStatusCode}`);
97341- let lastStatus;
97342- for (let attempt = 1; attempt <= 3; attempt++) {
97343- core.info(`Retry attempt ${attempt} of 3 due to rate limit...`);
97344- await new Promise(res => setTimeout(res, 2000 * attempt));
97345- try {
97346- const retryPath = await tc.downloadTool(downloadUrl, undefined, AUTH);
97347- core.info(`Retry succeeded.`);
97348- // Extract retry archive
97349- let retryExtractDir;
97350- if (utils_1.IS_WINDOWS) {
97351- retryExtractDir = await tc.extractZip(retryPath);
97352- }
97353- else {
97354- retryExtractDir = await tc.extractTar(retryPath);
97355- }
97356- const archiveName = fs_1.default.readdirSync(retryExtractDir)[0];
97357- const toolDir = path.join(retryExtractDir, archiveName);
97358- let installDir = toolDir;
97359- if (!(0, utils_1.isNightlyKeyword)(resolvedGraalPyVersion)) {
97360- installDir = await tc.cacheDir(toolDir, 'GraalPy', resolvedGraalPyVersion, architecture);
97361- }
97362- const binaryPath = path.join(installDir, 'bin');
97363- await createGraalPySymlink(binaryPath, resolvedGraalPyVersion);
97364- await installPip(binaryPath);
97365- return { installDir, resolvedGraalPyVersion };
97366- }
97367- catch (retryErr) {
97368- if (retryErr instanceof tc.HTTPError) {
97369- lastStatus = retryErr.httpStatusCode;
97370- core.warning(`Retry ${attempt} failed. HTTP ${lastStatus}`);
97371- }
97372- else {
97373- core.warning(`Retry ${attempt} failed: ${retryErr}`);
97374- }
97375- if (attempt === 3) {
97376- core.error(`All retries failed. Last HTTP status code: ${lastStatus ?? 'unknown'}`);
97377- throw retryErr;
97378- }
97379- }
97380- }
97364+ if (err instanceof tc.HTTPError &&
97365+ (err.httpStatusCode === 403 || err.httpStatusCode === 429)) {
97366+ core.info(`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`);
9738197367 }
97382- core.info(err.message);
97383- if (err.stack)
97368+ else {
97369+ core.info(err.message);
97370+ }
97371+ if (err.stack !== undefined) {
9738497372 core.debug(err.stack);
97373+ }
9738597374 }
9738697375 throw err;
9738797376 }
@@ -97393,25 +97382,25 @@ async function getAvailableGraalPyVersions() {
9739397382 headers.authorization = AUTH;
9739497383 }
9739597384 /*
97396- Get releases first.
97397- */
97385+ * Stable releases with retry
97386+ */
9739897387 let url = 'https://api.github.com/repos/oracle/graalpython/releases';
9739997388 const result = [];
9740097389 do {
97401- const response = await http.getJson(url, headers);
97390+ const response = await retry(() => http.getJson(url, headers), 4, 1500 );
9740297391 if (!response.result) {
9740397392 throw new Error(`Unable to retrieve the list of available GraalPy versions from '${url}'`);
9740497393 }
9740597394 result.push(...response.result);
9740697395 url = (0, utils_1.getNextPageUrl)(response);
9740797396 } while (url);
9740897397 /*
97409- Add pre -release builds.
97410- */
97398+ * Pre -release builds with retry
97399+ */
9741197400 url =
9741297401 'https://api.github.com/repos/graalvm/graal-languages-ea-builds/releases';
9741397402 do {
97414- const response = await http.getJson(url, headers);
97403+ const response = await retry(() => http.getJson(url, headers), 4, 1500 );
9741597404 if (!response.result) {
9741697405 throw new Error(`Unable to retrieve the list of available GraalPy versions from '${url}'`);
9741797406 }
@@ -97490,9 +97479,6 @@ function findAsset(item, architecture, platform) {
9749097479 const graalpyExt = platform == 'win32' ? 'zip' : 'tar.gz';
9749197480 const found = item.assets.filter(file => file.name.startsWith('graalpy') &&
9749297481 file.name.endsWith(`-${graalpyPlatform}-${graalpyArch}.${graalpyExt}`));
97493- /*
97494- In the future there could be more variants of GraalPy for a single release. Pick the shortest name, that one is the most likely to be the primary variant.
97495- */
9749697482 found.sort((f1, f2) => f1.name.length - f2.name.length);
9749797483 return found[0];
9749897484}
0 commit comments