Add support for maven/gradle versions

- Essentially use same concepts as jdk install to switch
  maven and gradle versions if those are defined with config.
- Bump to tool-cache 1.3.0 as 1.0.0 tries to use
  node_modules/@actions/tool-cache/scripts/externals/unzip which doesn't have
  execute flag so you'd get `Permission denied`
- `extractZipNix` in toolkit no longer support relative path so needs to
  resolve absolute path before passing file to these methods.
- Commit has my local build for `index.js` which probably should get
  recreated but makes it easier to test this from a PR branch.
- Fixes #40
This commit is contained in:
Janne Valkealahti 2020-01-17 12:58:57 +00:00
commit 7b8939ae83
11 changed files with 5380 additions and 2890 deletions

204
src/gradle-installer.ts Normal file
View file

@ -0,0 +1,204 @@
let tempDirectory = process.env['RUNNER_TEMP'] || '';
import * as core from '@actions/core';
import * as io from '@actions/io';
import * as tc from '@actions/tool-cache';
import * as fs from 'fs';
import * as path from 'path';
import * as semver from 'semver';
import * as httpm from 'typed-rest-client/HttpClient';
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 getGradle(
version: string,
gradleFile: string,
gradleMirror: string = 'https://services.gradle.org/distributions/'
): Promise<void> {
const toolName = 'gradle';
let toolPath = tc.find(toolName, version);
if (toolPath) {
core.debug(`Tool found in cache ${toolPath}`);
} else {
let compressedFileExtension = '';
if (!gradleFile) {
core.debug('Downloading Gradle from gradle.org');
let http: httpm.HttpClient = new httpm.HttpClient('spring-build-action');
let contents = await (await http.get(gradleMirror)).readBody();
let refs: string[] = [];
const regex = /<span class=\"name\">gradle-([\d\.]+)-bin\.zip<\/span>/g;
let match = regex.exec(contents);
while (match != null) {
refs.push(match[1]);
match = regex.exec(contents);
}
core.debug(`Found refs ${refs}`);
const downloadInfo = getDownloadInfo(refs, version, gradleMirror);
gradleFile = await tc.downloadTool(downloadInfo.url);
version = downloadInfo.version;
compressedFileExtension = '.zip';
} else {
core.debug('Retrieving Gradle from local path');
}
compressedFileExtension =
compressedFileExtension || getFileEnding(gradleFile);
let tempDir: string = path.join(
tempDirectory,
'temp_' + Math.floor(Math.random() * 2000000000)
);
const gradleDir = await unzipGradleDownload(
gradleFile,
compressedFileExtension,
tempDir
);
core.debug(`gradle extracted to ${gradleDir}`);
toolPath = await tc.cacheDir(
gradleDir,
toolName,
getCacheVersionString(version)
);
}
core.exportVariable('GRADLE_HOME', toolPath);
core.addPath(path.join(toolPath, 'bin'));
}
function getCacheVersionString(version: string) {
const versionArray = version.split('.');
const major = versionArray[0];
const minor = versionArray.length > 1 ? versionArray[1] : '0';
const patch = versionArray.length > 2 ? versionArray[2] : '0';
return `${major}.${minor}.${patch}`;
}
function getFileEnding(file: string): string {
let fileEnding = '';
if (file.endsWith('.zip')) {
fileEnding = '.zip';
} 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 ('.zip' === fileEnding) {
await tc.extractZip(file, destinationFolder);
} else {
throw new Error(`Failed to extract ${file} - only .zip supported`);
}
}
async function unzipGradleDownload(
repoRoot: string,
fileEnding: string,
destinationFolder: string
): Promise<string> {
// Create the destination folder if it doesn't exist
await io.mkdirP(destinationFolder);
const gradleFile = path.normalize(repoRoot);
const stats = fs.statSync(gradleFile);
if (stats.isFile()) {
await extractFiles(path.resolve(gradleFile), fileEnding, destinationFolder);
const gradleDirectory = path.join(
destinationFolder,
fs.readdirSync(destinationFolder)[0]
);
return gradleDirectory;
} else {
throw new Error(`Gradle argument ${gradleFile} is not a file`);
}
}
function getDownloadInfo(
refs: string[],
version: string,
gradleMirror: string
): {version: string; url: string} {
version = normalizeVersion(version);
const extension = '.zip';
// Maps version to url
let versionMap = new Map();
// Filter by platform
refs.forEach(ref => {
if (semver.satisfies(ref, version)) {
core.debug(`VersionMap add ${ref} ${version}`);
versionMap.set(ref, `${gradleMirror}gradle-${ref}-bin${extension}`);
}
});
// Choose the most recent satisfying version
let curVersion = '0.0.0';
let curUrl = '';
for (const entry of versionMap.entries()) {
const entryVersion = entry[0];
const entryUrl = entry[1];
core.debug(`VersionMap Entry ${entryVersion} ${entryUrl}`);
if (semver.gt(entryVersion, curVersion)) {
core.debug(`VersionMap semver gt ${entryVersion} ${entryUrl}`);
curUrl = entryUrl;
curVersion = entryVersion;
}
}
if (curUrl == '') {
throw new Error(
`No valid download found for version ${version}. Check ${gradleMirror} for a list of valid versions or download your own gradle file and add the gradleFile argument`
);
}
return {version: curVersion, url: curUrl};
}
function normalizeVersion(version: string): string {
if (version.slice(0, 2) === '1.') {
// Trim leading 1. for versions like 1.8
version = version.slice(2);
if (!version) {
throw new Error('1. is not a valid version');
}
}
if (version.split('.').length < 3) {
// Add trailing .x if it is missing
if (version[version.length - 1] != 'x') {
version = version + '.x';
}
}
return version;
}

