chore: minor refactoring

Signed-off-by: Emilien Escalle <emilien.escalle@escemi.com>
This commit is contained in:
Emilien Escalle 2026-02-03 10:44:17 +01:00 committed by Emilien Escalle
parent c36b7c122a
commit 4894d24920
3 changed files with 154 additions and 256 deletions

31
dist/index.js generated vendored
View File

@ -47949,8 +47949,13 @@ class DockerComposeInstallerService {
this.manualInstallerAdapter = manualInstallerAdapter;
}
async install({ composeVersion, cwd, githubToken }) {
const currentVersion = await this.getInstalledVersion(cwd);
const needsInstall = !currentVersion || (composeVersion && composeVersion !== currentVersion);
const currentVersion = await this.version({ cwd });
const normalizedCurrentVersion = currentVersion ? this.normalizeVersion(currentVersion) : null;
const normalizedRequestedVersion = composeVersion
? this.normalizeVersion(composeVersion)
: null;
const needsInstall = !currentVersion ||
(composeVersion && normalizedRequestedVersion !== normalizedCurrentVersion);
if (!needsInstall) {
return currentVersion;
}
@ -47962,22 +47967,25 @@ class DockerComposeInstallerService {
targetVersion = await this.getLatestVersion(githubToken);
}
await this.installVersion(targetVersion);
return this.version({ cwd });
}
async getInstalledVersion(cwd) {
try {
return await this.version({ cwd });
}
catch {
return null;
const installedVersion = await this.version({ cwd });
if (!installedVersion ||
this.normalizeVersion(installedVersion) !== this.normalizeVersion(targetVersion)) {
throw new Error(`Failed to install Docker Compose version "${targetVersion}", installed version is "${installedVersion ?? "unknown"}"`);
}
return installedVersion;
}
async version({ cwd }) {
try {
const result = await (0,dist.version)({
cwd,
});
return result.data.version;
}
catch {
// If version check fails (e.g., Docker Compose not installed), return null
return null;
}
}
async getLatestVersion(githubToken) {
const octokit = getOctokit(githubToken);
const response = await octokit.rest.repos.getLatestRelease({
@ -47986,6 +47994,9 @@ class DockerComposeInstallerService {
});
return response.data.tag_name;
}
normalizeVersion(version) {
return version.replace(/^v/i, "");
}
async installVersion(version) {
switch (process.platform) {
case "linux":

View File

@ -21,6 +21,52 @@ describe("DockerComposeInstallerService", () => {
let mockAgent: MockAgent;
let service: InstanceType<typeof DockerComposeInstallerService>;
const composeVersionResponse = (version: string) => ({
exitCode: 0,
out: "",
err: "",
data: {
version,
},
});
const installCompose = (composeVersion: string | null, githubToken: string | null) =>
service.install({
composeVersion,
cwd: "/path/to/cwd",
githubToken,
});
const setPlatform = (platform: NodeJS.Platform) => {
Object.defineProperty(process, "platform", {
value: platform,
});
};
const mockLatestRelease = (version: string) => {
const mockClient = mockAgent.get("https://api.github.com");
mockClient
.intercept({
path: "/repos/docker/compose/releases/latest",
method: "GET",
})
.reply(
200,
{
tag_name: version,
},
{
headers: {
"content-type": "application/json",
},
}
);
setGlobalDispatcher(mockClient);
Object.defineProperty(globalThis, "fetch", {
value: jest.fn(),
});
};
beforeEach(() => {
jest.clearAllMocks();
mockAgent = new MockAgent();
@ -36,21 +82,10 @@ describe("DockerComposeInstallerService", () => {
describe("install", () => {
it("should return current version when no version is provided", async () => {
// Arrange
versionMock.mockResolvedValue({
exitCode: 0,
out: "",
err: "",
data: {
version: "2.0.0",
},
});
versionMock.mockResolvedValue(composeVersionResponse("2.0.0"));
// Act
const result = await service.install({
composeVersion: null,
cwd: "/path/to/cwd",
githubToken: null,
});
const result = await installCompose(null, null);
// Assert
expect(result).toBe("2.0.0");
@ -59,21 +94,10 @@ describe("DockerComposeInstallerService", () => {
it("should not install anything when expected version is already installed", async () => {
// Arrange
versionMock.mockResolvedValue({
exitCode: 0,
out: "",
err: "",
data: {
version: "1.2.3",
},
});
versionMock.mockResolvedValue(composeVersionResponse("1.2.3"));
// Act
const result = await service.install({
composeVersion: "1.2.3",
cwd: "/path/to/cwd",
githubToken: null,
});
const result = await installCompose("v1.2.3", null);
// Assert
expect(result).toBe("1.2.3");
@ -82,35 +106,14 @@ describe("DockerComposeInstallerService", () => {
it("should install the requested version if it is not already installed", async () => {
// Arrange
versionMock.mockResolvedValueOnce({
exitCode: 0,
out: "",
err: "",
data: {
version: "1.2.3",
},
});
versionMock.mockResolvedValueOnce(composeVersionResponse("1.2.3"));
const expectedVersion = "1.3.0";
versionMock.mockResolvedValueOnce({
exitCode: 0,
out: "",
err: "",
data: {
version: expectedVersion,
},
});
Object.defineProperty(process, "platform", {
value: "linux",
});
versionMock.mockResolvedValueOnce(composeVersionResponse(expectedVersion));
setPlatform("linux");
// Act
const result = await service.install({
composeVersion: expectedVersion,
cwd: "/path/to/cwd",
githubToken: null,
});
const result = await installCompose(expectedVersion, null);
// Assert
expect(result).toBe(expectedVersion);
@ -119,58 +122,15 @@ describe("DockerComposeInstallerService", () => {
it("should install the latest version if requested", async () => {
// Arrange
versionMock.mockResolvedValueOnce({
exitCode: 0,
out: "",
err: "",
data: {
version: "1.2.3",
},
});
versionMock.mockResolvedValueOnce(composeVersionResponse("1.2.3"));
const latestVersion = "v1.4.0";
const mockClient = mockAgent.get("https://api.github.com");
mockClient
.intercept({
path: "/repos/docker/compose/releases/latest",
method: "GET",
})
.reply(
200,
{
tag_name: latestVersion,
},
{
headers: {
"content-type": "application/json",
},
}
);
setGlobalDispatcher(mockClient);
versionMock.mockResolvedValueOnce({
exitCode: 0,
out: "",
err: "",
data: {
version: latestVersion,
},
});
Object.defineProperty(process, "platform", {
value: "linux",
});
Object.defineProperty(globalThis, "fetch", {
value: jest.fn(),
});
mockLatestRelease(latestVersion);
versionMock.mockResolvedValueOnce(composeVersionResponse(latestVersion));
setPlatform("linux");
// Act
const result = await service.install({
composeVersion: "latest",
cwd: "/path/to/cwd",
githubToken: "token",
});
const result = await installCompose("latest", "token");
// Assert
expect(result).toBe(latestVersion);
@ -179,58 +139,26 @@ describe("DockerComposeInstallerService", () => {
it("should throw an error if the latest version if requested and no Github token is provided", async () => {
// Arrange
versionMock.mockResolvedValueOnce({
exitCode: 0,
out: "",
err: "",
data: {
version: "1.2.3",
},
});
versionMock.mockResolvedValueOnce(composeVersionResponse("1.2.3"));
// Act & Assert
await expect(
service.install({
composeVersion: "latest",
cwd: "/path/to/cwd",
githubToken: null,
})
).rejects.toThrow("GitHub token is required to install the latest version");
await expect(installCompose("latest", null)).rejects.toThrow(
"GitHub token is required to install the latest version"
);
});
it("should throw an error on unsupported platforms", async () => {
// Arrange
versionMock.mockResolvedValueOnce({
exitCode: 0,
out: "",
err: "",
data: {
version: "1.2.3",
},
});
versionMock.mockResolvedValueOnce(composeVersionResponse("1.2.3"));
const expectedVersion = "1.3.0";
versionMock.mockResolvedValueOnce({
exitCode: 0,
out: "",
err: "",
data: {
version: expectedVersion,
},
});
Object.defineProperty(process, "platform", {
value: "win32",
});
versionMock.mockResolvedValueOnce(composeVersionResponse(expectedVersion));
setPlatform("win32");
// Act & Assert
await expect(
service.install({
composeVersion: expectedVersion,
cwd: "/path/to/cwd",
githubToken: null,
})
).rejects.toThrow(`Unsupported platform: win32`);
await expect(installCompose(expectedVersion, null)).rejects.toThrow(
`Unsupported platform: win32`
);
expect(manualInstallerAdapterMock.install).not.toHaveBeenCalled();
});
@ -242,25 +170,11 @@ describe("DockerComposeInstallerService", () => {
const installedVersion = "2.0.0";
// After installation, version() returns the new version
versionMock.mockResolvedValueOnce({
exitCode: 0,
out: "",
err: "",
data: {
version: installedVersion,
},
});
Object.defineProperty(process, "platform", {
value: "linux",
});
versionMock.mockResolvedValueOnce(composeVersionResponse(installedVersion));
setPlatform("linux");
// Act
const result = await service.install({
composeVersion: installedVersion,
cwd: "/path/to/cwd",
githubToken: "token",
});
const result = await installCompose(installedVersion, "token");
// Assert
expect(result).toBe(installedVersion);
@ -271,58 +185,15 @@ describe("DockerComposeInstallerService", () => {
// Arrange: first call to version() doesn't find
versionMock.mockRejectedValueOnce(new Error("version check failed"));
// second call finds newly installed version
versionMock.mockResolvedValueOnce({
exitCode: 0,
out: "",
err: "",
data: {
version: "v1.4.0",
},
});
versionMock.mockResolvedValueOnce(composeVersionResponse("v1.4.0"));
const latestVersion = "v1.4.0";
const mockClient = mockAgent.get("https://api.github.com");
mockClient
.intercept({
path: "/repos/docker/compose/releases/latest",
method: "GET",
})
.reply(
200,
{
tag_name: latestVersion,
},
{
headers: {
"content-type": "application/json",
},
}
);
setGlobalDispatcher(mockClient);
versionMock.mockResolvedValueOnce({
exitCode: 0,
out: "",
err: "",
data: {
version: latestVersion,
},
});
Object.defineProperty(process, "platform", {
value: "linux",
});
Object.defineProperty(globalThis, "fetch", {
value: jest.fn(),
});
mockLatestRelease(latestVersion);
versionMock.mockResolvedValueOnce(composeVersionResponse(latestVersion));
setPlatform("linux");
// Act
const result = await service.install({
composeVersion: "latest",
cwd: "/path/to/cwd",
githubToken: "token",
});
const result = await installCompose("latest", "token");
// Assert
expect(result).toBe(latestVersion);
@ -332,41 +203,38 @@ describe("DockerComposeInstallerService", () => {
it("should throw if Compose is missing and no GitHub token is provided", async () => {
// Arrange: first call to version() doesn't find
versionMock.mockRejectedValueOnce(new Error("version check failed"));
setPlatform("linux");
Object.defineProperty(process, "platform", {
value: "linux",
});
await expect(
service.install({
composeVersion: "latest",
cwd: "/path/to/cwd",
githubToken: null,
})
).rejects.toThrow("GitHub token is required to install the latest version");
await expect(installCompose("latest", null)).rejects.toThrow(
"GitHub token is required to install the latest version"
);
});
it("should not install when the version is already installed and no version is specified", async () => {
// Arrange
versionMock.mockResolvedValue({
exitCode: 0,
out: "",
err: "",
data: {
version: "1.2.3",
},
});
versionMock.mockResolvedValue(composeVersionResponse("1.2.3"));
// Act
const result = await service.install({
composeVersion: "",
cwd: "/path/to/cwd",
githubToken: null,
});
const result = await installCompose("", null);
// Assert
expect(result).toBe("1.2.3");
expect(manualInstallerAdapterMock.install).not.toHaveBeenCalled();
});
it("should throw when installed version does not match target", async () => {
// Arrange
versionMock.mockResolvedValueOnce(composeVersionResponse("1.2.3"));
const targetVersion = "v1.4.0";
versionMock.mockResolvedValueOnce(composeVersionResponse("1.3.0"));
setPlatform("linux");
// Act & Assert
await expect(installCompose(targetVersion, "token")).rejects.toThrow(
`Failed to install Docker Compose version "${targetVersion}", installed version is "1.3.0"`
);
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(targetVersion);
});
});
});

View File

@ -17,9 +17,16 @@ export class DockerComposeInstallerService {
constructor(private readonly manualInstallerAdapter: ManualInstallerAdapter) {}
async install({ composeVersion, cwd, githubToken }: InstallInputs): Promise<string> {
const currentVersion = await this.getInstalledVersion(cwd);
const currentVersion = await this.version({ cwd });
const needsInstall = !currentVersion || (composeVersion && composeVersion !== currentVersion);
const normalizedCurrentVersion = currentVersion ? this.normalizeVersion(currentVersion) : null;
const normalizedRequestedVersion = composeVersion
? this.normalizeVersion(composeVersion)
: null;
const needsInstall =
!currentVersion ||
(composeVersion && normalizedRequestedVersion !== normalizedCurrentVersion);
if (!needsInstall) {
return currentVersion;
}
@ -35,22 +42,30 @@ export class DockerComposeInstallerService {
await this.installVersion(targetVersion);
return this.version({ cwd });
const installedVersion = await this.version({ cwd });
if (
!installedVersion ||
this.normalizeVersion(installedVersion) !== this.normalizeVersion(targetVersion)
) {
throw new Error(
`Failed to install Docker Compose version "${targetVersion}", installed version is "${installedVersion ?? "unknown"}"`
);
}
private async getInstalledVersion(cwd: string): Promise<string | null> {
return installedVersion;
}
private async version({ cwd }: VersionInputs): Promise<string | null> {
try {
return await this.version({ cwd });
} catch {
return null;
}
}
private async version({ cwd }: VersionInputs): Promise<string> {
const result = await version({
cwd,
});
return result.data.version;
} catch {
// If version check fails (e.g., Docker Compose not installed), return null
return null;
}
}
private async getLatestVersion(githubToken: string): Promise<string> {
@ -64,6 +79,10 @@ export class DockerComposeInstallerService {
return response.data.tag_name;
}
private normalizeVersion(version: string): string {
return version.replace(/^v/i, "");
}
private async installVersion(version: string): Promise<void> {
switch (process.platform) {
case "linux":