Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JavaToolInstallerV0] add Maven Toolchains declaration (#16492) #16493

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
"loc.input.help.cleanDestinationDirectory": "Select this option to clean the destination directory before JDK is extracted into it.",
"loc.input.label.createExtractDirectory": "Create directory for extracting",
"loc.input.help.createExtractDirectory": "By default, task is creating a directory similar to this JAVA_HOME_8_X64_OpenJDK_zip for extracting JDK. This option allows to disable creation of this folder, in this case, JDK will be located in the root of jdkDestinationDirectory",
"loc.input.label.createMavenToolchains": "Create Maven Toolchains declaration",
"loc.input.help.createMavenToolchains": "Create a Maven Toolchains declaration in the official default location describing the JDK that was installed with this task. Existing Maven Toolchains are modified accordingly.",
"loc.messages.DownloadFromAzureBlobStorage": "Downloading artifacts from Azure blob storage, Container Name: %s",
"loc.messages.SuccessFullyFetchedItemList": "Successfully fetched list of items",
"loc.messages.StorageAccountDoesNotExist": "Failed to get azure storage account with name %s.",
Expand Down
11 changes: 11 additions & 0 deletions Tasks/JavaToolInstallerV0/Tests/L0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,15 @@ describe('JavaToolInstaller L0 Suite', function () {

assert(testRunner.failed, 'task should have failed');
});

it('should run successfully when creating a Maven Toolchains Declaration', function () {
this.timeout(20000);

const testPath: string = path.join(__dirname, 'L0MavenToolchains.js');
const testRunner: ttm.MockTestRunner = new ttm.MockTestRunner(testPath);

testRunner.run();

assert(testRunner.succeeded, 'task should have succeeded.');
});
});
106 changes: 106 additions & 0 deletions Tasks/JavaToolInstallerV0/Tests/L0MavenToolchains.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import mockanswer = require('azure-pipelines-task-lib/mock-answer');
import tmrm = require('azure-pipelines-task-lib/mock-run');
import msRestAzure = require('azure-pipelines-tasks-azure-arm-rest-v2/azure-arm-common');
import path = require('path');
import os = require('os');
import mockTask = require('azure-pipelines-task-lib/mock-task');

const taskPath = path.join(__dirname, '..', 'javatoolinstaller.js');
const tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);

const destDir = path.join(os.homedir(), '.m2');
const destFile = path.join(destDir, 'toolchains.xml');

tr.setInput("versionSpec", "8.1");
tr.setInput("jdkSource", "Azure Storage")
tr.setInput("artifactProvider", "azureStorage");
tr.setInput("azureResourceManagerEndpoint", "ARM1");
tr.setInput("azureStorageAccountName", "storage1");
tr.setInput("azureContainerName", "container1");
tr.setInput("azureCommonVirtualPath", "");
tr.setInput("fileType", ".tar.gz");
tr.setInput("destinationFolder", "javaJDK");
tr.setInput("cleanDestinationFolder", "false");
tr.setInput("createMavenToolchains", "true");

// provide answers for task mock
const a: mockanswer.TaskLibAnswers = <mockanswer.TaskLibAnswers>{
exist: {[destDir]: true, [destFile]: true},
find: {[destDir]: [destDir], [destFile]: [destFile]},
rmRF: { },
stats: {[destDir]: {'isDirectory':'true'}, [destFile]: {'isFile':'true'}},
};
tr.setAnswers(a);

tr.registerMock("azure-pipelines-tasks-azure-arm-rest-v2/azure-arm-storage", {
StorageManagementClient: function (A, B) {
return {
storageAccounts: {
get: function (A) {
return {
properties: {
primaryEndpoints: {
blob: "primaryBlobUrl"
}
},
id: "StorageAccountUrl"
}
},
listkeys: function (A, B, C) {
return ["accesskey1", "accessKey2"];
}
}
}
},
StorageAccounts: {
getResourceGroupNameFromUri: function (A) {
return "storageAccountResouceGroupName";
}
}
});