View file

@ -118,12 +118,12 @@ async function extractFiles(
}
if ('.tar' === fileEnding || '.tar.gz' === fileEnding) {
await tc.extractTar(file, destinationFolder);
await tc.extractTar(path.resolve(file), destinationFolder);
} else if ('.zip' === fileEnding) {
await tc.extractZip(file, destinationFolder);
await tc.extractZip(path.resolve(file), destinationFolder);
} else {
// fall through and use sevenZip
await tc.extract7z(file, destinationFolder);
await tc.extract7z(path.resolve(file), destinationFolder);
}
}

218
src/maven-installer.ts Normal file
View file

@ -0,0 +1,218 @@
let tempDirectory = process.env['RUNNER_TEMP'] || '';
import * as core from '@actions/core';
import * as io from '@actions/io';
import * as tc from '@actions/tool-cache';
import * as fs from 'fs';
import * as path from 'path';
import * as semver from 'semver';
import * as httpm from 'typed-rest-client/HttpClient';
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 getMaven(
version: string,
mavenFile: string,
mavenMirror: string = 'https://archive.apache.org/dist/maven/maven-3/'
): Promise<void> {
const toolName = 'maven';
let toolPath = tc.find(toolName, version);
if (toolPath) {
core.debug(`Tool found in cache ${toolPath}`);
} else {
let compressedFileExtension = '';
if (!mavenFile) {
core.debug('Downloading Maven from Apache Mirror');
let http: httpm.HttpClient = new httpm.HttpClient('spring-build-action');
let contents = await (await http.get(mavenMirror)).readBody();
let refs: string[] = [];
const regex = /<a href=\"\d.*\">([\d\.]+)\/<\/a>/g;
let match = regex.exec(contents);
while (match != null) {
refs.push(match[1]);
match = regex.exec(contents);
}
core.debug(`Found refs ${refs}`);
const downloadInfo = getDownloadInfo(refs, version, mavenMirror);
mavenFile = await tc.downloadTool(downloadInfo.url);
version = downloadInfo.version;
compressedFileExtension = IS_WINDOWS ? '.zip' : '.tar.gz';
} else {
core.debug('Retrieving Maven from local path');
}
compressedFileExtension =
compressedFileExtension || getFileEnding(mavenFile);
let tempDir: string = path.join(
tempDirectory,
'temp_' + Math.floor(Math.random() * 2000000000)
);
const mavenDir = await unzipMavenDownload(
mavenFile,
compressedFileExtension,
tempDir
);
core.debug(`maven extracted to ${mavenDir}`);
toolPath = await tc.cacheDir(
mavenDir,
toolName,
getCacheVersionString(version)
);
}
core.exportVariable('M2_HOME', toolPath);
core.addPath(path.join(toolPath, 'bin'));
}
function getCacheVersionString(version: string) {
const versionArray = version.split('.');
const major = versionArray[0];
const minor = versionArray.length > 1 ? versionArray[1] : '0';
const patch = versionArray.length > 2 ? versionArray[2] : '0';
return `${major}.${minor}.${patch}`;
}
function getFileEnding(file: string): string {
let fileEnding = '';
if (file.endsWith('.tar.gz')) {
fileEnding = '.tar.gz';
} else if (file.endsWith('.zip')) {
fileEnding = '.zip';
} 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.gz' === fileEnding) {
await tc.extractTar(file, destinationFolder);
} else if ('.zip' === fileEnding) {
await tc.extractZip(file, destinationFolder);
} else {
throw new Error(
`Failed to extract ${file} - only .zip or .tar.gz supported`
);
}
}
async function unzipMavenDownload(
repoRoot: string,
fileEnding: string,
destinationFolder: string
): Promise<string> {
// Create the destination folder if it doesn't exist
await io.mkdirP(destinationFolder);
const mavenFile = path.normalize(repoRoot);
const stats = fs.statSync(mavenFile);
if (stats.isFile()) {
await extractFiles(path.resolve(mavenFile), fileEnding, destinationFolder);
const mavenDirectory = path.join(
destinationFolder,
fs.readdirSync(destinationFolder)[0]
);
return mavenDirectory;
} else {
throw new Error(`Maven argument ${mavenFile} is not a file`);
}
}
function getDownloadInfo(
refs: string[],
version: string,
mavenMirror: string
): {version: string; url: string} {
version = normalizeVersion(version);
let extension = '';
if (IS_WINDOWS) {
extension = `.zip`;
} else {
extension = `.tar.gz`;
}
// Maps version to url
let versionMap = new Map();
// Filter by platform
refs.forEach(ref => {
if (semver.satisfies(ref, version)) {
core.debug(`VersionMap add ${ref} ${version}`);
versionMap.set(
ref,
`${mavenMirror}${ref}/binaries/apache-maven-${ref}-bin${extension}`
);
}
});
// Choose the most recent satisfying version
let curVersion = '0.0.0';
let curUrl = '';
for (const entry of versionMap.entries()) {
const entryVersion = entry[0];
const entryUrl = entry[1];
core.debug(`VersionMap Entry ${entryVersion} ${entryUrl}`);
if (semver.gt(entryVersion, curVersion)) {
core.debug(`VersionMap semver gt ${entryVersion} ${entryUrl}`);
curUrl = entryUrl;
curVersion = entryVersion;
}
}
if (curUrl == '') {
throw new Error(
`No valid download found for version ${version}. Check ${mavenMirror} for a list of valid versions or download your own maven file and add the mavenFile argument`
);
}
return {version: curVersion, url: curUrl};
}
function normalizeVersion(version: string): string {
if (version.slice(0, 2) === '1.') {
// Trim leading 1. for versions like 1.8
version = version.slice(2);
if (!version) {
throw new Error('1. is not a valid version');
}
}
if (version.split('.').length < 3) {
// Add trailing .x if it is missing
if (version[version.length - 1] != 'x') {
version = version + '.x';
}
}
return version;
}

View file

@ -1,5 +1,7 @@
import * as core from '@actions/core';
import * as installer from './installer';
import * as mavenInstaller from './maven-installer';
import * as gradleInstaller from './gradle-installer';
import * as auth from './auth';
import * as path from 'path';
@ -15,6 +17,19 @@ async function run() {
await installer.getJava(version, arch, jdkFile, javaPackage);
const mavenVersion = core.getInput('maven-version', {required: false});
const mavenFile = core.getInput('maven-file', {required: false}) || '';
const mavenMirror = core.getInput('maven-mirror', {required: false});
if (mavenVersion) {
await mavenInstaller.getMaven(mavenVersion, mavenFile, mavenMirror);
}
const gradleVersion = core.getInput('gradle-version', {required: false});
const gradleFile = core.getInput('gradle-file', {required: false}) || '';
if (gradleVersion) {
await gradleInstaller.getGradle(gradleVersion, gradleFile);
}
const matchersPath = path.join(__dirname, '..', '.github');
console.log(`##[add-matcher]${path.join(matchersPath, 'java.json')}`);