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"