mirror of
https://github.com/hoverkraft-tech/compose-action.git
synced 2026-03-06 16:23:08 +08:00
Compare commits
3 Commits
bb5664383c
...
b160464f2e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b160464f2e | ||
|
|
4894d24920 | ||
|
|
c36b7c122a |
12
README.md
12
README.md
@ -49,7 +49,7 @@ Some extra options can be passed to the `docker compose down` command using the
|
||||
## Usage
|
||||
|
||||
```yaml
|
||||
- uses: hoverkraft-tech/compose-action@05da55b2bb8a5a759d1c4732095044bd9018c050 # v2.4.3
|
||||
- uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0
|
||||
with:
|
||||
# Additional options to pass to `docker` command.
|
||||
docker-flags: ""
|
||||
@ -139,7 +139,7 @@ jobs:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Run docker compose
|
||||
uses: hoverkraft-tech/compose-action@05da55b2bb8a5a759d1c4732095044bd9018c050 # v2.4.3
|
||||
uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0
|
||||
with:
|
||||
compose-file: "./docker/docker-compose.yml"
|
||||
|
||||
@ -153,7 +153,7 @@ jobs:
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: hoverkraft-tech/compose-action@05da55b2bb8a5a759d1c4732095044bd9018c050 # v2.4.3
|
||||
- uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0
|
||||
with:
|
||||
compose-file: "./docker/docker-compose.yml"
|
||||
env:
|
||||
@ -168,7 +168,7 @@ Perform `docker compose up` to some given service instead of all of them
|
||||
steps:
|
||||
# need checkout before using compose-action
|
||||
- uses: actions/checkout@v3
|
||||
- uses: hoverkraft-tech/compose-action@05da55b2bb8a5a759d1c4732095044bd9018c050 # v2.4.3
|
||||
- uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0
|
||||
with:
|
||||
compose-file: "./docker/docker-compose.yml"
|
||||
services: |
|
||||
@ -206,7 +206,7 @@ A full list of flags can be found in the [Docker compose documentation](https://
|
||||
steps:
|
||||
# need checkout before using compose-action
|
||||
- uses: actions/checkout@v3
|
||||
- uses: hoverkraft-tech/compose-action@05da55b2bb8a5a759d1c4732095044bd9018c050 # v2.4.3
|
||||
- uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0
|
||||
with:
|
||||
compose-file: "./docker/docker-compose.yml"
|
||||
compose-flags: "--profile profile-1"
|
||||
@ -220,7 +220,7 @@ This is useful when you have a base compose file and additional files for differ
|
||||
steps:
|
||||
# need checkout before using compose-action
|
||||
- uses: actions/checkout@v3
|
||||
- uses: hoverkraft-tech/compose-action@05da55b2bb8a5a759d1c4732095044bd9018c050 # v2.4.3
|
||||
- uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0
|
||||
with:
|
||||
compose-file: |
|
||||
./docker/docker-compose.yml
|
||||
|
||||
42
dist/index.js
generated
vendored
42
dist/index.js
generated
vendored
@ -47950,26 +47950,41 @@ class DockerComposeInstallerService {
|
||||
}
|
||||
async install({ composeVersion, cwd, githubToken }) {
|
||||
const currentVersion = await this.version({ cwd });
|
||||
if (!composeVersion) {
|
||||
const normalizedCurrentVersion = currentVersion ? this.normalizeVersion(currentVersion) : null;
|
||||
const normalizedRequestedVersion = composeVersion
|
||||
? this.normalizeVersion(composeVersion)
|
||||
: null;
|
||||
const needsInstall = !currentVersion ||
|
||||
(composeVersion && normalizedRequestedVersion !== normalizedCurrentVersion);
|
||||
if (!needsInstall) {
|
||||
return currentVersion;
|
||||
}
|
||||
if (currentVersion === composeVersion) {
|
||||
return currentVersion;
|
||||
}
|
||||
if (composeVersion === COMPOSE_VERSION_LATEST) {
|
||||
let targetVersion = composeVersion || COMPOSE_VERSION_LATEST;
|
||||
if (targetVersion === COMPOSE_VERSION_LATEST) {
|
||||
if (!githubToken) {
|
||||
throw new Error("GitHub token is required to install the latest version");
|
||||
}
|
||||
composeVersion = await this.getLatestVersion(githubToken);
|
||||
targetVersion = await this.getLatestVersion(githubToken);
|
||||
}
|
||||
await this.installVersion(composeVersion);
|
||||
return this.version({ cwd });
|
||||
await this.installVersion(targetVersion);
|
||||
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 }) {
|
||||
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);
|
||||
@ -47979,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":
|
||||
|
||||
@ -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();
|
||||
@ -30,27 +76,16 @@ describe("DockerComposeInstallerService", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
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,60 +139,102 @@ 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();
|
||||
});
|
||||
|
||||
it("should install when version check fails", async () => {
|
||||
// Arrange: first call to version() doesn't find
|
||||
versionMock.mockRejectedValueOnce(new Error("version not installed"));
|
||||
|
||||
const installedVersion = "2.0.0";
|
||||
|
||||
// After installation, version() returns the new version
|
||||
versionMock.mockResolvedValueOnce(composeVersionResponse(installedVersion));
|
||||
setPlatform("linux");
|
||||
|
||||
// Act
|
||||
const result = await installCompose(installedVersion, "token");
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(installedVersion);
|
||||
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(installedVersion);
|
||||
});
|
||||
|
||||
it("should install latest version when missing or unspecified", async () => {
|
||||
// Arrange: first call to version() doesn't find
|
||||
versionMock.mockRejectedValueOnce(new Error("version check failed"));
|
||||
// second call finds newly installed version
|
||||
versionMock.mockResolvedValueOnce(composeVersionResponse("v1.4.0"));
|
||||
|
||||
const latestVersion = "v1.4.0";
|
||||
mockLatestRelease(latestVersion);
|
||||
versionMock.mockResolvedValueOnce(composeVersionResponse(latestVersion));
|
||||
setPlatform("linux");
|
||||
|
||||
// Act
|
||||
const result = await installCompose("latest", "token");
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(latestVersion);
|
||||
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(latestVersion);
|
||||
});
|
||||
|
||||
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");
|
||||
|
||||
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(composeVersionResponse("1.2.3"));
|
||||
|
||||
// Act
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -19,31 +19,53 @@ export class DockerComposeInstallerService {
|
||||
async install({ composeVersion, cwd, githubToken }: InstallInputs): Promise<string> {
|
||||
const currentVersion = await this.version({ cwd });
|
||||
|
||||
if (!composeVersion) {
|
||||
const normalizedCurrentVersion = currentVersion ? this.normalizeVersion(currentVersion) : null;
|
||||
const normalizedRequestedVersion = composeVersion
|
||||
? this.normalizeVersion(composeVersion)
|
||||
: null;
|
||||
|
||||
const needsInstall =
|
||||
!currentVersion ||
|
||||
(composeVersion && normalizedRequestedVersion !== normalizedCurrentVersion);
|
||||
if (!needsInstall) {
|
||||
return currentVersion;
|
||||
}
|
||||
|
||||
if (currentVersion === composeVersion) {
|
||||
return currentVersion;
|
||||
}
|
||||
let targetVersion = composeVersion || COMPOSE_VERSION_LATEST;
|
||||
|
||||
if (composeVersion === COMPOSE_VERSION_LATEST) {
|
||||
if (targetVersion === COMPOSE_VERSION_LATEST) {
|
||||
if (!githubToken) {
|
||||
throw new Error("GitHub token is required to install the latest version");
|
||||
}
|
||||
composeVersion = await this.getLatestVersion(githubToken);
|
||||
targetVersion = await this.getLatestVersion(githubToken);
|
||||
}
|
||||
|
||||
await this.installVersion(composeVersion);
|
||||
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"}"`
|
||||
);
|
||||
}
|
||||
|
||||
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> {
|
||||
@ -57,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":
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user