Files
image-push/action.yaml
T
Dave Kowalski cc717790f7 untag built image after push to bound the dind image store
image-build loads each build into the runner pod's dind (load: true); the
tags then accumulated forever (dozens of stale 2-3GB tags per pod, a chunk
of the runner disk growth). Untag after a successful push and sweep the
now-dangling previous :latest. The buildkit build cache is separate and
refcounts its own layers, so the next build stays warm; when the repo is
'runner' itself the pod's next job re-pulls the ~380MB job image from the
local registry (cheap). Guarded (continue-on-error + || true) so cleanup
never fails a release.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 00:33:56 +00:00

112 lines
4.0 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'
keep:
description: Numeric tags to retain after prune; older ones are deleted
required: false
default: '3'
runs:
using: composite
steps:
- 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
continue-on-error: true
shell: bash
env:
TOKEN: ${{ inputs.token }}
ORG: ${{ inputs.org }}
NAME: ${{ inputs.name }}
KEEP: ${{ inputs.keep }}
run: |
set -euo pipefail
tea login add --name ci --url https://code.fritzlab.net \
--token "$TOKEN" --no-version-check >/dev/null 2>&1 || true
# 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