tr.registerMock("azure-pipelines-tasks-azure-arm-rest-v2/azure-arm-common", {
ApplicationTokenCredentials: function(A,B,C,D,E,F,G) {
return {};
}
});

tr.registerMock('./AzureStorageArtifacts/AzureStorageArtifactDownloader',{
AzureStorageArtifactDownloader: function(A,B,C) {
return {
downloadArtifacts: function(A,B) {
return "pathFromDownloader";
}
}
}
})

const mtl = require("azure-pipelines-tool-lib/tool")
const mtlClone = Object.assign({}, mtl);

mtlClone.prependPath = function(variable1: string, variable2: string) {
return {};
};

tr.registerMock("azure-pipelines-tool-lib/tool", mtlClone);

const mfs = require('fs')
const mfsClone = Object.assign({}, mfs);

mfsClone.lstatSync = function(variable: string) {
return {
isDirectory: function() {
return true;
}
};
};

mfsClone.existsSync = function (variable: string) {
if (variable === "DestinationDirectory\\econdlevelJDK2" || variable === "DestinationDirectory/econdlevelJDK2") {
return false;
} else return true;
}

tr.registerMock('fs', mfsClone);

tr.run();
5 changes: 5 additions & 0 deletions Tasks/JavaToolInstallerV0/javatoolinstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as telemetry from 'azure-pipelines-tasks-utility-common/telemetry';
import { AzureStorageArtifactDownloader } from './AzureStorageArtifacts/AzureStorageArtifactDownloader';
import { JavaFilesExtractor, BIN_FOLDER } from './FileExtractor/JavaFilesExtractor';
import taskutils = require('./taskutils');
import {configureToolchains} from "./toolchains";

const VOLUMES_FOLDER: string = '/Volumes';
const JDK_FOLDER: string = '/Library/Java/JavaVirtualMachines';
Expand All @@ -32,6 +33,7 @@ async function getJava(versionSpec: string, jdkArchitectureOption: string): Prom
const fromAzure: boolean = ('AzureStorage' == taskLib.getInput('jdkSourceOption', true));
const extractLocation: string = taskLib.getPathInput('jdkDestinationDirectory', true);
const cleanDestinationDirectory: boolean = taskLib.getBoolInput('cleanDestinationDirectory', false);
const createMavenToolchains = taskLib.getBoolInput('createMavenToolchains', false);
let compressedFileExtension: string;
let jdkDirectory: string;
const extendedJavaHome: string = `JAVA_HOME_${versionSpec}_${jdkArchitectureOption}`.toUpperCase();
Expand Down Expand Up @@ -76,6 +78,9 @@ async function getJava(versionSpec: string, jdkArchitectureOption: string): Prom
console.log(taskLib.loc('SetExtendedJavaHome', extendedJavaHome, jdkDirectory));
taskLib.setVariable(extendedJavaHome, jdkDirectory);
toolLib.prependPath(path.join(jdkDirectory, BIN_FOLDER));
if (createMavenToolchains) {
await configureToolchains(version, 'unknown', jdkDirectory);
}
}

