Implement feature versionSpec using SemVer for setup Maven

This commit is contained in:
Thach Nguyen 2022-07-29 04:37:38 +07:00
parent c970051976
commit 7a818935c0
10 changed files with 319 additions and 58 deletions

1
.gitignore vendored
View File

@ -16,6 +16,7 @@ lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html) # Diagnostic reports (https://nodejs.org/api/report.html)
report.*.*.*.*.json report.*.*.*.*.json
.scannerwork/
# Runtime data # Runtime data
pids pids

View File

@ -10,7 +10,7 @@ jobs:
- name: Set up Maven - name: Set up Maven
uses: stCarolas/setup-maven@v5 uses: stCarolas/setup-maven@v5
with: with:
maven-version: 3.8.2 maven-version: 3.8
``` ```
### Development using [Docker](https://docs.docker.com/) ### Development using [Docker](https://docs.docker.com/)
@ -41,5 +41,5 @@ Run `SonarScanner` from [the Docker image](https://hub.docker.com/r/sonarsource/
```batch ```batch
docker run --rm -it --link docker-sonarqube -v "%PWD%:/usr/src/app" -w /usr/src/app ^ docker run --rm -it --link docker-sonarqube -v "%PWD%:/usr/src/app" -w /usr/src/app ^
-e "SONAR_HOST_URL=http://docker-sonarqube:9000" -e "SONAR_LOGIN=<projectToken>" sonarsource/sonar-scanner-cli ^ -e "SONAR_HOST_URL=http://docker-sonarqube:9000" -e "SONAR_LOGIN=<projectToken>" sonarsource/sonar-scanner-cli ^
-Dsonar.projectKey=setup-maven -Dsonar.language=js -Dsonar.sources=. "-Dsonar.exclusions=dist/**" -Dsonar.projectKey=setup-maven -Dsonar.sources=. "-Dsonar.exclusions=dist/**,lib/**"
``` ```

View File

@ -1,5 +1,10 @@
describe('maven installer tests', () => { import * as installer from '../src/installer';
it('square root of 4 to equal 2', () => {
expect(Math.sqrt(4)).toBe(2); describe('getAvailableVersions', () => {
it('load real available versions', async () => {
const availableVersions = await installer.getAvailableVersions();
expect(availableVersions).toBeTruthy();
expect(availableVersions).toEqual(expect.arrayContaining(['3.2.5', '3.3.3', '3.8.2']));
}); });
}); });

View File

@ -5,7 +5,10 @@ inputs:
maven-version: maven-version:
description: 'Version Spec of the version to use. Examples: 3.x, 3.1.1, >=3.8.0' description: 'Version Spec of the version to use. Examples: 3.x, 3.1.1, >=3.8.0'
required: false required: false
default: '3.8.2' default: '3'
outputs:
version:
description: 'Actual version of Apache Maven that has been installed'
runs: runs:
using: 'node16' using: 'node16'
main: 'dist/index.js' main: 'dist/index.js'

197
dist/index.js generated vendored
View File

@ -4965,37 +4965,81 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getMaven = void 0; exports.findVersionForDownload = exports.downloadMaven = exports.getAvailableVersions = exports.setupMaven = void 0;
const path = __importStar(__nccwpck_require__(17));
const core = __importStar(__nccwpck_require__(186)); const core = __importStar(__nccwpck_require__(186));
const tc = __importStar(__nccwpck_require__(784)); const tc = __importStar(__nccwpck_require__(784));
const path = __importStar(__nccwpck_require__(17)); const http_client_1 = __nccwpck_require__(925);
function getMaven(version) { const semver = __importStar(__nccwpck_require__(911));
const utils_1 = __nccwpck_require__(314);
function setupMaven(versionSpec, installedVersion) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let toolPath = tc.find('maven', version); let toolPath = tc.find('maven', versionSpec);
if (!toolPath) { let resolvedVersion = utils_1.getVersionFromToolcachePath(toolPath);
toolPath = yield downloadMaven(version); if (installedVersion) {
if (!toolPath || semver.gte(installedVersion, resolvedVersion)) {
core.info(`Use system Maven version ${installedVersion} instead of the cached one: ${resolvedVersion}`);
return installedVersion;
}
}
else if (!toolPath) {
resolvedVersion = yield findVersionForDownload(versionSpec);
toolPath = yield downloadMaven(resolvedVersion);
} }
core.addPath(path.join(toolPath, 'bin')); core.addPath(path.join(toolPath, 'bin'));
return resolvedVersion;
}); });
} }
exports.getMaven = getMaven; exports.setupMaven = setupMaven;
const DOWNLOAD_BASE_URL = 'https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven'; const DOWNLOAD_BASE_URL = 'https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven';
function downloadMaven(version) { function getAvailableVersions() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const toolDirectoryName = `apache-maven-${version}`; const resourceUrl = `${DOWNLOAD_BASE_URL}/maven-metadata.xml`;
const downloadUrl = `${DOWNLOAD_BASE_URL}/${version}/${toolDirectoryName}-bin.tar.gz`; const http = new http_client_1.HttpClient('setup-maven', undefined, { allowRetries: true });
core.info(`Downloading Maven ${version} from ${downloadUrl} ...`); core.info(`Downloading Maven versions manifest from ${resourceUrl} ...`);
const response = yield http.get(resourceUrl);
const body = yield response.readBody();
if (response.message.statusCode !== http_client_1.HttpCodes.OK || !body) {
throw new Error(`Unable to get available versions from ${resourceUrl}`);
}
const availableVersions = body.match(/(?<=<version>)[^<>]+(?=<\/version>)/g) || [];
core.debug(`Available Maven versions: [${availableVersions}]`);
return availableVersions;
});
}
exports.getAvailableVersions = getAvailableVersions;
/**
* Download and extract a specified Maven version to the tool-cache.
*/
function downloadMaven(fullVersion) {
return __awaiter(this, void 0, void 0, function* () {
const toolDirectoryName = `apache-maven-${fullVersion}`;
const downloadUrl = `${DOWNLOAD_BASE_URL}/${fullVersion}/${toolDirectoryName}-bin.tar.gz`;
core.info(`Downloading Maven ${fullVersion} from ${downloadUrl} ...`);
const downloadPath = yield tc.downloadTool(downloadUrl); const downloadPath = yield tc.downloadTool(downloadUrl);
const extractedPath = yield tc.extractTar(downloadPath); const extractedPath = yield tc.extractTar(downloadPath);
const toolRoot = path.join(extractedPath, toolDirectoryName); const toolRoot = path.join(extractedPath, toolDirectoryName);
return tc.cacheDir(toolRoot, 'maven', version); return tc.cacheDir(toolRoot, 'maven', fullVersion);
}); });
} }
exports.downloadMaven = downloadMaven;
function findVersionForDownload(versionSpec) {
return __awaiter(this, void 0, void 0, function* () {
const availableVersions = yield getAvailableVersions();
const resolvedVersion = semver.maxSatisfying(availableVersions, versionSpec);
if (!resolvedVersion) {
throw new Error(`Could not find satisfied version for SemVer ${versionSpec}`);
}
core.debug(`Resolved version for download: ${resolvedVersion}`);
return resolvedVersion;
});
}
exports.findVersionForDownload = findVersionForDownload;
/***/ }), /***/ }),
/***/ 587: /***/ 399:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict"; "use strict";
@ -5029,23 +5073,115 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.run = void 0;
const core = __importStar(__nccwpck_require__(186)); const core = __importStar(__nccwpck_require__(186));
const installer = __importStar(__nccwpck_require__(574)); const semver = __importStar(__nccwpck_require__(911));
const utils_1 = __nccwpck_require__(314);
const installer_1 = __nccwpck_require__(574);
function run() { function run() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
const version = core.getInput('maven-version'); const versionSpec = core.getInput('maven-version') || '3';
if (version) { if (!semver.validRange(versionSpec)) {
yield installer.getMaven(version); core.setFailed(`Invalid SemVer notation '${versionSpec}' for a Maven version`);
return;
} }
let installedVersion = yield utils_1.getActiveMavenVersion();
if (installedVersion && !semver.satisfies(installedVersion, versionSpec)) {
installedVersion = undefined;
}
installedVersion = yield installer_1.setupMaven(versionSpec, installedVersion);
core.setOutput('version', installedVersion);
} }
catch (error) { catch (error) {
core.setFailed(error.message); core.setFailed(error.toString());
} }
}); });
} }
// noinspection JSIgnoredPromiseFromCall exports.run = run;
run();
/***/ }),
/***/ 314:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getExecOutput = exports.getActiveMavenVersion = exports.getVersionFromToolcachePath = void 0;
const path = __importStar(__nccwpck_require__(17));
const core = __importStar(__nccwpck_require__(186));
const exec = __importStar(__nccwpck_require__(514));
function getVersionFromToolcachePath(toolPath) {
return !toolPath ? toolPath : path.basename(path.dirname(toolPath));
}
exports.getVersionFromToolcachePath = getVersionFromToolcachePath;
/**
* Determine version of the current used Maven.
*/
function getActiveMavenVersion() {
return __awaiter(this, void 0, void 0, function* () {
try {
const { output } = yield getExecOutput('mvn', ['-v']);
const found = output.match(/^[^\d]*(\S+)/);
const installedVersion = !found ? '' : found[1];
core.debug(`Retrieved activated Maven version: ${installedVersion}`);
return installedVersion;
}
catch (error) {
core.info(`Failed to get activated Maven version. ${error}`);
}
return undefined;
});
}
exports.getActiveMavenVersion = getActiveMavenVersion;
/**
* Exec a command and get the standard output.
*
* @throws {Error} If the exit-code is non-zero.
*/
function getExecOutput(command, args) {
return __awaiter(this, void 0, void 0, function* () {
let output = '';
const exitCode = yield exec.exec(command, args, {
silent: true,
listeners: {
stdout: (data) => (output += data.toString())
}
});
return { exitCode, output };
});
}
exports.getExecOutput = getExecOutput;
/***/ }), /***/ }),
@ -5192,12 +5328,19 @@ module.exports = require("util");
/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; /******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/";
/******/ /******/
/************************************************************************/ /************************************************************************/
/******/ var __webpack_exports__ = {};
/******/ // startup // This entry need to be wrapped in an IIFE because it need to be in strict mode.
/******/ // Load entry module and return exports (() => {
/******/ // This entry module is referenced by other modules so it can't be inlined "use strict";
/******/ var __webpack_exports__ = __nccwpck_require__(587); var exports = __webpack_exports__;
/******/ module.exports = __webpack_exports__;
/******/ Object.defineProperty(exports, "__esModule", ({ value: true }));
const main_1 = __nccwpck_require__(399);
// noinspection JSIgnoredPromiseFromCall
main_1.run();
})();
module.exports = __webpack_exports__;
/******/ })() /******/ })()
; ;

