Fix Docker Compose installation when not present on self-hosted runners

Co-authored-by: neilime <314088+neilime@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-02-03 08:14:59 +00:00
parent 89658aac24
commit e2af797539
3 changed files with 141 additions and 11 deletions

24
dist/index.js generated vendored
View File

@ -47951,6 +47951,10 @@ class DockerComposeInstallerService {
async install({ composeVersion, cwd, githubToken }) {
const currentVersion = await this.version({ cwd });
if (!composeVersion) {
// If no version is specified and Docker Compose is not installed, throw an error
if (!currentVersion) {
throw new Error("Docker Compose is not installed and no compose-version was specified. Please specify a compose-version to install.");
}
return currentVersion;
}
if (currentVersion === composeVersion) {
@ -47963,13 +47967,23 @@ class DockerComposeInstallerService {
composeVersion = await this.getLatestVersion(githubToken);
}
await this.installVersion(composeVersion);
return this.version({ cwd });
const installedVersion = await this.version({ cwd });
if (!installedVersion) {
throw new Error("Failed to verify Docker Compose installation");
}
return installedVersion;
}
async version({ cwd }) {
const result = await (0,dist.version)({
cwd,
});
return result.data.version;
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);

View File

@ -57,6 +57,107 @@ describe("DockerComposeInstallerService", () => {
expect(manualInstallerAdapterMock.install).not.toHaveBeenCalled();
});
it("should throw an error when Docker Compose is not installed and no version is provided", async () => {
// Arrange
versionMock.mockRejectedValue(new Error("Docker Compose not found"));
// Act & Assert
await expect(
service.install({
composeVersion: null,
cwd: "/path/to/cwd",
githubToken: null,
})
).rejects.toThrow(
"Docker Compose is not installed and no compose-version was specified. Please specify a compose-version to install."
);
expect(manualInstallerAdapterMock.install).not.toHaveBeenCalled();
});
it("should install Docker Compose when it is not installed and a version is specified", async () => {
// Arrange
versionMock.mockRejectedValueOnce(new Error("Docker Compose not found"));
const expectedVersion = "v2.20.0";
versionMock.mockResolvedValueOnce({
exitCode: 0,
out: "",
err: "",
data: {
version: expectedVersion,
},
});
Object.defineProperty(process, "platform", {
value: "linux",
});
// Act
const result = await service.install({
composeVersion: expectedVersion,
cwd: "/path/to/cwd",
githubToken: null,
});
// Assert
expect(result).toBe(expectedVersion);
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(expectedVersion);
});
it("should install Docker Compose when it is not installed and latest version is requested", async () => {
// Arrange
versionMock.mockRejectedValueOnce(new Error("Docker Compose not found"));
const latestVersion = "v2.30.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(),
});
// Act
const result = await service.install({
composeVersion: "latest",
cwd: "/path/to/cwd",
githubToken: "token",
});
// Assert
expect(result).toBe(latestVersion);
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(latestVersion);
});
it("should not install anything when expected version is already installed", async () => {
// Arrange
versionMock.mockResolvedValue({

View File

@ -20,6 +20,12 @@ export class DockerComposeInstallerService {
const currentVersion = await this.version({ cwd });
if (!composeVersion) {
// If no version is specified and Docker Compose is not installed, throw an error
if (!currentVersion) {
throw new Error(
"Docker Compose is not installed and no compose-version was specified. Please specify a compose-version to install."
);
}
return currentVersion;
}
@ -36,14 +42,23 @@ export class DockerComposeInstallerService {
await this.installVersion(composeVersion);
return this.version({ cwd });
const installedVersion = await this.version({ cwd });
if (!installedVersion) {
throw new Error("Failed to verify Docker Compose installation");
}
return installedVersion;
}
private async version({ cwd }: VersionInputs): Promise<string> {
const result = await version({
cwd,
});
return result.data.version;
private async version({ cwd }: VersionInputs): Promise<string | null> {
try {
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> {