/**
Expand Down
55 changes: 55 additions & 0 deletions Tasks/JavaToolInstallerV0/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Tasks/JavaToolInstallerV0/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"azure-pipelines-task-lib": "^3.1.2",
"azure-pipelines-tasks-azure-arm-rest-v2": "2.0.4",
"azure-pipelines-tasks-utility-common": "^3.0.3",
"azure-pipelines-tool-lib": "^1.0.2"
"azure-pipelines-tool-lib": "^1.0.2",
"xmlbuilder2": "^2.4.1"
},
"devDependencies": {
"typescript": "4.0.2"
Expand Down
8 changes: 8 additions & 0 deletions Tasks/JavaToolInstallerV0/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@
"defaultValue": true,
"visibleRule": "jdkSourceOption != PreInstalled",
"helpMarkDown": "By default, task is creating a directory similar to this JAVA_HOME_8_X64_OpenJDK_zip for extracting JDK. This option allows to disable creation of this folder, in this case, JDK will be located in the root of jdkDestinationDirectory"
},
{
"name": "createMavenToolchains",
"type": "boolean",
"label": "Create Maven Toolchains declaration",
"required": false,
"defaultValue": false,
"helpMarkDown": "Create a Maven Toolchains declaration in the official default location describing the JDK that was installed with this task. Existing Maven Toolchains are modified accordingly."
}
],
"dataSourceBindings": [
Expand Down
8 changes: 8 additions & 0 deletions Tasks/JavaToolInstallerV0/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@
"defaultValue": true,
"visibleRule": "jdkSourceOption != PreInstalled",
"helpMarkDown": "ms-resource:loc.input.help.createExtractDirectory"
},
{
"name": "createMavenToolchains",
"type": "boolean",
"label": "ms-resource:loc.input.label.createMavenToolchains",
"required": false,
"defaultValue": false,
"helpMarkDown": "ms-resource:loc.input.help.createMavenToolchains"
}
],
"dataSourceBindings": [
Expand Down
121 changes: 121 additions & 0 deletions Tasks/JavaToolInstallerV0/toolchains.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import fs = require('fs');
import os = require('os');
import path = require('path');
import taskLib = require('azure-pipelines-task-lib/task');
import toolLib = require('azure-pipelines-tool-lib/tool');
import { create as xmlCreate } from 'xmlbuilder2';

const M2_DIR = '.m2';
const MVN_TOOLCHAINS_FILE: string = 'toolchains.xml';

export async function configureToolchains(version: string, vendor: string, jdkHome: string) {
const id = `${vendor}_${version}`;
const settingsDirectory = path.join(os.homedir(), M2_DIR);

await createToolchainsSettings(
version,
vendor,
id,
jdkHome,
settingsDirectory,
);
}

async function createToolchainsSettings(
version: string,
vendor: string,
id: string,
jdkHome: string,
settingsDirectory: string,
) {
toolLib.debug(`Creating ${MVN_TOOLCHAINS_FILE} for JDK version ${version} from ${vendor}`);
await taskLib.mkdirP(settingsDirectory);
const originalToolchains = await readExisting(settingsDirectory);
const updatedToolchains = generate(originalToolchains, version, vendor, id, jdkHome);
await write(settingsDirectory, updatedToolchains, true);
}

function generate(
original: string,
version: string,
vendor: string,
id: string,
jdkHome: string
) {
let xmlObj;
if (original && original.length > 0) {
xmlObj = xmlCreate(original)
.root()
.ele({
type: 'jdk',
provides: {
version: `${version}`,
vendor: `${vendor}`,
id: `${id}`
},
configuration: {
jdkHome: `${jdkHome}`
}
});
} else
xmlObj = xmlCreate({
toolchains: {
'@xmlns': 'https://maven.apache.org/TOOLCHAINS/1.1.0',
'@xmlns:xsi': 'https://www.w3.org/2001/XMLSchema-instance',
'@xsi:schemaLocation':
'https://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd',
toolchain: [
{
type: 'jdk',
provides: {
version: `${version}`,
vendor: `${vendor}`,
id: `${id}`
},
configuration: {
jdkHome: `${jdkHome}`
}
}
]
}
});

return xmlObj.end({
format: 'xml',
wellFormed: false,
headless: false,
prettyPrint: true,
width: 80
});
}

async function readExisting(directory: string) {
const location = path.join(directory, MVN_TOOLCHAINS_FILE);
if (fs.existsSync(location)) {
return fs.readFileSync(location, {
encoding: 'utf-8',
flag: 'r'
});
}
return '';
}

async function write(directory: string, settings: string, overwriteSettings: boolean) {
const location = path.join(directory, MVN_TOOLCHAINS_FILE);
const settingsExists = fs.existsSync(location);
if (settingsExists && overwriteSettings) {
toolLib.debug(`Overwriting existing file ${location}`);
} else if (!settingsExists) {
toolLib.debug(`Writing to ${location}`);
} else {
toolLib.debug(
`Skipping generation ${location} because file already exists and overwriting is not required`
);
return;
}

return fs.writeFileSync(location, settings, {
encoding: 'utf-8',
flag: 'w'
});
}