diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 9f47abb..379c315 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -14,4 +14,17 @@ jobs: steps: - name: "Update Tags" + id: tags uses: cssnr/update-version-tags-action@v1 + + - name: "Debug Tags" + run: | + echo "github.ref_name: ${{ github.ref_name }}" + echo "steps.tags.outputs.tags: ${{ steps.tags.outputs.tags }}" + + - name: "Update Release Notes Action" + uses: smashedr/update-release-notes-action@master + continue-on-error: true + with: + tags: "${{ steps.tags.outputs.tags }}" + location: tail diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 73ca91c..0cf3b90 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -18,15 +18,16 @@ on: env: PRIVATE_IMAGE: ${{ vars.PRIVATE_IMAGE || 'smashedr/alpine-private:latest' }} +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + jobs: test: name: "Test" if: ${{ !contains(github.event.head_commit.message, '#notest') }} runs-on: ubuntu-latest timeout-minutes: 5 - concurrency: - group: ${{ github.workflow }} - cancel-in-progress: true steps: - name: "Checkout" @@ -82,6 +83,51 @@ jobs: registry_pass: ${{ secrets.DOCKER_HUB_PASS }} summary: false + - name: "3: Write YAML" + if: ${{ !cancelled() && !github.event.act }} + uses: teunmooij/yaml@v1 + with: + to-file: "docker-compose.yaml" + data: | + {"version":"3.8","services":{"alpine":{"image":"alpine:latest","command":"tail -f /dev/null"}}} + + - name: "3: Test Compose" + if: ${{ !cancelled() && !github.event.act }} + uses: ./ + with: + name: test_stack-deploy-compose + file: docker-compose.yaml + host: ${{ secrets.DOCKER_HOST }} + port: ${{ secrets.DOCKER_PORT }} + user: ${{ secrets.DOCKER_USER }} + pass: ${{ secrets.DOCKER_PASS }} + #ssh_key: ${{ secrets.DOCKER_SSH_KEY }} + mode: compose + summary: false + + - name: "4: Write YAML" + if: ${{ !cancelled() }} + uses: teunmooij/yaml@v1 + with: + to-file: "docker-compose.yaml" + data: | + {"version":"3.8","services":{"alpine":{"image":"${{ env.PRIVATE_IMAGE }}","command":"tail -f /dev/null"}}} + + - name: "4: Test Compose SSH and Auth" + if: ${{ !cancelled() }} + uses: ./ + with: + name: test_stack-deploy-compose + file: docker-compose.yaml + host: ${{ secrets.DOCKER_HOST }} + port: ${{ secrets.DOCKER_PORT }} + user: ${{ secrets.DOCKER_USER }} + #pass: ${{ secrets.DOCKER_PASS }} + ssh_key: ${{ secrets.DOCKER_SSH_KEY }} + mode: compose + registry_user: ${{ vars.DOCKER_HUB_USER }} + registry_pass: ${{ secrets.DOCKER_HUB_PASS }} + - name: "Schedule Failure Notification" if: ${{ failure() && github.event_name == 'schedule' }} uses: sarisia/actions-status-discord@v1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0b59435..8bd3e0b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,7 @@ > [!WARNING] > This guide is a work in progress and may not be complete. -This is a basic contributing guide and is a work in progress. +Note: This guide is not updated for Compose but those tests work the same way. ## Workflow diff --git a/README.md b/README.md index 838de11..730e6e5 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ [![GitHub Tag Major](https://img.shields.io/github/v/tag/cssnr/stack-deploy-action?sort=semver&filter=!v*.*&logo=git&logoColor=white&labelColor=585858&label=%20)](https://github.com/cssnr/stack-deploy-action/tags) [![GitHub Tag Minor](https://img.shields.io/github/v/tag/cssnr/stack-deploy-action?sort=semver&filter=!v*.*.*&logo=git&logoColor=white&labelColor=585858&label=%20)](https://github.com/cssnr/stack-deploy-action/tags) [![GitHub Release Version](https://img.shields.io/github/v/release/cssnr/stack-deploy-action?logo=git&logoColor=white&labelColor=585858&label=%20)](https://github.com/cssnr/stack-deploy-action/releases/latest) -[![Release WF](https://img.shields.io/github/actions/workflow/status/cssnr/stack-deploy-action/release.yaml?logo=github&label=release)](https://github.com/cssnr/stack-deploy-action/actions/workflows/release.yaml) -[![Test WF](https://img.shields.io/github/actions/workflow/status/cssnr/stack-deploy-action/test.yaml?logo=github&label=test)](https://github.com/cssnr/stack-deploy-action/actions/workflows/test.yaml) -[![Lint WF](https://img.shields.io/github/actions/workflow/status/cssnr/stack-deploy-action/lint.yaml?logo=github&label=lint)](https://github.com/cssnr/stack-deploy-action/actions/workflows/lint.yaml) +[![Workflow Release](https://img.shields.io/github/actions/workflow/status/cssnr/stack-deploy-action/release.yaml?logo=github&label=release)](https://github.com/cssnr/stack-deploy-action/actions/workflows/release.yaml) +[![Workflow Test](https://img.shields.io/github/actions/workflow/status/cssnr/stack-deploy-action/test.yaml?logo=github&label=test)](https://github.com/cssnr/stack-deploy-action/actions/workflows/test.yaml) +[![Workflow Lint](https://img.shields.io/github/actions/workflow/status/cssnr/stack-deploy-action/lint.yaml?logo=github&label=lint)](https://github.com/cssnr/stack-deploy-action/actions/workflows/lint.yaml) [![GitHub Last Commit](https://img.shields.io/github/last-commit/cssnr/stack-deploy-action?logo=github&label=updated)](https://github.com/cssnr/stack-deploy-action/graphs/commit-activity) [![Codeberg Last Commit](https://img.shields.io/gitea/last-commit/cssnr/stack-deploy-action/master?gitea_url=https%3A%2F%2Fcodeberg.org%2F&logo=codeberg&logoColor=white&label=updated)](https://codeberg.org/cssnr/stack-deploy-action) [![GitHub Top Language](https://img.shields.io/github/languages/top/cssnr/stack-deploy-action?logo=htmx)](https://github.com/cssnr/stack-deploy-action) @@ -18,9 +18,14 @@ - [Inputs](#Inputs) - [Examples](#Examples) - [Tags](#Tags) +- [Features](#Features) - [Support](#Support) - [Contributing](#Contributing) +> [!TIP] +> ๐Ÿ’ก Now works with vanilla Docker hosts using **Compose. No Swarm Required!** +> Just set `mode: compose`. See the [Inputs](#Inputs) for more details... + This action deploys a docker stack from a compose file to a remote docker host using SSH Password or Key File Authentication. You can also optionally authenticate against a private registry using a username and password. @@ -36,58 +41,85 @@ For more details see [action.yaml](action.yaml) and [src/main.sh](src/main.sh). ## Inputs -| input | required | default | description | -| ------------- | :----------: | --------------------- | ----------------------------------------- | -| name | **Yes** | - | Docker Stack Name | -| file | - | `docker-compose.yaml` | Docker Compose File | -| host | **Yes** | - | Remote Docker Hostname or IP \* | -| port | - | `22` | Remote Docker Port | -| user | **Yes** | - | Remote Docker Username | -| pass | or `ssh_key` | - | Remote Docker Password \* | -| ssh_key | or `pass` | - | Remote SSH Key File \* | -| env_file | - | - | Docker Environment File \* | -| detach | - | `true` | Detach Flag, `false` to disable \* | -| prune | - | `false` | Prune Flag, `true` to enable | -| resolve_image | - | `always` | Options [`always`, `changed`, `never`] \* | -| registry_auth | - | - | Enable Registry Authentication \* | -| registry_host | - | - | Registry Authentication Host \* | -| registry_user | - | - | Registry Authentication Username \* | -| registry_pass | - | - | Registry Authentication Password \* | -| summary | - | `true` | Add Job Summary \* | - -_For additional details on inputs, see the stack deploy -[documentation](https://docs.docker.com/reference/cli/docker/stack/deploy/)._ - -**host** - The hostname or IP address of the remote docker server to deploy too. +| Input | Required | Default | Description | +| :------------------- | :----------: | :---------------------------------- | :---------------------------------------- | +| `name` | **Yes** | - | Docker Stack/Project Name \* | +| `file` | - | `docker-compose.yaml` | Docker Stack/Compose File | +| `mode`**ยน** | - | `swarm` | Deploy Mode: [`swarm`, `compose`] \* | +| `args`**ยน** | - | `--remove-orphans --force-recreate` | Additional Arguments for **Compose** \* | +| `host` | **Yes** | - | Remote Docker Hostname or IP \* | +| `port` | - | `22` | Remote Docker Port | +| `user` | **Yes** | - | Remote Docker Username | +| `pass` | or `ssh_key` | - | Remote Docker Password \* | +| `ssh_key` | or `pass` | - | Remote SSH Key File \* | +| `env_file` | - | - | Docker Environment File \* | +| `detach`**ยฒ** | - | `true` | Detach Flag, `false`, to disable \* | +| `prune`**ยฒ** | - | `false` | Prune Flag, `true`, to enable | +| `resolve_image`**ยฒ** | - | `always` | Resolve [`always`, `changed`, `never`] \* | +| `registry_auth`**ยฒ** | - | - | Enable Registry Authentication \* | +| `registry_host` | - | - | Registry Authentication Host \* | +| `registry_user` | - | - | Registry Authentication Username \* | +| `registry_pass` | - | - | Registry Authentication Password \* | +| `summary` | - | `true` | Add Job Summary \* | + +> **ยน** Compose Only. View the [Docs](https://docs.docker.com/reference/cli/docker/compose/up/). +> **ยฒ** Swarm Only. View the [Docs](https://docs.docker.com/reference/cli/docker/stack/deploy/). +> \* See Below for more details... + +
๐Ÿ“Ÿ Click Here to see how the deployment command is generated + +```shell +if [[ "${INPUT_MODE}" == "swarm" ]];then + DEPLOY_TYPE="Swarm" + COMMAND=("docker" "stack" "deploy" "-c" "${INPUT_FILE}" "${EXTRA_ARGS[@]}" "${INPUT_NAME}") +else + DEPLOY_TYPE="Compose" + COMMAND=("docker" "compose" "-f" "${INPUT_FILE}" "-p" "${INPUT_NAME}" "up" "-d" "-y" "${EXTRA_ARGS[@]}") +fi +``` + +
+ +**name:** Stack name for Swarm and project name for Compose. + +**mode:** _Compose only._ Set this to `compose` to use `compose up` instead of `stack deploy` for non-swarm hosts. + +**args:** _Compose only._ Compose arguments to pass to the `compose up` command. Only used for `mode: compose` deployments. +The `detach` flag defaults to false for compose. With no args the default is `--remove-orphans --force-recreate`. +Use an empty string to override. For more details, see the compose +[docs](https://docs.docker.com/reference/cli/docker/compose/up/). + +**host:** The hostname or IP address of the remote docker server to deploy too. If your hostname is behind a proxy like Cloudflare you will need to use the IP address. -**pass/ssh_key** - You must provide either a `pass` or `ssh_key`. +**pass/ssh_key:** You must provide either a `pass` or `ssh_key`, but not both. -**env_file** - Variables in this file are exported before running stack deploy. +**env_file:** Variables in this file are exported before running stack deploy. To use a docker `env_file` specify it in your compose file and make it available in a previous step. If you need compose file templating this can also be done in a previous step. +If using `mode: compose` you can also add the `compose_arg: --env-file stringArray`. -**detach** - Set this to `false` to not exit immediately and wait for the services to converge. +**detach:** _Swarm only._ Set this to `false` to not exit immediately and wait for the services to converge. This will generate extra output in the logs and is useful for debugging deployments. +Defaults to `false` in `mode: compose`. -**resolve_image** - When the default `always` is used, this argument is omitted. +**resolve_image:** _Swarm only._ When the default `always` is used, this argument is omitted. -**registry_auth** - Set to `true` to deploy with `--with-registry-auth`. +**registry_auth:** _Swarm only._ Set to `true` to deploy with `--with-registry-auth`. -**registry_host** - To run `docker login` on another registry. Example: `ghcr.io` +**registry_host:** To run `docker login` on another registry. Example: `ghcr.io`. -**registry_user/registry_pass** - Required to run `docker login` before stack deploy. +**registry_user/registry_pass:** Required to run `docker login` before stack deploy. -**summary** - Write a Summary for the job. To disable this set to `false`. +**summary:** Write a Summary for the job. To disable this set to `false`. -To view a workflow run, click on a recent -[Test](https://github.com/cssnr/stack-deploy-action/actions/workflows/test.yaml) job _(requires login)_. +To view a workflow run, click on a recent [Test](https://github.com/cssnr/stack-deploy-action/actions/workflows/test.yaml) job _(requires login)_. -
๐Ÿ‘€ View Example Job Summary +
๐Ÿ‘€ View Example Successful โœ”๏ธ Job Summary --- -๐ŸŽ‰ Stack `test_stack-deploy` Successfully Deployed. +๐Ÿš€ Swarm Stack `test_stack-deploy` Successfully Deployed. ```text docker stack deploy --detach=false --resolve-image=changed -c docker-compose.yaml test_stack-deploy @@ -114,6 +146,29 @@ verify: Service tdk8v42m0rvp9hz4rbfrtszb6 converged
+
๐Ÿ‘€ View Example Failure โŒ Job Summary + +--- + +โ›” Swarm Stack `test_stack-deploy` Failed to Deploy! + +```text +docker stack deploy -c docker-compose.yaml --detach=false --resolve-image=changed test_stack-deploy +``` + +
Errors + +```text +Creating network test_stack-deploy_default +failed to create network test_stack-deploy_default: Error response from daemon: network with name test_stack-deploy_default already exists +``` + +
+ +--- + +
+ ```yaml - name: 'Stack Deploy' uses: cssnr/stack-deploy-action@v1 @@ -131,7 +186,7 @@ verify: Service tdk8v42m0rvp9hz4rbfrtszb6 converged ๐Ÿ’ก _Click on an example heading to expand or collapse the example._ -
With password, docker login and --with-registry-auth +
With Password, docker login and --with-registry-auth ```yaml - name: 'Stack Deploy' @@ -149,8 +204,7 @@ verify: Service tdk8v42m0rvp9hz4rbfrtszb6 converged ```
- -
With SSH key, --prune, --detach=false and --resolve-image=changed +
With SSH Key, --prune, --detach=false and --resolve-image=changed ```yaml - name: 'Stack Deploy' @@ -168,8 +222,7 @@ verify: Service tdk8v42m0rvp9hz4rbfrtszb6 converged ```
- -
With All Inputs +
With All Swarm Inputs ```yaml - name: 'Stack Deploy' @@ -194,7 +247,84 @@ verify: Service tdk8v42m0rvp9hz4rbfrtszb6 converged ```
+
Compose with Defaults + +```yaml +- name: 'Compose Deploy' + uses: cssnr/stack-deploy-action@v1 + with: + name: 'stack-name' + file: 'docker-compose.yaml' + host: ${{ secrets.DOCKER_HOST }} + port: ${{ secrets.DOCKER_PORT }} + user: ${{ secrets.DOCKER_USER }} + ssh_key: ${{ secrets.DOCKER_SSH_KEY }} + mode: compose +``` + +
+
Compose with Custom Arguments + +```yaml +- name: 'Compose Deploy' + uses: cssnr/stack-deploy-action@v1 + with: + name: 'stack-name' + file: 'docker-compose.yaml' + host: ${{ secrets.DOCKER_HOST }} + port: ${{ secrets.DOCKER_PORT }} + user: ${{ secrets.DOCKER_USER }} + ssh_key: ${{ secrets.DOCKER_SSH_KEY }} + mode: compose + args: --remove-orphans --force-recreate +``` + +Note: these are the default arguments. If you use `args` this will override the default arguments unless they are included. +You can disable them by passing an empty string. For more details, see the compose up [docs](https://docs.docker.com/reference/cli/docker/compose/up/). + +
+
Compose with Private Image +```yaml +- name: 'Compose Deploy' + uses: cssnr/stack-deploy-action@v1 + with: + name: 'stack-name' + file: 'docker-compose.yaml' + host: ${{ secrets.DOCKER_HOST }} + port: ${{ secrets.DOCKER_PORT }} + user: ${{ secrets.DOCKER_USER }} + ssh_key: ${{ secrets.DOCKER_SSH_KEY }} + registry_host: 'ghcr.io' + registry_user: ${{ vars.GHCR_USER }} + registry_pass: ${{ secrets.GHCR_PASS }} + mode: compose +``` + +
+
With All Compose Inputs + +```yaml +- name: 'Stack Deploy' + uses: cssnr/stack-deploy-action@v1 + with: + name: 'stack-name' + file: 'docker-compose-swarm.yaml' + host: ${{ secrets.DOCKER_HOST }} + port: ${{ secrets.DOCKER_PORT }} + user: ${{ secrets.DOCKER_USER }} + pass: ${{ secrets.DOCKER_PASS }} # not needed with ssh_key + ssh_key: ${{ secrets.DOCKER_SSH_KEY }} # not needed with pass + env_file: 'stack.env' + registry_host: 'ghcr.io' + registry_user: ${{ vars.GHCR_USER }} + registry_pass: ${{ secrets.GHCR_PASS }} + mode: compose + args: --remove-orphans --force-recreate + summary: true +``` + +
Simple Workflow Example ```yaml @@ -225,7 +355,6 @@ jobs: ```
-
Full Workflow Example ```yaml @@ -332,14 +461,25 @@ https://github.com/cssnr/stack-deploy-action/network/dependents The following rolling [tags](https://github.com/cssnr/stack-deploy-action/tags) are maintained. -| Tag | Example | Target | Bugs | Feat. | Description | -| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------- | :--: | :---: | --------------------------------------------------------- | -| [![GitHub Tag Major](https://img.shields.io/github/v/tag/cssnr/stack-deploy-action?sort=semver&filter=!v*.*&style=for-the-badge&label=%20&color=limegreen)](https://github.com/cssnr/stack-deploy-action/releases/latest) | `vN` | `vN.x.x` | โœ… | โœ… | Includes new features but is always backwards compatible. | -| [![GitHub Tag Minor](https://img.shields.io/github/v/tag/cssnr/stack-deploy-action?sort=semver&filter=!v*.*.*&style=for-the-badge&label=%20&color=yellowgreen)](https://github.com/cssnr/stack-deploy-action/releases/latest) | `vN.N` | `vN.N.x` | โœ… | โŒ | Only receives bug fixes. This is the most stable tag. | -| [![GitHub Release](https://img.shields.io/github/v/release/cssnr/stack-deploy-action?style=for-the-badge&label=%20&color=orange)](https://github.com/cssnr/stack-deploy-action/releases/latest) | `vN.N.N` | `vN.N.N` | โŒ | โŒ | Not a rolling tag. **Not** recommended. | +| Version Tag | Rolling | Bugs | Feat. | Target | Example | +| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----: | :--: | :---: | :------- | :------- | +| [![GitHub Tag Major](https://img.shields.io/github/v/tag/cssnr/stack-deploy-action?sort=semver&filter=!v*.*&style=for-the-badge&label=%20&color=44cc10)](https://github.com/cssnr/stack-deploy-action/releases/latest) | โœ… | โœ… | โœ… | `vN.x.x` | `vN` | +| [![GitHub Tag Minor](https://img.shields.io/github/v/tag/cssnr/stack-deploy-action?sort=semver&filter=!v*.*.*&style=for-the-badge&label=%20&color=blue)](https://github.com/cssnr/stack-deploy-action/releases/latest) | โœ… | โœ… | โŒ | `vN.N.x` | `vN.N` | +| [![GitHub Release](https://img.shields.io/github/v/release/cssnr/stack-deploy-action?style=for-the-badge&label=%20&color=red)](https://github.com/cssnr/stack-deploy-action/releases/latest) | โŒ | โŒ | โŒ | `vN.N.N` | `vN.N.N` | You can view the release notes for each version on the [releases](https://github.com/cssnr/stack-deploy-action/releases) page. +## Features + +- Deploy to a remote host using SSH or Password authentication. +- Deploy using a remote context from the current working directory. +- Deploy from a compose file to either a Docker Swarm or Compose host. +- Displays output in logs, captures it in the Summary, and checks the status. +- Allows logging into a private registry and deploying with registry auth. +- Allows specifying all arguments for both Swarm and Compose deployments. + +Don't see your feature here? Request it below in the [Support](#Support) section. + # Support For general help or to request a feature see: diff --git a/action.yaml b/action.yaml index 4f770fe..5cd86dd 100644 --- a/action.yaml +++ b/action.yaml @@ -1,34 +1,42 @@ name: "Docker Stack Deploy" -description: "Deploy a Docker Stack to a Remote Host over SSH w/ Optional Registry Authentication" +description: "Deploy a Docker Compose or Swarm Stack to a Remote Host over SSH with Password or Key File Authentication" author: "Shane" branding: icon: "layers" color: "green" inputs: + name: + description: "Docker Stack Name" + required: true + file: + description: "Docker Compose File" + required: false + default: "docker-compose.yaml" + mode: + description: "Deploy Mode" + required: false + default: "swarm" + args: + description: "Compose Arguments" + required: false + default: "--remove-orphans --force-recreate" host: - description: "Docker Host" + description: "Remote Docker Host" required: true port: - description: "Docker Port" - default: "22" + description: "Remote Docker Port" required: false + default: "22" user: - description: "Docker User" + description: "Remote Docker User" required: true pass: - description: "Docker Pass" + description: "Remote Docker Pass" required: false ssh_key: - description: "SSH Key File" + description: "Remote SSH Key File" required: false - name: - description: "Docker Stack Name" - required: true - file: - description: "Docker Compose File" - required: false - default: "docker-compose.yaml" env_file: description: "Environment File" required: false diff --git a/src/main.sh b/src/main.sh index 38d5c58..bfd5220 100644 --- a/src/main.sh +++ b/src/main.sh @@ -3,9 +3,10 @@ set -e +# shellcheck disable=SC2317 function cleanup_trap() { _ST="$?" - if [ -z "${INPUT_SSH_KEY}" ];then + if [[ -z "${INPUT_SSH_KEY}" ]];then echo "๐Ÿงน Cleaning Up authorized_keys" ssh -o BatchMode=yes -o ConnectTimeout=30 -p "${INPUT_PORT}" "${INPUT_USER}@${INPUT_HOST}" \ "sed -i '/docker-stack-deploy-action/d' ~/.ssh/authorized_keys" @@ -19,6 +20,30 @@ function cleanup_trap() { exit "${_ST}" } +## Check Variables + +if [[ "${INPUT_MODE}" == "swarm" ]];then + if [[ "${INPUT_ARGS}" != "--remove-orphans --force-recreate" ]];then + echo "::warning::You set compose args but mode is swarm!" + fi +else +#elif [[ "${INPUT_MODE}" == "compose" ]];then + if [[ "${INPUT_DETACH}" != "true" ]];then + echo "::warning::You set detach but mode is compose!" + fi + if [[ "${INPUT_PRUNE}" != "false" ]];then + echo "::warning::You set prune but mode is compose!" + fi + if [[ "${INPUT_RESOLVE_IMAGE}" != "always" ]];then + echo "::warning::You set resolve_image but mode is compose!" + fi +#else +# echo "::error::Input mode must be set to swarm or compose!" +# exit 1 +fi + +## Setup Script + SSH_DIR="/root/.ssh" echo "::group::Starting Stack Deploy Action ${GITHUB_ACTION_REF}" @@ -27,12 +52,15 @@ echo "Script: ${0}" echo "Current Directory: $(pwd)" echo "Home Directory: ${HOME}" echo "SSH Directory: ${SSH_DIR}" + mkdir -p "${SSH_DIR}" ~/.ssh chmod 0700 "${SSH_DIR}" ~/.ssh ssh-keyscan -p "${INPUT_PORT}" -H "${INPUT_HOST}" >> "${SSH_DIR}/known_hosts" echo "::endgroup::" -if [ -z "${INPUT_SSH_KEY}" ];then +## Setup Authentication + +if [[ -z "${INPUT_SSH_KEY}" ]];then echo "::group::Copying SSH Key to Remote Host" ssh-keygen -q -f "${SSH_DIR}/id_rsa" -N "" -C "docker-stack-deploy-action" eval "$(ssh-agent -s)" @@ -51,6 +79,8 @@ echo "::endgroup::" trap cleanup_trap EXIT HUP INT QUIT PIPE TERM +## Setup Docker Context + echo "::group::Verifying Remote Docker Context" ssh -o BatchMode=yes -o ConnectTimeout=30 -p "${INPUT_PORT}" \ "${INPUT_USER}@${INPUT_HOST}" "docker info" > /dev/null @@ -61,7 +91,9 @@ docker context use remote docker context ls echo "::endgroup::" -if [ -n "${INPUT_ENV_FILE}" ];then +## Export Environment File + +if [[ -f "${INPUT_ENV_FILE}" ]];then echo -e "::group::Sourcing Environment File: \u001b[36;1m${INPUT_ENV_FILE}" stat "${INPUT_ENV_FILE}" set -a @@ -70,6 +102,8 @@ if [ -n "${INPUT_ENV_FILE}" ];then echo "::endgroup::" fi +## Docker Login + if [[ -n "${INPUT_REGISTRY_USER}" && -n "${INPUT_REGISTRY_PASS}" ]];then echo -e "::group::Logging in to Registry: \u001b[36;1m${INPUT_REGISTRY_HOST:-Docker Hub}" echo "${INPUT_REGISTRY_PASS}" | @@ -78,40 +112,66 @@ if [[ -n "${INPUT_REGISTRY_USER}" && -n "${INPUT_REGISTRY_PASS}" ]];then echo "::endgroup::" fi +## Collect Arguments + EXTRA_ARGS=() -if [[ -n "${INPUT_REGISTRY_AUTH}" ]];then - echo "::debug::Adding: --with-registry-auth" - EXTRA_ARGS+=("--with-registry-auth") -fi -if [[ "${INPUT_DETACH}" != "true" ]];then - echo "::debug::Adding: --detach=false" - EXTRA_ARGS+=("--detach=false") -fi -if [[ "${INPUT_PRUNE}" != "false" ]];then - echo "::debug::Adding: --prune" - EXTRA_ARGS+=("--prune") -fi -if [[ "${INPUT_RESOLVE_IMAGE}" != "always" ]];then - if [[ "${INPUT_RESOLVE_IMAGE}" == "changed" || "${INPUT_RESOLVE_IMAGE}" == "never" ]];then - echo "::debug::Adding: --resolve-image=${INPUT_RESOLVE_IMAGE}" - EXTRA_ARGS+=("--resolve-image=${INPUT_RESOLVE_IMAGE}") - else - echo "::error::Input resolve_image must be one of: always, changed, never" +if [[ "${INPUT_MODE}" == "swarm" ]];then + echo "::debug::Processing Swarm Arguments" + if [[ -n "${INPUT_REGISTRY_AUTH}" ]];then + echo "::debug::Adding: --with-registry-auth" + EXTRA_ARGS+=("--with-registry-auth") + fi + if [[ "${INPUT_DETACH}" != "true" ]];then + echo "::debug::Adding: --detach=false" + EXTRA_ARGS+=("--detach=false") fi + if [[ "${INPUT_PRUNE}" != "false" ]];then + echo "::debug::Adding: --prune" + EXTRA_ARGS+=("--prune") + fi + if [[ "${INPUT_RESOLVE_IMAGE}" != "always" ]];then + if [[ "${INPUT_RESOLVE_IMAGE}" == "changed" || "${INPUT_RESOLVE_IMAGE}" == "never" ]];then + echo "::debug::Adding: --resolve-image=${INPUT_RESOLVE_IMAGE}" + EXTRA_ARGS+=("--resolve-image=${INPUT_RESOLVE_IMAGE}") + else + echo "::warning::Input resolve_image must be one of: always, changed, never" + fi + fi +else + echo "::debug::Processing Compose Arguments" + echo "::debug::Adding: ${INPUT_ARGS}" + read -r -a args <<< "${INPUT_ARGS}" + EXTRA_ARGS+=("${args[@]}") fi -echo -e "::group::Deploying Stack: \u001b[36;1m${INPUT_NAME}" -COMMAND=("docker" "stack" "deploy" "${EXTRA_ARGS[@]}" "-c" "${INPUT_FILE}" "${INPUT_NAME}") +## Deploy Stack + +if [[ "${INPUT_MODE}" == "swarm" ]];then + DEPLOY_TYPE="Swarm" + COMMAND=("docker" "stack" "deploy" "-c" "${INPUT_FILE}" "${EXTRA_ARGS[@]}" "${INPUT_NAME}") +else + DEPLOY_TYPE="Compose" + COMMAND=("docker" "compose" "-f" "${INPUT_FILE}" "-p" "${INPUT_NAME}" "up" "-d" "-y" "${EXTRA_ARGS[@]}") +fi + +echo -e "::group::Deploying Docker ${DEPLOY_TYPE} Stack: \u001b[36;1m${INPUT_NAME}" echo -e "\u001b[33;1m${COMMAND[*]}\n" exec 5>&1 +set +e # shellcheck disable=SC2034 -STACK_RESULTS=$("${COMMAND[@]}" | tee >(cat >&5)) - +STACK_RESULTS=$( "${COMMAND[@]}" 2>&1 | tee >(cat >&5) ; exit "${PIPESTATUS[0]}" ) +EXIT_STATUS="$?" +set -e echo "::endgroup::" +## Write Summary + if [[ "${INPUT_SUMMARY}" == "true" ]];then echo "๐Ÿ“ Writing Job Summary" # shellcheck source=/src/summary.sh source /src/summary.sh >> "${GITHUB_STEP_SUMMARY}" ||\ echo "::error::Failed to Write Job Summary!" fi + +echo "::debug::EXIT_STATUS: ${EXIT_STATUS}" +exit "${EXIT_STATUS}" diff --git a/src/summary.sh b/src/summary.sh index 07b017e..4c307e1 100644 --- a/src/summary.sh +++ b/src/summary.sh @@ -1,15 +1,24 @@ #!/usr/bin/env bash +# shellcheck disable=SC2154 + +if [[ "${EXIT_STATUS}" == 0 ]];then + _result="๐Ÿš€ ${DEPLOY_TYPE} Stack \`${INPUT_NAME}\` Successfully Deployed." + _details="
Results" +else + _result="โ›” ${DEPLOY_TYPE} Stack \`${INPUT_NAME}\` Failed to Deploy!" + _details="
Errors" +fi cat << EOM ## Stack Deploy Action -๐ŸŽ‰ Stack \`${INPUT_NAME}\` Successfully Deployed. +${_result} \`\`\`text ${COMMAND[*]} \`\`\` -
Results +${_details} \`\`\`text ${STACK_RESULTS} @@ -17,7 +26,7 @@ ${STACK_RESULTS}
-[Report an issue or request a feature](https://github.com/cssnr/stack-deploy-action?tab=readme-ov-file#readme) +[View Documentation, Report Issues or Request Features](https://github.com/cssnr/stack-deploy-action?tab=readme-ov-file#readme) --- EOM