Before committing and pushing, query the Gitea API for open PRs in the apps repo. If any open PR modifies the same kustomization.yaml path and its diff contains the target tag, exit 0 — the PR is the intended review gate and the direct push would bypass it. Fails open on API errors so an outage does not block all deploys. Fixes bug-nxhza9j2atqk
119 lines
4.1 KiB
YAML
119 lines
4.1 KiB
YAML
name: Deploy Image (kustomize image-pin in apps repo)
|
|
description: |
|
|
Pin an image tag in fritzlab/apps via `kustomize edit set image`, validate the
|
|
rendered manifests, and push to apps-repo main. Retries on push conflict.
|
|
inputs:
|
|
image:
|
|
description: Full image name without tag (must match an entry in the target kustomization.yaml `images:` block)
|
|
required: true
|
|
tag:
|
|
description: Tag to pin. Defaults to github.run_number when empty.
|
|
required: false
|
|
default: ''
|
|
path:
|
|
description: Path inside fritzlab/apps to the manifests dir (e.g. sjc001/infra/chrony/manifests)
|
|
required: true
|
|
token:
|
|
description: CI_BOT_TOKEN with write access to fritzlab/apps
|
|
required: true
|
|
apps-repo:
|
|
description: Apps repo URL (without protocol)
|
|
required: false
|
|
default: code.fritzlab.net/fritzlab/apps
|
|
message:
|
|
description: Commit message. Defaults to "deploy <name> #<tag>".
|
|
required: false
|
|
default: ''
|
|
runs:
|
|
using: composite
|
|
steps:
|
|
- name: Pin image and push
|
|
shell: bash
|
|
env:
|
|
IMAGE: ${{ inputs.image }}
|
|
TAG_INPUT: ${{ inputs.tag }}
|
|
PATH_IN_REPO: ${{ inputs.path }}
|
|
TOKEN: ${{ inputs.token }}
|
|
APPS_REPO: ${{ inputs.apps-repo }}
|
|
MESSAGE: ${{ inputs.message }}
|
|
RUN_NUMBER: ${{ github.run_number }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
TAG="${TAG_INPUT:-$RUN_NUMBER}"
|
|
NAME="$(basename "$IMAGE")"
|
|
MSG="${MESSAGE:-deploy ${NAME} #${TAG}}"
|
|
|
|
WORK="$(mktemp -d)"
|
|
trap 'rm -rf "$WORK"' EXIT
|
|
|
|
git clone --depth 1 "https://ci-bot:${TOKEN}@${APPS_REPO}.git" "$WORK"
|
|
cd "$WORK"
|
|
git config user.name ci-bot
|
|
git config user.email ci-bot@fritzlab.net
|
|
|
|
cd "$PATH_IN_REPO"
|
|
kustomize edit set image "${IMAGE}=${IMAGE}:${TAG}"
|
|
|
|
# Validate the kustomization renders cleanly before we push.
|
|
if ! kustomize build . > /dev/null; then
|
|
echo "FATAL: kustomize build failed after image pin"
|
|
git --no-pager diff
|
|
exit 1
|
|
fi
|
|
|
|
if git -C "$WORK" diff --quiet; then
|
|
echo "apps repo already on ${NAME}:${TAG}, skipping"
|
|
exit 0
|
|
fi
|
|
|
|
# Detect an open PR already targeting the same image:tag in the same
|
|
# kustomization.yaml. If one is open, defer to it so the review gate
|
|
# is the actual control path — not a race with the direct push.
|
|
GITEA_HOST="${APPS_REPO%%/*}"
|
|
GITEA_REPO_PATH="${APPS_REPO#*/}"
|
|
GITEA_API="https://${GITEA_HOST}/api/v1"
|
|
KUSTOMIZATION_FILE="${PATH_IN_REPO}/kustomization.yaml"
|
|
|
|
BLOCKING_PR=""
|
|
PR_NUMS=$(curl -sf -H "Authorization: token ${TOKEN}" \
|
|
"${GITEA_API}/repos/${GITEA_REPO_PATH}/pulls?state=open&limit=50" \
|
|
| jq -r '.[].number' 2>/dev/null || true)
|
|
|
|
for pr_num in $PR_NUMS; do
|
|
FILES_JSON=$(curl -sf -H "Authorization: token ${TOKEN}" \
|
|
"${GITEA_API}/repos/${GITEA_REPO_PATH}/pulls/${pr_num}/files" || echo "[]")
|
|
if echo "$FILES_JSON" | jq -e \
|
|
--arg f "$KUSTOMIZATION_FILE" \
|
|
--arg t "$TAG" \
|
|
'any(.[]; .filename == $f and (.patch // "" | contains($t)))' \
|
|
> /dev/null 2>&1; then
|
|
BLOCKING_PR="$pr_num"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ -n "$BLOCKING_PR" ]; then
|
|
echo "PR #${BLOCKING_PR} is open and already targets ${NAME}:${TAG} in ${KUSTOMIZATION_FILE}; deferring to PR"
|
|
exit 0
|
|
fi
|
|
|
|
git -C "$WORK" add "${PATH_IN_REPO}/kustomization.yaml"
|
|
git -C "$WORK" commit -m "$MSG"
|
|
|
|
# Push with rebase-on-conflict retry: concurrent CI from another image
|
|
# repo could have pushed since our clone.
|
|
ATTEMPTS=0
|
|
until git -C "$WORK" push origin main; do
|
|
ATTEMPTS=$((ATTEMPTS + 1))
|
|
if [ "$ATTEMPTS" -ge 3 ]; then
|
|
echo "FATAL: push to apps repo failed after ${ATTEMPTS} attempts"
|
|
exit 1
|
|
fi
|
|
echo "push rejected, attempt ${ATTEMPTS}; rebasing and retrying"
|
|
git -C "$WORK" pull --rebase origin main
|
|
sleep $((ATTEMPTS * 2))
|
|
done
|
|
|
|
echo "deployed ${NAME}:${TAG}"
|