diff --git a/README.md b/README.md index 5a76cd21c2..303851c2c8 100644 --- a/README.md +++ b/README.md @@ -94,8 +94,12 @@ RUN install-tool docker 20.10.7 ### Url replacement You can replace the default urls used to download the tools. -This is currently only supported by the `docker` tool installer. -Checkout #1067 for additional support. +This is currently only supported by these tool installers: + +- `docker` +- `dart` + +Checkout [#1067](https://github.com/containerbase/base/issues/1067) for additional support. ```Dockerfile FROM containerbase/base diff --git a/src/cli/install-tool/index.ts b/src/cli/install-tool/index.ts index 3aec8f61f1..4b56b2afe2 100644 --- a/src/cli/install-tool/index.ts +++ b/src/cli/install-tool/index.ts @@ -1,5 +1,6 @@ import { Container } from 'inversify'; import { rootContainer } from '../services'; +import { InstallDartService } from '../tools/dart'; import { InstallDockerService } from '../tools/docker'; import { logger } from '../utils'; import { InstallLegacyToolService } from './install-legacy-tool.service'; @@ -16,6 +17,7 @@ function prepareContainer(): Container { // tool services container.bind(INSTALL_TOOL_TOKEN).to(InstallDockerService); + container.bind(INSTALL_TOOL_TOKEN).to(InstallDartService); logger.trace('preparing container done'); return container; diff --git a/src/cli/prepare-tool/index.ts b/src/cli/prepare-tool/index.ts index 349b96d304..6c7955a009 100644 --- a/src/cli/prepare-tool/index.ts +++ b/src/cli/prepare-tool/index.ts @@ -1,5 +1,6 @@ import { Container } from 'inversify'; import { rootContainer } from '../services'; +import { PrepareDartService } from '../tools/dart'; import { PrepareDockerService } from '../tools/docker'; import { logger } from '../utils'; import { PrepareLegacyToolsService } from './prepare-legacy-tools.service'; @@ -16,6 +17,7 @@ function prepareContainer(): Container { // tool services container.bind(PREPARE_TOOL_TOKEN).to(PrepareDockerService); + container.bind(PREPARE_TOOL_TOKEN).to(PrepareDartService); logger.trace('preparing container done'); return container; diff --git a/src/cli/services/env.service.ts b/src/cli/services/env.service.ts index dc44568a3f..d18ac35e14 100644 --- a/src/cli/services/env.service.ts +++ b/src/cli/services/env.service.ts @@ -30,6 +30,11 @@ export class EnvService { return env.CONTAINERBASE_CACHE_DIR ?? null; } + get home(): string { + // TODO: validate + return env.HOME!; + } + get isRoot(): boolean { return this.uid === 0; } diff --git a/src/cli/tools/dart/index.ts b/src/cli/tools/dart/index.ts new file mode 100644 index 0000000000..db2c1696ca --- /dev/null +++ b/src/cli/tools/dart/index.ts @@ -0,0 +1,109 @@ +import fs from 'node:fs/promises'; +import { join } 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 { EnvService, HttpService, PathService } from '../../services'; +import { logger } from '../../utils'; + +// Dart SDK sample urls +// https://storage.googleapis.com/dart-archive/channels/stable/release/1.11.0/sdk/dartsdk-linux-x64-release.zip +// https://storage.googleapis.com/dart-archive/channels/stable/release/2.18.0/sdk/dartsdk-linux-x64-release.zip +// https://storage.googleapis.com/dart-archive/channels/stable/release/2.19.4/sdk/dartsdk-linux-x64-release.zip.sha256sum +// https://storage.googleapis.com/dart-archive/channels/stable/release/2.19.4/sdk/dartsdk-linux-arm64-release.zip +// https://storage.googleapis.com/dart-archive/channels/stable/release/2.19.4/sdk/dartsdk-linux-arm64-release.zip.sha256sum + +@injectable() +export class PrepareDartService extends PrepareToolBaseService { + readonly name = 'dart'; + + constructor(@inject(EnvService) private envSvc: EnvService) { + super(); + } + + async execute(): Promise { + await fs.mkdir(`${this.envSvc.home}/.dart`); + await fs.writeFile( + `${this.envSvc.home}/.dart/dartdev.json`, + '{ "firstRun": false, "enabled": false }' + ); + await fs.mkdir(`${this.envSvc.userHome}/.dart`); + await fs.writeFile( + `${this.envSvc.userHome}/.dart/dartdev.json`, + '{ "firstRun": false, "enabled": false }' + ); + + // fs isn't recursive, so we use system binaries + await execa('chown', [ + '-R', + this.envSvc.userName, + `${this.envSvc.userHome}/.dart`, + ]); + await execa('chmod', ['-R', 'g=u', `${this.envSvc.userHome}/.dart`]); + } +} + +@injectable() +export class InstallDartService extends InstallToolBaseService { + readonly name = 'dart'; + + private get arch(): string { + switch (this.envSvc.arch) { + case 'arm64': + return 'arm64'; + case 'amd64': + return 'x64'; + } + } + + constructor( + @inject(EnvService) envSvc: EnvService, + @inject(PathService) pathSvc: PathService, + @inject(HttpService) private http: HttpService + ) { + super(pathSvc, envSvc); + } + + override async install(version: string): Promise { + const ver = semver.parse(version); + if (!ver) { + throw new Error(`Invalid version: ${version}`); + } + if (ver.major < 2) { + throw new Error(`Dart SDK version < v2 is not supported: ${version}`); + } + const channel = 'stable'; + const sdkUrl = `https://storage.googleapis.com/dart-archive/channels/${channel}/release/${version}/sdk`; + const sdkFile = `dartsdk-linux-${this.arch}-release.zip`; + const url = `${sdkUrl}/${sdkFile}`; + + logger.debug({ url: `${url}.sha256sum` }, `download ${this.name} checksum`); + const checksumFile = await this.http.download({ url: `${url}.sha256sum` }); + const expectedChecksum = (await fs.readFile(checksumFile, 'utf-8')) + .split('\n') + .find((l) => l.includes(sdkFile)) + ?.split(' ')[0]; + + logger.debug({ url }, `download ${this.name}`); + const file = await this.http.download({ + url, + expectedChecksum, + checksumType: 'sha256', + }); + + const path = await this.pathSvc.createVersionedToolPath(this.name, version); + await execa('bsdtar', ['-xf', file, '-C', path, '--strip', '1']); + } + + override async link(version: string): Promise { + const src = join(this.pathSvc.versionedToolPath(this.name, version), 'bin'); + + await this.shellwrapper({ name: 'dart', srcDir: src }); + } + + override async test(_version: string): Promise { + await execa('dart', ['--version'], { stdio: 'inherit' }); + } +} diff --git a/src/usr/local/containerbase/tools/v2/dart.sh b/src/usr/local/containerbase/tools/v2/dart.sh deleted file mode 100644 index 96a48d85d9..0000000000 --- a/src/usr/local/containerbase/tools/v2/dart.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash - -function prepare_tool() { - mkdir ~/.dart "${USER_HOME}/.dart" - echo '{ "firstRun": false, "enabled": false }' > ~/.dart/dartdev.json - echo '{ "firstRun": false, "enabled": false }' > "${USER_HOME}/.dart/dartdev.json" - chown -R "${USER_NAME}" "${USER_HOME}/.dart" - chmod -R g=u "${USER_HOME}/.dart/" -} - -# https://storage.googleapis.com/dart-archive/channels/stable/release/1.11.0/sdk/dartsdk-linux-x64-release.zip -# https://storage.googleapis.com/dart-archive/channels/stable/release/2.18.0/sdk/dartsdk-linux-x64-release.zip -# https://storage.googleapis.com/dart-archive/channels/stable/release/2.19.4/sdk/dartsdk-linux-x64-release.zip.sha256sum -# https://storage.googleapis.com/dart-archive/channels/stable/release/2.19.4/sdk/dartsdk-linux-arm64-release.zip -# https://storage.googleapis.com/dart-archive/channels/stable/release/2.19.4/sdk/dartsdk-linux-arm64-release.zip.sha256sum -function install_tool () { - local versioned_tool_path - local DART_SDK_CHANNEL="stable" - local DART_SDK_URL="https://storage.googleapis.com/dart-archive/channels/${DART_SDK_CHANNEL}/release/${TOOL_VERSION}/sdk" - local file - local arch=x64 - - local dart_file - local checksum_file - local expected_checksum - - if [[ "${ARCHITECTURE}" = "aarch64" ]]; then - arch=arm64 - fi - - # do we need those old unsupported dart versions? - # if [[ $MAJOR -lt 1 || ($MAJOR -eq 1 && $MINOR -lt 11) ]]; then - # echo "dart < 1.11.0 is not supported: ${MAJOR}.${MINOR}" >&2 - # exit 1 - # fi - - if [ "$MAJOR" -lt 2 ]; then - echo "dart < 2.0.0 is not supported: ${MAJOR}.${MINOR}" >&2 - exit 1 - fi - - dart_file="dartsdk-linux-${arch}-release.zip" - checksum_file=$(get_from_url "${DART_SDK_URL}/${dart_file}.sha256sum") - # get checksum from file - expected_checksum=$(grep "\\*${dart_file}" "${checksum_file}" | cut -d' ' -f1) - - file=$(get_from_url "${DART_SDK_URL}/${dart_file}" "${dart_file}" "${expected_checksum}" "sha256sum") - - versioned_tool_path=$(create_versioned_tool_path) - bsdtar -C "${versioned_tool_path}" --strip 1 -xf "${file}" -} - -function link_tool () { - local versioned_tool_path - versioned_tool_path=$(find_versioned_tool_path) - - shell_wrapper "${TOOL_NAME}" "${versioned_tool_path}/bin" - - [[ -n $SKIP_VERSION ]] || dart --version -}