248eb37110
Adds tag-keys input (comma-separated) to update arbitrary YAML keys instead of just 'tag'. Handles unquoted values, quoted values, and YAML anchors (e.g. rustImageTag: &rustImageTag "value"). Makes image-repository optional when using custom tag-keys.
144 lines
5.6 KiB
YAML
144 lines
5.6 KiB
YAML
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: <value>' 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 -c '
|
|
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()]
|
|
|
|
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)
|
|
# rustImageTag: &anchor "value" (YAML anchor with quotes)
|
|
# tag: *anchor (YAML alias — left alone by design)
|
|
#
|
|
# Group 1: everything up to and including the last quote or whitespace before the value
|
|
# Replacement: group(1) + new tag (preserving quoting style)
|
|
quoted = re.sub(
|
|
r"(" + re.escape(key) + r""":\s*(?:&\S+\s+)?)\"[^"]*\"""",
|
|
lambda m: m.group(1) + '"' + tag + '"',
|
|
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)
|
|
'
|
|
|
|
# ── 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"
|