mirror of
				https://github.com/actions/setup-java.git
				synced 2025-11-04 08:20:54 +00:00 
			
		
		
		
	Implement support for custom vendors in setup-java
This commit is contained in:
		
					parent
					
						
							
								e73e96a93b
							
						
					
				
			
			
				commit
				
					
						b2da088220
					
				
			
		
					 21 changed files with 34331 additions and 21391 deletions
				
			
		
							
								
								
									
										146
									
								
								src/distributions/adoptium/installer.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								src/distributions/adoptium/installer.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,146 @@
 | 
			
		|||
import * as core from '@actions/core';
 | 
			
		||||
import * as tc from '@actions/tool-cache';
 | 
			
		||||
 | 
			
		||||
import fs from 'fs';
 | 
			
		||||
import path from 'path';
 | 
			
		||||
import semver from 'semver';
 | 
			
		||||
 | 
			
		||||
import { JavaBase } from '../base-installer';
 | 
			
		||||
import { IAdoptiumAvailableVersions } from './models';
 | 
			
		||||
import { JavaInstallerOptions, JavaDownloadRelease, JavaInstallerResults } from '../base-models';
 | 
			
		||||
import { macOSJavaContentDir } from '../../constants';
 | 
			
		||||
import { extractJdkFile, getDownloadArchiveExtension } from '../../util';
 | 
			
		||||
 | 
			
		||||
export class AdoptiumDistribution extends JavaBase {
 | 
			
