Initial: flux-deploy composite action for K8s GitOps deployments
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
name: Flux GitOps Deploy
|
||||
description: Update a Flux HelmRelease image tag in a GitOps repo to trigger a Kubernetes deployment
|
||||
author: eric
|
||||
|
||||
inputs:
|
||||
image-repository:
|
||||
description: Full image repository (e.g. git.ericxliu.me/eric/workout)
|
||||
required: true
|
||||
image-tag:
|
||||
description: Image tag to deploy (e.g. 2026-06-17-abc1234)
|
||||
required: true
|
||||
gitops-repo:
|
||||
description: SSH URL of the GitOps repo
|
||||
default: git@github.com:eric-x-liu/k8s-flux.git
|
||||
required: false
|
||||
gitops-file:
|
||||
description: Path to the release YAML in the GitOps repo (e.g. apps/myapp/workout/release.yaml)
|
||||
required: true
|
||||
deploy-key:
|
||||
description: SSH private key with push access to the GitOps repo
|
||||
required: true
|
||||
git-user-name:
|
||||
description: Git commit author name
|
||||
default: gitops-ci
|
||||
required: false
|
||||
git-user-email:
|
||||
description: Git commit author email
|
||||
default: gitops-ci@users.noreply.git.ericxliu.me
|
||||
required: false
|
||||
commit-message:
|
||||
description: "Custom commit message. Supports {app} and {tag} placeholders."
|
||||
default: ""
|
||||
required: false
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Update GitOps image tag
|
||||
shell: bash
|
||||
env:
|
||||
IMAGE_REPOSITORY: ${{ inputs.image-repository }}
|
||||
IMAGE_TAG: ${{ inputs.image-tag }}
|
||||
GITOPS_REPO: ${{ inputs.gitops-repo }}
|
||||
GITOPS_FILE: ${{ inputs.gitops-file }}
|
||||
DEPLOY_KEY: ${{ inputs.deploy-key }}
|
||||
GIT_USER_NAME: ${{ inputs.git-user-name }}
|
||||
GIT_USER_EMAIL: ${{ inputs.git-user-email }}
|
||||
COMMIT_MESSAGE: ${{ inputs.commit-message }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# ── SSH setup ──────────────────────────────────────────────
|
||||
mkdir -p "$HOME/.ssh"
|
||||
printf '%s\n' "$DEPLOY_KEY" > "$HOME/.ssh/flux_deploy_key"
|
||||
chmod 600 "$HOME/.ssh/flux_deploy_key"
|
||||
ssh-keyscan -H github.com >> "$HOME/.ssh/known_hosts" 2>/dev/null
|
||||
export GIT_SSH_COMMAND="ssh -i $HOME/.ssh/flux_deploy_key -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes"
|
||||
|
||||
# ── Clone GitOps repo ──────────────────────────────────────
|
||||
workdir="$(mktemp -d)"
|
||||
git clone "$GITOPS_REPO" "$workdir/gitops"
|
||||
cd "$workdir/gitops"
|
||||
|
||||
# ── Update image repository + tag via regex ────────────────
|
||||
python3 -c '
|
||||
import os, pathlib, re
|
||||
|
||||
path = pathlib.Path(os.environ["GITOPS_FILE"])
|
||||
repo = os.environ["IMAGE_REPOSITORY"]
|
||||
tag = os.environ["IMAGE_TAG"]
|
||||
|
||||
text = path.read_text()
|
||||
text = re.sub(r"(repository:\s*)\S+", lambda m: m.group(1) + repo, text, count=1)
|
||||
text = re.sub(r"(tag:\s*)\S+", lambda m: m.group(1) + tag, text, count=1)
|
||||
path.write_text(text)
|
||||
'
|
||||
|
||||
# ── Commit + push (idempotent) ────────────────────────────
|
||||
if git diff --quiet -- "$GITOPS_FILE"; then
|
||||
echo "✅ GitOps already points to $IMAGE_REPOSITORY:$IMAGE_TAG — nothing to do"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Derive app name from the gitops file path for the default commit message
|
||||
APP_NAME="$(basename "$(dirname "$GITOPS_FILE")")"
|
||||
|
||||
if [ -z "$COMMIT_MESSAGE" ]; then
|
||||
COMMIT_MESSAGE="Update ${APP_NAME} image to ${IMAGE_TAG}"
|
||||
else
|
||||
COMMIT_MESSAGE="${COMMIT_MESSAGE//\{app\}/$APP_NAME}"
|
||||
COMMIT_MESSAGE="${COMMIT_MESSAGE//\{tag\}/$IMAGE_TAG}"
|
||||
fi
|
||||
|
||||
git config user.name "$GIT_USER_NAME"
|
||||
git config user.email "$GIT_USER_EMAIL"
|
||||
git add "$GITOPS_FILE"
|
||||
git commit -m "$COMMIT_MESSAGE"
|
||||
git push origin main
|
||||
|
||||
echo "🚀 Pushed: $IMAGE_REPOSITORY:$IMAGE_TAG → $GITOPS_FILE"
|
||||
@@ -0,0 +1,72 @@
|
||||
# actions-k8s
|
||||
|
||||
Reusable Gitea Actions for Kubernetes deployments via Flux GitOps.
|
||||
|
||||
## actions/flux-deploy
|
||||
|
||||
Composite action that updates a [Flux HelmRelease](https://fluxcd.io/flux/components/helm/) image tag in a GitOps repo to trigger a Kubernetes deployment.
|
||||
|
||||
This action clones the GitOps repo, updates the `repository` and `tag` fields in the specified release YAML, commits, and pushes. If the release already points to the requested image, the action exits cleanly (idempotent).
|
||||
|
||||
### Usage
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ci]
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- name: Add SSH_KEY
|
||||
run: |
|
||||
echo "${{ secrets.SSH_KEY }}" > $HOME/.ssh/id_rsa
|
||||
chmod 400 $HOME/.ssh/id_rsa
|
||||
ssh-keyscan -H git.ericxliu.me > $HOME/.ssh/known_hosts
|
||||
|
||||
- name: Checkout code
|
||||
uses: https://gitea.com/actions/checkout@v4
|
||||
with:
|
||||
ssh-key: ${{ secrets.SSH_KEY }}
|
||||
|
||||
- name: Build and push image
|
||||
id: image
|
||||
uses: https://git.ericxliu.me/eric/actions-docker/.gitea/actions/docker-build@main
|
||||
with:
|
||||
image-name: ${{ github.event.repository.name }}
|
||||
push: true
|
||||
|
||||
- name: Deploy to K8s via Flux
|
||||
uses: https://git.ericxliu.me/eric/actions-k8s/.gitea/actions/flux-deploy@main
|
||||
with:
|
||||
image-repository: ${{ github.server_url }}/${{ github.repository }}
|
||||
image-tag: ${{ steps.image.outputs.tag }}
|
||||
gitops-file: apps/myapp/my-app/release.yaml
|
||||
deploy-key: ${{ secrets.K8S_FLUX_DEPLOY_KEY }}
|
||||
```
|
||||
|
||||
### Inputs
|
||||
|
||||
| Input | Required | Default | Description |
|
||||
|-------|----------|---------|-------------|
|
||||
| `image-repository` | **Yes** | — | Full image repository (e.g. `git.ericxliu.me/eric/workout`) |
|
||||
| `image-tag` | **Yes** | — | Image tag to deploy (e.g. `2026-06-17-abc1234`) |
|
||||
| `gitops-repo` | No | `git@github.com:eric-x-liu/k8s-flux.git` | SSH URL of the GitOps repo |
|
||||
| `gitops-file` | **Yes** | — | Path to release YAML in the GitOps repo |
|
||||
| `deploy-key` | **Yes** | — | SSH private key with push access to the GitOps repo |
|
||||
| `git-user-name` | No | `gitops-ci` | Git commit author name |
|
||||
| `git-user-email` | No | `gitops-ci@users.noreply.git.ericxliu.me` | Git commit author email |
|
||||
| `commit-message` | No | `Update {app} image to {tag}` | Custom commit message (`{app}` and `{tag}` are replaced) |
|
||||
|
||||
### How It Works
|
||||
|
||||
1. Sets up SSH with the provided deploy key
|
||||
2. Clones the GitOps repo (e.g. `k8s-flux`)
|
||||
3. Updates `repository:` and `tag:` fields in the specified release YAML via regex
|
||||
4. If no changes detected → exits cleanly (idempotent)
|
||||
5. Commits and pushes to trigger Flux reconciliation
|
||||
|
||||
### Requirements
|
||||
|
||||
- The GitOps repo must be accessible via SSH with the provided deploy key
|
||||
- The release YAML must contain `repository:` and `tag:` fields (standard bjw-s `app-template` chart format)
|
||||
- `python3` must be available on the runner (standard on `ubuntu-latest`)
|
||||
Reference in New Issue
Block a user