Features/installer (#1)
* Initial attempt * Clean up * Extract right directory * Log whats happening * Read correct directory * Full path * Allow java to be found in cache * Add tests
This commit is contained in:
		| @ -1,3 +1,98 @@ | ||||
| describe('TODO - Add a test suite', () => { | ||||
|   it('TODO - Add a test', async () => {}); | ||||
| import io = require('@actions/io'); | ||||
| import fs = require('fs'); | ||||
| import path = require('path'); | ||||
| import child_process = require('child_process'); | ||||
|  | ||||
| const toolDir = path.join(__dirname, 'runner', 'tools'); | ||||
| const tempDir = path.join(__dirname, 'runner', 'temp'); | ||||
| const javaDir = path.join(__dirname, 'runner', 'java'); | ||||
|  | ||||
| process.env['RUNNER_TOOLSDIRECTORY'] = toolDir; | ||||
| process.env['RUNNER_TEMPDIRECTORY'] = tempDir; | ||||
| import * as installer from '../src/installer'; | ||||
|  | ||||
| let javaFilePath = ''; | ||||
| let javaUrl = ''; | ||||
| if (process.platform === 'win32') { | ||||
|   javaFilePath = path.join(javaDir, 'java_win.zip'); | ||||
|   javaUrl = | ||||
|     'https://download.java.net/java/GA/jdk12/33/GPL/openjdk-12_windows-x64_bin.zip'; | ||||
| } else if (process.platform === 'darwin') { | ||||
|   javaFilePath = path.join(javaDir, 'java_mac.tar.gz'); | ||||
|   javaUrl = | ||||
|     'https://download.java.net/java/GA/jdk12/33/GPL/openjdk-12_osx-x64_bin.tar.gz'; | ||||
| } else { | ||||
|   javaFilePath = path.join(javaDir, 'java_linux.tar.gz'); | ||||
|   javaUrl = | ||||
|     'https://download.java.net/java/GA/jdk12/33/GPL/openjdk-12_linux-x64_bin.tar.gz'; | ||||
| } | ||||
|  | ||||
| describe('installer tests', () => { | ||||
|   beforeAll(async () => { | ||||
|     await io.rmRF(toolDir); | ||||
|     await io.rmRF(tempDir); | ||||
|     if (!fs.existsSync(`${javaFilePath}.complete`)) { | ||||
|       // Download java | ||||
|       await io.mkdirP(javaDir); | ||||
|  | ||||
|       console.log('Downloading java'); | ||||
|       child_process.execSync(`curl "${javaUrl}" > "${javaFilePath}"`); | ||||
|       // Write complete file so we know it was successful | ||||
|       fs.writeFileSync(`${javaFilePath}.complete`, 'content'); | ||||
|     } | ||||
|   }, 300000); | ||||
|  | ||||
|   afterAll(async () => { | ||||
|     try { | ||||
|       await io.rmRF(toolDir); | ||||
|       await io.rmRF(tempDir); | ||||
|     } catch { | ||||
|       console.log('Failed to remove test directories'); | ||||
|     } | ||||
|   }, 100000); | ||||
|  | ||||
|   it('Acquires version of Java if no matching version is installed', async () => { | ||||
|     await installer.getJava('12', 'x64', javaFilePath); | ||||
|     const JavaDir = path.join(toolDir, 'Java', '12.0.0', 'x64'); | ||||
|  | ||||
|     expect(fs.existsSync(`${JavaDir}.complete`)).toBe(true); | ||||
|     expect(fs.existsSync(path.join(JavaDir, 'bin'))).toBe(true); | ||||
|   }, 100000); | ||||
|  | ||||
|   it('Throws if invalid directory to jdk', async () => { | ||||
|     let thrown = false; | ||||
|     try { | ||||
|       await installer.getJava('1000', 'x64', 'bad path'); | ||||
|     } catch { | ||||
|       thrown = true; | ||||
|     } | ||||
|     expect(thrown).toBe(true); | ||||
|   }); | ||||
|  | ||||
|   it('Uses version of Java installed in cache', async () => { | ||||
|     const JavaDir: string = path.join(toolDir, 'Java', '250.0.0', 'x64'); | ||||
|     await io.mkdirP(JavaDir); | ||||
|     fs.writeFileSync(`${JavaDir}.complete`, 'hello'); | ||||
|     // This will throw if it doesn't find it in the cache (because no such version exists) | ||||
|     await installer.getJava( | ||||
|       '250', | ||||
|       'x64', | ||||
|       'path shouldnt matter, found in cache' | ||||
|     ); | ||||
|     return; | ||||
|   }); | ||||
|  | ||||
|   it('Doesnt use version of Java that was only partially installed in cache', async () => { | ||||
|     const JavaDir: string = path.join(toolDir, 'Java', '251.0.0', 'x64'); | ||||
|     await io.mkdirP(JavaDir); | ||||
|     let thrown = false; | ||||
|     try { | ||||
|       // This will throw if it doesn't find it in the cache (because no such version exists) | ||||
|       await installer.getJava('251', 'x64', 'bad path'); | ||||
|     } catch { | ||||
|       thrown = true; | ||||
|     } | ||||
|     expect(thrown).toBe(true); | ||||
|     return; | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| @ -3,7 +3,14 @@ description: 'Setup your runner with Java' | ||||
| author: 'GitHub' | ||||
| inputs:  | ||||
|   version: | ||||
|     description: 'Version of JDK to use' | ||||
|     description: 'A number that specifies the JDK version to make available on the path. Use a whole number version, such as 10' | ||||
|     required: true | ||||
|   architecture: | ||||
|     description: 'The architecture (x86, x64) of the JDK.' | ||||
|     required: true | ||||
|   jdkFile: | ||||
|     description: 'Path to where the compressed JDK is located. The path could be in your source repository or a local path on the agent.' | ||||
|     required: true | ||||
| runs: | ||||
|   using: 'node12' | ||||
|   main: 'lib/setup-java.js' | ||||
|  | ||||
							
								
								
									
										142
									
								
								lib/installer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								lib/installer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,142 @@ | ||||
| "use strict"; | ||||
| var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||||
|     return new (P || (P = Promise))(function (resolve, reject) { | ||||
|         function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||||
|         function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||||
|         function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||||
|         step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||||
|     }); | ||||
| }; | ||||
| var __importStar = (this && this.__importStar) || function (mod) { | ||||
|     if (mod && mod.__esModule) return mod; | ||||
|     var result = {}; | ||||
|     if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||||
|     result["default"] = mod; | ||||
|     return result; | ||||
| }; | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| let tempDirectory = process.env['RUNNER_TEMPDIRECTORY'] || ''; | ||||
| const core = __importStar(require("@actions/core")); | ||||
| const io = __importStar(require("@actions/io")); | ||||
| const exec = __importStar(require("@actions/exec")); | ||||
| const tc = __importStar(require("@actions/tool-cache")); | ||||
| const fs = __importStar(require("fs")); | ||||
| const path = __importStar(require("path")); | ||||
| const IS_WINDOWS = process.platform === 'win32'; | ||||
| if (!tempDirectory) { | ||||
|     let baseLocation; | ||||
|     if (IS_WINDOWS) { | ||||
|         // On windows use the USERPROFILE env variable | ||||
|         baseLocation = process.env['USERPROFILE'] || 'C:\\'; | ||||
|     } | ||||
|     else { | ||||
|         if (process.platform === 'darwin') { | ||||
|             baseLocation = '/Users'; | ||||
|         } | ||||
|         else { | ||||
|             baseLocation = '/home'; | ||||
|         } | ||||
|     } | ||||
|     tempDirectory = path.join(baseLocation, 'actions', 'temp'); | ||||
| } | ||||
| function getJava(version, arch, jdkFile) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         let toolPath = tc.find('Java', version); | ||||
|         if (toolPath) { | ||||
|             core.debug(`Tool found in cache ${toolPath}`); | ||||
|         } | ||||
|         else { | ||||
|             core.debug('Retrieving Jdk from local path'); | ||||
|             const compressedFileExtension = getFileEnding(jdkFile); | ||||
|             let tempDir = path.join(tempDirectory, 'temp_' + Math.floor(Math.random() * 2000000000)); | ||||
|             const jdkDir = yield unzipJavaDownload(jdkFile, compressedFileExtension, tempDir); | ||||
|             core.debug(`jdk extracted to ${jdkDir}`); | ||||
|             toolPath = yield tc.cacheDir(jdkDir, 'Java', `${version}.0.0`, arch); | ||||
|         } | ||||
|         let extendedJavaHome = 'JAVA_HOME_' + version + '_' + arch; | ||||
|         core.exportVariable('JAVA_HOME', toolPath); | ||||
|         core.exportVariable(extendedJavaHome, toolPath); | ||||
|         core.addPath(path.join(toolPath, 'bin')); | ||||
|     }); | ||||
| } | ||||
| exports.getJava = getJava; | ||||
| function getFileEnding(file) { | ||||
|     let fileEnding = ''; | ||||
|     if (file.endsWith('.tar')) { | ||||
|         fileEnding = '.tar'; | ||||
|     } | ||||
|     else if (file.endsWith('.tar.gz')) { | ||||
|         fileEnding = '.tar.gz'; | ||||
|     } | ||||
|     else if (file.endsWith('.zip')) { | ||||
|         fileEnding = '.zip'; | ||||
|     } | ||||
|     else if (file.endsWith('.7z')) { | ||||
|         fileEnding = '.7z'; | ||||
|     } | ||||
|     else { | ||||
|         throw new Error(`${file} has an unsupported file extension`); | ||||
|     } | ||||
|     return fileEnding; | ||||
| } | ||||
| function extractFiles(file, fileEnding, destinationFolder) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         const stats = fs.statSync(file); | ||||
|         if (!stats) { | ||||
|             throw new Error(`Failed to extract ${file} - it doesn't exist`); | ||||
|         } | ||||
|         else if (stats.isDirectory()) { | ||||
|             throw new Error(`Failed to extract ${file} - it is a directory`); | ||||
|         } | ||||
|         if ('.tar' === fileEnding || '.tar.gz' === fileEnding) { | ||||
|             yield tc.extractTar(file, destinationFolder); | ||||
|         } | ||||
|         else if ('.zip' === fileEnding) { | ||||
|             yield tc.extractZip(file, destinationFolder); | ||||
|         } | ||||
|         else { | ||||
|             // fall through and use sevenZip | ||||
|             yield tc.extract7z(file, destinationFolder); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| // This method recursively finds all .pack files under fsPath and unpacks them with the unpack200 tool | ||||
| function unpackJars(fsPath, javaBinPath) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         if (fs.existsSync(fsPath)) { | ||||
|             if (fs.lstatSync(fsPath).isDirectory()) { | ||||
|                 for (const file in fs.readdirSync(fsPath)) { | ||||
|                     const curPath = path.join(fsPath, file); | ||||
|                     yield unpackJars(curPath, javaBinPath); | ||||
|                 } | ||||
|             } | ||||
|             else if (path.extname(fsPath).toLowerCase() === '.pack') { | ||||
|                 // Unpack the pack file synchonously | ||||
|                 const p = path.parse(fsPath); | ||||
|                 const toolName = IS_WINDOWS ? 'unpack200.exe' : 'unpack200'; | ||||
|                 const args = IS_WINDOWS ? '-r -v -l ""' : ''; | ||||
|                 const name = path.join(p.dir, p.name); | ||||
|                 yield exec.exec(`"${path.join(javaBinPath, toolName)}"`, [ | ||||
|                     `${args} "${name}.pack" "${name}.jar"` | ||||
|                 ]); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| function unzipJavaDownload(repoRoot, fileEnding, destinationFolder) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         // Create the destination folder if it doesn't exist | ||||
|         yield io.mkdirP(destinationFolder); | ||||
|         const jdkFile = path.normalize(repoRoot); | ||||
|         const stats = fs.statSync(jdkFile); | ||||
|         if (stats.isFile()) { | ||||
|             yield extractFiles(jdkFile, fileEnding, destinationFolder); | ||||
|             const jdkDirectory = path.join(destinationFolder, fs.readdirSync(destinationFolder)[0]); | ||||
|             yield unpackJars(jdkDirectory, path.join(jdkDirectory, 'bin')); | ||||
|             return jdkDirectory; | ||||
|         } | ||||
|         else { | ||||
|             throw new Error(`Jdk argument ${jdkFile} is not a file`); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| @ -16,10 +16,18 @@ var __importStar = (this && this.__importStar) || function (mod) { | ||||
| }; | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| const core = __importStar(require("@actions/core")); | ||||
| const installer = __importStar(require("./installer")); | ||||
| function run() { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         const myInput = core.getInput('myInput'); | ||||
|         core.debug(`Hello ${myInput}`); | ||||
|         try { | ||||
|             const version = core.getInput('version', { required: true }); | ||||
|             const arch = core.getInput('architecture', { required: true }); | ||||
|             const jdkFile = core.getInput('jdkFile', { required: true }); | ||||
|             yield installer.getJava(version, arch, jdkFile); | ||||
|         } | ||||
|         catch (error) { | ||||
|             core.setFailed(error.message); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| run(); | ||||
|  | ||||
							
								
								
									
										140
									
								
								src/installer.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								src/installer.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,140 @@ | ||||
| let tempDirectory = process.env['RUNNER_TEMPDIRECTORY'] || ''; | ||||
|  | ||||
| import * as core from '@actions/core'; | ||||
| import * as io from '@actions/io'; | ||||
| import * as exec from '@actions/exec'; | ||||
| import * as tc from '@actions/tool-cache'; | ||||
| import * as fs from 'fs'; | ||||
| import * as path from 'path'; | ||||
|  | ||||
| const IS_WINDOWS = process.platform === 'win32'; | ||||
|  | ||||
| if (!tempDirectory) { | ||||
|   let baseLocation; | ||||
|   if (IS_WINDOWS) { | ||||
|     // On windows use the USERPROFILE env variable | ||||
|     baseLocation = process.env['USERPROFILE'] || 'C:\\'; | ||||
|   } else { | ||||
|     if (process.platform === 'darwin') { | ||||
|       baseLocation = '/Users'; | ||||
|     } else { | ||||
|       baseLocation = '/home'; | ||||
|     } | ||||
|   } | ||||
|   tempDirectory = path.join(baseLocation, 'actions', 'temp'); | ||||
| } | ||||
|  | ||||
| export async function getJava( | ||||
|   version: string, | ||||
|   arch: string, | ||||
|   jdkFile: string | ||||
| ): Promise<void> { | ||||
|   let toolPath = tc.find('Java', version); | ||||
|  | ||||
|   if (toolPath) { | ||||
|     core.debug(`Tool found in cache ${toolPath}`); | ||||
|   } else { | ||||
|     core.debug('Retrieving Jdk from local path'); | ||||
|     const compressedFileExtension = getFileEnding(jdkFile); | ||||
|     let tempDir: string = path.join( | ||||
|       tempDirectory, | ||||
|       'temp_' + Math.floor(Math.random() * 2000000000) | ||||
|     ); | ||||
|     const jdkDir = await unzipJavaDownload( | ||||
|       jdkFile, | ||||
|       compressedFileExtension, | ||||
|       tempDir | ||||
|     ); | ||||
|     core.debug(`jdk extracted to ${jdkDir}`); | ||||
|     toolPath = await tc.cacheDir(jdkDir, 'Java', `${version}.0.0`, arch); | ||||
|   } | ||||
|  | ||||
|   let extendedJavaHome = 'JAVA_HOME_' + version + '_' + arch; | ||||
|   core.exportVariable('JAVA_HOME', toolPath); | ||||
|   core.exportVariable(extendedJavaHome, toolPath); | ||||
|   core.addPath(path.join(toolPath, 'bin')); | ||||
| } | ||||
|  | ||||
| function getFileEnding(file: string): string { | ||||
|   let fileEnding = ''; | ||||
|  | ||||
|   if (file.endsWith('.tar')) { | ||||
|     fileEnding = '.tar'; | ||||
|   } else if (file.endsWith('.tar.gz')) { | ||||
|     fileEnding = '.tar.gz'; | ||||
|   } else if (file.endsWith('.zip')) { | ||||
|     fileEnding = '.zip'; | ||||
|   } else if (file.endsWith('.7z')) { | ||||
|     fileEnding = '.7z'; | ||||
|   } else { | ||||
|     throw new Error(`${file} has an unsupported file extension`); | ||||
|   } | ||||
|  | ||||
|   return fileEnding; | ||||
| } | ||||
|  | ||||
| async function extractFiles( | ||||
|   file: string, | ||||
|   fileEnding: string, | ||||
|   destinationFolder: string | ||||
| ): Promise<void> { | ||||
|   const stats = fs.statSync(file); | ||||
|   if (!stats) { | ||||
|     throw new Error(`Failed to extract ${file} - it doesn't exist`); | ||||
|   } else if (stats.isDirectory()) { | ||||
|     throw new Error(`Failed to extract ${file} - it is a directory`); | ||||
|   } | ||||
|  | ||||
|   if ('.tar' === fileEnding || '.tar.gz' === fileEnding) { | ||||
|     await tc.extractTar(file, destinationFolder); | ||||
|   } else if ('.zip' === fileEnding) { | ||||
|     await tc.extractZip(file, destinationFolder); | ||||
|   } else { | ||||
|     // fall through and use sevenZip | ||||
|     await tc.extract7z(file, destinationFolder); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // This method recursively finds all .pack files under fsPath and unpacks them with the unpack200 tool | ||||
| async function unpackJars(fsPath: string, javaBinPath: string) { | ||||
|   if (fs.existsSync(fsPath)) { | ||||
|     if (fs.lstatSync(fsPath).isDirectory()) { | ||||
|       for (const file in fs.readdirSync(fsPath)) { | ||||
|         const curPath = path.join(fsPath, file); | ||||
|         await unpackJars(curPath, javaBinPath); | ||||
|       } | ||||
|     } else if (path.extname(fsPath).toLowerCase() === '.pack') { | ||||
|       // Unpack the pack file synchonously | ||||
|       const p = path.parse(fsPath); | ||||
|       const toolName = IS_WINDOWS ? 'unpack200.exe' : 'unpack200'; | ||||
|       const args = IS_WINDOWS ? '-r -v -l ""' : ''; | ||||
|       const name = path.join(p.dir, p.name); | ||||
|       await exec.exec(`"${path.join(javaBinPath, toolName)}"`, [ | ||||
|         `${args} "${name}.pack" "${name}.jar"` | ||||
|       ]); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function unzipJavaDownload( | ||||
|   repoRoot: string, | ||||
|   fileEnding: string, | ||||
|   destinationFolder: string | ||||
| ): Promise<string> { | ||||
|   // Create the destination folder if it doesn't exist | ||||
|   await io.mkdirP(destinationFolder); | ||||
|  | ||||
|   const jdkFile = path.normalize(repoRoot); | ||||
|   const stats = fs.statSync(jdkFile); | ||||
|   if (stats.isFile()) { | ||||
|     await extractFiles(jdkFile, fileEnding, destinationFolder); | ||||
|     const jdkDirectory = path.join( | ||||
|       destinationFolder, | ||||
|       fs.readdirSync(destinationFolder)[0] | ||||
|     ); | ||||
|     await unpackJars(jdkDirectory, path.join(jdkDirectory, 'bin')); | ||||
|     return jdkDirectory; | ||||
|   } else { | ||||
|     throw new Error(`Jdk argument ${jdkFile} is not a file`); | ||||
|   } | ||||
| } | ||||
| @ -1,8 +1,16 @@ | ||||
| import * as core from '@actions/core'; | ||||
| import * as installer from './installer'; | ||||
|  | ||||
| async function run() { | ||||
|   const myInput = core.getInput('myInput'); | ||||
|   core.debug(`Hello ${myInput}`); | ||||
|   try { | ||||
|     const version = core.getInput('version', {required: true}); | ||||
|     const arch = core.getInput('architecture', {required: true}); | ||||
|     const jdkFile = core.getInput('jdkFile', {required: true}); | ||||
|  | ||||
|     await installer.getJava(version, arch, jdkFile); | ||||
|   } catch (error) { | ||||
|     core.setFailed(error.message); | ||||
|   } | ||||
| } | ||||
|  | ||||
| run(); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Danny McCormick
					Danny McCormick