diff --git a/.forgejo/workflows/tag-release.yml b/.forgejo/workflows/tag-release.yml index b1c3e06..829765b 100644 --- a/.forgejo/workflows/tag-release.yml +++ b/.forgejo/workflows/tag-release.yml @@ -18,6 +18,7 @@ on: - aws-configure - cache - checkout + - helm-deploy - pnpm-build - publish-static-contents - terraform-apply diff --git a/README.md b/README.md index d1a1bf6..dd85ab3 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Shared actions for Forgejo CI/CD pipelines. | [aws-configure](aws-configure) | Authenticate with AWS via OIDC | | [cache](cache) | Cache files between workflow runs | | [checkout](checkout) | Action for checking out a repository | +| [helm-deploy](helm-deploy) | Deploy a service to Kubernetes via Helm over SSH | | [pnpm-build](pnpm-build) | Action for building and validating with PNPM | | [publish-static-contents](publish-static-contents) | Syncs frontend assets to S3 and invalidates a CloudFront distribution | | [terraform-apply](terraform-apply) | Apply Terraform configuration files using the official Terraform CLI | diff --git a/helm-deploy/README.md b/helm-deploy/README.md new file mode 100644 index 0000000..75c4cfa --- /dev/null +++ b/helm-deploy/README.md @@ -0,0 +1,34 @@ +# helm-deploy + +Deploy a service to Kubernetes via Helm over SSH. + +## Inputs + +| Input | Required | Default | Description | +|-------|----------|---------|-------------| +| `service-name` | Yes | — | Helm release name | +| `helm-host` | Yes | — | SSH target host (e.g. `dsp1-stage.schmalzgroup.net`) | +| `image-tag` | Yes | — | Docker image tag to deploy | +| `ssh-key` | Yes | — | Private SSH key content | +| `overrides-file` | No | `kubernetes/overrides-pu.yaml` | Local path to Helm values override file | +| `namespace` | No | `dsp` | Kubernetes namespace | +| `helm-repo` | No | `nexus-helm-repository` | Helm chart repository name | +| `helm-chart` | No | `DSP-Blueprint` | Chart name in the repository | + +## Usage + +```yaml +- uses: https://schmalz-git.git.onstackit.cloud/schmalz/shared-actions/helm-deploy@helm-deploy-v1 + with: + service-name: my-service + helm-host: dsp1-stage.schmalzgroup.net + image-tag: ${{ github.sha }} + ssh-key: ${{ secrets.HELM_SSH_KEY }} +``` + +## Notes + +- The SSH key is written to a temporary file with `600` permissions and removed after the job, even on failure. +- The overrides file is copied to the remote host via `scp` before the Helm upgrade. +- `helm upgrade --install` is run with `--atomic` so a failed release is automatically rolled back. +- `StrictHostKeyChecking=no` is used; ensure the host is trusted within your network or add host verification as needed. diff --git a/helm-deploy/action.yml b/helm-deploy/action.yml new file mode 100644 index 0000000..be3cfc5 --- /dev/null +++ b/helm-deploy/action.yml @@ -0,0 +1,93 @@ +name: helm-deploy +description: Deploy a service to Kubernetes via Helm over SSH + +inputs: + service-name: + description: Helm release name + required: true + helm-host: + description: SSH target (e.g., dsp1-stage.schmalzgroup.net) + required: true + overrides-file: + description: Local path to Helm values override file + required: false + default: kubernetes/overrides-pu.yaml + image-tag: + description: Docker image tag to deploy + required: true + ssh-key: + description: Private SSH key content + required: true + namespace: + description: Kubernetes namespace + required: false + default: dsp + helm-repo: + description: Helm chart repository name + required: false + default: nexus-helm-repository + helm-chart: + description: Chart name in the repo + required: false + default: DSP-Blueprint + +runs: + using: composite + steps: + - name: Setup SSH key + shell: bash + env: + SSH_KEY: ${{ inputs.ssh-key }} + run: | + set -euo pipefail + SSH_KEY_FILE=$(mktemp) + printf '%s\n' "$SSH_KEY" > "$SSH_KEY_FILE" + chmod 600 "$SSH_KEY_FILE" + echo "SSH_KEY_FILE=$SSH_KEY_FILE" >> "$GITHUB_ENV" + + - name: Copy overrides file + shell: bash + env: + HELM_HOST: ${{ inputs.helm-host }} + SERVICE_NAME: ${{ inputs.service-name }} + OVERRIDES_FILE: ${{ inputs.overrides-file }} + run: | + set -euo pipefail + scp -i "$SSH_KEY_FILE" \ + -o StrictHostKeyChecking=no \ + -o BatchMode=yes \ + -o ConnectTimeout=10 \ + "$OVERRIDES_FILE" \ + "root@${HELM_HOST}:/tmp/${SERVICE_NAME}-overrides.yaml" + + - name: Helm deploy + shell: bash + env: + HELM_HOST: ${{ inputs.helm-host }} + SERVICE_NAME: ${{ inputs.service-name }} + NAMESPACE: ${{ inputs.namespace }} + HELM_REPO: ${{ inputs.helm-repo }} + HELM_CHART: ${{ inputs.helm-chart }} + IMAGE_TAG: ${{ inputs.image-tag }} + run: | + set -euo pipefail + ssh -i "$SSH_KEY_FILE" \ + -o StrictHostKeyChecking=no \ + -o BatchMode=yes \ + -o ConnectTimeout=10 \ + -o ServerAliveInterval=30 \ + -o ServerAliveCountMax=5 \ + "root@${HELM_HOST}" \ + "helm repo update && \ + helm upgrade --install --create-namespace \ + -n '${NAMESPACE}' \ + '${SERVICE_NAME}' \ + '${HELM_REPO}/${HELM_CHART}' \ + -f '/tmp/${SERVICE_NAME}-overrides.yaml' \ + --set image.tag='${IMAGE_TAG}' \ + --atomic --debug" + + - name: Cleanup SSH key + if: always() + shell: bash + run: rm -f "$SSH_KEY_FILE"