Skip to content

Commit

Permalink
fix DTS 1674974; build-task-team#10 (#13209)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandrKravchuk authored Jul 10, 2020
1 parent 900d618 commit 6ca9a89
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 85 deletions.
110 changes: 76 additions & 34 deletions Tasks/JavaToolInstallerV0/FileExtractor/JavaFilesExtractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class JavaFilesExtractor {
}
}

private isTar(file): boolean {
private static isTar(file): boolean {
const name = file.toLowerCase();
// standard gnu-tar extension formats with recognized auto compression formats
// https://www.gnu.org/software/tar/manual/html_section/tar_69.html
Expand Down Expand Up @@ -70,6 +70,24 @@ export class JavaFilesExtractor {
}
}

public static getFileEnding(file: string): string {
let fileEnding = '';

if (file.endsWith('.tar')) {
fileEnding = '.tar';
} else if (file.endsWith('.tar.gz')) {
fileEnding = '.tar.gz';
} else if (file.endsWith('.zip')) {
fileEnding = '.zip';
} else if (file.endsWith('.7z')) {
fileEnding = '.7z';
} else {
throw new Error(taskLib.loc('UnsupportedFileExtension'));
}

return fileEnding;
}

private async extractFiles(file: string, fileEnding: string): Promise<void> {
const stats = taskLib.stats(file);
if (!stats) {
Expand All @@ -81,7 +99,7 @@ export class JavaFilesExtractor {
if (this.win) {
if ('.tar' === fileEnding) { // a simple tar
this.sevenZipExtract(file, this.destinationFolder);
} else if (this.isTar(file)) { // a compressed tar, e.g. 'fullFilePath/test.tar.gz'
} else if (JavaFilesExtractor.isTar(file)) { // a compressed tar, e.g. 'fullFilePath/test.tar.gz'
// e.g. 'fullFilePath/test.tar.gz' --> 'test.tar.gz'
const shortFileName = path.basename(file);
// e.g. 'destinationFolder/_test.tar.gz_'
Expand Down Expand Up @@ -119,19 +137,18 @@ export class JavaFilesExtractor {
}

// This method recursively finds all .pack files under fsPath and unpacks them with the unpack200 tool
private unpackJars(fsPath: string, javaBinPath: string) {
public static unpackJars(fsPath: string, javaBinPath: string) {
if (fs.existsSync(fsPath)) {
if (fs.lstatSync(fsPath).isDirectory()) {
let self = this;
fs.readdirSync(fsPath).forEach(function(file,index){
fs.readdirSync(fsPath).forEach(function(file){
const curPath = path.join(fsPath, file);
self.unpackJars(curPath, javaBinPath);
JavaFilesExtractor.unpackJars(curPath, javaBinPath);
});
} else if (path.extname(fsPath).toLowerCase() === '.pack') {
// Unpack the pack file synchonously
// Unpack the pack file synchronously
const p = path.parse(fsPath);
const toolName = process.platform.match(/^win/i) ? 'unpack200.exe' : 'unpack200';
const args = process.platform.match(/^win/i) ? '-r -v -l ""' : '';
const args = process.platform.match(/^win/i) ? '-r -v -l ""' : '';
const name = path.join(p.dir, p.name);
taskLib.execSync(path.join(javaBinPath, toolName), `${args} "${name}.pack" "${name}.jar"`);
}
Expand All @@ -143,35 +160,76 @@ export class JavaFilesExtractor {
* @param pathsArray - contains paths to all the files inside the structure
* @param root - path to the directory we want to get the structure of
*/
private static sliceStructure(pathsArray: Array<string>, root: string = pathsArray[0]): Array<string>{
public static sliceStructure(pathsArray: Array<string>, root: string = pathsArray[0]): Array<string>{
const dirPathLength = root.length;
const structureObject: IDirectoriesDictionary = {};
for(let i = 0; i < pathsArray.length; i++){
const pathStr = pathsArray[i];
const cleanPathStr = pathStr.slice(dirPathLength + 1);
if (cleanPathStr === '') {
continue;
}
const dirPathArray = cleanPathStr.split(path.sep);
// Create the list of unique values
structureObject[dirPathArray[0]] = null;
}
return Object.keys(structureObject);
}

/**
* Returns name w/o file ending
* @param name - name of the file
*/
public static getStrippedName(name: string): string {
const fileBaseName: string = path.basename(name);
const fileEnding: string = JavaFilesExtractor.getFileEnding(fileBaseName);
return fileBaseName.substring(0, fileBaseName.length - fileEnding.length);
}

/**
* Returns path to JAVA_HOME, or throw exception if the extracted archive isn't valid
* @param pathToStructure - path to files extracted from the JDK archive
*/
public static getJavaHomeFromStructure(pathToStructure): string {
const structure: Array<string> = taskLib.find(pathToStructure);
const rootDirectoriesArray: Array<string> = JavaFilesExtractor.sliceStructure(structure);
let jdkDirectory: string;
if (rootDirectoriesArray.find(dir => dir === BIN_FOLDER)){
jdkDirectory = pathToStructure;
} else {
jdkDirectory = path.join(pathToStructure, rootDirectoriesArray[0]);
const ifBinExistsInside: boolean = fs.existsSync(path.join(jdkDirectory, BIN_FOLDER));
if (rootDirectoriesArray.length > 1 || !ifBinExistsInside){
throw new Error(taskLib.loc('WrongArchiveStructure'));
}
}
return jdkDirectory;
}

/**
* Validate files structure if it can be a JDK, then set JAVA_HOME and returns it.
* @param pathToExtractedJDK - path to files extracted from the JDK archive
* @param withValidation - validate files and search bin inside
*/
public static setJavaHome(pathToExtractedJDK: string, withValidation: boolean = true): string {
let jdkDirectory: string = withValidation ?
JavaFilesExtractor.getJavaHomeFromStructure(pathToExtractedJDK) :
pathToExtractedJDK;
console.log(taskLib.loc('SetJavaHome', jdkDirectory));
taskLib.setVariable('JAVA_HOME', jdkDirectory);
return jdkDirectory;
}

public async unzipJavaDownload(repoRoot: string, fileEnding: string, extractLocation: string): Promise<string> {
this.destinationFolder = extractLocation;
let initialDirectoriesList: string[];
let finalDirectoriesList: string[];
let jdkDirectory: string;

// Create the destination folder if it doesn't exist
if (!taskLib.exist(this.destinationFolder)) {
console.log(taskLib.loc('CreateDestDir', this.destinationFolder));
taskLib.mkdirP(this.destinationFolder);
}

initialDirectoriesList = taskLib.find(this.destinationFolder).filter(x => taskLib.stats(x).isDirectory());

const jdkFile = path.normalize(repoRoot);

let stats: taskLib.FsStats;
try {
stats = taskLib.stats(jdkFile);
Expand All @@ -181,27 +239,11 @@ export class JavaFilesExtractor {
}
throw(error);
}

if (stats.isFile()) {
await this.extractFiles(jdkFile, fileEnding);
finalDirectoriesList = taskLib.find(this.destinationFolder).filter(x => taskLib.stats(x).isDirectory());
const freshExtractedDirectories = finalDirectoriesList.filter(dir => initialDirectoriesList.indexOf(dir) < 0);
const rootStructure = JavaFilesExtractor.sliceStructure(freshExtractedDirectories, this.destinationFolder);
let jdkDirectory = freshExtractedDirectories[0];
if (rootStructure.find(dir => dir === BIN_FOLDER)){
// In case of 'bin' folder was extracted directly to the destination folder
jdkDirectory = path.join(jdkDirectory, '..');
} else {
// Check if bin is inside the current JDK directory
const ifBinExists = fs.existsSync(path.join(jdkDirectory, BIN_FOLDER));
if (rootStructure.length > 1 || !ifBinExists){
throw new Error(taskLib.loc('WrongArchiveStructure'));
}
}
taskLib.debug(`Using folder "${jdkDirectory}" for JDK`);
this.unpackJars(jdkDirectory, path.join(jdkDirectory, 'bin'));
return jdkDirectory;
}
const jdkDirectory: string = JavaFilesExtractor.getJavaHomeFromStructure(this.destinationFolder);
JavaFilesExtractor.unpackJars(jdkDirectory, path.join(jdkDirectory, BIN_FOLDER));
return jdkDirectory;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@
"loc.messages.JavaNotPreinstalled": "Java %s is not preinstalled on this agent",
"loc.messages.UsePreinstalledJava": "Use preinstalled JDK from %s",
"loc.messages.WrongArchiveStructure": "JDK file is not valid. Verify if JDK file contains only one root folder with 'bin' inside."
}
}
84 changes: 36 additions & 48 deletions Tasks/JavaToolInstallerV0/javatoolinstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,20 @@ async function getJava(versionSpec: string) {
const fromAzure: boolean = ('AzureStorage' == taskLib.getInput('jdkSourceOption', true));
const extractLocation: string = taskLib.getPathInput('jdkDestinationDirectory', true);
const cleanDestinationDirectory: boolean = taskLib.getBoolInput('cleanDestinationDirectory', false);
const unpackArchive = async (unpackDir, jdkFileName, fileExt) => {
const javaFilesExtractor = new JavaFilesExtractor();
if (!cleanDestinationDirectory && taskLib.exist(unpackDir)){
// do nothing since the files were extracted and ready for using
console.log(taskLib.loc('ArchiveWasExtractedEarlier'));
} else {
// unpack files to specified directory
console.log(taskLib.loc('ExtractingArchiveToPath', unpackDir));
await javaFilesExtractor.unzipJavaDownload(jdkFileName, fileExt, unpackDir);
}
};
let compressedFileExtension: string;
let jdkDirectory: string;
let extractionDirectory: string;
const extendedJavaHome: string = `JAVA_HOME_${versionSpec}_${taskLib.getInput('jdkArchitectureOption', true)}`;

toolLib.debug('Trying to get tool from local cache first');
Expand All @@ -36,7 +48,6 @@ async function getJava(versionSpec: string) {
// Clean the destination folder before downloading and extracting?
if (cleanDestinationDirectory && taskLib.exist(extractLocation) && taskLib.stats(extractLocation).isDirectory) {
console.log(taskLib.loc('CleanDestDir', extractLocation));

// delete the contents of the destination directory but leave the directory in place
fs.readdirSync(extractLocation)
.forEach((item: string) => {
Expand All @@ -53,30 +64,32 @@ async function getJava(versionSpec: string) {
throw new Error(taskLib.loc('JavaNotPreinstalled', versionSpec));
}
console.log(taskLib.loc('UsePreinstalledJava', preInstalledJavaDirectory));
jdkDirectory = preInstalledJavaDirectory;
} else if (fromAzure) { //Download JDK from an Azure blob storage location and extract.
console.log(taskLib.loc('RetrievingJdkFromAzure'));
const fileNameAndPath: string = taskLib.getInput('azureCommonVirtualFile', false);
compressedFileExtension = getFileEnding(fileNameAndPath);

const azureDownloader = new AzureStorageArtifactDownloader(taskLib.getInput('azureResourceManagerEndpoint', true),
taskLib.getInput('azureStorageAccountName', true), taskLib.getInput('azureContainerName', true), "");
await azureDownloader.downloadArtifacts(extractLocation, '*' + fileNameAndPath);
await sleepFor(250); //Wait for the file to be released before extracting it.

const extractSource = buildFilePath(extractLocation, compressedFileExtension, fileNameAndPath);
const javaFilesExtractor = new JavaFilesExtractor();
jdkDirectory = await javaFilesExtractor.unzipJavaDownload(extractSource, compressedFileExtension, extractLocation);
} else { //JDK is in a local directory. Extract to specified target directory.
console.log(taskLib.loc('RetrievingJdkFromLocalPath'));
compressedFileExtension = getFileEnding(taskLib.getInput('jdkFile', true));
const javaFilesExtractor = new JavaFilesExtractor();
jdkDirectory = await javaFilesExtractor.unzipJavaDownload(taskLib.getInput('jdkFile', true), compressedFileExtension, extractLocation);
jdkDirectory = JavaFilesExtractor.setJavaHome(preInstalledJavaDirectory, false);
} else {
let extractDirectoryName;
let jdkFileName;
if (fromAzure) {
// download from azure and save to temporary directory
console.log(taskLib.loc('RetrievingJdkFromAzure'));
const fileNameAndPath: string = taskLib.getInput('azureCommonVirtualFile', false);
const azureDownloader = new AzureStorageArtifactDownloader(taskLib.getInput('azureResourceManagerEndpoint', true),
taskLib.getInput('azureStorageAccountName', true), taskLib.getInput('azureContainerName', true), "");
await azureDownloader.downloadArtifacts(extractLocation, '*' + fileNameAndPath);
await sleepFor(250); //Wait for the file to be released before extracting it.
jdkFileName = path.join(extractLocation, fileNameAndPath);
} else {
// get from local directory
console.log(taskLib.loc('RetrievingJdkFromLocalPath'));
jdkFileName = taskLib.getInput('jdkFile', true);
}
// unpack the archive, set `JAVA_HOME` and save it for further processing
compressedFileExtension = JavaFilesExtractor.getFileEnding(jdkFileName);
extractDirectoryName = `${extendedJavaHome}_${JavaFilesExtractor.getStrippedName(jdkFileName)}_${compressedFileExtension.substr(1)}`;
extractionDirectory = path.join(extractLocation, extractDirectoryName);
await unpackArchive(extractionDirectory, jdkFileName, compressedFileExtension);
jdkDirectory = JavaFilesExtractor.setJavaHome(extractionDirectory);
}

console.log(taskLib.loc('SetJavaHome', jdkDirectory));
console.log(taskLib.loc('SetExtendedJavaHome', extendedJavaHome, jdkDirectory));
taskLib.setVariable('JAVA_HOME', jdkDirectory);
taskLib.setVariable(extendedJavaHome, jdkDirectory);
toolLib.prependPath(path.join(jdkDirectory, BIN_FOLDER));
}
Expand All @@ -87,29 +100,4 @@ function sleepFor(sleepDurationInMillisecondsSeconds): Promise<any> {
});
}

function buildFilePath(localPathRoot: string, fileEnding: string, fileNameAndPath: string): string {
const fileName = fileNameAndPath.split(/[\\\/]/).pop();
const extractSource = path.join(localPathRoot, fileName);

return extractSource;
}

function getFileEnding(file: string): string {
let fileEnding = '';

if (file.endsWith('.tar')) {
fileEnding = '.tar';
} else if (file.endsWith('.tar.gz')) {
fileEnding = '.tar.gz';
} else if (file.endsWith('.zip')) {
fileEnding = '.zip';
} else if (file.endsWith('.7z')) {
fileEnding = '.7z';
} else {
throw new Error(taskLib.loc('UnsupportedFileExtension'));
}

return fileEnding;
}

run();
2 changes: 1 addition & 1 deletion Tasks/JavaToolInstallerV0/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"version": {
"Major": 0,
"Minor": 173,
"Patch": 0
"Patch": 1
},
"satisfies": [
"Java"
Expand Down
2 changes: 1 addition & 1 deletion Tasks/JavaToolInstallerV0/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"version": {
"Major": 0,
"Minor": 173,
"Patch": 0
"Patch": 1
},
"satisfies": [
"Java"
Expand Down

0 comments on commit 6ca9a89

Please sign in to comment.