From 44cc5df636740ceaef2b3f31155daa05699a3348 Mon Sep 17 00:00:00 2001 From: David Staheli Date: Fri, 13 May 2016 10:58:27 -0400 Subject: [PATCH 1/3] Refactor Maven TypeScript version. Also, when Maven version is set to 'Default', first look for Maven in the M2_HOME env var path. If not found there, look in the system path. --- Tasks/Maven/maventask.ts | 297 ++++++++++-------- Tasks/Maven/task.json | 2 +- Tasks/Maven/task.loc.json | 2 +- Tests/L0/Maven/_suite.ts | 95 ++++-- .../Maven/{mavenGood.json => response.json} | 0 Tests/L0/Maven/responseM2_HOME.json | 68 ++++ 6 files changed, 297 insertions(+), 167 deletions(-) rename Tests/L0/Maven/{mavenGood.json => response.json} (100%) create mode 100644 Tests/L0/Maven/responseM2_HOME.json diff --git a/Tasks/Maven/maventask.ts b/Tasks/Maven/maventask.ts index 03e772ba4500..2b55c85ba328 100644 --- a/Tasks/Maven/maventask.ts +++ b/Tasks/Maven/maventask.ts @@ -3,97 +3,181 @@ import tl = require('vsts-task-lib/task'); import path = require('path'); -var mvntool = ''; -var mavenVersionSelection = tl.getInput('mavenVersionSelection', true); +var mavenPOMFile: string = tl.getPathInput('mavenPOMFile', true, true); +var javaHomeSelection: string = tl.getInput('javaHomeSelection', true); +var mavenVersionSelection: string = tl.getInput('mavenVersionSelection', true); +var mavenGoals: string[] = tl.getDelimitedInput('goals', ' ', true); // This assumes that goals cannot contain spaces +var mavenOptions: string = tl.getInput('options', false); // Options can have spaces and quotes so we need to treat this as one string and not try to parse it +var publishJUnitResults: string = tl.getInput('publishJUnitResults'); +var testResultsFiles: string = tl.getInput('testResultsFiles', true); + +// Determine the version and path of Maven to use +var mvnExec: string = ''; if (mavenVersionSelection == 'Path') { - tl.debug('Using maven path from user input'); + // The path to Maven has been explicitly specified + tl.debug('Using Maven path from user input'); var mavenPath = tl.getPathInput('mavenPath', true, true); - mvntool = path.join(mavenPath, 'bin/mvn'); + mvnExec = path.join(mavenPath, 'bin/mvn'); + // Set the M2_HOME variable to a custom Maven installation path? if (tl.getBoolInput('mavenSetM2Home')) { - tl.setEnvVar("M2_HOME", mavenPath); + tl.setEnvVar('M2_HOME', mavenPath); tl.debug('M2_HOME set to ' + mavenPath) } -} else { - tl.debug('Using maven from standard system path'); - mvntool = tl.which('mvn', true); } -tl.debug('Maven binary: ' + mvntool); - -var mavenPOMFile = tl.getPathInput('mavenPOMFile', true, true); -var mavenOptions = tl.getInput('options', false); // options can have spaces and quotes so we need to treat this as one string and not try to parse it -var mavenGoals = tl.getDelimitedInput('goals', ' ', true); // This assumes that goals cannot contain spaces - -var mvnv = tl.createToolRunner(mvntool); -mvnv.arg('-version'); - -var mvnb = tl.createToolRunner(mvntool); -mvnb.arg('-f'); -mvnb.pathArg(mavenPOMFile); -mvnb.argString(mavenOptions); -mvnb.arg(mavenGoals); - -// update JAVA_HOME if user selected specific JDK version or set path manually -var javaHomeSelection = tl.getInput('javaHomeSelection', true); -var specifiedJavaHome = null; +else { + // mavenVersionSelection is set to 'Default' + + // First, look for Maven in the M2_HOME variable + var m2HomeEnvVar: string = null; + m2HomeEnvVar = tl.getVariable('M2_HOME'); + if (m2HomeEnvVar) { + tl.debug('Using M2_HOME environment variable value for Maven path: ' + m2HomeEnvVar); + mvnExec = path.join(m2HomeEnvVar, 'bin/mvn'); + } + // Second, look for Maven in the system path + else { + tl.debug('M2_HOME environment variable is not set, so Maven will be sought in the system path'); + mvnExec = tl.which('mvn', true); + } +} +tl.debug('Maven executable: ' + mvnExec); +// Set JAVA_HOME to the JDK version (default, 1.7, 1.8, etc.) or the path specified by the user +var specifiedJavaHome: string = null; if (javaHomeSelection == 'JDKVersion') { - tl.debug('Using JDK version to find and set JAVA_HOME'); - var jdkVersion = tl.getInput('jdkVersion'); - var jdkArchitecture = tl.getInput('jdkArchitecture'); + // Set JAVA_HOME to the specified JDK version (default, 1.7, 1.8, etc.) + tl.debug('Using the specified JDK version to find and set JAVA_HOME'); + var jdkVersion: string = tl.getInput('jdkVersion'); + var jdkArchitecture: string = tl.getInput('jdkArchitecture'); if (jdkVersion != 'default') { // jdkVersion should be in the form of 1.7, 1.8, or 1.10 // jdkArchitecture is either x64 or x86 // envName for version 1.7 and x64 would be "JAVA_HOME_7_X64" - var envName = "JAVA_HOME_" + jdkVersion.slice(2) + "_" + jdkArchitecture.toUpperCase(); + var envName: string = "JAVA_HOME_" + jdkVersion.slice(2) + "_" + jdkArchitecture.toUpperCase(); specifiedJavaHome = tl.getVariable(envName); if (!specifiedJavaHome) { tl.error('Failed to find specified JDK version. Please make sure environment variable ' + envName + ' exists and is set to the location of a corresponding JDK.'); tl.exit(1); } } -} else { - tl.debug('Using path from user input to set JAVA_HOME'); - var jdkUserInputPath = tl.getPathInput('jdkUserInputPath', true, true); +} +else { + // Set JAVA_HOME to the path specified by the user + tl.debug('Setting JAVA_HOME to the path specified by user input'); + var jdkUserInputPath: string = tl.getPathInput('jdkUserInputPath', true, true); specifiedJavaHome = jdkUserInputPath; } +// Set JAVA_HOME as determined above (if different than default) if (specifiedJavaHome) { tl.debug('Set JAVA_HOME to ' + specifiedJavaHome); process.env['JAVA_HOME'] = specifiedJavaHome; } -var publishJUnitResults = tl.getInput('publishJUnitResults'); -var testResultsFiles = tl.getInput('testResultsFiles', true); +// Maven task orchestration occurs as follows: +// 1. Check that Maven exists by executing it to retrieve its version. +// 2. Run Maven with the user goals. Compilation or test errors will cause this to fail. +// 3. Always try to run the SonarQube analysis if it is enabled. +// In case the build has failed, the analysis will still succeed but the report will have less data. +// 4. Always publish test results even if tests fail, causing this task to fail. +// 5. If #2 above failed, exit with an error code to mark the entire step as failed. Same for #4. -function publishTestResults(publishJUnitResults, testResultsFiles: string) { - var matchingTestResultsFiles: string[] = undefined; +var userRunFailed: boolean = false; +var sonarQubeRunFailed: boolean = false; + +// Setup tool runner that executes Maven only to retrieve its version +var mvnGetVersion = tl.createToolRunner(mvnExec); +mvnGetVersion.arg('-version'); + +// 1. Check that Maven exists by executing it to retrieve its version. +mvnGetVersion.exec() +.fail(function (err) { + console.error("Maven is not installed on the agent"); + tl.exit(1); // tl.exit sets the step result but does not stop execution + process.exit(1); +}) +.then(function (code) { + // Setup tool runner to execute Maven goals + var mvnRun = tl.createToolRunner(mvnExec); + mvnRun.arg('-f'); + mvnRun.pathArg(mavenPOMFile); + mvnRun.argString(mavenOptions); + mvnRun.arg(mavenGoals); + + // Read Maven standard output + mvnRun.on('stdout', function (data) { + processMavenOutput(data); + }); + + // 2. Run Maven with the user goals. Compilation or test errors will cause this to fail. + return mvnRun.exec(); // Run Maven with the user specified goals +}) +.fail(function (err) { + console.error(err.message); + userRunFailed = true; // Record the error and continue +}) +.then(function (code) { + // 3. Always try to run the SonarQube analysis if it is enabled. + var mvnsq = getSonarQubeRunner(); + if (mvnsq) { + // Run Maven with the sonar:sonar goal, even if the user-goal Maven failed (e.g. test failures). + // Note that running sonar:sonar along with the user goals is not supported due to a SonarQube bug. + return mvnsq.exec() + } +}) +.fail(function (err) { + console.error(err.message); + console.error("SonarQube analysis failed"); + sonarQubeRunFailed = true; +}) +.then(function () { + // 4. Always publish test results even if tests fail, causing this task to fail. if (publishJUnitResults == 'true') { - //check for pattern in testResultsFiles - if (testResultsFiles.indexOf('*') >= 0 || testResultsFiles.indexOf('?') >= 0) { - tl.debug('Pattern found in testResultsFiles parameter'); - var buildFolder = tl.getVariable('agent.buildDirectory'); - tl.debug(`buildFolder=${buildFolder}`); - var allFiles = tl.find(buildFolder); - matchingTestResultsFiles = tl.match(allFiles, testResultsFiles, { - matchBase: true - }); - } else { - tl.debug('No pattern found in testResultsFiles parameter'); - matchingTestResultsFiles = [testResultsFiles]; - } + publishTestResults(testResultsFiles); + } + + // Set overall success or failure + if (userRunFailed || sonarQubeRunFailed) { + tl.exit(1); // Set task failure + } + else { + tl.exit(0); // Set task success + } - if (!matchingTestResultsFiles || matchingTestResultsFiles.length == 0) { - tl.warning('No test result files matching ' + testResultsFiles + ' were found, so publishing JUnit test results is being skipped.'); - return 0; - } - - var tp = new tl.TestPublisher("JUnit"); - tp.publish(matchingTestResultsFiles, true, "", "", "", true); + // Do not force an exit as publishing results is async and it won't have finished +}) + +// Publishes test results from files matching the specified pattern. +function publishTestResults(testResultsFiles: string) { + var matchingTestResultsFiles: string[] = undefined; + + // Check for pattern in testResultsFiles + if (testResultsFiles.indexOf('*') >= 0 || testResultsFiles.indexOf('?') >= 0) { + tl.debug('Pattern found in testResultsFiles parameter'); + var buildFolder = tl.getVariable('agent.buildDirectory'); + tl.debug(`buildFolder=${buildFolder}`); + var allFiles = tl.find(buildFolder); + matchingTestResultsFiles = tl.match(allFiles, testResultsFiles, { + matchBase: true + }); } + else { + tl.debug('No pattern found in testResultsFiles parameter'); + matchingTestResultsFiles = [testResultsFiles]; + } + + if (!matchingTestResultsFiles || matchingTestResultsFiles.length == 0) { + tl.warning('No test result files matching ' + testResultsFiles + ' were found, so publishing JUnit test results is being skipped.'); + return 0; + } + + var tp = new tl.TestPublisher("JUnit"); + tp.publish(matchingTestResultsFiles, true, "", "", "", true); } +// Gets the SonarQube tool runner if SonarQube analysis is enabled. function getSonarQubeRunner() { if (!tl.getBoolInput('sqAnalysisEnabled')) { console.log("SonarQube analysis is not enabled"); @@ -102,15 +186,16 @@ function getSonarQubeRunner() { console.log("SonarQube analysis is enabled"); var mvnsq; - var sqEndpoint = getEndpointDetails("sqConnectedServiceName"); + var sqEndpoint = getSonarQubeEndpointDetails("sqConnectedServiceName"); if (tl.getBoolInput('sqDbDetailsRequired')) { var sqDbUrl = tl.getInput('sqDbUrl', false); var sqDbUsername = tl.getInput('sqDbUsername', false); var sqDbPassword = tl.getInput('sqDbPassword', false); - mvnsq = createMavenSQRunner(sqEndpoint.Url, sqEndpoint.Username, sqEndpoint.Password, sqDbUrl, sqDbUsername, sqDbPassword); - } else { - mvnsq = createMavenSQRunner(sqEndpoint.Url, sqEndpoint.Username, sqEndpoint.Password); + mvnsq = createMavenSonarQubeRunner(sqEndpoint.Url, sqEndpoint.Username, sqEndpoint.Password, sqDbUrl, sqDbUsername, sqDbPassword); + } + else { + mvnsq = createMavenSonarQubeRunner(sqEndpoint.Url, sqEndpoint.Username, sqEndpoint.Password); } mvnsq.arg('-f'); @@ -121,7 +206,8 @@ function getSonarQubeRunner() { return mvnsq; } -function getEndpointDetails(inputFieldName) { +// Gets SonarQube connection endpoint details. +function getSonarQubeEndpointDetails(inputFieldName) { var errorMessage = "Could not decode the generic endpoint. Please ensure you are running the latest agent (min version 0.3.2)" if (!tl.getEndpointUrl) { throw new Error(errorMessage); @@ -139,8 +225,8 @@ function getEndpointDetails(inputFieldName) { // Currently the username and the password are required, but in the future they will not be mandatory // - so not validating the values here - var hostUsername = getAuthParameter(genericEndpoint, 'username'); - var hostPassword = getAuthParameter(genericEndpoint, 'password'); + var hostUsername = getSonarQubeAuthParameter(genericEndpoint, 'username'); + var hostPassword = getSonarQubeAuthParameter(genericEndpoint, 'password'); tl.debug("hostUsername: " + hostUsername); return { @@ -150,10 +236,11 @@ function getEndpointDetails(inputFieldName) { }; } +// Gets a SonarQube authentication parameter from the specified connection endpoint. // The endpoint stores the auth details as JSON. Unfortunately the structure of the JSON has changed through time, namely the keys were sometimes upper-case. // To work around this, we can perform case insensitive checks in the property dictionary of the object. Note that the PowerShell implementation does not suffer from this problem. // See https://github.com/Microsoft/vso-agent/blob/bbabbcab3f96ef0cfdbae5ef8237f9832bef5e9a/src/agent/plugins/release/artifact/jenkinsArtifact.ts for a similar implementation -function getAuthParameter(endpoint, paramName) { +function getSonarQubeAuthParameter(endpoint, paramName) { var paramValue = null; var auth = tl.getEndpointAuthorization(endpoint, false); @@ -179,8 +266,9 @@ function getAuthParameter(endpoint, paramName) { return paramValue; } -function createMavenSQRunner(sqHostUrl, sqHostUsername, sqHostPassword, sqDbUrl?, sqDbUsername?, sqDbPassword?) { - var mvnsq = tl.createToolRunner(mvntool); +// Creates the tool runner for executing SonarQube. +function createMavenSonarQubeRunner(sqHostUrl, sqHostUsername, sqHostPassword, sqDbUrl?, sqDbUsername?, sqDbPassword?) { + var mvnsq = tl.createToolRunner(mvnExec); mvnsq.arg('-Dsonar.host.url=' + sqHostUrl); if (sqHostUsername) { @@ -202,33 +290,34 @@ function createMavenSQRunner(sqHostUrl, sqHostUsername, sqHostPassword, sqDbUrl? return mvnsq; } +// Processes Maven output for errors and warnings and reports them to the build summary. function processMavenOutput(data) { - if(data == null) { + if (data == null) { return; } data = data.toString(); var input = data; var severity = 'NONE'; - if(data.charAt(0) === '[') { + if (data.charAt(0) === '[') { var rightIndex = data.indexOf(']'); - if(rightIndex > 0) { + if (rightIndex > 0) { severity = data.substring(1, rightIndex); if(severity === 'ERROR' || severity === 'WARNING') { - // Try to match output like + // Try to match output like: // /Users/user/agent/_work/4/s/project/src/main/java/com/contoso/billingservice/file.java:[linenumber, columnnumber] error message here // A successful match will return an array of 5 strings - full matched string, file path, line number, column number, error message input = input.substring(rightIndex + 1); + var match: any; + var matches: any[] = []; var compileErrorsRegex = /([a-zA-Z0-9_ \-\/.]+):\[([0-9]+),([0-9]+)\](.*)/g; - var matches = []; - var match; while (match = compileErrorsRegex.exec(input.toString())) { matches = matches.concat(match); } if(matches != null) { - var index = 0; + var index: number = 0; while (index + 4 < matches.length) { tl.debug('full match = ' + matches[index + 0]); tl.debug('file path = ' + matches[index + 1]); @@ -236,8 +325,8 @@ function processMavenOutput(data) { tl.debug('column number = ' + matches[index + 3]); tl.debug('message = ' + matches[index + 4]); - // task.issue is only for xplat agent and doesn't provide the sourcepath link on summary page - // we should use task.logissue when xplat agent is not used anymore so this will workon the coreCLR agent + // task.issue is only for the xplat agent and doesn't provide the sourcepath link on the summary page. + // We should use task.logissue when the xplat agent is retired so this will work on the CoreCLR agent. tl.command('task.issue', { type: severity.toLowerCase(), sourcepath: matches[index + 1], @@ -252,59 +341,3 @@ function processMavenOutput(data) { } } } - -/* -Maven task orchestration: -1. Check that maven exists -2. Run maven with the user goals. Compilation or test errors will cause this to fail -3. Always try to publish tests results -4. Always try to run the SonarQube analysis if it is enabled. In case the build has failed, the analysis -will still succeed but the report will have less data. -5. If #2 above failed, exit with an error code to mark the entire step as failed. Same for #4. -*/ - -var userRunFailed = false; -var sqRunFailed = false; - -mvnv.exec() -.fail(function (err) { - console.error("Maven is not installed on the agent"); - tl.exit(1); // tl.exit sets the step result but does not stop execution - process.exit(1); -}) -.then(function (code) { - //read maven stdout - mvnb.on('stdout', function (data) { - processMavenOutput(data); - }); - return mvnb.exec(); // run Maven with the user specified goals -}) -.fail(function (err) { - console.error(err.message); - userRunFailed = true; // record the error and continue -}) -.then(function (code) { - var mvnsq = getSonarQubeRunner(); - - if (mvnsq) { - // run Maven with the sonar:sonar goal, even if the user-goal Maven failed (e.g. test failures) - // note that running sonar:sonar along with the user goals is not supported due to a SonarQube bug - return mvnsq.exec() - } -}) -.fail(function (err) { - console.error(err.message); - console.error("SonarQube analysis failed"); - sqRunFailed = true; -}) -.then(function () { - // publish test results even if tests fail, causing Maven to fail; - publishTestResults(publishJUnitResults, testResultsFiles); - if (userRunFailed || sqRunFailed) { - tl.exit(1); // mark task failure - } else { - tl.exit(0); // mark task success - } - - // do not force an exit as publishing results is async and it won't have finished -}) diff --git a/Tasks/Maven/task.json b/Tasks/Maven/task.json index f8835a9237a2..1e20145a020e 100644 --- a/Tasks/Maven/task.json +++ b/Tasks/Maven/task.json @@ -16,7 +16,7 @@ "version": { "Major": 1, "Minor": 0, - "Patch": 48 + "Patch": 49 }, "minimumAgentVersion": "1.89.0", "instanceNameFormat": "Maven $(mavenPOMFile)", diff --git a/Tasks/Maven/task.loc.json b/Tasks/Maven/task.loc.json index 76a0ea4ac023..e1a487a2ff99 100644 --- a/Tasks/Maven/task.loc.json +++ b/Tasks/Maven/task.loc.json @@ -16,7 +16,7 @@ "version": { "Major": 1, "Minor": 0, - "Patch": 48 + "Patch": 49 }, "minimumAgentVersion": "1.89.0", "instanceNameFormat": "ms-resource:loc.instanceNameFormat", diff --git a/Tests/L0/Maven/_suite.ts b/Tests/L0/Maven/_suite.ts index 7459f0d51e72..9fecffc6d7a7 100644 --- a/Tests/L0/Maven/_suite.ts +++ b/Tests/L0/Maven/_suite.ts @@ -21,11 +21,11 @@ describe('maven Suite', function() { }); - it('run maven with all default inputs', (done) => { - setResponseFile('mavenGood.json'); + it('run maven with all default inputs and M2_HOME not set', (done) => { + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', ''); tr.setInput('goals', 'package'); @@ -48,12 +48,41 @@ describe('maven Suite', function() { done(err); }); }) + + it('run maven with all default inputs and M2_HOME set', (done) => { + setResponseFile('responseM2_HOME.json'); + + var tr = new trm.TaskRunner('maven', true); + tr.setInput('mavenVersionSelection', 'Default'); + tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file + tr.setInput('options', ''); + tr.setInput('goals', 'package'); + tr.setInput('javaHomeSelection', 'JDKVersion'); + tr.setInput('jdkVersion', 'default'); + tr.setInput('publishJUnitResults', 'true'); + tr.setInput('testResultsFiles', '**/TEST-*.xml'); + process.env['M2_HOME'] = '/anotherHome/bin/maven/bin/mvn'; + + tr.run() + .then(() => { + assert(tr.ran('/home/bin/maven/bin/mvn -version'), 'it should have run mvn -version'); + assert(tr.ran('/home/bin/maven/bin/mvn -f pom.xml package'), 'it should have run mvn -f pom.xml package'); + assert(tr.invokedToolCount == 2, 'should have only run maven 2 times'); + assert(tr.resultWasSet, 'task should have set a result'); + assert(tr.stderr.length == 0, 'should not have written to stderr'); + assert(tr.succeeded, 'task should have succeeded'); + done(); + }) + .fail((err) => { + done(err); + }); + }) it('run maven with missing mavenVersionSelection', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); - //tr.setInput('mavenVersionSelection', 'default'); + //tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', ''); tr.setInput('goals', 'package'); @@ -77,7 +106,7 @@ describe('maven Suite', function() { }) it('run maven with INVALID mavenVersionSelection', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); tr.setInput('mavenVersionSelection', 'garbage'); @@ -105,7 +134,7 @@ describe('maven Suite', function() { }) it('run maven with mavenVersionSelection set to Path (mavenPath valid)', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); tr.setInput('mavenVersionSelection', 'Path'); @@ -134,7 +163,7 @@ describe('maven Suite', function() { }) it('run maven with mavenVersionSelection set to Path (mavenPath missing)', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); tr.setInput('mavenVersionSelection', 'Path'); @@ -161,7 +190,7 @@ describe('maven Suite', function() { }) it('run maven with mavenVersionSelection set to Path (mavenPath INVALID)', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); tr.setInput('mavenVersionSelection', 'Path'); @@ -189,7 +218,7 @@ describe('maven Suite', function() { }) it('run maven with mavenSetM2Home set to garbage', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); tr.setInput('mavenVersionSelection', 'Path'); @@ -219,7 +248,7 @@ describe('maven Suite', function() { }) it('run maven with mavenSetM2Home set to true', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); tr.setInput('mavenVersionSelection', 'Path'); @@ -250,10 +279,10 @@ describe('maven Suite', function() { }) it('run maven with options set', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', '/o /p "/t:i o" /n /s'); tr.setInput('goals', 'package'); @@ -278,10 +307,10 @@ describe('maven Suite', function() { }) it('run maven with goals not set', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', '/o /p /t /i /o /n /s'); //tr.setInput('goals', 'package'); @@ -305,10 +334,10 @@ describe('maven Suite', function() { }) it('run maven with tasks set to multiple', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', '/o /p /t /i /o /n /s'); tr.setInput('goals', 'build test package'); @@ -333,10 +362,10 @@ describe('maven Suite', function() { }) it('run maven with missing publishJUnitResults input', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', '/o /p /t /i /o /n /s'); tr.setInput('goals', 'build test package'); @@ -361,7 +390,7 @@ describe('maven Suite', function() { it('run maven with publishJUnitResults set to "garbage"', (done) => { var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', '/o /p /t /i /o /n /s'); tr.setInput('goals', 'build test package'); @@ -387,7 +416,7 @@ describe('maven Suite', function() { it('run maven and publish tests', (done) => { var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', ''); tr.setInput('goals', 'package'); @@ -413,10 +442,10 @@ describe('maven Suite', function() { }) it('fails if missing testResultsFiles input', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', ''); tr.setInput('goals', 'build test package'); @@ -439,10 +468,10 @@ describe('maven Suite', function() { }) it('fails if missing javaHomeSelection input', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', ''); tr.setInput('goals', 'package'); @@ -466,10 +495,10 @@ describe('maven Suite', function() { }) it('run maven with jdkVersion set to 1.8', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', ''); tr.setInput('goals', 'package'); @@ -496,10 +525,10 @@ describe('maven Suite', function() { }) it('run maven with jdkVersion set to 1.5', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', ''); tr.setInput('goals', 'package'); @@ -524,10 +553,10 @@ describe('maven Suite', function() { }) it('run maven with Valid inputs but it fails', (done) => { - setResponseFile('mavenGood.json'); + setResponseFile('response.json'); var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', ''); tr.setInput('goals', 'FAIL package'); @@ -554,7 +583,7 @@ describe('maven Suite', function() { it('run maven including SonarQube analysis', (done) => { var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', ''); tr.setInput('goals', 'package'); @@ -583,7 +612,7 @@ describe('maven Suite', function() { it('run maven including SonarQube analysis (with db details)', (done) => { var tr = new trm.TaskRunner('maven', true); - tr.setInput('mavenVersionSelection', 'default'); + tr.setInput('mavenVersionSelection', 'Default'); tr.setInput('mavenPOMFile', 'pom.xml'); // Make that checkPath returns true for this filename in the response file tr.setInput('options', ''); tr.setInput('goals', 'package'); diff --git a/Tests/L0/Maven/mavenGood.json b/Tests/L0/Maven/response.json similarity index 100% rename from Tests/L0/Maven/mavenGood.json rename to Tests/L0/Maven/response.json diff --git a/Tests/L0/Maven/responseM2_HOME.json b/Tests/L0/Maven/responseM2_HOME.json new file mode 100644 index 000000000000..22960742fce3 --- /dev/null +++ b/Tests/L0/Maven/responseM2_HOME.json @@ -0,0 +1,68 @@ +{ + "exec": { + "/home/bin/maven/bin/mvn -version": { + "code": 0, + "stdout": "Maven version 1.0.0" + }, + "/home/bin/maven/bin/mvn -f pom.xml package": { + "code": 0, + "stdout": "Maven package done" + }, + "/home/bin/maven2/bin/mvn -version": { + "code": 0, + "stdout": "Maven version 1.0.0" + }, + "/home/bin/maven2/bin/mvn -f pom.xml package": { + "code": 0, + "stdout": "Maven package done" + }, + "/home/bin/maven/bin/mvn -f pom.xml /o /p /t:i o /n /s package": { + "code": 0, + "stdout": "Maven package done" + }, + "/home/bin/maven/bin/mvn -f pom.xml /o /p /t /i /o /n /s package": { + "code": 0, + "stdout": "Maven package done" + }, + "/home/bin/maven/bin/mvn -f pom.xml /o /p /t /i /o /n /s build test package": { + "code": 0, + "stdout": "Maven package done" + }, + "/home/bin/maven/bin/mvn -f pom.xml FAIL package": { + "code": 2, + "stdout": "Running Maven...", + "stderr": "FAILED" + }, + "/home/bin/maven/bin/mvn -Dsonar.host.url=http://sonarqube/end/point -Dsonar.login=uname -Dsonar.password=pword -f pom.xml sonar:sonar":{ + "code": 0, + "stdout": "Maven package and SQ analysis done" + }, + "/home/bin/maven/bin/mvn -Dsonar.host.url=http://sonarqube/end/point -Dsonar.login=uname -Dsonar.password=pword -Dsonar.jdbc.url=dbURL -Dsonar.jdbc.username=dbUser -Dsonar.jdbc.password=dbPass -f pom.xml sonar:sonar":{ + "code": 0, + "stdout": "Maven package and SQ analysis done" + } + }, + "checkPath" : { + "/home/bin/maven": true, + "/home/bin/maven/bin/mvn": true, + "/home/bin/maven2": true, + "pom.xml": true + }, + "getVariable" : { + "M2_HOME": "/home/bin/maven", + "JAVA_HOME_8_X86": "/user/local/bin/Java8", + "agent.buildDirectory": "/user/build", + "ENDPOINT_URL_ID1": "http://sonarqube/end/point", + "ENDPOINT_AUTH_ID1": "{\"scheme\":\"UsernamePassword\", \"parameters\": {\"username\": \"uname\", \"password\": \"pword\"}}" + }, + "find": { + "/user/build": [ + "/user/build/fun/test-123.xml" + ] + }, + "match": { + "**/TEST-*.xml": [ + "/user/build/fun/test-123.xml" + ] + } +} \ No newline at end of file From 24a2f5c02bed273bed5a21ed9f4a32284e2c5498 Mon Sep 17 00:00:00 2001 From: David Staheli Date: Fri, 13 May 2016 13:13:12 -0400 Subject: [PATCH 2/3] Separate args passed to path.join() --- Tasks/Maven/maventask.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Tasks/Maven/maventask.ts b/Tasks/Maven/maventask.ts index 2b55c85ba328..8ba87611853d 100644 --- a/Tasks/Maven/maventask.ts +++ b/Tasks/Maven/maventask.ts @@ -17,7 +17,7 @@ if (mavenVersionSelection == 'Path') { // The path to Maven has been explicitly specified tl.debug('Using Maven path from user input'); var mavenPath = tl.getPathInput('mavenPath', true, true); - mvnExec = path.join(mavenPath, 'bin/mvn'); + mvnExec = path.join(mavenPath, 'bin', 'mvn'); // Set the M2_HOME variable to a custom Maven installation path? if (tl.getBoolInput('mavenSetM2Home')) { @@ -33,7 +33,7 @@ else { m2HomeEnvVar = tl.getVariable('M2_HOME'); if (m2HomeEnvVar) { tl.debug('Using M2_HOME environment variable value for Maven path: ' + m2HomeEnvVar); - mvnExec = path.join(m2HomeEnvVar, 'bin/mvn'); + mvnExec = path.join(m2HomeEnvVar, 'bin', 'mvn'); } // Second, look for Maven in the system path else { @@ -52,13 +52,13 @@ if (javaHomeSelection == 'JDKVersion') { var jdkArchitecture: string = tl.getInput('jdkArchitecture'); if (jdkVersion != 'default') { - // jdkVersion should be in the form of 1.7, 1.8, or 1.10 - // jdkArchitecture is either x64 or x86 - // envName for version 1.7 and x64 would be "JAVA_HOME_7_X64" + // jdkVersion must be in the form of "1.7", "1.8", or "1.10" + // jdkArchitecture is either "x64" or "x86" + // envName for version=1.7 and architecture=x64 would be "JAVA_HOME_7_X64" var envName: string = "JAVA_HOME_" + jdkVersion.slice(2) + "_" + jdkArchitecture.toUpperCase(); specifiedJavaHome = tl.getVariable(envName); if (!specifiedJavaHome) { - tl.error('Failed to find specified JDK version. Please make sure environment variable ' + envName + ' exists and is set to the location of a corresponding JDK.'); + tl.error('Failed to find specified JDK version. Make sure environment variable ' + envName + ' exists and is set to the location of a corresponding JDK.'); tl.exit(1); } } @@ -135,7 +135,7 @@ mvnGetVersion.exec() .then(function () { // 4. Always publish test results even if tests fail, causing this task to fail. if (publishJUnitResults == 'true') { - publishTestResults(testResultsFiles); + publishJUnitTestResults(testResultsFiles); } // Set overall success or failure @@ -149,9 +149,9 @@ mvnGetVersion.exec() // Do not force an exit as publishing results is async and it won't have finished }) -// Publishes test results from files matching the specified pattern. -function publishTestResults(testResultsFiles: string) { - var matchingTestResultsFiles: string[] = undefined; +// Publishes JUnit test results from files matching the specified pattern. +function publishJUnitTestResults(testResultsFiles: string) { + var matchingJUnitResultFiles: string[] = undefined; // Check for pattern in testResultsFiles if (testResultsFiles.indexOf('*') >= 0 || testResultsFiles.indexOf('?') >= 0) { @@ -159,22 +159,22 @@ function publishTestResults(testResultsFiles: string) { var buildFolder = tl.getVariable('agent.buildDirectory'); tl.debug(`buildFolder=${buildFolder}`); var allFiles = tl.find(buildFolder); - matchingTestResultsFiles = tl.match(allFiles, testResultsFiles, { + matchingJUnitResultFiles = tl.match(allFiles, testResultsFiles, { matchBase: true }); } else { tl.debug('No pattern found in testResultsFiles parameter'); - matchingTestResultsFiles = [testResultsFiles]; + matchingJUnitResultFiles = [testResultsFiles]; } - if (!matchingTestResultsFiles || matchingTestResultsFiles.length == 0) { + if (!matchingJUnitResultFiles || matchingJUnitResultFiles.length == 0) { tl.warning('No test result files matching ' + testResultsFiles + ' were found, so publishing JUnit test results is being skipped.'); return 0; } var tp = new tl.TestPublisher("JUnit"); - tp.publish(matchingTestResultsFiles, true, "", "", "", true); + tp.publish(matchingJUnitResultFiles, true, "", "", "", true); } // Gets the SonarQube tool runner if SonarQube analysis is enabled. From 6559fa80afe0cd827bbc6f3f911aa09ca2f5592f Mon Sep 17 00:00:00 2001 From: David Staheli Date: Fri, 13 May 2016 13:21:42 -0400 Subject: [PATCH 3/3] Set env vars with tl.setVariable() --- Tasks/Maven/maventask.ts | 6 ++---- Tests/L0/Maven/_suite.ts | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Tasks/Maven/maventask.ts b/Tasks/Maven/maventask.ts index 8ba87611853d..cf64affa9303 100644 --- a/Tasks/Maven/maventask.ts +++ b/Tasks/Maven/maventask.ts @@ -21,8 +21,7 @@ if (mavenVersionSelection == 'Path') { // Set the M2_HOME variable to a custom Maven installation path? if (tl.getBoolInput('mavenSetM2Home')) { - tl.setEnvVar('M2_HOME', mavenPath); - tl.debug('M2_HOME set to ' + mavenPath) + tl.setVariable('M2_HOME', mavenPath); } } else { @@ -72,8 +71,7 @@ else { // Set JAVA_HOME as determined above (if different than default) if (specifiedJavaHome) { - tl.debug('Set JAVA_HOME to ' + specifiedJavaHome); - process.env['JAVA_HOME'] = specifiedJavaHome; + tl.setVariable('JAVA_HOME', specifiedJavaHome); } // Maven task orchestration occurs as follows: diff --git a/Tests/L0/Maven/_suite.ts b/Tests/L0/Maven/_suite.ts index 9fecffc6d7a7..553149742fd7 100644 --- a/Tests/L0/Maven/_suite.ts +++ b/Tests/L0/Maven/_suite.ts @@ -270,7 +270,7 @@ describe('maven Suite', function() { assert(tr.resultWasSet, 'task should have set a result'); assert(tr.stderr.length == 0, 'should not have written to stderr'); assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf('M2_HOME set to /home/bin/maven2') >= 0, 'M2_HOME not set'); + assert(tr.stdout.indexOf('set M2_HOME=/home/bin/maven2') >= 0, 'M2_HOME not set'); done(); }) .fail((err) => { @@ -516,7 +516,7 @@ describe('maven Suite', function() { assert(tr.resultWasSet, 'task should have set a result'); assert(tr.stderr.length == 0, 'should not have written to stderr'); assert(tr.succeeded, 'task should have succeeded'); - assert(tr.stdout.indexOf('Set JAVA_HOME to /user/local/bin/Java8') >= 0, 'JAVA_HOME not set correctly'); + assert(tr.stdout.indexOf('set JAVA_HOME=/user/local/bin/Java8') >= 0, 'JAVA_HOME not set correctly'); done(); }) .fail((err) => {