Skip to content

Commit 6ed2c67

Browse files
Fix for Candidate Not Iterable Error (#1082)
* candidates not iterable * update the error message * update error to debug * update debug to info * error message updates
1 parent e348410 commit 6ed2c67

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
@@ -97461,16 +97461,36 @@ function findReleaseFromManifest(semanticVersionSpec, architecture, manifest) {
9746197461
});
9746297462
}
9746397463
exports.findReleaseFromManifest = findReleaseFromManifest;
97464+
function isIToolRelease(obj) {
97465+
return (typeof obj === 'object' &&
97466+
obj !== null &&
97467+
typeof obj.version === 'string' &&
97468+
typeof obj.stable === 'boolean' &&
97469+
Array.isArray(obj.files) &&
97470+
obj.files.every((file) => typeof file.filename === 'string' &&
97471+
typeof file.platform === 'string' &&
97472+
typeof file.arch === 'string' &&
97473+
typeof file.download_url === 'string'));
97474+
}
9746497475
function getManifest() {
9746597476
return __awaiter(this, void 0, void 0, function* () {
9746697477
try {
97467-
return yield getManifestFromRepo();
97478+
const repoManifest = yield getManifestFromRepo();
97479+
if (Array.isArray(repoManifest) &&
97480+
repoManifest.length &&
97481+
repoManifest.every(isIToolRelease)) {
97482+
return repoManifest;
97483+
}
97484+
throw new Error('The repository manifest is invalid or does not include any valid tool release (IToolRelease) entries.');
9746897485
}
9746997486
catch (err) {
9747097487
core.debug('Fetching the manifest via the API failed.');
9747197488
if (err instanceof Error) {
9747297489
core.debug(err.message);
9747397490
}
97491+
else {
97492+
core.error('An unexpected error occurred while fetching the manifest.');
97493+
}
9747497494
}
9747597495
return yield getManifestFromURL();
9747697496
});
@@ -97518,6 +97538,9 @@ function installPython(workingDirectory) {
9751897538
}
9751997539
function installCpythonFromRelease(release) {
9752097540
return __awaiter(this, void 0, void 0, function* () {
97541+
if (!release.files || release.files.length === 0) {
97542+
throw new Error('No files found in the release to download.');
97543+
}
9752197544
const downloadUrl = release.files[0].download_url;
9752297545
core.info(`Download from "${downloadUrl}"`);
9752397546
let pythonPath = '';
@@ -97538,8 +97561,11 @@ function installCpythonFromRelease(release) {
9753897561
catch (err) {
9753997562
if (err instanceof tc.HTTPError) {
9754097563
// Rate limit?
97541-
if (err.httpStatusCode === 403 || err.httpStatusCode === 429) {
97542-
core.info(`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`);
97564+
if (err.httpStatusCode === 403) {
97565+
core.error(`Received HTTP status code 403. This indicates a permission issue or restricted access.`);
97566+
}
97567+
else if (err.httpStatusCode === 429) {
97568+
core.info(`Received HTTP status code 429. This usually indicates the rate limit has been exceeded`);
9754397569
}
9754497570
else {
9754597571
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)