Skip to content
Open
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
2 changes: 2 additions & 0 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"tofu",
"uv",
"vendir",
"vp",
"wally",
"yarn"
],
Expand Down Expand Up @@ -161,6 +162,7 @@
"tofu",
"uv",
"vendir",
"vp",
"wally",
"yarn"
],
Expand Down
14 changes: 14 additions & 0 deletions docs/custom-registries.md
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,20 @@ https://github.com/vmware-tanzu/carvel-vendir/releases/download/v0.46.2/vendir-l
https://github.com/vmware-tanzu/carvel-vendir/releases/download/v0.46.2/vendir-linux-arm64
```

## `vp`

Vite+ releases are downloaded from:

- `https://github.com/voidzero-dev/vite-plus/releases`

Samples:

```txt
https://github.com/voidzero-dev/vite-plus/releases/download/v1.0.0/vp-x86_64-unknown-linux-gnu.tar.gz
https://github.com/voidzero-dev/vite-plus/releases/download/v1.0.0/vp-aarch64-unknown-linux-gnu.tar.gz
https://github.com/voidzero-dev/vite-plus/releases/download/v1.0.0/vp-checksums.txt
```

## `wally`

Wally releases are downloaded from:
Expand Down
2 changes: 2 additions & 0 deletions src/cli/install-tool/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import {
YarnVersionResolver,
} from '../tools/node/resolver.ts';
import { NpmBaseInstallService } from '../tools/node/utils.ts';
import { VpInstallService } from '../tools/node/vp.ts';
import {
ComposerInstallService,
ComposerVersionResolver,
Expand Down Expand Up @@ -199,6 +200,7 @@ async function prepareInstallContainer(): Promise<Container> {
container.bind(INSTALL_TOOL_TOKEN).to(TerraformInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(TofuInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(VendirInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(VpInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(WallyInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(YarnInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(YarnSlimInstallService);
Expand Down
1 change: 1 addition & 0 deletions src/cli/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const NoPrepareTools = [
'tofu',
'uv',
'vendir',
'vp',
'wally',
'yarn',
'yarn-slim',
Expand Down
149 changes: 149 additions & 0 deletions src/cli/tools/node/vp.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import fs from 'node:fs/promises';
import { arch } from 'node:os';
import { join } from 'node:path';
import { execa } from 'execa';
import type { Container } from 'inversify';
import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest';

import {
CompressionService,
HttpService,
LinkToolService,
} from '../../services/index.ts';
import {
VP_SYNC_VERSIONS_UNAVAILABLE,
VpInstallService,
parseVitePlusChecksum,
vitePlusAssetName,
} from './vp.ts';
import { testContainer } from '~test/di.ts';
import { ensurePaths } from '~test/path.ts';

vi.mock('execa');

describe('cli/tools/node/vp', () => {
describe('release assets', () => {
test.each([
['amd64', 'vp-x86_64-unknown-linux-gnu.tar.gz'],
['arm64', 'vp-aarch64-unknown-linux-gnu.tar.gz'],
] as const)('maps %s to the official release asset', (arch, expected) => {
expect(vitePlusAssetName(arch)).toBe(expected);
});

test('selects an exact release asset checksum', () => {
expect(
parseVitePlusChecksum(
[
'a'.repeat(64) + ' vp-aarch64-unknown-linux-gnu.tar.gz',
'b'.repeat(64) + ' vp-x86_64-unknown-linux-gnu.tar.gz',
'',
].join('\n'),
'vp-x86_64-unknown-linux-gnu.tar.gz',
),
).toBe('b'.repeat(64));
});

test('rejects missing or malformed checksums', () => {
expect(() =>
parseVitePlusChecksum('', 'vp-x86_64-unknown-linux-gnu.tar.gz'),
).toThrow('Cannot find checksum');
expect(() =>
parseVitePlusChecksum(
'not-a-checksum vp-x86_64-unknown-linux-gnu.tar.gz',
'vp-x86_64-unknown-linux-gnu.tar.gz',
),
).toThrow('Cannot find checksum');
});
});

describe('VpInstallService', () => {
let child: Container;
let service: VpInstallService;

beforeAll(async () => {
await ensurePaths([
'opt/containerbase/bin',
'opt/containerbase/tools',
'tmp/containerbase',
'var/lib/containerbase',
]);
});

beforeEach(async () => {
child = await testContainer();
child.bind(HttpService).toSelf();
child.bind(CompressionService).toSelf();
child.bind(LinkToolService).toSelf();
child.bind(VpInstallService).toSelf();
service = await child.getAsync(VpInstallService);
});

test('downloads, verifies, and extracts the exact prebuilt release', async () => {
const filename = vitePlusAssetName(
arch() === 'arm64' ? 'arm64' : 'amd64',
);
const checksum = 'c'.repeat(64);
const checksumFile = join(globalThis.cacheDir, 'vp-checksums.txt');
const archiveFile = join(globalThis.cacheDir, filename);
await fs.writeFile(checksumFile, `${checksum} ${filename}\n`);
await fs.writeFile(archiveFile, 'archive');

const download = vi
.spyOn(HttpService.prototype, 'download')
.mockResolvedValueOnce(checksumFile)
.mockResolvedValueOnce(archiveFile);
vi.spyOn(HttpService.prototype, 'exists').mockResolvedValueOnce(true);
const extract = vi
.spyOn(CompressionService.prototype, 'extract')
.mockResolvedValueOnce();

await service.install('0.4.0');

expect(download).toHaveBeenNthCalledWith(1, {
url: 'https://github.com/voidzero-dev/vite-plus/releases/download/v0.4.0/vp-checksums.txt',
});
expect(download).toHaveBeenNthCalledWith(2, {
url: `https://github.com/voidzero-dev/vite-plus/releases/download/v0.4.0/${filename}`,
checksumType: 'sha256',
expectedChecksum: checksum,
});
expect(extract).toHaveBeenCalledWith({
file: archiveFile,
cwd: expect.stringMatching(/\/vp\/0\.4\.0\/bin$/),
});
});

test('rejects releases that predate the bundled planner', async () => {
vi.spyOn(HttpService.prototype, 'exists').mockResolvedValueOnce(false);
const download = vi.spyOn(HttpService.prototype, 'download');

await expect(service.install('0.3.0')).rejects.toThrow(
`${VP_SYNC_VERSIONS_UNAVAILABLE}:0.3.0`,
);
expect(download).not.toHaveBeenCalled();
});

test('links vp with the Node runtime needed by the bundled planner', async () => {
const shellwrapper = vi
.spyOn(LinkToolService.prototype, 'shellwrapper')
.mockResolvedValueOnce();

await service.link('0.4.0');

expect(shellwrapper).toHaveBeenCalledWith('vp', {
srcDir: expect.stringMatching(/\/vp\/0\.4\.0\/bin$/),
extraToolEnvs: ['node'],
});
});

test('checks the installed vp version', async () => {
await service.test('0.4.0');

expect(execa).toHaveBeenCalledExactlyOnceWith(
'vp',
['--version'],
expect.any(Object),
);
});
});
});
77 changes: 77 additions & 0 deletions src/cli/tools/node/vp.ts
Comment thread
afonsojramos marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import fs from 'node:fs/promises';
import { join } from 'node:path';
import { injectFromHierarchy, injectable } from 'inversify';
import { BaseInstallService } from '../../install-tool/base-install.service.ts';
import type { Arch } from '../../utils/index.ts';

// Stable machine-readable marker consumed by Renovate. Do not reword it.
export const VP_SYNC_VERSIONS_UNAVAILABLE =
'CONTAINERBASE_VP_SYNC_VERSIONS_UNAVAILABLE';

export function vitePlusAssetName(arch: Arch): string {
const target = arch === 'arm64' ? 'aarch64' : 'x86_64';
return `vp-${target}-unknown-linux-gnu.tar.gz`;
}

export function parseVitePlusChecksum(
checksums: string,
filename: string,
): string {
for (const line of checksums.split('\n')) {
const match = /^([a-f\d]{64})\s+\*?(.+)$/i.exec(line.trim());
const checksum = match?.[1];
if (checksum && match?.[2] === filename) {
return checksum.toLowerCase();
}
}
throw new Error(`Cannot find checksum for '${filename}' in vp-checksums.txt`);
}

@injectable()
@injectFromHierarchy()
export class VpInstallService extends BaseInstallService {
readonly name = 'vp';
override readonly parent = 'node';

override async install(version: string): Promise<void> {
const baseUrl = `https://github.com/voidzero-dev/vite-plus/releases/download/v${version}/`;
const filename = vitePlusAssetName(this.envSvc.arch);
const checksumUrl = `${baseUrl}vp-checksums.txt`;

if (!(await this.http.exists(checksumUrl))) {
throw new Error(
`${VP_SYNC_VERSIONS_UNAVAILABLE}:${version}: Vite+ release does not provide the sync-versions planner`,
);
}

const checksumFile = await this.http.download({
url: checksumUrl,
});
const expectedChecksum = parseVitePlusChecksum(
await fs.readFile(checksumFile, 'utf8'),
filename,
);
const file = await this.http.download({
url: `${baseUrl}${filename}`,
checksumType: 'sha256',
expectedChecksum,
});

await this.pathSvc.ensureToolPath(this.name);
const path = join(
await this.pathSvc.createVersionedToolPath(this.name, version),
'bin',
);
await fs.mkdir(path);
await this.compress.extract({ file, cwd: path });
}

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

override async test(_version: string): Promise<void> {
await this._spawn(this.name, ['--version']);
}
}
3 changes: 3 additions & 0 deletions test/Dockerfile.distro
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ RUN install-tool pnpm 10.34.5
# renovate: datasource=npm packageName=@yarnpkg/cli-dist
RUN install-tool yarn 4.18.1

# renovate: datasource=github-releases packageName=voidzero-dev/vite-plus
RUN install-tool vp 1.0.0

#--------------------------------------
# Image: test-php
#--------------------------------------
Expand Down
19 changes: 19 additions & 0 deletions test/node/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,24 @@ RUN set -ex; \
grep -q 'No tools installed.' /tmp/tools-after.txt; \
true

#--------------------------------------
# test: vp
#--------------------------------------
FROM build AS test-vp

USER root

# renovate: datasource=github-releases depName=vp packageName=voidzero-dev/vite-plus
ARG VP_VERSION=1.0.0
RUN install-tool vp "${VP_VERSION}"

COPY test/node/vp/sync-versions.mjs /test/vp/

USER 12021
SHELL ["/bin/sh", "-c"]

RUN node /test/vp/sync-versions.mjs "${VP_VERSION}"

#--------------------------------------
# final
#--------------------------------------
Expand All @@ -607,6 +625,7 @@ COPY --from=testn /.dummy /.dummy
COPY --from=testo /.dummy /.dummy
COPY --from=testp /.dummy /.dummy
COPY --from=testq /.dummy /.dummy
COPY --from=test-vp /.dummy /.dummy

COPY --from=test-v20 /.dummy /.dummy
COPY --from=test-v22 /.dummy /.dummy
17 changes: 17 additions & 0 deletions test/node/Dockerfile.arm64
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,22 @@ RUN install-tool renovate 44.115.13
# # renovate: datasource=npm
# RUN npm install -g re2@1.20.9

#--------------------------------------
# Image: vp
#--------------------------------------
FROM test-node AS test-vp

# renovate: datasource=github-releases depName=vp packageName=voidzero-dev/vite-plus
ARG VP_VERSION=1.0.0
RUN install-tool vp "${VP_VERSION}"

COPY test/node/vp/sync-versions.mjs /test/vp/

USER 12021
SHELL ["/bin/sh", "-c"]

RUN node /test/vp/sync-versions.mjs "${VP_VERSION}"

#--------------------------------------
# Image: final
#--------------------------------------
Expand All @@ -85,3 +101,4 @@ COPY --from=test-node /.dummy /.dummy
COPY --from=test-pnpm /.dummy /.dummy
COPY --from=test-yarn /.dummy /.dummy
COPY --from=test-renovate /.dummy /.dummy
COPY --from=test-vp /.dummy /.dummy
Loading