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). Used to update 'repository:' fields in standard bjw-s app-template format. Optional when using tag-keys for non-standard layouts. required: false default: "" image-tag: description: Image tag to deploy (e.g. 2026-06-17-abc1234) required: true tag-keys: description: | Comma-separated YAML keys whose values should be set to image-tag. Defaults to 'tag' which matches the standard 'tag: ' format. For non-standard layouts, specify custom keys (e.g. 'rustImageTag,uiImageTag'). Handles both unquoted values and quoted values with optional YAML anchors. required: false default: "tag" 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 }} TAG_KEYS: ${{ inputs.tag-keys }} 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 fields via regex ────────────────────────── python3 << 'PYEOF' import os, pathlib, re path = pathlib.Path(os.environ["GITOPS_FILE"]) repo = os.environ.get("IMAGE_REPOSITORY", "") tag = os.environ["IMAGE_TAG"] tag_keys = [k.strip() for k in os.environ["TAG_KEYS"].split(",") if k.strip()] Q = '"' text = path.read_text() # Update repository field (only if image-repository is provided) if repo: text = re.sub(r"(repository:\s*)\S+", lambda m: m.group(1) + repo, text, count=1) # Update each tag key for key in tag_keys: # Pattern matches both formats: # tag: 2026-06-17-abc1234 (unquoted) # tag: "2026-06-17-abc1234" (quoted) # imageTag: &anchor "value" (YAML anchor with quotes) # tag: *anchor (YAML alias — left alone by design) pat_quoted = r"(" + re.escape(key) + r':\s*(?:&\S+\s+)?)"[^"]*"' quoted = re.sub( pat_quoted, lambda m: m.group(1) + Q + tag + Q, text ) if quoted != text: text = quoted else: # Unquoted value (e.g., tag: 2026-06-17-abc1234) text = re.sub( r"(" + re.escape(key) + r":\s*)\S+", lambda m: m.group(1) + tag, text, count=1 ) path.write_text(text) PYEOF # ── Commit + push (idempotent) ──────────────────────────── if git diff --quiet -- "$GITOPS_FILE"; then echo "✅ GitOps already points to ${IMAGE_REPOSITORY:-(custom keys)}:$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: ${APP_NAME} → $IMAGE_TAG"