Skip to content

Commit 46ca045

Browse files
author
Danny McCormick
authored
Features/installer (actions#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
1 parent 62b9fcd commit 46ca045

6 files changed

Lines changed: 407 additions & 7 deletions

File tree

__tests__/setup-java.test.ts

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,98 @@
1-
describe('TODO - Add a test suite', () => {
2-
it('TODO - Add a test', async () => {});
1+
import io = require('@actions/io');
2+
import fs = require('fs');
3+
import path = require('path');
4+
import child_process = require('child_process');
5+
6+
const toolDir = path.join(__dirname, 'runner', 'tools');
7+
const tempDir = path.join(__dirname, 'runner', 'temp');
8+
const javaDir = path.join(__dirname, 'runner', 'java');
9+
10+
process.env['RUNNER_TOOLSDIRECTORY'] = toolDir;
11+
process.env['RUNNER_TEMPDIRECTORY'] = tempDir;
12+
import * as installer from '../src/installer';
13+
14+
let javaFilePath = '';
15+
let javaUrl = '';
16+
if (process.platform === 'win32') {
17+
javaFilePath = path.join(javaDir, 'java_win.zip');
18+
javaUrl =
19+
'https://download.java.net/java/GA/jdk12/33/GPL/openjdk-12_windows-x64_bin.zip';
20+
} else if (process.platform === 'darwin') {
21+
javaFilePath = path.join(javaDir, 'java_mac.tar.gz');
22+
javaUrl =
23+
'https://download.java.net/java/GA/jdk12/33/GPL/openjdk-12_osx-x64_bin.tar.gz';
24+
} else {
25+
javaFilePath = path.join(javaDir, 'java_linux.tar.gz');
26+
javaUrl =
27+
'https://download.java.net/java/GA/jdk12/33/GPL/openjdk-12_linux-x64_bin.tar.gz';
28+
}
29+
30+
describe('installer tests', () => {
31+
beforeAll(async () => {
32+
await io.rmRF(toolDir);
33+
await io.rmRF(tempDir);
34+
if (!fs.existsSync(`${javaFilePath}.complete`)) {
35+
// Download java
36+
await io.mkdirP(javaDir);
37+
38+
console.log('Downloading java');
39+
child_process.execSync(`curl "${javaUrl}" > "${javaFilePath}"`);
40+
// Write complete file so we know it was successful
41+
fs.writeFileSync(`${javaFilePath}.complete`, 'content');
42+
}
43+
}, 300000);
44+
45+
afterAll(async () => {
46+
try {
47+
await io.rmRF(toolDir);
48+
await io.rmRF(tempDir);
49+
} catch {
50+
console.log('Failed to remove test directories');
51+
}
52+
}, 100000);
53+
54+
it('Acquires version of Java if no matching version is installed', async () => {
55+
await installer.getJava('12', 'x64', javaFilePath);
56+
const JavaDir = path.join(toolDir, 'Java', '12.0.0', 'x64');
57+
58+
expect(fs.existsSync(`${JavaDir}.complete`)).toBe(true);
59+
expect(fs.existsSync(path.join(JavaDir, 'bin'))).toBe(true);
60+
}, 100000);
61+
62+
it('Throws if invalid directory to jdk', async () => {
63+
let thrown = false;
64+
try {
65+
await installer.getJava('1000', 'x64', 'bad path');
66+
} catch {
67+
thrown = true;
68+
}
69+
expect(thrown).toBe(true);
70+
});
71+
72+
it('Uses version of Java installed in cache', async () => {
73+
const JavaDir: string = path.join(toolDir, 'Java', '250.0.0', 'x64');
74+
await io.mkdirP(JavaDir);
75+
fs.writeFileSync(`${JavaDir}.complete`, 'hello');
76+
// This will throw if it doesn't find it in the cache (because no such version exists)
77+
await installer.getJava(
78+
'250',
79+
'x64',
80+
'path shouldnt matter, found in cache'
81+
);
82+
return;
83+
});
84+
85+
it('Doesnt use version of Java that was only partially installed in cache', async () => {
86+
const JavaDir: string = path.join(toolDir, 'Java', '251.0.0', 'x64');
87+
await io.mkdirP(JavaDir);
88+
let thrown = false;
89+
try {
90+
// This will throw if it doesn't find it in the cache (because no such version exists)
91+
await installer.getJava('251', 'x64', 'bad path');
92+
} catch {
93+
thrown = true;
94+
}
95+
expect(thrown).toBe(true);
96+
return;
97+
});
398
});

action.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ description: 'Setup your runner with Java'
33
author: 'GitHub'
44
inputs:
55
version:
6-
description: 'Version of JDK to use'
6+
description: 'A number that specifies the JDK version to make available on the path. Use a whole number version, such as 10'
7+
required: true
8+
architecture:
9+
description: 'The architecture (x86, x64) of the JDK.'
10+
required: true
11+
jdkFile:
12+
description: 'Path to where the compressed JDK is located. The path could be in your source repository or a local path on the agent.'
13+
required: true
714
runs:
815
using: 'node12'
916
main: 'lib/setup-java.js'

lib/installer.js

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
"use strict";
2+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3+
return new (P || (P = Promise))(function (resolve, reject) {
4+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6+
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
7+
step((generator = generator.apply(thisArg, _arguments || [])).next());
8+
});
9+
};
10+
var __importStar = (this && this.__importStar) || function (mod) {
11+
if (mod && mod.__esModule) return mod;
12+
var result = {};
13+
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
14+
result["default"] = mod;
15+
return result;
16+
};
17+
Object.defineProperty(exports, "__esModule", { value: true });
18+
let tempDirectory = process.env['RUNNER_TEMPDIRECTORY'] || '';
19+
const core = __importStar(require("@actions/core"));
20+
const io = __importStar(require("@actions/io"));
21+
const exec = __importStar(require("@actions/exec"));
22+
const tc = __importStar(require("@actions/tool-cache"));
23+
const fs = __importStar(require("fs"));
24+
const path = __importStar(require("path"));
25+
const IS_WINDOWS = process.platform === 'win32';
26+
if (!tempDirectory) {
27+
let baseLocation;
28+
if (IS_WINDOWS) {
29+
// On windows use the USERPROFILE env variable
30+
baseLocation = process.env['USERPROFILE'] || 'C:\\';
31+
}
32+
else {
33+
if (process.platform === 'darwin') {
34+
baseLocation = '/Users';
35+
}
36+
else {
37+
baseLocation = '/home';
38+
}
39+
}
40+
tempDirectory = path.join(baseLocation, 'actions', 'temp');
41+
}
42+
function getJava(version, arch, jdkFile) {
43+
return __awaiter(this, void 0, void 0, function* () {
44+
let toolPath = tc.find('Java', version);
45+
if (toolPath) {
46+
core.debug(`Tool found in cache ${toolPath}`);
47+
}
48+
else {
49+
core.debug('Retrieving Jdk from local path');
50+
const compressedFileExtension = getFileEnding(jdkFile);
51+
let tempDir = path.join(tempDirectory, 'temp_' + Math.floor(Math.random() * 2000000000));
52+
const jdkDir = yield unzipJavaDownload(jdkFile, compressedFileExtension, tempDir);
53+
core.debug(`jdk extracted to ${jdkDir}`);
54+
toolPath = yield tc.cacheDir(jdkDir, 'Java', `${version}.0.0`, arch);
55+
}
56+
let extendedJavaHome = 'JAVA_HOME_' + version + '_' + arch;
57+
core.exportVariable('JAVA_HOME', toolPath);
58+
core.exportVariable(extendedJavaHome, toolPath);
59+
core.addPath(path.join(toolPath, 'bin'));
60+
});
61+
}
62+
exports.getJava = getJava;
63+
function getFileEnding(file) {
64+
let fileEnding = '';
65+
if (file.endsWith('.tar')) {
66+
fileEnding = '.tar';
67+
}
68+
else if (file.endsWith('.tar.gz')) {
69+
fileEnding = '.tar.gz';
70+
}
71+
else if (file.endsWith('.zip')) {
72+
fileEnding = '.zip';
73+
}
74+
else if (file.endsWith('.7z')) {
75+
fileEnding = '.7z';
76+
}
77+
else {
78+
throw new Error(`${file} has an unsupported file extension`);
79+
}
80+
return fileEnding;
81+
}
82+
function extractFiles(file, fileEnding, destinationFolder) {
83+
return __awaiter(this, void 0, void 0, function* () {
84+
const stats = fs.statSync(file);
85+
if (!stats) {
86+
throw new Error(`Failed to extract ${file} - it doesn't exist`);
87+
}
88+
else if (stats.isDirectory()) {
89+
throw new Error(`Failed to extract ${file} - it is a directory`);
90+
}
91+
if ('.tar' === fileEnding || '.tar.gz' === fileEnding) {
92+
yield tc.extractTar(file, destinationFolder);
93+
}
94+
else if ('.zip' === fileEnding) {
95+
yield tc.extractZip(file, destinationFolder);
96+
}
97+
else {
98+
// fall through and use sevenZip
99+
yield tc.extract7z(file, destinationFolder);
100+
}
101+
});
102+
}
103+
// This method recursively finds all .pack files under fsPath and unpacks them with the unpack200 tool
104+
function unpackJars(fsPath, javaBinPath) {
105+
return __awaiter(this, void 0, void 0, function* () {
106+
if (fs.existsSync(fsPath)) {
107+
if (fs.lstatSync(fsPath).isDirectory()) {
108+
for (const file in fs.readdirSync(fsPath)) {
109+
const curPath = path.join(fsPath, file);
110+
yield unpackJars(curPath, javaBinPath);
111+
}
112+
}
113+
else if (path.extname(fsPath).toLowerCase() === '.pack') {
114+
// Unpack the pack file synchonously
115+
const p = path.parse(fsPath);
116+
const toolName = IS_WINDOWS ? 'unpack200.exe' : 'unpack200';
117+
const args = IS_WINDOWS ? '-r -v -l ""' : '';
118+
const name = path.join(p.dir, p.name);
119+
yield exec.exec(`"${path.join(javaBinPath, toolName)}"`, [
120+
`${args} "${name}.pack" "${name}.jar"`
121+
]);
122+
}
123+
}
124+
});
125+
}
126+
function unzipJavaDownload(repoRoot, fileEnding, destinationFolder) {
127+
return __awaiter(this, void 0, void 0, function* () {
128+
// Create the destination folder if it doesn't exist
129+
yield io.mkdirP(destinationFolder);
130+
const jdkFile = path.normalize(repoRoot);
131+
const stats = fs.statSync(jdkFile);
132+
if (stats.isFile()) {
133+
yield extractFiles(jdkFile, fileEnding, destinationFolder);
134+
const jdkDirectory = path.join(destinationFolder, fs.readdirSync(destinationFolder)[0]);
135+
yield unpackJars(jdkDirectory, path.join(jdkDirectory, 'bin'));
136+
return jdkDirectory;
137+
}
138+
else {
139+
throw new Error(`Jdk argument ${jdkFile} is not a file`);
140+
}
141+
});
142+
}

lib/setup-java.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,18 @@ var __importStar = (this && this.__importStar) || function (mod) {
1616
};
1717
Object.defineProperty(exports, "__esModule", { value: true });
1818
const core = __importStar(require("@actions/core"));
19+
const installer = __importStar(require("./installer"));
1920
function run() {
2021
return __awaiter(this, void 0, void 0, function* () {
21-
const myInput = core.getInput('myInput');
22-
core.debug(`Hello ${myInput}`);
22+
try {
23+
const version = core.getInput('version', { required: true });
24+
const arch = core.getInput('architecture', { required: true });
25+
const jdkFile = core.getInput('jdkFile', { required: true });
26+
yield installer.getJava(version, arch, jdkFile);
27+
}
28+
catch (error) {
29+
core.setFailed(error.message);
30+
}
2331
});
2432
}
2533
run();

0 commit comments

Comments
 (0)