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

feat(cli): convert java installer #2749

Merged
merged 6 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/cli/install-tool/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
import { InstallFlutterService } from '../tools/flutter';
import { InstallFluxService } from '../tools/flux';
import { InstallGleamService } from '../tools/gleam';
import {
InstallJavaJdkService,
InstallJavaJreService,
InstallJavaService,
} from '../tools/java';

Check warning on line 15 in src/cli/install-tool/index.ts

View check run for this annotation

Codecov / codecov/patch

src/cli/install-tool/index.ts#L11-L15

Added lines #L11 - L15 were not covered by tests
import { InstallMavenService } from '../tools/java/maven';
import { InstallNodeService } from '../tools/node';
import {
Expand Down Expand Up @@ -49,6 +54,9 @@
container.bind(INSTALL_TOOL_TOKEN).to(InstallDotnetService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallFlutterService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallFluxService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallJavaService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallJavaJreService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallJavaJdkService);

Check warning on line 59 in src/cli/install-tool/index.ts

View check run for this annotation

Codecov / codecov/patch

src/cli/install-tool/index.ts#L57-L59

Added lines #L57 - L59 were not covered by tests
container.bind(INSTALL_TOOL_TOKEN).to(InstallMavenService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallNodeService);
container.bind(INSTALL_TOOL_TOKEN).to(InstallRenovateService);
Expand Down
8 changes: 8 additions & 0 deletions src/cli/prepare-tool/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import { PrepareDockerService } from '../tools/docker';
import { PrepareDotnetService } from '../tools/dotnet';
import { PrepareFlutterService } from '../tools/flutter';
import {
PrepareJavaJdkService,
PrepareJavaJreService,
PrepareJavaService,
} from '../tools/java';

Check warning on line 11 in src/cli/prepare-tool/index.ts

View check run for this annotation

Codecov / codecov/patch

src/cli/prepare-tool/index.ts#L7-L11

Added lines #L7 - L11 were not covered by tests
import { logger } from '../utils';
import { PrepareLegacyToolsService } from './prepare-legacy-tools.service';
import { PREPARE_TOOL_TOKEN, PrepareToolService } from './prepare-tool.service';
Expand All @@ -22,6 +27,9 @@
container.bind(PREPARE_TOOL_TOKEN).to(PrepareDotnetService);
container.bind(PREPARE_TOOL_TOKEN).to(PrepareDockerService);
container.bind(PREPARE_TOOL_TOKEN).to(PrepareFlutterService);
container.bind(PREPARE_TOOL_TOKEN).to(PrepareJavaService);
container.bind(PREPARE_TOOL_TOKEN).to(PrepareJavaJreService);
container.bind(PREPARE_TOOL_TOKEN).to(PrepareJavaJdkService);

Check warning on line 32 in src/cli/prepare-tool/index.ts

View check run for this annotation

Codecov / codecov/patch

src/cli/prepare-tool/index.ts#L30-L32

Added lines #L30 - L32 were not covered by tests

logger.trace('preparing container done');
return container;
Expand Down
186 changes: 186 additions & 0 deletions src/cli/tools/java/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { execa } from 'execa';
import { inject, injectable } from 'inversify';
import semver from 'semver';
import { InstallToolBaseService } from '../../install-tool/install-tool-base.service';
import { PrepareToolBaseService } from '../../prepare-tool/prepare-tool-base.service';
import {
CompressionService,
EnvService,
HttpService,
PathService,
} from '../../services';
import { logger } from '../../utils';
import { resolveJavaDownloadUrl, resolveLatestJavaLtsVersion } from './utils';

@injectable()
export class PrepareJavaService extends PrepareToolBaseService {
readonly name: string = 'java';

constructor(
@inject(PathService) pathSvc: PathService,
@inject(EnvService) envSvc: EnvService,
@inject(HttpService) private readonly httpSvc: HttpService,
@inject(CompressionService)
private readonly compressionSvc: CompressionService,
) {
super(pathSvc, envSvc);
}

override async execute(): Promise<void> {
const ssl = this.pathSvc.sslPath;

if (await this.isPrepared()) {
// cert store already there
return;
}

const version = await resolveLatestJavaLtsVersion(
this.httpSvc,
'jre',
this.envSvc.arch,
);
if (!version) {
throw new Error('Could not resolve latest java version');
}

const pkg = await resolveJavaDownloadUrl(
this.httpSvc,
'jre',
this.envSvc.arch,
version,
);

if (!pkg) {
throw new Error(`Could not resolve download url for java ${version}`);
}

logger.debug(`downloading cacerts from ${version}`);

const jre = await this.httpSvc.download({
url: pkg.link,
checksumType: 'sha256',
expectedChecksum: pkg.checksum,
fileName: pkg.name,
});

const tmp = path.join(this.pathSvc.tmpDir, 'java');

await fs.mkdir(tmp, { recursive: true });

await this.compressionSvc.extract({ file: jre, cwd: tmp, strip: 1 });

await fs.cp(
path.join(tmp, 'lib/security/cacerts'),
path.join(ssl, 'cacerts'),
);

// cleanup will be done by caller
}

override async isPrepared(): Promise<boolean> {
return await this.pathSvc.fileExists(
path.join(this.pathSvc.sslPath, 'cacerts'),
);
}
}

@injectable()
export class PrepareJavaJdkService extends PrepareJavaService {
override readonly name = 'java-jdk';
}

@injectable()
export class PrepareJavaJreService extends PrepareJavaService {
override readonly name = 'java-jre';
}

@injectable()
export class InstallJavaService extends InstallToolBaseService {
override name: string = 'java';

constructor(
@inject(EnvService) envSvc: EnvService,
@inject(PathService) pathSvc: PathService,
@inject(HttpService) private http: HttpService,
@inject(CompressionService) private compress: CompressionService,
) {
super(pathSvc, envSvc);
}

override async install(version: string): Promise<void> {
const type = this.name === 'java-jre' ? 'jre' : 'jdk';

const pkg = await resolveJavaDownloadUrl(
this.http,
type,
this.envSvc.arch,
version,
);

if (!pkg) {
throw new Error(
`Could not resolve download url for java ${version} and type ${type}`,
);
}

const file = await this.http.download({
url: pkg.link,
checksumType: 'sha256',
expectedChecksum: pkg.checksum,
fileName: pkg.name,
});

// TODO: create recursive
if (!(await this.pathSvc.findToolPath(this.name))) {
await this.pathSvc.createToolPath(this.name);
}

const cwd = await this.pathSvc.createVersionedToolPath(this.name, version);
await this.compress.extract({
file,
cwd,
strip: 1,
});

const v = semver.parse(version);
// we've a different cacerts location in java 8 jdk
const cacerts =
v?.major === 8 && this.name !== 'java-jre'
? path.join(cwd, 'jre/lib/security/cacerts')
: path.join(cwd, 'lib/security/cacerts');
await fs.rm(cacerts);
await fs.symlink(path.join(this.pathSvc.sslPath, 'cacerts'), cacerts);
}

override async isPrepared(): Promise<boolean> {
return await this.pathSvc.fileExists(
path.join(this.pathSvc.sslPath, 'cacerts'),
);
}

override async link(version: string): Promise<void> {
const src = path.join(
this.pathSvc.versionedToolPath(this.name, version),
'bin',
);
await this.shellwrapper({ srcDir: src, name: 'java' });
}

override async test(_version: string): Promise<void> {
await execa('java', ['-version'], {
stdio: ['inherit', 'inherit', 1],
});
}
}

@injectable()
export class InstallJavaJreService extends InstallJavaService {
override readonly name = 'java-jre';
}

@injectable()
export class InstallJavaJdkService extends InstallJavaService {
override readonly name = 'java-jdk';
}

Check warning on line 186 in src/cli/tools/java/index.ts

View check run for this annotation

Codecov / codecov/patch

src/cli/tools/java/index.ts#L1-L186

Added lines #L1 - L186 were not covered by tests
32 changes: 32 additions & 0 deletions src/cli/tools/java/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { z } from 'zod';

// https://api.adoptium.net/q/swagger-ui

const AdoptiumVersionData = z.object({
semver: z.string(),
});

export const AdoptiumReleaseVersions = z.object({
versions: z.array(AdoptiumVersionData),
});

const AdoptiumPackage = z.object({
/**
* sha256 checksum
*/
checksum: z.string(),
link: z.string(),
name: z.string(),
});

export type AdoptiumPackage = z.infer<typeof AdoptiumPackage>;

const AdoptiumBinary = z.object({
package: AdoptiumPackage,
});

const AdoptiumRelease = z.object({
binaries: z.array(AdoptiumBinary),
});

export const AdoptiumReleases = z.array(AdoptiumRelease);

Check warning on line 32 in src/cli/tools/java/schema.ts

View check run for this annotation

Codecov / codecov/patch

src/cli/tools/java/schema.ts#L1-L32

Added lines #L1 - L32 were not covered by tests
44 changes: 44 additions & 0 deletions src/cli/tools/java/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { HttpService } from '../../services';
import type { Arch } from '../../utils';
import {
type AdoptiumPackage,
AdoptiumReleaseVersions,
AdoptiumReleases,
} from './schema';

export async function resolveLatestJavaLtsVersion(
http: HttpService,
type: 'jre' | 'jdk',
arch: Arch,
): Promise<string> {
const base_url = 'https://api.adoptium.net/v3/info/release_versions';
const api_args =
'heap_size=normal&os=linux&page=0&page_size=1&project=jdk&release_type=ga&lts=true&semver=true';

const res = AdoptiumReleaseVersions.parse(
await http.getJson(
`${base_url}?architecture=${arch === 'amd64' ? 'x64' : 'aarch64'}&image_type=${type}&${api_args}`,
),
);

return res.versions[0]!.semver;
}

export async function resolveJavaDownloadUrl(
http: HttpService,
type: 'jre' | 'jdk',
arch: Arch,
version: string,
): Promise<AdoptiumPackage | undefined> {
const base_url = 'https://api.adoptium.net/v3/assets/version';
const api_args =
'heap_size=normal&os=linux&page=0&page_size=1&project=jdk&semver=true';

const res = AdoptiumReleases.parse(
await http.getJson(
`${base_url}/${version}?architecture=${arch === 'amd64' ? 'x64' : 'aarch64'}&image_type=${type}&${api_args}`,
),
);

return res?.[0]?.binaries?.[0]?.package;
}

Check warning on line 44 in src/cli/tools/java/utils.ts

View check run for this annotation

Codecov / codecov/patch

src/cli/tools/java/utils.ts#L1-L44

Added lines #L1 - L44 were not covered by tests
22 changes: 0 additions & 22 deletions src/usr/local/containerbase/tools/v2/java-jdk.sh

This file was deleted.

22 changes: 0 additions & 22 deletions src/usr/local/containerbase/tools/v2/java-jre.sh

This file was deleted.

23 changes: 0 additions & 23 deletions src/usr/local/containerbase/tools/v2/java.sh

This file was deleted.

Loading