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
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
!test
!dist/docker
!dist/cli
!dist/test
2 changes: 2 additions & 0 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
"tofu",
"uv",
"vendir",
"vp",
"wally",
"yarn"
],
Expand Down Expand Up @@ -160,6 +161,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 @@ -776,6 +776,20 @@ Samples:
https://github.com/vmware-tanzu/carvel-vendir/releases/download/v0.22.0/vendir-linux-amd64
```

## `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/v0.3.1/vp-x86_64-unknown-linux-gnu.tar.gz
https://github.com/voidzero-dev/vite-plus/releases/download/v0.3.1/vp-aarch64-unknown-linux-gnu.tar.gz
https://github.com/voidzero-dev/vite-plus/releases/download/v0.3.1/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 @@ -80,6 +80,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 @@ -192,6 +193,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.0

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

#--------------------------------------
# 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 @@ -575,6 +575,24 @@ RUN set -ex; \
if [ -n "$(ls -A /usr/local/bin/)" ]; then echo "tools not uninstalled" >&2; exit 1; fi;\
true

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

USER root

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

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Bug: Docker COPY targets unbuilt dist/test/vp-sync-versions.mjs

Both node Dockerfiles run COPY dist/test/vp-sync-versions.mjs /test/vp-sync-versions.mjs, and .dockerignore was updated to un-ignore dist/test, but the planner script lives at test/node/vp/sync-versions.mjs. The build pipeline (tools/build.js) only populates dist/docker, dist/app, dist/package.json and dist/cli; no step copies/renames test/node/vp/sync-versions.mjs to dist/test/vp-sync-versions.mjs. Unless such a copy step is added, the COPY layer fails with "file not found" and the test-vp stage cannot build. Add a build step that emits dist/test/vp-sync-versions.mjs (or point the COPY at the actual source path).

Emit the planner script to dist/test during build so the Docker COPY resolves.:

# in tools/build.js, add after the docker copy step:
shell.mkdir('-p', 'dist/test');
shell.cp('test/node/vp/sync-versions.mjs', 'dist/test/vp-sync-versions.mjs');
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎


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

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

#--------------------------------------
# final
#--------------------------------------
Expand All @@ -596,6 +614,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.17.1
# # 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=0.3.1
RUN install-tool vp "${VP_VERSION}"

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

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