diff --git a/__tests__/install-python.test.ts b/__tests__/install-python.test.ts index c3a6e7b4..70a109ac 100644 --- a/__tests__/install-python.test.ts +++ b/__tests__/install-python.test.ts @@ -9,7 +9,25 @@ import * as tc from '@actions/tool-cache'; jest.mock('@actions/http-client'); jest.mock('@actions/tool-cache'); -const mockManifest = [{version: '1.0.0'}]; +const mockManifest = [ + { + version: '3.9.0', + stable: true, + release_url: 'https://example.com/release-url', + files: [ + { + filename: 'python-3.9.0-macosx10.9.pkg', + arch: 'x64', + platform: 'darwin', + download_url: 'https://example.com/download-url' + } + ] + } +]; + +beforeEach(() => { + jest.resetAllMocks(); +}); describe('getManifest', () => { it('should return manifest from repo', async () => { diff --git a/dist/setup/index.js b/dist/setup/index.js index f1e3e297..1cabe1fc 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -91646,20 +91646,49 @@ exports.findReleaseFromManifest = findReleaseFromManifest; function getManifest() { return __awaiter(this, void 0, void 0, function* () { try { - return yield getManifestFromRepo(); + const manifestFromRepo = yield getManifestFromRepo(); + core.info('Successfully fetched the manifest from the repo.'); + validateManifest(manifestFromRepo); + return manifestFromRepo; } catch (err) { - core.debug('Fetching the manifest via the API failed.'); - if (err instanceof Error) { - core.debug(err.message); - } + logError('Fetching the manifest via the API failed.', err); + } + try { + const manifestFromURL = yield getManifestFromURL(); + core.info('Successfully fetched the manifest from the URL.'); + return manifestFromURL; + } + catch (err) { + logError('Fetching the manifest via the URL failed.', err); + // Rethrow the error or return a default value + throw new Error('Failed to fetch the manifest from both the repo and the URL.'); } - return yield getManifestFromURL(); }); } exports.getManifest = getManifest; +function validateManifest(manifest) { + if (!Array.isArray(manifest) || !manifest.every(isValidManifestEntry)) { + throw new Error('Invalid manifest response'); + } +} +function isValidManifestEntry(entry) { + return (typeof entry.version === 'string' && + typeof entry.stable === 'boolean' && + typeof entry.release_url === 'string' && + Array.isArray(entry.files) && + entry.files.every(isValidFileEntry)); +} +function isValidFileEntry(file) { + return (typeof file.filename === 'string' && + typeof file.arch === 'string' && + typeof file.platform === 'string' && + (typeof file.platform_version === 'string' || + file.platform_version === undefined) && + typeof file.download_url === 'string'); +} function getManifestFromRepo() { - core.debug(`Getting manifest from ${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}@${MANIFEST_REPO_BRANCH}`); + core.info(`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.getManifestFromRepo = getManifestFromRepo; @@ -91690,34 +91719,29 @@ function installPython(workingDirectory) { } } }; - if (utils_1.IS_WINDOWS) { - yield exec.exec('powershell', ['./setup.ps1'], options); - } - else { - yield exec.exec('bash', ['./setup.sh'], options); - } + const script = utils_1.IS_WINDOWS ? 'powershell ./setup.ps1' : 'bash ./setup.sh'; + yield exec.exec(script, [], options); }); } function installCpythonFromRelease(release) { return __awaiter(this, void 0, void 0, function* () { const downloadUrl = release.files[0].download_url; core.info(`Download from "${downloadUrl}"`); - let pythonPath = ''; try { const fileName = (0, utils_1.getDownloadFileName)(downloadUrl); - pythonPath = yield tc.downloadTool(downloadUrl, fileName, AUTH); + const pythonPath = yield tc.downloadTool(downloadUrl, fileName, AUTH); core.info('Extract downloaded archive'); - let pythonExtractedFolder; - if (utils_1.IS_WINDOWS) { - pythonExtractedFolder = yield tc.extractZip(pythonPath); - } - else { - pythonExtractedFolder = yield tc.extractTar(pythonPath); - } + const pythonExtractedFolder = utils_1.IS_WINDOWS + ? yield tc.extractZip(pythonPath) + : yield tc.extractTar(pythonPath); core.info('Execute installation script'); yield installPython(pythonExtractedFolder); } catch (err) { + handleDownloadError(err); + throw err; + } + function handleDownloadError(err) { if (err instanceof tc.HTTPError) { // Rate limit? if (err.httpStatusCode === 403 || err.httpStatusCode === 429) { @@ -91730,11 +91754,16 @@ function installCpythonFromRelease(release) { core.debug(err.stack); } } - throw err; } }); } exports.installCpythonFromRelease = installCpythonFromRelease; +function logError(message, err) { + core.info(message); + if (err instanceof Error) { + core.info(err.message); + } +} /***/ }), diff --git a/src/install-python.ts b/src/install-python.ts index d3421bf8..c40604f8 100644 --- a/src/install-python.ts +++ b/src/install-python.ts @@ -28,24 +28,60 @@ export async function findReleaseFromManifest( manifest, architecture ); - return foundRelease; } export async function getManifest(): Promise { try { - return await getManifestFromRepo(); + const manifestFromRepo = await getManifestFromRepo(); + core.info('Successfully fetched the manifest from the repo.'); + validateManifest(manifestFromRepo); + return manifestFromRepo; } catch (err) { - core.debug('Fetching the manifest via the API failed.'); - if (err instanceof Error) { - core.debug(err.message); - } + logError('Fetching the manifest via the API failed.', err); } - return await getManifestFromURL(); + try { + const manifestFromURL = await getManifestFromURL(); + core.info('Successfully fetched the manifest from the URL.'); + return manifestFromURL; + } catch (err) { + logError('Fetching the manifest via the URL failed.', err); + // Rethrow the error or return a default value + throw new Error( + 'Failed to fetch the manifest from both the repo and the URL.' + ); + } +} + +function validateManifest(manifest: any): void { + if (!Array.isArray(manifest) || !manifest.every(isValidManifestEntry)) { + throw new Error('Invalid manifest response'); + } +} + +function isValidManifestEntry(entry: any): boolean { + return ( + typeof entry.version === 'string' && + typeof entry.stable === 'boolean' && + typeof entry.release_url === 'string' && + Array.isArray(entry.files) && + entry.files.every(isValidFileEntry) + ); +} + +function isValidFileEntry(file: any): boolean { + return ( + typeof file.filename === 'string' && + typeof file.arch === 'string' && + typeof file.platform === 'string' && + (typeof file.platform_version === 'string' || + file.platform_version === undefined) && + typeof file.download_url === 'string' + ); } export function getManifestFromRepo(): Promise { - core.debug( + core.info( `Getting manifest from ${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}@${MANIFEST_REPO_BRANCH}` ); return tc.getManifestFromRepo( @@ -85,32 +121,30 @@ async function installPython(workingDirectory: string) { } }; - if (IS_WINDOWS) { - await exec.exec('powershell', ['./setup.ps1'], options); - } else { - await exec.exec('bash', ['./setup.sh'], options); - } + const script = IS_WINDOWS ? 'powershell ./setup.ps1' : 'bash ./setup.sh'; + await exec.exec(script, [], options); } export async function installCpythonFromRelease(release: tc.IToolRelease) { const downloadUrl = release.files[0].download_url; core.info(`Download from "${downloadUrl}"`); - let pythonPath = ''; try { const fileName = getDownloadFileName(downloadUrl); - pythonPath = await tc.downloadTool(downloadUrl, fileName, AUTH); + const pythonPath = await tc.downloadTool(downloadUrl, fileName, AUTH); core.info('Extract downloaded archive'); - let pythonExtractedFolder; - if (IS_WINDOWS) { - pythonExtractedFolder = await tc.extractZip(pythonPath); - } else { - pythonExtractedFolder = await tc.extractTar(pythonPath); - } + const pythonExtractedFolder = IS_WINDOWS + ? await tc.extractZip(pythonPath) + : await tc.extractTar(pythonPath); core.info('Execute installation script'); await installPython(pythonExtractedFolder); } catch (err) { + handleDownloadError(err); + throw err; + } + + function handleDownloadError(err: any): void { if (err instanceof tc.HTTPError) { // Rate limit? if (err.httpStatusCode === 403 || err.httpStatusCode === 429) { @@ -124,6 +158,12 @@ export async function installCpythonFromRelease(release: tc.IToolRelease) { core.debug(err.stack); } } - throw err; + } +} + +function logError(message: string, err: any): void { + core.info(message); + if (err instanceof Error) { + core.info(err.message); } }