mirror of
https://github.com/hoverkraft-tech/compose-action.git
synced 2026-07-03 12:12:50 +08:00
feat: support OCI artifact compose-file inputs
Signed-off-by: Emilien Escalle <emilien.escalle@escemi.com>
This commit is contained in:
parent
b542f028fa
commit
d2bee4f07e
42
.github/workflows/__check-action.yml
vendored
42
.github/workflows/__check-action.yml
vendored
@ -170,6 +170,14 @@ jobs:
|
|||||||
docker compose -f ./test/docker-compose.yml ps | grep test-service-a-1 || (echo "Service service-a is not running under custom context" && exit 1)
|
docker compose -f ./test/docker-compose.yml ps | grep test-service-a-1 || (echo "Service service-a is not running under custom context" && exit 1)
|
||||||
(docker compose -f ./test/docker-compose.yml ps | grep test-service-b-1 && echo "Service service-b should not be running without profile" && exit 1) || true
|
(docker compose -f ./test/docker-compose.yml ps | grep test-service-b-1 && echo "Service service-b should not be running without profile" && exit 1) || true
|
||||||
|
|
||||||
|
- name: Given OCI compose artifact when running action
|
||||||
|
assertion-name: "Then the OCI compose application runs"
|
||||||
|
compose-file: oci://localhost:5000/compose-action-test:latest
|
||||||
|
publish-oci-artifact: true
|
||||||
|
source-compose-file: ./test/docker-compose.yml
|
||||||
|
assertion: |
|
||||||
|
docker compose -f "$OCI_COMPOSE_FILE" ps service-a | grep service-a | grep "Up" || (echo "Service service-a is not running from the OCI artifact" && exit 1)
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DOCKER_COMPOSE_VERSION: ${{ matrix.expected-compose-version || '' }}
|
DOCKER_COMPOSE_VERSION: ${{ matrix.expected-compose-version || '' }}
|
||||||
steps:
|
steps:
|
||||||
@ -200,6 +208,39 @@ jobs:
|
|||||||
|
|
||||||
core.exportVariable('DOCKER_COMPOSE_VERSION', dockerComposeVersion);
|
core.exportVariable('DOCKER_COMPOSE_VERSION', dockerComposeVersion);
|
||||||
|
|
||||||
|
- name: "Arrange: start local OCI registry"
|
||||||
|
if: ${{ matrix.publish-oci-artifact }}
|
||||||
|
# Keep the registry alive until the job ends because the action post hook
|
||||||
|
# reuses the OCI reference for docker compose logs and down.
|
||||||
|
run: |
|
||||||
|
OCI_REGISTRY_IMAGE=registry:2.8.3
|
||||||
|
OCI_REGISTRY_MAX_RETRIES=10
|
||||||
|
OCI_REGISTRY_RETRY_DELAY_SECONDS=1
|
||||||
|
|
||||||
|
# Keep the registry on port 5000 so it matches the OCI reference configured in the test matrix.
|
||||||
|
OCI_REGISTRY_CONTAINER_ID=$(docker run -d -p 5000:5000 "$OCI_REGISTRY_IMAGE")
|
||||||
|
echo "OCI_REGISTRY_CONTAINER_ID=$OCI_REGISTRY_CONTAINER_ID" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
retry_count=0
|
||||||
|
while [ "$retry_count" -lt "$OCI_REGISTRY_MAX_RETRIES" ]; do
|
||||||
|
if curl --fail --silent http://localhost:5000/v2/ >/dev/null; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
retry_count=$((retry_count + 1))
|
||||||
|
sleep "$OCI_REGISTRY_RETRY_DELAY_SECONDS"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Local OCI registry did not become ready on localhost:5000"
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
- name: "Arrange: publish compose application as OCI artifact"
|
||||||
|
if: ${{ matrix.publish-oci-artifact }}
|
||||||
|
run: |
|
||||||
|
OCI_REPOSITORY="${{ matrix.compose-file }}"
|
||||||
|
OCI_PUBLISH_TARGET="${OCI_REPOSITORY#oci://}"
|
||||||
|
docker compose -f "${{ matrix.source-compose-file }}" publish "$OCI_PUBLISH_TARGET"
|
||||||
|
|
||||||
- name: "Arrange: ensure original docker compose version is not the expected one"
|
- name: "Arrange: ensure original docker compose version is not the expected one"
|
||||||
if: ${{ matrix.ensure-version-mismatch }}
|
if: ${{ matrix.ensure-version-mismatch }}
|
||||||
run: |
|
run: |
|
||||||
@ -230,3 +271,4 @@ jobs:
|
|||||||
run: ${{ matrix.assertion }}
|
run: ${{ matrix.assertion }}
|
||||||
env:
|
env:
|
||||||
IMAGE_NAME: ${{ matrix.image-name || '' }}
|
IMAGE_NAME: ${{ matrix.image-name || '' }}
|
||||||
|
OCI_COMPOSE_FILE: ${{ matrix.compose-file || '' }}
|
||||||
|
|||||||
32
README.md
32
README.md
@ -54,7 +54,7 @@ Some extra options can be passed to the `docker compose down` command using the
|
|||||||
# Additional options to pass to `docker` command.
|
# Additional options to pass to `docker` command.
|
||||||
docker-flags: ""
|
docker-flags: ""
|
||||||
|
|
||||||
# Path to compose file(s). It can be a list of files. It can be absolute or relative to the current working directory (cwd).
|
# Path to compose file(s). It can be a list of files. It can be absolute or relative to the current working directory (cwd), or an OCI artifact reference starting with `oci://`.
|
||||||
# Default: `./docker-compose.yml`
|
# Default: `./docker-compose.yml`
|
||||||
compose-file: ./docker-compose.yml
|
compose-file: ./docker-compose.yml
|
||||||
|
|
||||||
@ -96,21 +96,21 @@ Some extra options can be passed to the `docker compose down` command using the
|
|||||||
|
|
||||||
## Inputs
|
## Inputs
|
||||||
|
|
||||||
| **Input** | **Description** | **Required** | **Default** |
|
| **Input** | **Description** | **Required** | **Default** |
|
||||||
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------- | ------------ | ------------------------- |
|
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------------------------- |
|
||||||
| **`docker-flags`** | Additional options to pass to `docker` command. | **false** | - |
|
| **`docker-flags`** | Additional options to pass to `docker` command. | **false** | - |
|
||||||
| **`compose-file`** | Path to compose file(s). It can be a list of files. It can be absolute or relative to the current working directory (cwd). | **false** | `./docker-compose.yml` |
|
| **`compose-file`** | Path to compose file(s). It can be a list of files. It can be absolute or relative to the current working directory (cwd), or an OCI artifact reference starting with `oci://`. | **false** | `./docker-compose.yml` |
|
||||||
| **`services`** | Services to perform `docker compose up`. | **false** | - |
|
| **`services`** | Services to perform `docker compose up`. | **false** | - |
|
||||||
| **`up-flags`** | Additional options to pass to `docker compose up` command. | **false** | - |
|
| **`up-flags`** | Additional options to pass to `docker compose up` command. | **false** | - |
|
||||||
| **`down-flags`** | Additional options to pass to `docker compose down` command. | **false** | - |
|
| **`down-flags`** | Additional options to pass to `docker compose down` command. | **false** | - |
|
||||||
| **`compose-flags`** | Additional options to pass to `docker compose` command. | **false** | - |
|
| **`compose-flags`** | Additional options to pass to `docker compose` command. | **false** | - |
|
||||||
| **`cwd`** | Current working directory | **false** | `${{ github.workspace }}` |
|
| **`cwd`** | Current working directory | **false** | `${{ github.workspace }}` |
|
||||||
| **`compose-version`** | Compose version to use. | **false** | - |
|
| **`compose-version`** | Compose version to use. | **false** | - |
|
||||||
| | If null (default), it will use the current installed version. | | |
|
| | If null (default), it will use the current installed version. | | |
|
||||||
| | If "latest", it will install the latest version. | | |
|
| | If "latest", it will install the latest version. | | |
|
||||||
| **`services-log-level`** | The log level used for Docker Compose service logs. | **false** | `debug` |
|
| **`services-log-level`** | The log level used for Docker Compose service logs. | **false** | `debug` |
|
||||||
| | Can be one of "debug", "info". | | |
|
| | Can be one of "debug", "info". | | |
|
||||||
| **`github-token`** | The GitHub token used to create an authenticated client (to fetch the latest version of Docker Compose). | **false** | `${{ github.token }}` |
|
| **`github-token`** | The GitHub token used to create an authenticated client (to fetch the latest version of Docker Compose). | **false** | `${{ github.token }}` |
|
||||||
|
|
||||||
<!-- inputs:end -->
|
<!-- inputs:end -->
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@ inputs:
|
|||||||
description: "Additional options to pass to `docker` command."
|
description: "Additional options to pass to `docker` command."
|
||||||
required: false
|
required: false
|
||||||
compose-file:
|
compose-file:
|
||||||
description: "Path to compose file(s). It can be a list of files. It can be absolute or relative to the current working directory (cwd)."
|
description: "Path to compose file(s). It can be a list of files. It can be absolute or relative to the current working directory (cwd), or an OCI artifact reference starting with `oci://`."
|
||||||
required: false
|
required: false
|
||||||
default: "./docker-compose.yml"
|
default: "./docker-compose.yml"
|
||||||
services:
|
services:
|
||||||
|
|||||||
6
dist/index.js
generated
vendored
6
dist/index.js
generated
vendored
@ -43808,9 +43808,13 @@ class InputService {
|
|||||||
getComposeFiles() {
|
getComposeFiles() {
|
||||||
const cwd = this.getCwd();
|
const cwd = this.getCwd();
|
||||||
const composeFiles = getMultilineInput(InputNames.ComposeFile).filter((composeFile) => {
|
const composeFiles = getMultilineInput(InputNames.ComposeFile).filter((composeFile) => {
|
||||||
if (!composeFile.trim().length) {
|
const trimmedComposeFile = composeFile.trim();
|
||||||
|
if (!trimmedComposeFile.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (trimmedComposeFile.startsWith("oci://")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const possiblePaths = [(0,external_node_path_namespaceObject.join)(cwd, composeFile), composeFile];
|
const possiblePaths = [(0,external_node_path_namespaceObject.join)(cwd, composeFile), composeFile];
|
||||||
for (const path of possiblePaths) {
|
for (const path of possiblePaths) {
|
||||||
if ((0,external_node_fs_namespaceObject.existsSync)(path)) {
|
if ((0,external_node_fs_namespaceObject.existsSync)(path)) {
|
||||||
|
|||||||
6
dist/post.js
generated
vendored
6
dist/post.js
generated
vendored
@ -40060,9 +40060,13 @@ class InputService {
|
|||||||
getComposeFiles() {
|
getComposeFiles() {
|
||||||
const cwd = this.getCwd();
|
const cwd = this.getCwd();
|
||||||
const composeFiles = getMultilineInput(InputNames.ComposeFile).filter((composeFile) => {
|
const composeFiles = getMultilineInput(InputNames.ComposeFile).filter((composeFile) => {
|
||||||
if (!composeFile.trim().length) {
|
const trimmedComposeFile = composeFile.trim();
|
||||||
|
if (!trimmedComposeFile.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (trimmedComposeFile.startsWith("oci://")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const possiblePaths = [(0,external_node_path_namespaceObject.join)(cwd, composeFile), composeFile];
|
const possiblePaths = [(0,external_node_path_namespaceObject.join)(cwd, composeFile), composeFile];
|
||||||
for (const path of possiblePaths) {
|
for (const path of possiblePaths) {
|
||||||
if ((0,external_node_fs_namespaceObject.existsSync)(path)) {
|
if ((0,external_node_fs_namespaceObject.existsSync)(path)) {
|
||||||
|
|||||||
@ -139,6 +139,31 @@ describe("InputService", () => {
|
|||||||
expect(inputs.composeFiles).toEqual(["./compose.yml"]);
|
expect(inputs.composeFiles).toEqual(["./compose.yml"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should accept OCI compose files without checking the file system", () => {
|
||||||
|
getMultilineInputMock.mockImplementation((inputName) => {
|
||||||
|
switch (inputName) {
|
||||||
|
case InputNames.ComposeFile:
|
||||||
|
return ["oci://docker.io/hoverkraft/compose-app:latest"];
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
getInputMock.mockImplementation((inputName) => {
|
||||||
|
switch (inputName) {
|
||||||
|
case InputNames.Cwd:
|
||||||
|
return "/current/working/directory";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const inputs = service.getInputs();
|
||||||
|
|
||||||
|
expect(inputs.composeFiles).toEqual(["oci://docker.io/hoverkraft/compose-app:latest"]);
|
||||||
|
expect(existsSyncMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it("should throws an error when a compose file does not exist", () => {
|
it("should throws an error when a compose file does not exist", () => {
|
||||||
getMultilineInputMock.mockImplementation((inputName) => {
|
getMultilineInputMock.mockImplementation((inputName) => {
|
||||||
switch (inputName) {
|
switch (inputName) {
|
||||||
|
|||||||
@ -54,10 +54,16 @@ export class InputService {
|
|||||||
private getComposeFiles(): string[] {
|
private getComposeFiles(): string[] {
|
||||||
const cwd = this.getCwd();
|
const cwd = this.getCwd();
|
||||||
const composeFiles = getMultilineInput(InputNames.ComposeFile).filter((composeFile: string) => {
|
const composeFiles = getMultilineInput(InputNames.ComposeFile).filter((composeFile: string) => {
|
||||||
if (!composeFile.trim().length) {
|
const trimmedComposeFile = composeFile.trim();
|
||||||
|
|
||||||
|
if (!trimmedComposeFile.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trimmedComposeFile.startsWith("oci://")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const possiblePaths = [join(cwd, composeFile), composeFile];
|
const possiblePaths = [join(cwd, composeFile), composeFile];
|
||||||
|
|
||||||
for (const path of possiblePaths) {
|
for (const path of possiblePaths) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user