update with enhance code and test

This commit is contained in:
mahabaleshwars 2025-10-29 21:16:50 +05:30
parent ba6d7216a9
commit bcde537b63
3 changed files with 441 additions and 96 deletions

View File

@ -21,7 +21,8 @@ jest.mock('../../src/util', () => ({
jest.mock('fs', () => ({ jest.mock('fs', () => ({
...jest.requireActual('fs'), ...jest.requireActual('fs'),
readdirSync: jest.fn() readdirSync: jest.fn(),
existsSync: jest.fn()
})); }));
beforeAll(() => { beforeAll(() => {
@ -106,11 +107,13 @@ describe('GraalVMDistribution', () => {
(util.extractJdkFile as jest.Mock).mockResolvedValue('/tmp/extracted'); (util.extractJdkFile as jest.Mock).mockResolvedValue('/tmp/extracted');
// Mock renameWinArchive - it returns the same path (no renaming) // Mock renameWinArchive - it returns the same path (no renaming)
// But it appears the implementation might not even call this
(util.renameWinArchive as jest.Mock).mockImplementation((p: string) => p); (util.renameWinArchive as jest.Mock).mockImplementation((p: string) => p);
(util.getDownloadArchiveExtension as jest.Mock).mockReturnValue('tar.gz'); (util.getDownloadArchiveExtension as jest.Mock).mockReturnValue('tar.gz');
// Mock fs.existsSync to return true for extracted path
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readdirSync as jest.Mock).mockReturnValue(['graalvm-jdk-17.0.5']); (fs.readdirSync as jest.Mock).mockReturnValue(['graalvm-jdk-17.0.5']);
jest jest
@ -130,6 +133,9 @@ describe('GraalVMDistribution', () => {
'tar.gz' 'tar.gz'
); );
// Verify path existence check
expect(fs.existsSync).toHaveBeenCalledWith('/tmp/extracted');
// Verify directory reading // Verify directory reading
expect(fs.readdirSync).toHaveBeenCalledWith('/tmp/extracted'); expect(fs.readdirSync).toHaveBeenCalledWith('/tmp/extracted');
@ -154,16 +160,59 @@ describe('GraalVMDistribution', () => {
expect(core.info).toHaveBeenCalledWith('Extracting Java archive...'); expect(core.info).toHaveBeenCalledWith('Extracting Java archive...');
}); });
it('should verify that renameWinArchive is available but may not be called', () => { it('should throw error when extracted path does not exist', async () => {
// Just verify the mock is set up correctly (fs.existsSync as jest.Mock).mockReturnValue(false);
const originalPath = '/tmp/archive.tar.gz';
// Call the mock directly to verify it works await expect(
const result = util.renameWinArchive(originalPath); (distribution as any).downloadTool(javaRelease)
).rejects.toThrow(
'Extraction failed: path /tmp/extracted does not exist'
);
// Verify it returns the same path (no renaming) expect(core.error).toHaveBeenCalledWith(
expect(result).toBe(originalPath); expect.stringContaining('Failed to download and extract GraalVM:')
expect(util.renameWinArchive).toHaveBeenCalledWith(originalPath); );
});
it('should throw error when extracted directory is empty', async () => {
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readdirSync as jest.Mock).mockReturnValue([]);
await expect(
(distribution as any).downloadTool(javaRelease)
).rejects.toThrow(
'Extraction failed: no files found in extracted directory'
);
expect(core.error).toHaveBeenCalledWith(
expect.stringContaining('Failed to download and extract GraalVM:')
);
});
it('should handle download errors', async () => {
const downloadError = new Error('Network error during download');
(tc.downloadTool as jest.Mock).mockRejectedValue(downloadError);
await expect(
(distribution as any).downloadTool(javaRelease)
).rejects.toThrow('Network error during download');
expect(core.error).toHaveBeenCalledWith(
'Failed to download and extract GraalVM: Error: Network error during download'
);
});
it('should handle extraction errors', async () => {
const extractError = new Error('Failed to extract archive');
(util.extractJdkFile as jest.Mock).mockRejectedValue(extractError);
await expect(
(distribution as any).downloadTool(javaRelease)
).rejects.toThrow('Failed to extract archive');
expect(core.error).toHaveBeenCalledWith(
'Failed to download and extract GraalVM: Error: Failed to extract archive'
);
}); });
it('should handle different archive extensions', async () => { it('should handle different archive extensions', async () => {
@ -195,6 +244,38 @@ describe('GraalVMDistribution', () => {
jest.spyOn(distribution, 'getPlatform').mockReturnValue('linux'); jest.spyOn(distribution, 'getPlatform').mockReturnValue('linux');
}); });
describe('input validation', () => {
it('should throw error for null version range', async () => {
await expect(
(distribution as any).findPackageForDownload(null)
).rejects.toThrow('Version range is required and must be a string');
});
it('should throw error for undefined version range', async () => {
await expect(
(distribution as any).findPackageForDownload(undefined)
).rejects.toThrow('Version range is required and must be a string');
});
it('should throw error for empty string version range', async () => {
await expect(
(distribution as any).findPackageForDownload('')
).rejects.toThrow('Version range is required and must be a string');
});
it('should throw error for non-string version range', async () => {
await expect(
(distribution as any).findPackageForDownload(123)
).rejects.toThrow('Version range is required and must be a string');
});
it('should throw error for invalid version format', async () => {
await expect(
(distribution as any).findPackageForDownload('abc')
).rejects.toThrow('Invalid version format: abc');
});
});
describe('stable builds', () => { describe('stable builds', () => {
it('should construct correct URL for specific version', async () => { it('should construct correct URL for specific version', async () => {
const mockResponse = { const mockResponse = {
@ -236,13 +317,17 @@ describe('GraalVMDistribution', () => {
await expect( await expect(
(distribution as any).findPackageForDownload('17') (distribution as any).findPackageForDownload('17')
).rejects.toThrow('Unsupported architecture: x86'); ).rejects.toThrow(
'Unsupported architecture: x86. Supported architectures are: x64, aarch64'
);
}); });
it('should throw error for JDK versions less than 17', async () => { it('should throw error for JDK versions less than 17', async () => {
await expect( await expect(
(distribution as any).findPackageForDownload('11') (distribution as any).findPackageForDownload('11')
).rejects.toThrow('GraalVM is only supported for JDK 17 and later'); ).rejects.toThrow(
'GraalVM is only supported for JDK 17 and later. Requested version: 11'
);
}); });
it('should throw error for non-jdk package types', async () => { it('should throw error for non-jdk package types', async () => {
@ -265,10 +350,54 @@ describe('GraalVMDistribution', () => {
await expect( await expect(
(distribution as any).findPackageForDownload('17.0.99') (distribution as any).findPackageForDownload('17.0.99')
).rejects.toThrow('Could not find GraalVM for SemVer 17.0.99'); ).rejects.toThrow(
'Could not find GraalVM for SemVer 17.0.99. Please check if this version is available at https://download.oracle.com/graalvm'
);
}); });
it('should throw error for other HTTP errors', async () => { it('should throw error for unauthorized access (401)', async () => {
const mockResponse = {
message: {statusCode: 401}
} as http.HttpClientResponse;
mockHttpClient.head.mockResolvedValue(mockResponse);
await expect(
(distribution as any).findPackageForDownload('17')
).rejects.toThrow(
'Access denied when downloading GraalVM. Status code: 401. Please check your credentials or permissions.'
);
});
it('should throw error for forbidden access (403)', async () => {
const mockResponse = {
message: {statusCode: 403}
} as http.HttpClientResponse;
mockHttpClient.head.mockResolvedValue(mockResponse);
await expect(
(distribution as any).findPackageForDownload('17')
).rejects.toThrow(
'Access denied when downloading GraalVM. Status code: 403. Please check your credentials or permissions.'
);
});
it('should throw error for other HTTP errors with status message', async () => {
const mockResponse = {
message: {
statusCode: 500,
statusMessage: 'Internal Server Error'
}
} as http.HttpClientResponse;
mockHttpClient.head.mockResolvedValue(mockResponse);
await expect(
(distribution as any).findPackageForDownload('17')
).rejects.toThrow(
'HTTP request for GraalVM failed with status code: 500 (Internal Server Error)'
);
});
it('should throw error for other HTTP errors without status message', async () => {
const mockResponse = { const mockResponse = {
message: {statusCode: 500} message: {statusCode: 500}
} as http.HttpClientResponse; } as http.HttpClientResponse;
@ -277,7 +406,7 @@ describe('GraalVMDistribution', () => {
await expect( await expect(
(distribution as any).findPackageForDownload('17') (distribution as any).findPackageForDownload('17')
).rejects.toThrow( ).rejects.toThrow(
'Http request for GraalVM failed with status code: 500' 'HTTP request for GraalVM failed with status code: 500 (Unknown error)'
); );
}); });
}); });
@ -368,6 +497,11 @@ describe('GraalVMDistribution', () => {
await expect( await expect(
(distribution as any).findPackageForDownload('23') (distribution as any).findPackageForDownload('23')
).rejects.toThrow("Unable to find latest version for '23-ea'"); ).rejects.toThrow("Unable to find latest version for '23-ea'");
// Verify error logging
expect(core.error).toHaveBeenCalledWith(
'Available versions: 23-ea-20240716'
);
}); });
it('should throw error when no matching file for architecture in EA build', async () => { it('should throw error when no matching file for architecture in EA build', async () => {
@ -401,7 +535,14 @@ describe('GraalVMDistribution', () => {
await expect( await expect(
(distribution as any).findPackageForDownload('23') (distribution as any).findPackageForDownload('23')
).rejects.toThrow("Unable to find file metadata for '23-ea'"); ).rejects.toThrow(
`Unable to find file for architecture 'x64' and platform '${currentPlatform}'`
);
// Verify error logging
expect(core.error).toHaveBeenCalledWith(
expect.stringContaining('Available files for architecture x64:')
);
}); });
it('should throw error when no matching platform in EA build', async () => { it('should throw error when no matching platform in EA build', async () => {
@ -430,9 +571,14 @@ describe('GraalVMDistribution', () => {
.spyOn(distribution as any, 'distributionArchitecture') .spyOn(distribution as any, 'distributionArchitecture')
.mockReturnValue('x64'); .mockReturnValue('x64');
const currentPlatform =
process.platform === 'win32' ? 'windows' : process.platform;
await expect( await expect(
(distribution as any).findPackageForDownload('23') (distribution as any).findPackageForDownload('23')
).rejects.toThrow("Unable to find file metadata for '23-ea'"); ).rejects.toThrow(
`Unable to find file for architecture 'x64' and platform '${currentPlatform}'`
);
}); });
it('should throw error when filename does not start with graalvm-jdk-', async () => { it('should throw error when filename does not start with graalvm-jdk-', async () => {
@ -466,7 +612,9 @@ describe('GraalVMDistribution', () => {
await expect( await expect(
(distribution as any).findPackageForDownload('23') (distribution as any).findPackageForDownload('23')
).rejects.toThrow("Unable to find file metadata for '23-ea'"); ).rejects.toThrow(
"Invalid filename format: wrong-prefix-23_linux-x64_bin.tar.gz. Expected to start with 'graalvm-jdk-'"
);
}); });
it('should throw error when EA version JSON is not found', async () => { it('should throw error when EA version JSON is not found', async () => {
@ -478,7 +626,9 @@ describe('GraalVMDistribution', () => {
await expect( await expect(
(distribution as any).findPackageForDownload('23') (distribution as any).findPackageForDownload('23')
).rejects.toThrow("No GraalVM EA build found for version '23-ea'"); ).rejects.toThrow(
"No GraalVM EA build found for version '23-ea'. Please check if the version is correct."
);
}); });
}); });
}); });
@ -540,6 +690,16 @@ describe('GraalVMDistribution', () => {
url: 'https://example.com/download/graalvm-jdk-23_linux-x64_bin.tar.gz', url: 'https://example.com/download/graalvm-jdk-23_linux-x64_bin.tar.gz',
version: '23-ea-20240716' version: '23-ea-20240716'
}); });
// Verify debug logging
expect(core.debug).toHaveBeenCalledWith('Searching for EA build: 23-ea');
expect(core.debug).toHaveBeenCalledWith('Found 2 EA versions');
expect(core.debug).toHaveBeenCalledWith(
'Latest version found: 23-ea-20240716'
);
expect(core.debug).toHaveBeenCalledWith(
'Download URL: https://example.com/download/graalvm-jdk-23_linux-x64_bin.tar.gz'
);
}); });
it('should throw error when no latest version found', async () => { it('should throw error when no latest version found', async () => {
@ -549,6 +709,10 @@ describe('GraalVMDistribution', () => {
await expect( await expect(
(distribution as any).findEABuildDownloadUrl('23-ea') (distribution as any).findEABuildDownloadUrl('23-ea')
).rejects.toThrow("Unable to find latest version for '23-ea'"); ).rejects.toThrow("Unable to find latest version for '23-ea'");
expect(core.error).toHaveBeenCalledWith(
'Available versions: 23-ea-20240716, 23-ea-20240709'
);
}); });
it('should throw error when no matching file for architecture', async () => { it('should throw error when no matching file for architecture', async () => {
@ -570,7 +734,13 @@ describe('GraalVMDistribution', () => {
await expect( await expect(
(distribution as any).findEABuildDownloadUrl('23-ea') (distribution as any).findEABuildDownloadUrl('23-ea')
).rejects.toThrow("Unable to find file metadata for '23-ea'"); ).rejects.toThrow(
`Unable to find file for architecture 'x64' and platform '${currentPlatform}'`
);
expect(core.error).toHaveBeenCalledWith(
expect.stringContaining('Available files for architecture x64:')
);
}); });
it('should throw error when filename does not start with graalvm-jdk-', async () => { it('should throw error when filename does not start with graalvm-jdk-', async () => {
@ -592,7 +762,9 @@ describe('GraalVMDistribution', () => {
await expect( await expect(
(distribution as any).findEABuildDownloadUrl('23-ea') (distribution as any).findEABuildDownloadUrl('23-ea')
).rejects.toThrow("Unable to find file metadata for '23-ea'"); ).rejects.toThrow(
"Invalid filename format: wrong-name.tar.gz. Expected to start with 'graalvm-jdk-'"
);
}); });
it('should work with aarch64 architecture', async () => { it('should work with aarch64 architecture', async () => {
@ -631,7 +803,9 @@ describe('GraalVMDistribution', () => {
await expect( await expect(
(distribution as any).findEABuildDownloadUrl('23-ea') (distribution as any).findEABuildDownloadUrl('23-ea')
).rejects.toThrow("Unable to find file metadata for '23-ea'"); ).rejects.toThrow(
`Unable to find file for architecture 'x64' and platform '${currentPlatform}'`
);
}); });
}); });
@ -666,11 +840,29 @@ describe('GraalVMDistribution', () => {
); );
}); });
it('should handle HTTP errors properly', async () => { it('should handle 404 errors with specific message', async () => {
mockHttpClient.getJson.mockRejectedValue(new Error('Network error')); const error404 = new Error('Not Found: 404');
mockHttpClient.getJson.mockRejectedValue(error404);
await expect((distribution as any).fetchEAJson('23-ea')).rejects.toThrow( await expect((distribution as any).fetchEAJson('23-ea')).rejects.toThrow(
'Network error' "GraalVM EA version '23-ea' not found. Please verify the version exists in the EA builds repository."
);
});
it('should handle generic HTTP errors with context', async () => {
const networkError = new Error('Network timeout');
mockHttpClient.getJson.mockRejectedValue(networkError);
await expect((distribution as any).fetchEAJson('23-ea')).rejects.toThrow(
"Failed to fetch GraalVM EA version information for '23-ea': Network timeout"
);
});
it('should handle non-Error exceptions', async () => {
mockHttpClient.getJson.mockRejectedValue('String error');
await expect((distribution as any).fetchEAJson('23-ea')).rejects.toThrow(
"Failed to fetch GraalVM EA version information for '23-ea'"
); );
}); });
}); });

110
dist/setup/index.js vendored
View File

@ -130530,31 +130530,51 @@ const util_1 = __nccwpck_require__(92629);
const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm'; const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm';
const IS_WINDOWS = process.platform === 'win32'; const IS_WINDOWS = process.platform === 'win32';
const GRAALVM_PLATFORM = IS_WINDOWS ? 'windows' : process.platform; const GRAALVM_PLATFORM = IS_WINDOWS ? 'windows' : process.platform;
const GRAALVM_MIN_VERSION = 17;
const SUPPORTED_ARCHITECTURES = ['x64', 'aarch64'];
class GraalVMDistribution extends base_installer_1.JavaBase { class GraalVMDistribution extends base_installer_1.JavaBase {
constructor(installerOptions) { constructor(installerOptions) {
super('GraalVM', installerOptions); super('GraalVM', installerOptions);
} }
downloadTool(javaRelease) { downloadTool(javaRelease) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
core.info(`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`); try {
let javaArchivePath = yield tc.downloadTool(javaRelease.url); core.info(`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`);
core.info(`Extracting Java archive...`); let javaArchivePath = yield tc.downloadTool(javaRelease.url);
const extension = (0, util_1.getDownloadArchiveExtension)(); core.info(`Extracting Java archive...`);
if (IS_WINDOWS) { const extension = (0, util_1.getDownloadArchiveExtension)();
javaArchivePath = (0, util_1.renameWinArchive)(javaArchivePath); if (IS_WINDOWS) {
javaArchivePath = (0, util_1.renameWinArchive)(javaArchivePath);
}
const extractedJavaPath = yield (0, util_1.extractJdkFile)(javaArchivePath, extension);
// Add validation for extracted path
if (!fs_1.default.existsSync(extractedJavaPath)) {
throw new Error(`Extraction failed: path ${extractedJavaPath} does not exist`);
}
const dirContents = fs_1.default.readdirSync(extractedJavaPath);
if (dirContents.length === 0) {
throw new Error('Extraction failed: no files found in extracted directory');
}
const archivePath = path_1.default.join(extractedJavaPath, dirContents[0]);
const version = this.getToolcacheVersionName(javaRelease.version);
const javaPath = yield tc.cacheDir(archivePath, this.toolcacheFolderName, version, this.architecture);
return { version: javaRelease.version, path: javaPath };
}
catch (error) {
core.error(`Failed to download and extract GraalVM: ${error}`);
throw error;
} }
const extractedJavaPath = yield (0, util_1.extractJdkFile)(javaArchivePath, extension);
const archivePath = path_1.default.join(extractedJavaPath, fs_1.default.readdirSync(extractedJavaPath)[0]);
const version = this.getToolcacheVersionName(javaRelease.version);
const javaPath = yield tc.cacheDir(archivePath, this.toolcacheFolderName, version, this.architecture);
return { version: javaRelease.version, path: javaPath };
}); });
} }
findPackageForDownload(range) { findPackageForDownload(range) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// Add input validation
if (!range || typeof range !== 'string') {
throw new Error('Version range is required and must be a string');
}
const arch = this.distributionArchitecture(); const arch = this.distributionArchitecture();
if (!['x64', 'aarch64'].includes(arch)) { if (!SUPPORTED_ARCHITECTURES.includes(arch)) {
throw new Error(`Unsupported architecture: ${this.architecture}`); throw new Error(`Unsupported architecture: ${this.architecture}. Supported architectures are: ${SUPPORTED_ARCHITECTURES.join(', ')}`);
} }
if (!this.stable) { if (!this.stable) {
return this.findEABuildDownloadUrl(`${range}-ea`); return this.findEABuildDownloadUrl(`${range}-ea`);
@ -130565,10 +130585,14 @@ class GraalVMDistribution extends base_installer_1.JavaBase {
const platform = this.getPlatform(); const platform = this.getPlatform();
const extension = (0, util_1.getDownloadArchiveExtension)(); const extension = (0, util_1.getDownloadArchiveExtension)();
const major = range.includes('.') ? range.split('.')[0] : range; const major = range.includes('.') ? range.split('.')[0] : range;
const fileUrl = this.constructFileUrl(range, major, platform, arch, extension); const majorVersion = parseInt(major);
if (parseInt(major) < 17) { if (isNaN(majorVersion)) {
throw new Error('GraalVM is only supported for JDK 17 and later'); throw new Error(`Invalid version format: ${range}`);
} }
if (majorVersion < GRAALVM_MIN_VERSION) {
throw new Error(`GraalVM is only supported for JDK ${GRAALVM_MIN_VERSION} and later. Requested version: ${major}`);
}
const fileUrl = this.constructFileUrl(range, major, platform, arch, extension);
const response = yield this.http.head(fileUrl); const response = yield this.http.head(fileUrl);
this.handleHttpResponse(response, range); this.handleHttpResponse(response, range);
return { url: fileUrl, version: range }; return { url: fileUrl, version: range };
@ -130580,43 +130604,71 @@ class GraalVMDistribution extends base_installer_1.JavaBase {
: `${GRAALVM_DL_BASE}/${range}/latest/graalvm-jdk-${range}_${platform}-${arch}_bin.${extension}`; : `${GRAALVM_DL_BASE}/${range}/latest/graalvm-jdk-${range}_${platform}-${arch}_bin.${extension}`;
} }
handleHttpResponse(response, range) { handleHttpResponse(response, range) {
if (response.message.statusCode === http_client_1.HttpCodes.NotFound) { const statusCode = response.message.statusCode;
throw new Error(`Could not find GraalVM for SemVer ${range}`); if (statusCode === http_client_1.HttpCodes.NotFound) {
throw new Error(`Could not find GraalVM for SemVer ${range}. Please check if this version is available at ${GRAALVM_DL_BASE}`);
} }
if (response.message.statusCode !== http_client_1.HttpCodes.OK) { if (statusCode === http_client_1.HttpCodes.Unauthorized ||
throw new Error(`Http request for GraalVM failed with status code: ${response.message.statusCode}`); statusCode === http_client_1.HttpCodes.Forbidden) {
throw new Error(`Access denied when downloading GraalVM. Status code: ${statusCode}. Please check your credentials or permissions.`);
}
if (statusCode !== http_client_1.HttpCodes.OK) {
throw new Error(`HTTP request for GraalVM failed with status code: ${statusCode} (${response.message.statusMessage || 'Unknown error'})`);
} }
} }
findEABuildDownloadUrl(javaEaVersion) { findEABuildDownloadUrl(javaEaVersion) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
core.debug(`Searching for EA build: ${javaEaVersion}`);
const versions = yield this.fetchEAJson(javaEaVersion); const versions = yield this.fetchEAJson(javaEaVersion);
core.debug(`Found ${versions.length} EA versions`);
const latestVersion = versions.find(v => v.latest); const latestVersion = versions.find(v => v.latest);
if (!latestVersion) { if (!latestVersion) {
core.error(`Available versions: ${versions.map(v => v.version).join(', ')}`);
throw new Error(`Unable to find latest version for '${javaEaVersion}'`); throw new Error(`Unable to find latest version for '${javaEaVersion}'`);
} }
core.debug(`Latest version found: ${latestVersion.version}`);
const arch = this.distributionArchitecture(); const arch = this.distributionArchitecture();
const file = latestVersion.files.find(f => f.arch === arch && f.platform === GRAALVM_PLATFORM); const file = latestVersion.files.find(f => f.arch === arch && f.platform === GRAALVM_PLATFORM);
if (!file || !file.filename.startsWith('graalvm-jdk-')) { if (!file) {
throw new Error(`Unable to find file metadata for '${javaEaVersion}'`); core.error(`Available files for architecture ${arch}: ${JSON.stringify(latestVersion.files)}`);
throw new Error(`Unable to find file for architecture '${arch}' and platform '${GRAALVM_PLATFORM}'`);
} }
if (!file.filename.startsWith('graalvm-jdk-')) {
throw new Error(`Invalid filename format: ${file.filename}. Expected to start with 'graalvm-jdk-'`);
}
const downloadUrl = `${latestVersion.download_base_url}${file.filename}`;
core.debug(`Download URL: ${downloadUrl}`);
return { return {
url: `${latestVersion.download_base_url}${file.filename}`, url: downloadUrl,
version: latestVersion.version version: latestVersion.version
}; };
}); });
} }
fetchEAJson(javaEaVersion) { fetchEAJson(javaEaVersion) {
var _a;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const url = `https://api.github.com/repos/graalvm/oracle-graalvm-ea-builds/contents/versions/${javaEaVersion}.json?ref=main`; const url = `https://api.github.com/repos/graalvm/oracle-graalvm-ea-builds/contents/versions/${javaEaVersion}.json?ref=main`;
const headers = (0, util_1.getGitHubHttpHeaders)(); const headers = (0, util_1.getGitHubHttpHeaders)();
core.debug(`Trying to fetch available version info for GraalVM EA builds from '${url}'`); core.debug(`Trying to fetch available version info for GraalVM EA builds from '${url}'`);
const fetchedJson = yield this.http try {
.getJson(url, headers) const response = yield this.http.getJson(url, headers);
.then(res => res.result); if (!response.result) {
if (!fetchedJson) { throw new Error(`No GraalVM EA build found for version '${javaEaVersion}'. Please check if the version is correct.`);
throw new Error(`No GraalVM EA build found for version '${javaEaVersion}'. Please check if the version is correct.`); }
return response.result;
}
catch (error) {
if (error instanceof Error) {
// Check if it's a 404 error (file not found)
if ((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('404')) {
throw new Error(`GraalVM EA version '${javaEaVersion}' not found. Please verify the version exists in the EA builds repository.`);
}
// Re-throw with more context
throw new Error(`Failed to fetch GraalVM EA version information for '${javaEaVersion}': ${error.message}`);
}
// If it's not an Error instance, throw a generic error
throw new Error(`Failed to fetch GraalVM EA version information for '${javaEaVersion}'`);
} }
return fetchedJson;
}); });
} }
getPlatform(platform = process.platform) { getPlatform(platform = process.platform) {

View File

@ -20,6 +20,10 @@ import {
const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm'; const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm';
const IS_WINDOWS = process.platform === 'win32'; const IS_WINDOWS = process.platform === 'win32';
const GRAALVM_PLATFORM = IS_WINDOWS ? 'windows' : process.platform; const GRAALVM_PLATFORM = IS_WINDOWS ? 'windows' : process.platform;
const GRAALVM_MIN_VERSION = 17;
const SUPPORTED_ARCHITECTURES = ['x64', 'aarch64'] as const;
type SupportedArchitecture = (typeof SUPPORTED_ARCHITECTURES)[number];
type OsVersions = 'linux' | 'macos' | 'windows';
export class GraalVMDistribution extends JavaBase { export class GraalVMDistribution extends JavaBase {
constructor(installerOptions: JavaInstallerOptions) { constructor(installerOptions: JavaInstallerOptions) {
@ -29,38 +33,67 @@ export class GraalVMDistribution extends JavaBase {
protected async downloadTool( protected async downloadTool(
javaRelease: JavaDownloadRelease javaRelease: JavaDownloadRelease
): Promise<JavaInstallerResults> { ): Promise<JavaInstallerResults> {
core.info( try {
`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...` core.info(
); `Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`
let javaArchivePath = await tc.downloadTool(javaRelease.url); );
let javaArchivePath = await tc.downloadTool(javaRelease.url);
core.info(`Extracting Java archive...`); core.info(`Extracting Java archive...`);
const extension = getDownloadArchiveExtension(); const extension = getDownloadArchiveExtension();
if (IS_WINDOWS) { if (IS_WINDOWS) {
javaArchivePath = renameWinArchive(javaArchivePath); javaArchivePath = renameWinArchive(javaArchivePath);
}
const extractedJavaPath = await extractJdkFile(
javaArchivePath,
extension
);
// Add validation for extracted path
if (!fs.existsSync(extractedJavaPath)) {
throw new Error(
`Extraction failed: path ${extractedJavaPath} does not exist`
);
}
const dirContents = fs.readdirSync(extractedJavaPath);
if (dirContents.length === 0) {
throw new Error(
'Extraction failed: no files found in extracted directory'
);
}
const archivePath = path.join(extractedJavaPath, dirContents[0]);
const version = this.getToolcacheVersionName(javaRelease.version);
const javaPath = await tc.cacheDir(
archivePath,
this.toolcacheFolderName,
version,
this.architecture
);
return {version: javaRelease.version, path: javaPath};
} catch (error) {
core.error(`Failed to download and extract GraalVM: ${error}`);
throw error;
} }
const extractedJavaPath = await extractJdkFile(javaArchivePath, extension);
const archivePath = path.join(
extractedJavaPath,
fs.readdirSync(extractedJavaPath)[0]
);
const version = this.getToolcacheVersionName(javaRelease.version);
const javaPath = await tc.cacheDir(
archivePath,
this.toolcacheFolderName,
version,
this.architecture
);
return {version: javaRelease.version, path: javaPath};
} }
protected async findPackageForDownload( protected async findPackageForDownload(
range: string range: string
): Promise<JavaDownloadRelease> { ): Promise<JavaDownloadRelease> {
// Add input validation
if (!range || typeof range !== 'string') {
throw new Error('Version range is required and must be a string');
}
const arch = this.distributionArchitecture(); const arch = this.distributionArchitecture();
if (!['x64', 'aarch64'].includes(arch)) { if (!SUPPORTED_ARCHITECTURES.includes(arch as SupportedArchitecture)) {
throw new Error(`Unsupported architecture: ${this.architecture}`); throw new Error(
`Unsupported architecture: ${this.architecture}. Supported architectures are: ${SUPPORTED_ARCHITECTURES.join(', ')}`
);
} }
if (!this.stable) { if (!this.stable) {
@ -74,6 +107,18 @@ export class GraalVMDistribution extends JavaBase {
const platform = this.getPlatform(); const platform = this.getPlatform();
const extension = getDownloadArchiveExtension(); const extension = getDownloadArchiveExtension();
const major = range.includes('.') ? range.split('.')[0] : range; const major = range.includes('.') ? range.split('.')[0] : range;
const majorVersion = parseInt(major);
if (isNaN(majorVersion)) {
throw new Error(`Invalid version format: ${range}`);
}
if (majorVersion < GRAALVM_MIN_VERSION) {
throw new Error(
`GraalVM is only supported for JDK ${GRAALVM_MIN_VERSION} and later. Requested version: ${major}`
);
}
const fileUrl = this.constructFileUrl( const fileUrl = this.constructFileUrl(
range, range,
major, major,
@ -82,10 +127,6 @@ export class GraalVMDistribution extends JavaBase {
extension extension
); );
if (parseInt(major) < 17) {
throw new Error('GraalVM is only supported for JDK 17 and later');
}
const response = await this.http.head(fileUrl); const response = await this.http.head(fileUrl);
this.handleHttpResponse(response, range); this.handleHttpResponse(response, range);
@ -105,12 +146,26 @@ export class GraalVMDistribution extends JavaBase {
} }
private handleHttpResponse(response: any, range: string): void { private handleHttpResponse(response: any, range: string): void {
if (response.message.statusCode === HttpCodes.NotFound) { const statusCode = response.message.statusCode;
throw new Error(`Could not find GraalVM for SemVer ${range}`);
} if (statusCode === HttpCodes.NotFound) {
if (response.message.statusCode !== HttpCodes.OK) {
throw new Error( throw new Error(
`Http request for GraalVM failed with status code: ${response.message.statusCode}` `Could not find GraalVM for SemVer ${range}. Please check if this version is available at ${GRAALVM_DL_BASE}`
);
}
if (
statusCode === HttpCodes.Unauthorized ||
statusCode === HttpCodes.Forbidden
) {
throw new Error(
`Access denied when downloading GraalVM. Status code: ${statusCode}. Please check your credentials or permissions.`
);
}
if (statusCode !== HttpCodes.OK) {
throw new Error(
`HTTP request for GraalVM failed with status code: ${statusCode} (${response.message.statusMessage || 'Unknown error'})`
); );
} }
} }
@ -118,22 +173,46 @@ export class GraalVMDistribution extends JavaBase {
private async findEABuildDownloadUrl( private async findEABuildDownloadUrl(
javaEaVersion: string javaEaVersion: string
): Promise<JavaDownloadRelease> { ): Promise<JavaDownloadRelease> {
core.debug(`Searching for EA build: ${javaEaVersion}`);
const versions = await this.fetchEAJson(javaEaVersion); const versions = await this.fetchEAJson(javaEaVersion);
core.debug(`Found ${versions.length} EA versions`);
const latestVersion = versions.find(v => v.latest); const latestVersion = versions.find(v => v.latest);
if (!latestVersion) { if (!latestVersion) {
core.error(
`Available versions: ${versions.map(v => v.version).join(', ')}`
);
throw new Error(`Unable to find latest version for '${javaEaVersion}'`); throw new Error(`Unable to find latest version for '${javaEaVersion}'`);
} }
core.debug(`Latest version found: ${latestVersion.version}`);
const arch = this.distributionArchitecture(); const arch = this.distributionArchitecture();
const file = latestVersion.files.find( const file = latestVersion.files.find(
f => f.arch === arch && f.platform === GRAALVM_PLATFORM f => f.arch === arch && f.platform === GRAALVM_PLATFORM
); );
if (!file || !file.filename.startsWith('graalvm-jdk-')) {
throw new Error(`Unable to find file metadata for '${javaEaVersion}'`); if (!file) {
core.error(
`Available files for architecture ${arch}: ${JSON.stringify(latestVersion.files)}`
);
throw new Error(
`Unable to find file for architecture '${arch}' and platform '${GRAALVM_PLATFORM}'`
);
} }
if (!file.filename.startsWith('graalvm-jdk-')) {
throw new Error(
`Invalid filename format: ${file.filename}. Expected to start with 'graalvm-jdk-'`
);
}
const downloadUrl = `${latestVersion.download_base_url}${file.filename}`;
core.debug(`Download URL: ${downloadUrl}`);
return { return {
url: `${latestVersion.download_base_url}${file.filename}`, url: downloadUrl,
version: latestVersion.version version: latestVersion.version
}; };
} }
@ -147,16 +226,38 @@ export class GraalVMDistribution extends JavaBase {
core.debug( core.debug(
`Trying to fetch available version info for GraalVM EA builds from '${url}'` `Trying to fetch available version info for GraalVM EA builds from '${url}'`
); );
const fetchedJson = await this.http
.getJson<GraalVMEAVersion[]>(url, headers)
.then(res => res.result);
if (!fetchedJson) { try {
const response = await this.http.getJson<GraalVMEAVersion[]>(
url,
headers
);
if (!response.result) {
throw new Error(
`No GraalVM EA build found for version '${javaEaVersion}'. Please check if the version is correct.`
);
}
return response.result;
} catch (error) {
if (error instanceof Error) {
// Check if it's a 404 error (file not found)
if (error.message?.includes('404')) {
throw new Error(
`GraalVM EA version '${javaEaVersion}' not found. Please verify the version exists in the EA builds repository.`
);
}
// Re-throw with more context
throw new Error(
`Failed to fetch GraalVM EA version information for '${javaEaVersion}': ${error.message}`
);
}
// If it's not an Error instance, throw a generic error
throw new Error( throw new Error(
`No GraalVM EA build found for version '${javaEaVersion}'. Please check if the version is correct.` `Failed to fetch GraalVM EA version information for '${javaEaVersion}'`
); );
} }
return fetchedJson;
} }
public getPlatform(platform: NodeJS.Platform = process.platform): OsVersions { public getPlatform(platform: NodeJS.Platform = process.platform): OsVersions {