View File

@ -38,9 +38,10 @@
"typescript": "^4.2.3" "typescript": "^4.2.3"
}, },
"prettier": { "prettier": {
"printWidth": 100,
"semi": true, "semi": true,
"singleQuote": true,
"trailingComma": "none", "trailingComma": "none",
"bracketSpacing": true,
"arrowParens": "avoid" "arrowParens": "avoid"
}, },
"jest": { "jest": {

View File

@ -1,29 +1,77 @@
import * as path from 'path';
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as tc from '@actions/tool-cache'; import * as tc from '@actions/tool-cache';
import { HttpClient, HttpCodes } from '@actions/http-client';
import * as semver from 'semver';
import * as path from 'path'; import { getVersionFromToolcachePath } from './utils';
export async function getMaven(version: string) { export async function setupMaven(versionSpec: string, installedVersion?: string): Promise<string> {
let toolPath = tc.find('maven', version); let toolPath = tc.find('maven', versionSpec);
let resolvedVersion = getVersionFromToolcachePath(toolPath);
if (!toolPath) { if (installedVersion) {
toolPath = await downloadMaven(version); if (!toolPath || semver.gte(installedVersion, resolvedVersion)) {
core.info(
`Use system Maven version ${installedVersion} instead of the cached one: ${resolvedVersion}`
);
return installedVersion;
}
} else if (!toolPath) {
resolvedVersion = await findVersionForDownload(versionSpec);
toolPath = await downloadMaven(resolvedVersion);
} }
core.addPath(path.join(toolPath, 'bin')); core.addPath(path.join(toolPath, 'bin'));
return resolvedVersion;
} }
const DOWNLOAD_BASE_URL = 'https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven'; const DOWNLOAD_BASE_URL = 'https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven';
async function downloadMaven(version: string): Promise<string> { export async function getAvailableVersions(): Promise<string[]> {
const toolDirectoryName = `apache-maven-${version}`; const resourceUrl = `${DOWNLOAD_BASE_URL}/maven-metadata.xml`;
const downloadUrl = `${DOWNLOAD_BASE_URL}/${version}/${toolDirectoryName}-bin.tar.gz`; const http = new HttpClient('setup-maven', undefined, { allowRetries: true });
core.info(`Downloading Maven ${version} from ${downloadUrl} ...`); core.info(`Downloading Maven versions manifest from ${resourceUrl} ...`);
const response = await http.get(resourceUrl);
const body = await response.readBody();
if (response.message.statusCode !== HttpCodes.OK || !body) {
throw new Error(`Unable to get available versions from ${resourceUrl}`);
}
const availableVersions = body.match(/(?<=<version>)[^<>]+(?=<\/version>)/g) || [];
core.debug(`Available Maven versions: [${availableVersions}]`);
return availableVersions;
}
/**
* Download and extract a specified Maven version to the tool-cache.
*/
export async function downloadMaven(fullVersion: string): Promise<string> {
const toolDirectoryName = `apache-maven-${fullVersion}`;
const downloadUrl = `${DOWNLOAD_BASE_URL}/${fullVersion}/${toolDirectoryName}-bin.tar.gz`;
core.info(`Downloading Maven ${fullVersion} from ${downloadUrl} ...`);
const downloadPath = await tc.downloadTool(downloadUrl); const downloadPath = await tc.downloadTool(downloadUrl);
const extractedPath = await tc.extractTar(downloadPath); const extractedPath = await tc.extractTar(downloadPath);
const toolRoot = path.join(extractedPath, toolDirectoryName); const toolRoot = path.join(extractedPath, toolDirectoryName);
return tc.cacheDir(toolRoot, 'maven', version); return tc.cacheDir(toolRoot, 'maven', fullVersion);
}
export async function findVersionForDownload(versionSpec: string): Promise<string> {
const availableVersions = await getAvailableVersions();
const resolvedVersion = semver.maxSatisfying(availableVersions, versionSpec);
if (!resolvedVersion) {
throw new Error(`Could not find satisfied version for SemVer ${versionSpec}`);
}
core.debug(`Resolved version for download: ${resolvedVersion}`);
return resolvedVersion;
} }

26
src/main.ts Normal file
View File

@ -0,0 +1,26 @@
import * as core from '@actions/core';
import * as semver from 'semver';
import { getActiveMavenVersion } from './utils';
import { setupMaven } from './installer';
export async function run() {
try {
const versionSpec = core.getInput('maven-version') || '3';
if (!semver.validRange(versionSpec)) {
core.setFailed(`Invalid SemVer notation '${versionSpec}' for a Maven version`);
return;
}
let installedVersion = await getActiveMavenVersion();
if (installedVersion && !semver.satisfies(installedVersion, versionSpec)) {
installedVersion = undefined;
}
installedVersion = await setupMaven(versionSpec, installedVersion);
core.setOutput('version', installedVersion);
} catch (error) {
core.setFailed(error.toString());
}
}

View File

@ -1,17 +1,4 @@
import * as core from '@actions/core'; import { run } from './main';
import * as installer from './installer';
async function run() {
try {
const version = core.getInput('maven-version');
if (version) {
await installer.getMaven(version);
}
} catch (error) {
core.setFailed(error.message);
}
}
// noinspection JSIgnoredPromiseFromCall // noinspection JSIgnoredPromiseFromCall
run(); run();

47
src/utils.ts Normal file
View File

@ -0,0 +1,47 @@
import * as path from 'path';
import * as core from '@actions/core';
import * as exec from '@actions/exec';
export function getVersionFromToolcachePath(toolPath: string) {
return !toolPath ? toolPath : path.basename(path.dirname(toolPath));
}
/**
* Determine version of the current used Maven.
*/
export async function getActiveMavenVersion(): Promise<string | undefined> {
try {
const { output } = await getExecOutput('mvn', ['-v']);
const found = output.match(/^[^\d]*(\S+)/);
const installedVersion = !found ? '' : found[1];
core.debug(`Retrieved activated Maven version: ${installedVersion}`);
return installedVersion;
} catch (error) {
core.info(`Failed to get activated Maven version. ${error}`);
}
return undefined;
}
/**
* Exec a command and get the standard output.
*
* @throws {Error} If the exit-code is non-zero.
*/
export async function getExecOutput(
command: string,
args?: string[]
): Promise<{ exitCode: number; output: string }> {
let output = '';
const exitCode = await exec.exec(command, args, {
silent: true,
listeners: {
stdout: (data: Buffer) => (output += data.toString())
}
});
return { exitCode, output };
}