		||||
  constructor(installerOptions: JavaInstallerOptions) {
 | 
			
		||||
    super('Adoptium', installerOptions);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected async findPackageForDownload(version: semver.Range): Promise<JavaDownloadRelease> {
 | 
			
		||||
    const availableVersionsRaw = await this.getAvailableVersions();
 | 
			
		||||
    const availableVersionsWithBinaries = availableVersionsRaw
 | 
			
		||||
      .filter(item => item.binaries.length > 0)
 | 
			
		||||
      .map(item => {
 | 
			
		||||
        return {
 | 
			
		||||
          version: item.version_data.semver,
 | 
			
		||||
          url: item.binaries[0].package.link
 | 
			
		||||
        } as JavaDownloadRelease;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    const satisfiedVersions = availableVersionsWithBinaries
 | 
			
		||||
      .filter(item => semver.satisfies(item.version, version))
 | 
			
		||||
      .sort((a, b) => {
 | 
			
		||||
        return -semver.compareBuild(a.version, b.version);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    const resolvedFullVersion = satisfiedVersions.length > 0 ? satisfiedVersions[0] : null;
 | 
			
		||||
    if (!resolvedFullVersion) {
 | 
			
		||||
      const availableOptions = availableVersionsWithBinaries.map(item => item.version).join(', ');
 | 
			
		||||
      const availableOptionsMessage = availableOptions
 | 
			
		||||
        ? `\nAvailable versions: ${availableOptions}`
 | 
			
		||||
        : '';
 | 
			
		||||
      throw new Error(
 | 
			
		||||
        `Could not find satisfied version for SemVer '${version.raw}'. ${availableOptionsMessage}`
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return resolvedFullVersion;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected async downloadTool(javaRelease: JavaDownloadRelease): Promise<JavaInstallerResults> {
 | 
			
		||||
    let javaPath: string;
 | 
			
		||||
    let extractedJavaPath: string;
 | 
			
		||||
 | 
			
		||||
    core.info(
 | 
			
		||||
      `Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`
 | 
			
		||||
    );
 | 
			
		||||
    const javaArchivePath = await tc.downloadTool(javaRelease.url);
 | 
			
		||||
 | 
			
		||||
    core.info(`Extracting Java archive...`);
 | 
			
		||||
    let extension = getDownloadArchiveExtension();
 | 
			
		||||
 | 
			
		||||
    extractedJavaPath = await extractJdkFile(javaArchivePath, extension);
 | 
			
		||||
 | 
			
		||||
    const archiveName = fs.readdirSync(extractedJavaPath)[0];
 | 
			
		||||
    const archivePath = path.join(extractedJavaPath, archiveName);
 | 
			
		||||
    const version = this.getToolcacheVersionName(javaRelease.version);
 | 
			
		||||
 | 
			
		||||
    javaPath = await tc.cacheDir(archivePath, this.toolcacheFolderName, version, this.architecture);
 | 
			
		||||
 | 
			
		||||
    if (process.platform === 'darwin') {
 | 
			
		||||
      javaPath = path.join(javaPath, macOSJavaContentDir);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return { version: javaRelease.version, path: javaPath };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async getAvailableVersions(): Promise<IAdoptiumAvailableVersions[]> {
 | 
			
		||||
    const platform = this.getPlatformOption();
 | 
			
		||||
    const arch = this.architecture;
 | 
			
		||||
    const imageType = this.packageType;
 | 
			
		||||
    const versionRange = '[1.0,100.0]'; // retrieve all available versions
 | 
			
		||||
    const encodedVersionRange = encodeURI(versionRange);
 | 
			
		||||
    const releaseType = this.stable ? 'ga' : 'ea';
 | 
			
		||||
 | 
			
		||||
    console.time('adopt-retrieve-available-versions');
 | 
			
		||||
 | 
			
		||||
    const baseRequestArguments = [
 | 
			
		||||
      `project=jdk`,
 | 
			
		||||
      'vendor=adoptopenjdk',
 | 
			
		||||
      `heap_size=normal`,
 | 
			
		||||
      `jvm_impl=hotspot`,
 | 
			
		||||
      'sort_method=DEFAULT',
 | 
			
		||||
      'sort_order=DESC',
 | 
			
		||||
      `os=${platform}`,
 | 
			
		||||
      `architecture=${arch}`,
 | 
			
		||||
      `image_type=${imageType}`,
 | 
			
		||||
      `release_type=${releaseType}`
 | 
			
		||||
    ].join('&');
 | 
			
		||||
 | 
			
		||||
    // need to iterate through all pages to retrieve the list of all versions
 | 
			
		||||
    // Adopt API doesn't provide way to retrieve the count of pages to iterate so infinity loop
 | 
			
		||||
    let page_index = 0;
 | 
			
		||||
    const availableVersions: IAdoptiumAvailableVersions[] = [];
 | 
			
		||||
    while (true) {
 | 
			
		||||
      const requestArguments = `${baseRequestArguments}&page_size=20&page=${page_index}`;
 | 
			
		||||
      const availableVersionsUrl = `https://api.adoptopenjdk.net/v3/assets/version/${encodedVersionRange}?${requestArguments}`;
 | 
			
		||||
      if (core.isDebug() && page_index === 0) {
 | 
			
		||||
        // url is identical except page_index so print it once for debug
 | 
			
		||||
        core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const paginationPage = (
 | 
			
		||||
        await this.http.getJson<IAdoptiumAvailableVersions[]>(availableVersionsUrl)
 | 
			
		||||
      ).result;
 | 
			
		||||
      if (paginationPage === null || paginationPage.length === 0) {
 | 
			
		||||
        // break infinity loop because we have reached end of pagination
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      availableVersions.push(...paginationPage);
 | 
			
		||||
      page_index++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (core.isDebug()) {
 | 
			
		||||
      core.startGroup('Print information about available versions');
 | 
			
		||||
      console.timeEnd('adopt-retrieve-available-versions');
 | 
			
		||||
      console.log(`Available versions: [${availableVersions.length}]`);
 | 
			
		||||
      console.log(availableVersions.map(item => item.version_data.semver).join(', '));
 | 
			
		||||
      core.endGroup();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return availableVersions;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private getPlatformOption(): string {
 | 
			
		||||
    // Adopt has own platform names so need to map them
 | 
			
		||||
    switch (process.platform) {
 | 
			
		||||
      case 'darwin':
 | 
			
		||||
        return 'mac';
 | 
			
		||||
      case 'win32':
 | 
			
		||||
        return 'windows';
 | 
			
		||||
      default:
 | 
			
		||||
        return process.platform;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								src/distributions/adoptium/models.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/distributions/adoptium/models.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
export interface IAdoptiumAvailableVersions {
 | 
			
		||||
  binaries: [
 | 
			
		||||
    {
 | 
			
		||||
      architecture: string;
 | 
			
		||||
      heap_size: string;
 | 
			
		||||
      image_type: string;
 | 
			
		||||
      jvm_impl: string;
 | 
			
		||||
      os: string;
 | 
			
		||||
      package: {
 | 
			
		||||
        checksum: string;
 | 
			
		||||
        checksum_link: string;
 | 
			
		||||
        download_count: number;
 | 
			
		||||
        link: string;
 | 
			
		||||
        metadata_link: string;
 | 
			
		||||
        name: string;
 | 
			
		||||
        size: string;
 | 
			
		||||
      };
 | 
			
		||||
      project: string;
 | 
			
		||||
      scm_ref: string;
 | 
			
		||||
      updated_at: string;
 | 
			
		||||
    }
 | 
			
		||||
  ];
 | 
			
		||||
  id: string;
 | 
			
		||||
  release_link: string;
 | 
			
		||||
  release_name: string;
 | 
			
		||||
  release_type: string;
 | 
			
		||||
  vendor: string;
 | 
			
		||||
  version_data: {
 | 
			
		||||
    build: number;
 | 
			
		||||
    major: number;
 | 
			
		||||
    minor: number;
 | 
			
		||||
    openjdk_version: string;
 | 
			
		||||
    security: string;
 | 
			
		||||
    semver: string;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										115
									
								
								src/distributions/base-installer.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/distributions/base-installer.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,115 @@
 | 
			
		|||
import * as tc from '@actions/tool-cache';
 | 
			
		||||
import * as core from '@actions/core';
 | 
			
		||||
import semver from 'semver';
 | 
			
		||||
import path from 'path';
 | 
			
		||||
import * as httpm from '@actions/http-client';
 | 
			
		||||
import { getVersionFromToolcachePath } from '../util';
 | 
			
		||||
import { JavaDownloadRelease, JavaInstallerOptions, JavaInstallerResults } from './base-models';
 | 
			
		||||
 | 
			
		||||
export abstract class JavaBase {
 | 
			
		||||
  protected http: httpm.HttpClient;
 | 
			
		||||
  protected version: semver.Range;
 | 
			
		||||
  protected architecture: string;
 | 
			
		||||
  protected packageType: string;
 | 
			
		||||
  protected stable: boolean;
 | 
			
		||||
 | 
			
		||||
  constructor(protected distribution: string, installerOptions: JavaInstallerOptions) {
 | 
			
		||||
    this.http = new httpm.HttpClient('setup-java', undefined, {
 | 
			
		||||
      allowRetries: true,
 | 
			
		||||
      maxRetries: 3
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    ({ version: this.version, stable: this.stable } = this.normalizeVersion(
 | 
			
		||||
      installerOptions.version
 | 
			
		||||
    ));
 | 
			
		||||
    this.architecture = installerOptions.arch;
 | 
			
		||||
    this.packageType = installerOptions.packageType;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected abstract downloadTool(javaRelease: JavaDownloadRelease): Promise<JavaInstallerResults>;
 | 
			
		||||
  protected abstract findPackageForDownload(range: semver.Range): Promise<JavaDownloadRelease>;
 | 
			
		||||
 | 
			
		||||
  public async setupJava(): Promise<JavaInstallerResults> {
 | 
			
		||||
    let foundJava = this.findInToolcache();
 | 
			
		||||
    if (foundJava) {
 | 
			
		||||
      core.info(`Resolved Java ${foundJava.version} from tool-cache`);
 | 
			
		||||
    } else {
 | 
			
		||||
      core.info(`Java ${this.version.raw} is not found in tool-cache. Trying to download...`);
 | 
			
		||||
      const javaRelease = await this.findPackageForDownload(this.version);
 | 
			
		||||
      foundJava = await this.downloadTool(javaRelease);
 | 
			
		||||
      core.info(`Java ${foundJava.version} was downloaded`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    core.info(`Setting Java ${foundJava.version} as default`);
 | 
			
		||||
    this.setJavaDefault(foundJava.version, foundJava.path);
 | 
			
		||||
 | 
			
		||||
    return foundJava;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected get toolcacheFolderName(): string {
 | 
			
		||||
    return `Java_${this.distribution}_${this.packageType}`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected getToolcacheVersionName(resolvedVersion: string): string {
 | 
			
		||||
    let version = resolvedVersion;
 | 
			
		||||
    if (!this.stable) {
 | 
			
		||||
      const cleanVersion = semver.clean(version);
 | 
			
		||||
      return `${cleanVersion}-ea`;
 | 
			
		||||
    }
 | 
			
		||||
    return version;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected findInToolcache(): JavaInstallerResults | null {
 | 
			
		||||
    // we can't use tc.find directly because firstly, we need to filter versions by stability
 | 
			
		||||
    // if *-ea is provided, take only ea versions from toolcache, otherwise - only stable versions
 | 
			
		||||
    const availableVersions = tc
 | 
			
		||||
      .findAllVersions(this.toolcacheFolderName, this.architecture)
 | 
			
		||||
      .filter(item => item.endsWith('-ea') === !this.stable);
 | 
			
		||||
 | 
			
		||||
    const satisfiedVersions = availableVersions
 | 
			
		||||
      .filter(item => semver.satisfies(item.replace(/-ea$/, ''), this.version))
 | 
			
		||||
      .sort(semver.rcompare);
 | 
			
		||||
    if (!satisfiedVersions || satisfiedVersions.length === 0) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const javaPath = tc.find(this.toolcacheFolderName, satisfiedVersions[0], this.architecture);
 | 
			
		||||
    if (!javaPath) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      version: getVersionFromToolcachePath(javaPath),
 | 
			
		||||
      path: javaPath
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected setJavaDefault(version: string, toolPath: string) {
 | 
			
		||||
    core.exportVariable('JAVA_HOME', toolPath);
 | 
			
		||||
    core.addPath(path.join(toolPath, 'bin'));
 | 
			
		||||
    core.setOutput('distribution', this.distribution);
 | 
			
		||||
    core.setOutput('path', toolPath);
 | 
			
		||||
    core.setOutput('version', version);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // this function validates and parse java version to its normal semver notation
 | 
			
		||||
  protected normalizeVersion(version: string) {
 | 
			
		||||
    let stable = true;
 | 
			
		||||
 | 
			
		||||
    if (version.endsWith('-ea')) {
 | 
			
		||||
      version = version.replace('-ea', '');
 | 
			
		||||
      stable = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!semver.validRange(version)) {
 | 
			
		||||
      throw new Error(
 | 
			
		||||
        `The string '${version}' is not valid SemVer notation for Java version. Please check README file for code snippets and more detailed information`
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      version: new semver.Range(version),
 | 
			
		||||
      stable
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								src/distributions/base-models.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/distributions/base-models.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
export interface JavaInstallerOptions {
 | 
			
		||||
  version: string;
 | 
			
		||||
  arch: string;
 | 
			
		||||
  packageType: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface JavaInstallerResults {
 | 
			
		||||
  version: string;
 | 
			
		||||
  path: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface JavaDownloadRelease {
 | 
			
		||||
  version: string;
 | 
			
		||||
  url: string;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								src/distributions/distribution-factory.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/distributions/distribution-factory.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
import { AdoptiumDistribution } from './adoptium/installer';
 | 
			
		||||
import { JavaBase } from './base-installer';
 | 
			
		||||
import { JavaInstallerOptions } from './base-models';
 | 
			
		||||
import { LocalDistribution } from './local/installer';
 | 
			
		||||
import { ZuluDistribution } from './zulu/installer';
 | 
			
		||||
 | 
			
		||||
enum JavaDistribution {
 | 
			
		||||
  Adoptium = 'adoptium',
 | 
			
		||||
  Zulu = 'zulu',
 | 
			
		||||
  JdkFile = 'jdkfile'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getJavaDistribution(
 | 
			
		||||
  distributionName: string,
 | 
			
		||||
  installerOptions: JavaInstallerOptions,
 | 
			
		||||
  jdkFile?: string
 | 
			
		||||
): JavaBase | null {
 | 
			
		||||
  switch (distributionName) {
 | 
			
		||||
    case JavaDistribution.JdkFile:
 | 
			
		||||
      return new LocalDistribution(installerOptions, jdkFile);
 | 
			
		||||
    case JavaDistribution.Adoptium:
 | 
			
		||||
      return new AdoptiumDistribution(installerOptions);
 | 
			
		||||
    case JavaDistribution.Zulu:
 | 
			
		||||
      return new ZuluDistribution(installerOptions);
 | 
			
		||||
    default:
 | 
			
		||||
      return null;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										77
									
								
								src/distributions/local/installer.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/distributions/local/installer.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,77 @@
 | 
			
		|||
import * as tc from '@actions/tool-cache';
 | 
			
		||||
import * as core from '@actions/core';
 | 
			
		||||
 | 
			
		||||
import fs from 'fs';
 | 
			
		||||
import path from 'path';
 | 
			
		||||
import semver from 'semver';
 | 
			
		||||
 | 
			
		||||
import { JavaBase } from '../base-installer';
 | 
			
		||||
import { JavaInstallerOptions, JavaDownloadRelease, JavaInstallerResults } from '../base-models';
 | 
			
		||||
import { extractJdkFile } from '../../util';
 | 
			
		||||
import { macOSJavaContentDir } from '../../constants';
 | 
			
		||||
 | 
			
		||||
export class LocalDistribution extends JavaBase {
 | 
			
		||||
  constructor(installerOptions: JavaInstallerOptions, private jdkFile?: string) {
 | 
			
		||||
    super('jdkfile', installerOptions);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async setupJava(): Promise<JavaInstallerResults> {
 | 
			
		||||
    let foundJava = this.findInToolcache();
 | 
			
		||||
 | 
			
		||||
    if (foundJava) {
 | 
			
		||||
      core.info(`Resolved Java ${foundJava.version} from tool-cache`);
 | 
			
		||||
    } else {
 | 
			
		||||
      core.info(
 | 
			
		||||
        `Java ${this.version.raw} is not found in tool-cache. Trying to unpack JDK file...`
 | 
			
		||||
      );
 | 
			
		||||
      if (!this.jdkFile) {
 | 
			
		||||
        throw new Error("'jdkFile' is not specified");
 | 
			
		||||
      }
 | 
			
		||||
      const jdkFilePath = path.resolve(this.jdkFile);
 | 
			
		||||
      const stats = fs.statSync(jdkFilePath);
 | 
			
		||||
 | 
			
		||||
      if (!stats.isFile()) {
 | 
			
		||||
        throw new Error(`JDK file is not found in path '${jdkFilePath}'`);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      core.info(`Extracting Java from '${jdkFilePath}'`);
 | 
			
		||||
 | 
			
		||||
      const extractedJavaPath = await extractJdkFile(jdkFilePath);
 | 
			
		||||
      const archiveName = fs.readdirSync(extractedJavaPath)[0];
 | 
			
		||||
      const archivePath = path.join(extractedJavaPath, archiveName);
 | 
			
		||||
      const javaVersion = this.version.raw;
 | 
			
		||||
 | 
			
		||||
      let javaPath = await tc.cacheDir(
 | 
			
		||||
        archivePath,
 | 
			
		||||
        this.toolcacheFolderName,
 | 
			
		||||
        this.getToolcacheVersionName(javaVersion),
 | 
			
		||||
        this.architecture
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      if (
 | 
			
		||||
        process.platform === 'darwin' &&
 | 
			
		||||
        fs.existsSync(path.join(javaPath, macOSJavaContentDir))
 | 
			
		||||
      ) {
 | 
			
		||||
        javaPath = path.join(javaPath, macOSJavaContentDir);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      foundJava = {
 | 
			
		||||
        version: javaVersion,
 | 
			
		||||
        path: javaPath
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    core.info(`Setting Java ${foundJava.version} as default`);
 | 
			
		||||
 | 
			
		||||
    this.setJavaDefault(foundJava.version, foundJava.path);
 | 
			
		||||
    return foundJava;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected async findPackageForDownload(version: semver.Range): Promise<JavaDownloadRelease> {
 | 
			
		||||
    throw new Error('Should not be implemented');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected async downloadTool(javaRelease: JavaDownloadRelease): Promise<JavaInstallerResults> {
 | 
			
		||||
    throw new Error('Should not be implemented');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										163
									
								
								src/distributions/zulu/installer.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								src/distributions/zulu/installer.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,163 @@
 | 
			
		|||
import * as core from '@actions/core';
 | 
			
		||||
import * as tc from '@actions/tool-cache';
 | 
			
		||||
 | 
			
		||||
import path from 'path';
 | 
			
		||||
import fs from 'fs';
 | 
			
		||||
import semver from 'semver';
 | 
			
		||||
 | 
			
		||||
import { JavaBase } from '../base-installer';
 | 
			
		||||
import { IZuluVersions } from './models';
 | 
			
		||||
import { extractJdkFile, getDownloadArchiveExtension } from '../../util';
 | 
			
		||||
import { JavaDownloadRelease, JavaInstallerOptions, JavaInstallerResults } from '../base-models';
 | 
			
		||||
 | 
			
		||||
export class ZuluDistribution extends JavaBase {
 | 
			
		||||
  constructor(installerOptions: JavaInstallerOptions) {
 | 
			
		||||
    super('Zulu', installerOptions);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected async findPackageForDownload(version: semver.Range): Promise<JavaDownloadRelease> {
 | 
			
		||||
    const availableVersionsRaw = await this.getAvailableVersions();
 | 
			
		||||
    const availableVersions = availableVersionsRaw.map(item => {
 | 
			
		||||
      return {
 | 
			
		||||
        version: this.convertVersionToSemver(item.jdk_version),
 | 
			
		||||
        url: item.url,
 | 
			
		||||
        zuluVersion: this.convertVersionToSemver(item.zulu_version)
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const satisfiedVersions = availableVersions
 | 
			
		||||
      .filter(item => semver.satisfies(item.version, version))
 | 
			
		||||
      .sort((a, b) => {
 | 
			
		||||
        // Azul provides two versions: jdk_version and azul_version
 | 
			
		||||
        // we should sort by both fields by descending
 | 
			
		||||
        return (
 | 
			
		||||
          -semver.compareBuild(a.version, b.version) ||
 | 
			
		||||
          -semver.compareBuild(a.zuluVersion, b.zuluVersion)
 | 
			
		||||
        );
 | 
			
		||||
      })
 | 
			
		||||
      .map(item => {
 | 
			
		||||
        return {
 | 
			
		||||
          version: item.version,
 | 
			
		||||
          url: item.url
 | 
			
		||||
        } as JavaDownloadRelease;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    const resolvedFullVersion = satisfiedVersions.length > 0 ? satisfiedVersions[0] : null;
 | 
			
		||||
    if (!resolvedFullVersion) {
 | 
			
		||||
      const availableOptions = availableVersions.map(item => item.version).join(', ');
 | 
			
		||||
      const availableOptionsMessage = availableOptions
 | 
			
		||||
        ? `\nAvailable versions: ${availableOptions}`
 | 
			
		||||
        : '';
 | 
			
		||||
      throw new Error(
 | 
			
		||||
        `Could not find satisfied version for semver ${version.raw}. ${availableOptionsMessage}`
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return resolvedFullVersion;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected async downloadTool(javaRelease: JavaDownloadRelease): Promise<JavaInstallerResults> {
 | 
			
		||||
    let extractedJavaPath: string;
 | 
			
		||||
 | 
			
		||||
    core.info(
 | 
			
		||||
      `Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`
 | 
			
		||||
    );
 | 
			
		||||
    const javaArchivePath = await tc.downloadTool(javaRelease.url);
 | 
			
		||||
 | 
			
		||||
    core.info(`Extracting Java archive...`);
 | 
			
		||||
    let extension = getDownloadArchiveExtension();
 | 
			
		||||
 | 
			
		||||
    extractedJavaPath = await extractJdkFile(javaArchivePath, extension);
 | 
			
		||||
 | 
			
		||||
    const archiveName = fs.readdirSync(extractedJavaPath)[0];
 | 
			
		||||
    const archivePath = path.join(extractedJavaPath, archiveName);
 | 
			
		||||
 | 
			
		||||
    const javaPath = await tc.cacheDir(
 | 
			
		||||
      archivePath,
 | 
			
		||||
      this.toolcacheFolderName,
 | 
			
		||||
      this.getToolcacheVersionName(javaRelease.version),
 | 
			
		||||
      this.architecture
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return { version: javaRelease.version, path: javaPath };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async getAvailableVersions(): Promise<IZuluVersions[]> {
 | 
			
		||||
    const { arch, hw_bitness, abi } = this.getArchitectureOptions();
 | 
			
		||||
    const [bundleType, features] = this.packageType.split('+');
 | 
			
		||||
    const platform = this.getPlatformOption();
 | 
			
		||||
    const extension = getDownloadArchiveExtension();
 | 
			
		||||
    const javafx = features?.includes('fx') ?? false;
 | 
			
		||||
    const releaseStatus = this.stable ? 'ga' : 'ea';
 | 
			
		||||
 | 
			
		||||
    console.time('azul-retrieve-available-versions');
 | 
			
		||||
    const requestArguments = [
 | 
			
		||||
      `os=${platform}`,
 | 
			
		||||
      `ext=${extension}`,
 | 
			
		||||
      `bundle_type=${bundleType}`,
 | 
			
		||||
      `javafx=${javafx}`,
 | 
			
		||||
      `arch=${arch}`,
 | 
			
		||||
      `hw_bitness=${hw_bitness}`,
 | 
			
		||||
      `release_status=${releaseStatus}`,
 | 
			
		||||
      abi ? `abi=${abi}` : null,
 | 
			
		||||
      features ? `features=${features}` : null
 | 
			
		||||
    ]
 | 
			
		||||
      .filter(Boolean)
 | 
			
		||||
      .join('&');
 | 
			
		||||
 | 
			
		||||
    const availableVersionsUrl = `https://api.azul.com/zulu/download/community/v1.0/bundles/?${requestArguments}`;
 | 
			
		||||
    if (core.isDebug()) {
 | 
			
		||||
      core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const availableVersions =
 | 
			
		||||
      (await this.http.getJson<Array<IZuluVersions>>(availableVersionsUrl)).result ?? [];
 | 
			
		||||
 | 
			
		||||
    if (core.isDebug()) {
 | 
			
		||||
      core.startGroup('Print information about available versions');
 | 
			
		||||
      console.timeEnd('azul-retrieve-available-versions');
 | 
			
		||||
      console.log(`Available versions: [${availableVersions.length}]`);
 | 
			
		||||
      console.log(availableVersions.map(item => item.jdk_version.join('.')).join(', '));
 | 
			
		||||
      core.endGroup();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return availableVersions;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private getArchitectureOptions(): {
 | 
			
		||||
    arch: string;
 | 
			
		||||
    hw_bitness: string;
 | 
			
		||||
    abi: string;
 | 
			
		||||
  } {
 | 
			
		||||
    if (this.architecture == 'x64') {
 | 
			
		||||
      return { arch: 'x86', hw_bitness: '64', abi: '' };
 | 
			
		||||
    } else if (this.architecture == 'x86') {
 | 
			
		||||
      return { arch: 'x86', hw_bitness: '32', abi: '' };
 | 
			
		||||
    } else {
 | 
			
		||||
      return { arch: this.architecture, hw_bitness: '', abi: '' };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private getPlatformOption(): string {
 | 
			
		||||
    // Azul has own platform names so need to map them
 | 
			
		||||
    switch (process.platform) {
 | 
			
		||||
      case 'darwin':
 | 
			
		||||
        return 'macos';
 | 
			
		||||
      case 'win32':
 | 
			
		||||
        return 'windows';
 | 
			
		||||
      default:
 | 
			
		||||
        return process.platform;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Azul API returns jdk_version as array of digits like [11, 0, 2, 1]
 | 
			
		||||
  private convertVersionToSemver(version_array: number[]) {
 | 
			
		||||
    const mainVersion = version_array.slice(0, 3).join('.');
 | 
			
		||||
    if (version_array.length > 3) {
 | 
			
		||||
      // intentionally ignore more than 4 numbers because it is invalid semver
 | 
			
		||||
      return `${mainVersion}+${version_array[3]}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return mainVersion;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								src/distributions/zulu/models.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/distributions/zulu/models.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
export interface IZuluVersions {
 | 
			
		||||
  id: number;
 | 
			
		||||
  name: string;
 | 
			
		||||
  url: string;
 | 
			
		||||
  jdk_version: Array<number>;
 | 
			
		||||
  zulu_version: Array<number>;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue