Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker and docker compose error in doctor sub-command #6

Merged
merged 1 commit into from
Apr 30, 2024
Merged
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
5 changes: 5 additions & 0 deletions .changeset/ten-llamas-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cartesi/cli": patch
---

fixed checking docker and docker compose in doctor command
136 changes: 96 additions & 40 deletions apps/cli/src/commands/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,75 +9,131 @@ export default class DoctorCommand extends BaseCommand<typeof DoctorCommand> {

private static MINIMUM_DOCKER_VERSION = "23.0.0"; // Replace with our minimum required Docker version
private static MINIMUM_DOCKER_COMPOSE_VERSION = "2.21.0"; // Replace with our minimum required Docker Compose version
private static MINIMUM_BUILDX_VERSION = "0.13.0"; // Replace with our minimum required Buildx version

private static isDockerVersionValid(version: string): boolean {
return semver.gte(version, DoctorCommand.MINIMUM_DOCKER_VERSION);
}
private async checkDocker(): Promise<true | never> {
try {
const { stdout: dockerVersion } = await execa("docker", [
"version",
"--format",
"{{json .Client.Version}}",
]);

private static isDockerComposeVersionValid(version: string): boolean {
const v = semver.coerce(version);
return (
v !== null &&
semver.gte(v, DoctorCommand.MINIMUM_DOCKER_COMPOSE_VERSION)
);
}
const v = semver.coerce(dockerVersion);
if (
v !== null &&
!semver.gte(v, DoctorCommand.MINIMUM_DOCKER_VERSION)
) {
throw new Error(
`Unsupported Docker version. Minimum required version is ${DoctorCommand.MINIMUM_DOCKER_VERSION}. Installed version is ${v}.`,
);
}
} catch (e: unknown) {
if (
e instanceof Error &&
(e as NodeJS.ErrnoException).code === "ENOENT"
) {
throw new Error("Docker not found");
} else {
throw e;
}
}

private static isBuildxRiscv64Supported(buildxOutput: string): boolean {
return buildxOutput.includes("riscv64");
return true;
}

public async run() {
private async checkCompose(): Promise<true | never> {
try {
// Check Docker version
const { stdout: dockerVersionOutput } = await execa("docker", [
const { stdout: dockerComposeVersion } = await execa("docker", [
"compose",
"version",
"--format",
"{{json .Client.Version}}",
"--short",
]);
const dockerVersion = JSON.parse(dockerVersionOutput);

if (!DoctorCommand.isDockerVersionValid(dockerVersion)) {
const v = semver.coerce(dockerComposeVersion);
if (
v !== null &&
!semver.gte(v, DoctorCommand.MINIMUM_DOCKER_COMPOSE_VERSION)
) {
throw new Error(
`Unsupported Docker Compose version. Minimum required version is ${DoctorCommand.MINIMUM_DOCKER_COMPOSE_VERSION}. Installed version is ${v}.`,
);
}
} catch (e: unknown) {
if (
e instanceof Error &&
(e as Error & { exitCode?: number }).exitCode === 125
) {
throw new Error(
`Unsupported Docker version. Minimum required version is ${DoctorCommand.MINIMUM_DOCKER_VERSION}. Installed version is ${dockerVersion}.`,
"Docker Compose is required but not installed or the command execution failed. Please refer to the Docker Compose documentation for installation instructions: https://docs.docker.com/compose/install/",
);
} else {
throw e;
}
}

// Check Docker Compose version
const { stdout: dockerComposeVersionOutput } = await execa(
"docker",
["compose", "version", "--short"],
);
const dockerComposeVersion = dockerComposeVersionOutput.trim();
return true;
}

private async checkBuildx(): Promise<true | never> {
try {
const { stdout: buildxOutput } = await execa("docker", [
"buildx",
"version",
]);

const v = semver.coerce(buildxOutput);
if (
!DoctorCommand.isDockerComposeVersionValid(dockerComposeVersion)
v !== null &&
!semver.gte(v, DoctorCommand.MINIMUM_BUILDX_VERSION)
) {
throw new Error(
`Unsupported Docker Compose version. Minimum required version is ${DoctorCommand.MINIMUM_DOCKER_COMPOSE_VERSION}. Installed version is ${dockerComposeVersion}.`,
`Unsupported Docker Buildx version. Minimum required version is ${DoctorCommand.MINIMUM_BUILDX_VERSION}. Installed version is ${v}.`,
);
}

// Check Docker Buildx version and riscv64 support
const { stdout: buildxOutput } = await execa("docker", [
const { stdout: platformsOutput } = await execa("docker", [
"buildx",
"ls",
"--format",
"{{.Platforms}}",
]);
const buildxRiscv64Supported =
DoctorCommand.isBuildxRiscv64Supported(buildxOutput);

if (!buildxRiscv64Supported) {
const buildxPlatforms: string[] = platformsOutput
.split(",")
.map((platform) => platform.trim());

if (!buildxPlatforms.includes("linux/riscv64")) {
throw new Error(
"Your system does not support riscv64 architecture.",
"Your system does not support riscv64 architecture. Run `docker run --privileged --rm tonistiigi/binfmt:riscv` to enable riscv64 support.",
);
}

this.log(`Your system is ready for ${this.config.bin}.`);
} catch (error: unknown) {
if (error instanceof Error) {
this.error(error.message);
} catch (e: unknown) {
if (
e instanceof Error &&
(e as Error & { exitCode?: number }).exitCode === 125
) {
throw new Error(
"Docker Buildx is required but not installed. Please refer to the Docker Desktop documentation for installation instructions: https://docs.docker.com/desktop/",
);
} else {
this.error(String(error));
throw e;
}
}

return true;
}

public async run(): Promise<void> {
try {
if (await this.checkDocker()) {
await this.checkCompose();
await this.checkBuildx();
}
} catch (e: unknown) {
this.error(e as Error);
}

this.log("Your system is ready.");
}
}
Loading