pr / contract (pull_request) Successful in 10s
Emit a fixed credential-free login_setup warning and fail the optional prune step before any dependent package API calls if its existing login setup fails. Preserve successful pruning, retention, registry upload and local cleanup. Exercise the extracted prune shell with fake Tea and disposable configuration, including failed setup with a previous login and unchanged successful targets. This identifies the failed stage, not the suppressed authentication cause. Ticket: https://agenthub.fritzlab.net/t-c1070dtgpqqy Authored-By: Codex (GPT-5) <noreply@openai.com>
130 lines
4.6 KiB
YAML
130 lines
4.6 KiB
YAML
name: Push Image
|
|
description: Push a locally-built image to code.fritzlab.net and prune old numeric tags.
|
|
inputs:
|
|
image:
|
|
description: Full image name without tag (e.g. code.fritzlab.net/fritzlab/chrony)
|
|
required: true
|
|
tag:
|
|
description: Tag to push. Defaults to github.run_number when empty.
|
|
required: false
|
|
default: ''
|
|
token:
|
|
description: CI_BOT_TOKEN — registry login + Gitea package API for cleanup
|
|
required: true
|
|
org:
|
|
description: Gitea org for package API (e.g. fritzlab, dns)
|
|
required: true
|
|
name:
|
|
description: Package/image name (e.g. chrony, base, runner)
|
|
required: true
|
|
latest:
|
|
description: Also push a :latest tag
|
|
required: false
|
|
default: 'true'
|
|
prune:
|
|
description: Prune old numeric registry tags; false retains every remote tag
|
|
required: false
|
|
default: 'true'
|
|
keep:
|
|
description: Numeric tags to retain after prune; older ones are deleted
|
|
required: false
|
|
default: '3'
|
|
runs:
|
|
using: composite
|
|
steps:
|
|
- name: Validate pruning policy
|
|
shell: bash
|
|
env:
|
|
PRUNE: ${{ inputs.prune }}
|
|
run: |
|
|
case "$PRUNE" in
|
|
true|false) ;;
|
|
*) echo "prune must be true or false" >&2; exit 1 ;;
|
|
esac
|
|
|
|
- name: Log in to registry
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: code.fritzlab.net
|
|
username: ci-bot
|
|
password: ${{ inputs.token }}
|
|
|
|
- name: Push
|
|
shell: bash
|
|
env:
|
|
IMAGE: ${{ inputs.image }}
|
|
TAG_INPUT: ${{ inputs.tag }}
|
|
RUN_NUMBER: ${{ github.run_number }}
|
|
LATEST: ${{ inputs.latest }}
|
|
run: |
|
|
set -euo pipefail
|
|
TAG="${TAG_INPUT:-$RUN_NUMBER}"
|
|
echo "Pushing ${IMAGE}:${TAG}"
|
|
docker push "${IMAGE}:${TAG}"
|
|
if [ "$LATEST" = "true" ]; then
|
|
docker tag "${IMAGE}:${TAG}" "${IMAGE}:latest"
|
|
echo "Pushing ${IMAGE}:latest"
|
|
docker push "${IMAGE}:latest"
|
|
fi
|
|
|
|
- name: Prune old tags
|
|
if: ${{ inputs.prune == 'true' }}
|
|
continue-on-error: true
|
|
shell: bash
|
|
env:
|
|
TOKEN: ${{ inputs.token }}
|
|
ORG: ${{ inputs.org }}
|
|
NAME: ${{ inputs.name }}
|
|
KEEP: ${{ inputs.keep }}
|
|
run: |
|
|
set -euo pipefail
|
|
if ! tea login add --name ci --url https://code.fritzlab.net \
|
|
--token "$TOKEN" --no-version-check >/dev/null 2>&1; then
|
|
echo "::warning::image-push prune failed at login_setup; package cleanup skipped" >&2
|
|
exit 1
|
|
fi
|
|
# Paginate the package list. The org has more container versions than a
|
|
# single API page returns (Gitea caps page size at MAX_RESPONSE_ITEMS),
|
|
# so an unpaginated call silently drops a package's older tags from the
|
|
# list and they never get pruned — that's how tags crept past KEEP.
|
|
page=1
|
|
while :; do
|
|
resp=$(tea api "/packages/${ORG}?type=container&limit=50&page=${page}")
|
|
[ "$(jq 'length' <<<"$resp")" -eq 0 ] && break
|
|
jq -r --arg n "$NAME" \
|
|
'.[] | select(.name==$n) | select(.version | test("^[0-9]+$")) | .version' \
|
|
<<<"$resp"
|
|
page=$((page + 1))
|
|
done \
|
|
| sort -n | head -n -"$KEEP" \
|
|
| while read -r tag; do
|
|
echo "deleting ${NAME}:$tag"
|
|
tea api -X DELETE "/packages/${ORG}/container/${NAME}/${tag}"
|
|
done
|
|
|
|
- name: Untag local image
|
|
# image-build loads the built image into the runner's dind (load: true).
|
|
# Nothing needs the local tag after the push, but the tags used to pile up
|
|
# forever in the pod's dind image store (dozens of stale 2-3GB tags). Untag
|
|
# after pushing to reclaim the store; the buildkit BUILD cache is separate
|
|
# and refcounts its own layers, so the next build of this image is still
|
|
# warm. (When THIS repo is `runner`, untagging :latest costs the pod's next
|
|
# job one ~380MB re-pull of the job image from the local registry — cheap.)
|
|
# The image prune sweeps the now-dangling previous :latest that a re-tag
|
|
# orphans; it only removes untagged images no container/tag references, so
|
|
# shared layers survive.
|
|
continue-on-error: true
|
|
shell: bash
|
|
env:
|
|
IMAGE: ${{ inputs.image }}
|
|
TAG_INPUT: ${{ inputs.tag }}
|
|
RUN_NUMBER: ${{ github.run_number }}
|
|
LATEST: ${{ inputs.latest }}
|
|
run: |
|
|
TAG="${TAG_INPUT:-$RUN_NUMBER}"
|
|
docker rmi "${IMAGE}:${TAG}" || true
|
|
if [ "$LATEST" = "true" ]; then
|
|
docker rmi "${IMAGE}:latest" || true
|
|
fi
|
|
docker image prune -f || true
|