Skip to content

Commit

Permalink
feat: partial readonly fs support
Browse files Browse the repository at this point in the history
  • Loading branch information
viceice committed Sep 27, 2024
1 parent b328bd7 commit 6dc53a2
Show file tree
Hide file tree
Showing 37 changed files with 567 additions and 230 deletions.
2 changes: 2 additions & 0 deletions src/cli/command/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Cli } from 'clipanion';
import type { CliMode } from '../utils';
import { logger } from '../utils/logger';
import { DownloadFileCommand } from './download-file';
import { InitToolCommand } from './init-tool';
import { InstallGemCommand, InstallGemShortCommand } from './install-gem';
import { InstallNpmCommand, InstallNpmShortCommand } from './install-npm';
import { InstallPipCommand, InstallPipShortCommand } from './install-pip';
Expand Down Expand Up @@ -40,4 +41,5 @@ export function prepareCommands(cli: Cli, mode: CliMode | null): void {
cli.register(InstallPipCommand);
cli.register(InstallToolCommand);
cli.register(PrepareToolCommand);
cli.register(InitToolCommand);
}
26 changes: 26 additions & 0 deletions src/cli/command/init-tool.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Cli } from 'clipanion';
import { describe, expect, test, vi } from 'vitest';
import { prepareCommands } from '.';

const mocks = vi.hoisted(() => ({
installTool: vi.fn(),
prepareTools: vi.fn(),
initializeTools: vi.fn(),
}));

vi.mock('../install-tool', () => mocks);
vi.mock('../prepare-tool', () => mocks);

describe('index', () => {
test('init-tool', async () => {
const cli = new Cli({ binaryName: 'cli' });
prepareCommands(cli, null);

expect(await cli.run(['init', 'tool', 'node'])).toBe(0);
expect(mocks.initializeTools).toHaveBeenCalledOnce();
expect(mocks.initializeTools).toHaveBeenCalledWith(['node'], false);

mocks.initializeTools.mockRejectedValueOnce(new Error('test'));
expect(await cli.run(['init', 'tool', 'node'])).toBe(1);
});
});
47 changes: 47 additions & 0 deletions src/cli/command/init-tool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Command, Option } from 'clipanion';
import prettyMilliseconds from 'pretty-ms';
import { initializeTools } from '../prepare-tool';
import { logger } from '../utils';

export class InitToolCommand extends Command {
static override paths = [['init', 'tool']];

static override usage = Command.Usage({
description:
'Initialize a tool into the container. This creates missing files and directories.',
examples: [
['Initialize node', '$0 init tool node'],
['Initialize all tools', '$0 init tool all'],
],
});

tools = Option.Rest({ required: 1 });

dryRun = Option.Boolean('-d,--dry-run', false);

async execute(): Promise<number | void> {
const start = Date.now();
let error = false;
logger.info(`Initializing tools ${this.tools.join(', ')}...`);
try {
return await initializeTools(this.tools, this.dryRun);
} catch (err) {
error = true;
logger.debug(err);
if (err instanceof Error) {
logger.fatal(err.message);
}
return 1;
} finally {
if (error) {
logger.fatal(
`Initialize tools ${this.tools.join(', ')} failed in ${prettyMilliseconds(Date.now() - start)}.`,
);
} else {
logger.info(
`Initialize tools ${this.tools.join(', ')} succeded in ${prettyMilliseconds(Date.now() - start)}.`,
);
}
}
}
}
17 changes: 15 additions & 2 deletions src/cli/install-tool/base-install.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { join } from 'node:path';
import { codeBlock } from 'common-tags';
import { injectable } from 'inversify';
import type { EnvService, PathService } from '../services';
import { NoPrepareTools } from '../tools';
import { NoInitTools, NoPrepareTools } from '../tools';
import { isValid } from '../utils';

