Files
image-push/action.yaml
T
dfritz 2d8a13840f
pr / contract (pull_request) Successful in 10s
Stop package cleanup after failed Tea login setup
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>
2026-10-09 19:53:10 +00:00

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