From d9ce52374bc1910e28928bdfd713ad58682579d7 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Fri, 14 Jul 2023 14:59:35 +0200 Subject: [PATCH 1/4] feat(cli): extract compression service --- src/cli/services/compression.service.ts | 32 +++++++++++++++++++++++++ src/cli/services/index.ts | 10 +++++++- src/cli/tools/dart/index.ts | 12 +++++++--- src/cli/tools/docker/index.ts | 23 +++++++++++------- 4 files changed, 64 insertions(+), 13 deletions(-) create mode 100644 src/cli/services/compression.service.ts diff --git a/src/cli/services/compression.service.ts b/src/cli/services/compression.service.ts new file mode 100644 index 0000000000..fc76449d12 --- /dev/null +++ b/src/cli/services/compression.service.ts @@ -0,0 +1,32 @@ +import { createReadStream } from 'node:fs'; +import { pipeline } from 'node:stream/promises'; +import { execa } from 'execa'; +import { injectable } from 'inversify'; +import tar from 'tar'; + +export interface ExtractConfig { + file: string; + cwd: string; + strip?: number; + + files?: string[]; +} + +@injectable() +export class CompressionService { + async extract({ file, cwd, strip, files }: ExtractConfig): Promise { + if (file.endsWith('.zip')) { + await execa('bsdtar', [ + '-xf', + file, + '-C', + cwd, + ...(strip ? ['--strip', `${strip}`] : []), + ...(files ?? []), + ]); + return; + } + + await pipeline(createReadStream(file), tar.x({ cwd, strip }, files)); + } +} diff --git a/src/cli/services/index.ts b/src/cli/services/index.ts index 01bbac3dc5..0c1e16cd2e 100644 --- a/src/cli/services/index.ts +++ b/src/cli/services/index.ts @@ -1,10 +1,17 @@ import { Container } from 'inversify'; +import { CompressionService } from './compression.service'; import { EnvService } from './env.service'; import { HttpService } from './http.service'; import { PathService } from './path.service'; import { VersionService } from './version.service'; -export { EnvService, PathService, VersionService, HttpService }; +export { + EnvService, + PathService, + VersionService, + HttpService, + CompressionService as ExtractService, +}; export const rootContainer = new Container(); @@ -12,3 +19,4 @@ rootContainer.bind(EnvService).toSelf(); rootContainer.bind(PathService).toSelf(); rootContainer.bind(VersionService).toSelf(); rootContainer.bind(HttpService).toSelf(); +rootContainer.bind(CompressionService).toSelf(); diff --git a/src/cli/tools/dart/index.ts b/src/cli/tools/dart/index.ts index 55e4b77df9..87516d1bbc 100644 --- a/src/cli/tools/dart/index.ts +++ b/src/cli/tools/dart/index.ts @@ -5,7 +5,12 @@ 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 { + EnvService, + ExtractService, + HttpService, + PathService, +} from '../../services'; // Dart SDK sample urls // https://storage.googleapis.com/dart-archive/channels/stable/release/1.11.0/sdk/dartsdk-linux-x64-release.zip @@ -60,7 +65,8 @@ export class InstallDartService extends InstallToolBaseService { constructor( @inject(EnvService) envSvc: EnvService, @inject(PathService) pathSvc: PathService, - @inject(HttpService) private http: HttpService + @inject(HttpService) private http: HttpService, + @inject(ExtractService) private extract: ExtractService ) { super(pathSvc, envSvc); } @@ -91,7 +97,7 @@ export class InstallDartService extends InstallToolBaseService { }); const path = await this.pathSvc.createVersionedToolPath(this.name, version); - await execa('bsdtar', ['-xf', file, '-C', path, '--strip', '1']); + await this.extract.extract({ file, cwd: path, strip: 1 }); } override async link(version: string): Promise { diff --git a/src/cli/tools/docker/index.ts b/src/cli/tools/docker/index.ts index 05af988e9e..02836cc8e2 100644 --- a/src/cli/tools/docker/index.ts +++ b/src/cli/tools/docker/index.ts @@ -1,13 +1,15 @@ -import { createReadStream } from 'node:fs'; import fs from 'node:fs/promises'; import { join } from 'node:path'; -import { pipeline } from 'node:stream/promises'; import { execa } from 'execa'; import { inject, injectable } from 'inversify'; -import tar from 'tar'; 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 { + EnvService, + ExtractService, + HttpService, + PathService, +} from '../../services'; @injectable() export class PrepareDockerService extends PrepareToolBaseService { @@ -39,7 +41,8 @@ export class InstallDockerService extends InstallToolBaseService { constructor( @inject(EnvService) envSvc: EnvService, @inject(PathService) pathSvc: PathService, - @inject(HttpService) private http: HttpService + @inject(HttpService) private http: HttpService, + @inject(ExtractService) private extract: ExtractService ) { super(pathSvc, envSvc); } @@ -53,10 +56,12 @@ export class InstallDockerService extends InstallToolBaseService { 'bin' ); await fs.mkdir(path); - await pipeline( - createReadStream(file), - tar.x({ cwd: path, strip: 1 }, ['docker/docker']) - ); + await this.extract.extract({ + file, + cwd: path, + strip: 1, + files: ['docker/docker'], + }); } override async link(version: string): Promise { From 27bd70ad38ff98e8b1706fe72e80c6531e3d9625 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Fri, 14 Jul 2023 15:02:10 +0200 Subject: [PATCH 2/4] fix: rename class --- src/cli/services/index.ts | 2 +- src/cli/tools/dart/index.ts | 6 +++--- src/cli/tools/docker/index.ts | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cli/services/index.ts b/src/cli/services/index.ts index 0c1e16cd2e..2f18a7429b 100644 --- a/src/cli/services/index.ts +++ b/src/cli/services/index.ts @@ -10,7 +10,7 @@ export { PathService, VersionService, HttpService, - CompressionService as ExtractService, + CompressionService, }; export const rootContainer = new Container(); diff --git a/src/cli/tools/dart/index.ts b/src/cli/tools/dart/index.ts index 87516d1bbc..40c5e0c24d 100644 --- a/src/cli/tools/dart/index.ts +++ b/src/cli/tools/dart/index.ts @@ -6,8 +6,8 @@ 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, - ExtractService, HttpService, PathService, } from '../../services'; @@ -66,7 +66,7 @@ export class InstallDartService extends InstallToolBaseService { @inject(EnvService) envSvc: EnvService, @inject(PathService) pathSvc: PathService, @inject(HttpService) private http: HttpService, - @inject(ExtractService) private extract: ExtractService + @inject(CompressionService) private compress: CompressionService ) { super(pathSvc, envSvc); } @@ -97,7 +97,7 @@ export class InstallDartService extends InstallToolBaseService { }); const path = await this.pathSvc.createVersionedToolPath(this.name, version); - await this.extract.extract({ file, cwd: path, strip: 1 }); + await this.compress.extract({ file, cwd: path, strip: 1 }); } override async link(version: string): Promise { diff --git a/src/cli/tools/docker/index.ts b/src/cli/tools/docker/index.ts index 02836cc8e2..3acda158cc 100644 --- a/src/cli/tools/docker/index.ts +++ b/src/cli/tools/docker/index.ts @@ -5,8 +5,8 @@ import { inject, injectable } from 'inversify'; import { InstallToolBaseService } from '../../install-tool/install-tool-base.service'; import { PrepareToolBaseService } from '../../prepare-tool/prepare-tool-base.service'; import { + CompressionService, EnvService, - ExtractService, HttpService, PathService, } from '../../services'; @@ -42,7 +42,7 @@ export class InstallDockerService extends InstallToolBaseService { @inject(EnvService) envSvc: EnvService, @inject(PathService) pathSvc: PathService, @inject(HttpService) private http: HttpService, - @inject(ExtractService) private extract: ExtractService + @inject(CompressionService) private compress: CompressionService ) { super(pathSvc, envSvc); } @@ -56,7 +56,7 @@ export class InstallDockerService extends InstallToolBaseService { 'bin' ); await fs.mkdir(path); - await this.extract.extract({ + await this.compress.extract({ file, cwd: path, strip: 1, From 1b7fae4bdee7465e4c83db7805e6564dcc21b47b Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Fri, 14 Jul 2023 15:32:09 +0200 Subject: [PATCH 3/4] fix(cli): validate version --- src/cli/install-tool/install-tool-base.service.ts | 14 +++++++++----- src/cli/install-tool/install-tool.service.ts | 2 +- src/cli/tools/dart/index.ts | 2 +- src/cli/tools/docker/index.ts | 2 +- src/cli/utils/versions.ts | 4 ++++ 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/cli/install-tool/install-tool-base.service.ts b/src/cli/install-tool/install-tool-base.service.ts index 5b78c88e34..5ecf682090 100644 --- a/src/cli/install-tool/install-tool-base.service.ts +++ b/src/cli/install-tool/install-tool-base.service.ts @@ -2,10 +2,10 @@ import { chmod, chown, stat, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; import { injectable } from 'inversify'; import type { EnvService, PathService } from '../services'; -import { fileRights, logger } from '../utils'; +import { fileRights, isValid, logger } from '../utils'; export interface ShellWrapperConfig { - name: string; + name?: string; srcDir: string; exports?: string; } @@ -32,6 +32,10 @@ export abstract class InstallToolBaseService { abstract link(version: string): Promise; + needsPrepare(): boolean { + return true; + } + test(_version: string): Promise { return Promise.resolve(); } @@ -40,8 +44,8 @@ export abstract class InstallToolBaseService { return this.name; } - validate(_version: string): boolean { - return true; + validate(version: string): boolean { + return isValid(version); } protected async shellwrapper({ @@ -49,7 +53,7 @@ export abstract class InstallToolBaseService { srcDir, exports, }: ShellWrapperConfig): Promise { - const tgt = join(this.pathSvc.binDir, name); + const tgt = join(this.pathSvc.binDir, name ?? this.name); let content = `#!/bin/bash diff --git a/src/cli/install-tool/install-tool.service.ts b/src/cli/install-tool/install-tool.service.ts index 94110639bf..c957cad267 100644 --- a/src/cli/install-tool/install-tool.service.ts +++ b/src/cli/install-tool/install-tool.service.ts @@ -38,7 +38,7 @@ export class InstallToolService { return; } - if (!(await this.pathSvc.findToolPath(tool))) { + if (toolSvc.needsPrepare() && !(await this.pathSvc.findToolPath(tool))) { logger.debug({ tool }, 'tool not prepared'); const res = await prepareTools([tool], dryRun); if (res) { diff --git a/src/cli/tools/dart/index.ts b/src/cli/tools/dart/index.ts index 40c5e0c24d..c2055c9fb8 100644 --- a/src/cli/tools/dart/index.ts +++ b/src/cli/tools/dart/index.ts @@ -103,7 +103,7 @@ export class InstallDartService extends InstallToolBaseService { override async link(version: string): Promise { const src = join(this.pathSvc.versionedToolPath(this.name, version), 'bin'); - await this.shellwrapper({ name: 'dart', srcDir: src }); + await this.shellwrapper({ srcDir: src }); } override async test(_version: string): Promise { diff --git a/src/cli/tools/docker/index.ts b/src/cli/tools/docker/index.ts index 3acda158cc..b802e35b6b 100644 --- a/src/cli/tools/docker/index.ts +++ b/src/cli/tools/docker/index.ts @@ -67,7 +67,7 @@ export class InstallDockerService extends InstallToolBaseService { override async link(version: string): Promise { const src = join(this.pathSvc.versionedToolPath(this.name, version), 'bin'); - await this.shellwrapper({ name: 'docker', srcDir: src }); + await this.shellwrapper({ srcDir: src }); } override async test(_version: string): Promise { diff --git a/src/cli/utils/versions.ts b/src/cli/utils/versions.ts index 8d6c60387f..26354b4cce 100644 --- a/src/cli/utils/versions.ts +++ b/src/cli/utils/versions.ts @@ -1,6 +1,10 @@ import semver from 'semver'; import { type StrictValidator, makeValidator } from 'typanion'; +export function isValid(version: string): boolean { + return semver.valid(version) !== null; +} + export function validateSemver(): StrictValidator { return makeValidator({ test: (value, state): value is string => { From 5ac57f3c4118ccef20fac65fc46bad0117b9a843 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Fri, 14 Jul 2023 16:22:35 +0200 Subject: [PATCH 4/4] fix: missing fallback --- src/cli/install-tool/install-tool-base.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/install-tool/install-tool-base.service.ts b/src/cli/install-tool/install-tool-base.service.ts index 5ecf682090..418b66a79d 100644 --- a/src/cli/install-tool/install-tool-base.service.ts +++ b/src/cli/install-tool/install-tool-base.service.ts @@ -66,7 +66,7 @@ export abstract class InstallToolBaseService { content += `export ${exports}\n`; } - content += `${srcDir}/${name} "$@"\n`; + content += `${srcDir}/${name ?? this.name} "$@"\n`; await writeFile(tgt, content, { encoding: 'utf8' }); await this.setOwner({ file: tgt });