Skip to content

Commit 358da85

Browse files
Fix for Candidate Not Iterable Error (actions#1082)
* candidates not iterable * update the error message * update error to debug * update debug to info * error message updates
1 parent 56f2fac commit 358da85

File tree

4 files changed

+91
-9
lines changed

4 files changed

+91
-9
lines changed

.github/workflows/e2e-cache.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ jobs:
8282
python-version: pypy-3.10-v7.x
8383
- os: ubuntu-22.04-arm
8484
python-version: pypy-3.11-v7.x
85+
- os: ubuntu-22.04-arm
86+
python-version: pypy-3.10-v7.x
8587
steps:
8688
- uses: actions/checkout@v4
8789
- name: Setup Python

__tests__/install-python.test.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,29 @@ import * as tc from '@actions/tool-cache';
88

99
jest.mock('@actions/http-client');
1010
jest.mock('@actions/tool-cache');
11-
12-
const mockManifest = [{version: '1.0.0'}];
11+
jest.mock('@actions/tool-cache', () => ({
12+
getManifestFromRepo: jest.fn()
13+
}));
14+
const mockManifest = [
15+
{
16+
version: '1.0.0',
17+
stable: true,
18+
files: [
19+
{
20+
filename: 'tool-v1.0.0-linux-x64.tar.gz',
21+
platform: 'linux',
22+
arch: 'x64',
23+
download_url: 'https://example.com/tool-v1.0.0-linux-x64.tar.gz'
24+
}
25+
]
26+
}
27+
];
1328

1429
describe('getManifest', () => {
30+
beforeEach(() => {
31+
jest.resetAllMocks();
32+
});
33+
1534
it('should return manifest from repo', async () => {
1635
(tc.getManifestFromRepo as jest.Mock).mockResolvedValue(mockManifest);
1736
const manifest = await getManifest();

dist/setup/index.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97483,16 +97483,36 @@ function findReleaseFromManifest(semanticVersionSpec, architecture, manifest) {
9748397483
});
9748497484
}
9748597485
exports.findReleaseFromManifest = findReleaseFromManifest;
97486+
function isIToolRelease(obj) {
97487+
return (typeof obj === 'object' &&
97488+
obj !== null &&
97489+
typeof obj.version === 'string' &&
97490+
typeof obj.stable === 'boolean' &&
97491+
Array.isArray(obj.files) &&
97492+
obj.files.every((file) => typeof file.filename === 'string' &&
97493+
typeof file.platform === 'string' &&
97494+
typeof file.arch === 'string' &&
97495+
typeof file.download_url === 'string'));
97496+
}
9748697497
function getManifest() {
9748797498
return __awaiter(this, void 0, void 0, function* () {
9748897499
try {
97489-
return yield getManifestFromRepo();
97500+
const repoManifest = yield getManifestFromRepo();
97501+
if (Array.isArray(repoManifest) &&
97502+
repoManifest.length &&
97503+
repoManifest.every(isIToolRelease)) {
97504+
return repoManifest;
97505+
}
97506+
throw new Error('The repository manifest is invalid or does not include any valid tool release (IToolRelease) entries.');
9749097507
}
9749197508
catch (err) {
9749297509
core.debug('Fetching the manifest via the API failed.');
9749397510
if (err instanceof Error) {
9749497511
core.debug(err.message);
9749597512
}
97513+
else {
97514+
core.error('An unexpected error occurred while fetching the manifest.');
97515+
}
9749697516
}
9749797517
return yield getManifestFromURL();
9749897518
});
@@ -97540,6 +97560,9 @@ function installPython(workingDirectory) {
9754097560
}
9754197561
function installCpythonFromRelease(release) {
9754297562
return __awaiter(this, void 0, void 0, function* () {
97563+
if (!release.files || release.files.length === 0) {
97564+
throw new Error('No files found in the release to download.');
97565+
}
9754397566
const downloadUrl = release.files[0].download_url;
9754497567
core.info(`Download from "${downloadUrl}"`);
9754597568
let pythonPath = '';
@@ -97560,8 +97583,11 @@ function installCpythonFromRelease(release) {
9756097583
catch (err) {
9756197584
if (err instanceof tc.HTTPError) {
9756297585
// Rate limit?
97563-
if (err.httpStatusCode === 403 || err.httpStatusCode === 429) {
97564-
core.info(`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`);
97586+
if (err.httpStatusCode === 403) {
97587+
core.error(`Received HTTP status code 403. This indicates a permission issue or restricted access.`);
97588+
}
97589+
else if (err.httpStatusCode === 429) {
97590+
core.info(`Received HTTP status code 429. This usually indicates the rate limit has been exceeded`);
9756597591
}
9756697592
else {
9756797593
core.info(err.message);

src/install-python.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as exec from '@actions/exec';
55
import * as httpm from '@actions/http-client';
66
import {ExecOptions} from '@actions/exec/lib/interfaces';
77
import {IS_WINDOWS, IS_LINUX, getDownloadFileName} from './utils';
8+
import {IToolRelease} from '@actions/tool-cache';
89

910
const TOKEN = core.getInput('token');
1011
const AUTH = !TOKEN ? undefined : `token ${TOKEN}`;
@@ -31,14 +32,41 @@ export async function findReleaseFromManifest(
3132

3233
return foundRelease;
3334
}
34-
35+
function isIToolRelease(obj: any): obj is IToolRelease {
36+
return (
37+
typeof obj === 'object' &&
38+
obj !== null &&
39+
typeof obj.version === 'string' &&
40+
typeof obj.stable === 'boolean' &&
41+
Array.isArray(obj.files) &&
42+
obj.files.every(
43+
(file: any) =>
44+
typeof file.filename === 'string' &&
45+
typeof file.platform === 'string' &&
46+
typeof file.arch === 'string' &&
47+
typeof file.download_url === 'string'
48+
)
49+
);
50+
}
3551
export async function getManifest(): Promise<tc.IToolRelease[]> {
3652
try {
37-
return await getManifestFromRepo();
53+
const repoManifest = await getManifestFromRepo();
54+
if (
55+
Array.isArray(repoManifest) &&
56+
repoManifest.length &&
57+
repoManifest.every(isIToolRelease)
58+
) {
59+
return repoManifest;
60+
}
61+
throw new Error(
62+
'The repository manifest is invalid or does not include any valid tool release (IToolRelease) entries.'
63+
);
3864
} catch (err) {
3965
core.debug('Fetching the manifest via the API failed.');
4066
if (err instanceof Error) {
4167
core.debug(err.message);
68+
} else {
69+
core.error('An unexpected error occurred while fetching the manifest.');
4270
}
4371
}
4472
return await getManifestFromURL();
@@ -93,6 +121,9 @@ async function installPython(workingDirectory: string) {
93121
}
94122

95123
export async function installCpythonFromRelease(release: tc.IToolRelease) {
124+
if (!release.files || release.files.length === 0) {
125+
throw new Error('No files found in the release to download.');
126+
}
96127
const downloadUrl = release.files[0].download_url;
97128

98129
core.info(`Download from "${downloadUrl}"`);
@@ -113,9 +144,13 @@ export async function installCpythonFromRelease(release: tc.IToolRelease) {
113144
} catch (err) {
114145
if (err instanceof tc.HTTPError) {
115146
// Rate limit?
116-
if (err.httpStatusCode === 403 || err.httpStatusCode === 429) {
147+
if (err.httpStatusCode === 403) {
148+
core.error(
149+
`Received HTTP status code 403. This indicates a permission issue or restricted access.`
150+
);
151+
} else if (err.httpStatusCode === 429) {
117152
core.info(
118-
`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`
153+
`Received HTTP status code 429. This usually indicates the rate limit has been exceeded`
119154
);
120155
} else {
121156
core.info(err.message);

0 commit comments

Comments
 (0)