export interface ShellWrapperConfig {
Expand Down Expand Up @@ -35,12 +35,20 @@ export abstract class BaseInstallService {
return !!(await this.pathSvc.findVersionedToolPath(this.name, version));
}

async isInitialized(): Promise<boolean> {
return await this.pathSvc.isInitialized(this.name);
}

async isPrepared(): Promise<boolean> {
return null !== (await this.pathSvc.findToolPath(this.name));
return await this.pathSvc.isPrepared(this.name);
}

abstract link(version: string): Promise<void>;

needsInitialize(): boolean {
return !NoInitTools.includes(this.name);
}

needsPrepare(): boolean {
return !NoPrepareTools.includes(this.name);
}
Expand Down Expand Up @@ -78,6 +86,11 @@ export abstract class BaseInstallService {
. ${this.pathSvc.envFile}
fi
if [[ ! -f "${this.pathSvc.toolInitPath(this.name)}" ]]; then
# set logging to only warn and above to not interfere with tool output
CONTAINERBASE_LOG_LEVEL=warn containerbase-cli init tool "${this.name}"
fi
# load tool envs
for n in ${envs.join(' ')}; do
if [[ -f "${this.pathSvc.toolsPath}/\${n}/env.sh" ]]; then
Expand Down
12 changes: 11 additions & 1 deletion src/cli/install-tool/install-tool.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { deleteAsync } from 'del';
import { inject, injectable, multiInject, optional } from 'inversify';
import { prepareTools } from '../prepare-tool';
import { initializeTools, prepareTools } from '../prepare-tool';
import { EnvService, PathService, VersionService } from '../services';
import { cleanAptFiles, cleanTmpFiles, isDockerBuild, logger } from '../utils';
import type { BaseInstallService } from './base-install.service';
Expand Down Expand Up @@ -31,6 +31,8 @@ export class InstallToolService {
'supported tools',
);

await this.pathSvc.ensureBasePaths();

try {
const toolSvc = this.toolSvcs.find((t) => t.name === tool);
if (toolSvc) {
Expand All @@ -48,6 +50,14 @@ export class InstallToolService {
}
}

if (toolSvc.needsInitialize() && !(await toolSvc.isInitialized())) {
logger.debug({ tool }, 'tool not initialized');
const res = await initializeTools([tool], dryRun);
if (res) {
return res;
}
}

logger.debug({ tool }, 'validate tool');
if (!(await toolSvc.validate(version))) {
logger.fatal({ tool, version }, 'tool version not supported');
Expand Down
7 changes: 6 additions & 1 deletion src/cli/prepare-tool/base-prepare.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ export abstract class BasePrepareService {
@inject(EnvService) protected readonly envSvc: EnvService,
) {}

abstract execute(): Promise<void> | void;
prepare(): Promise<void> | void {
// noting to do;
}
initialize(): Promise<void> | void {
// noting to do;
}

toString(): string {
return this.name;
Expand Down
19 changes: 14 additions & 5 deletions src/cli/prepare-tool/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'node:fs/promises';
import { beforeAll, describe, expect, test, vi } from 'vitest';
import { rootPath } from '../../../test/path';
import { prepareTools } from '.';
import { initializeTools, prepareTools } from '.';

vi.mock('del');
vi.mock('execa');
Expand All @@ -15,13 +15,22 @@ vi.mock('node:process', async (importOriginal) => ({

describe('index', () => {
beforeAll(async () => {
const prepDir = rootPath('var/lib/containerbase/tool.prep.d');
await fs.mkdir(prepDir, {
recursive: true,
});
for (const p of [
'var/lib/containerbase/tool.prep.d',
'tmp/containerbase/tool.init.d',
]) {
const prepDir = rootPath(p);
await fs.mkdir(prepDir, {
recursive: true,
});
}
});

test('prepareTools', async () => {
expect(await prepareTools(['bun', 'dummy'])).toBeUndefined();

Check failure on line 30 in src/cli/prepare-tool/index.spec.ts

View workflow job for this annotation

GitHub Actions / build / vitest

AssertionError: expected 1 to be undefined

at src/cli/prepare-tool/index.spec.ts:30:50
});

test('initializeTools', async () => {
expect(await initializeTools(['bun', 'dummy'])).toBeUndefined();
});
});
10 changes: 9 additions & 1 deletion src/cli/prepare-tool/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,13 @@ export function prepareTools(
dryRun = false,
): Promise<number | void> {
const container = prepareContainer();
return container.get(PrepareToolService).execute(tools, dryRun);
return container.get(PrepareToolService).prepare(tools, dryRun);
}

export function initializeTools(
tools: string[],
dryRun = false,
): Promise<number | void> {
const container = prepareContainer();
return container.get(PrepareToolService).initialize(tools, dryRun);
}
13 changes: 10 additions & 3 deletions src/cli/prepare-tool/prepare-legacy-tools.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@ import { logger } from '../utils';

@injectable()
export class PrepareLegacyToolsService {
async execute(tools: string[]): Promise<void> {
logger.debug(`Preparing legacy tools ${tools.join(', ')} ...`);
await execa('/usr/local/containerbase/bin/prepare-tool.sh', tools, {
async prepare(tool: string): Promise<void> {
logger.debug(`Preparing legacy tool ${tool} ...`);
await execa('/usr/local/containerbase/bin/prepare-tool.sh', [tool], {
stdio: ['inherit', 'inherit', 1],
});
}

async initialize(tool: string): Promise<void> {
logger.debug(`Initializing legacy tool ${tool} ...`);
await execa('/usr/local/containerbase/bin/init-tool.sh', [tool], {
stdio: ['inherit', 'inherit', 1],
});
}
Expand Down
Loading

0 comments on commit 6dc53a2

Please sign in to comment.