fix(gitea-actions): serve CI images from the Gitea registry #12

Merged
bastian merged 12 commits from feat/ci-images-registry into master 2026-09-28 12:19:57 -04:00
4 changed files with 108 additions and 55 deletions
Showing only changes of commit 42e6f5271d - Show all commits
+44 -5
View File
@@ -22,18 +22,57 @@ act_runner_bin: /usr/local/bin/act_runner
act_runner_config_dir: /etc/act_runner
act_runner_work_dir: /var/lib/act_runner
# Job container images (built locally into the gitea-runner rootless image
# store by tasks/images.yml; never pulled — force_pull is false).
gitea_ci_image: localhost/gitea-ci:latest
# Job container images. tasks/images.yml builds them into the gitea-runner
# rootless store and pushes them to the Gitea container registry.
#
# They used to live only under localhost/, and the nightly podman prune
# (roles/podman: podman_prune_ci_until) deletes any CI-user image older than
# 48h that no container is using -- so every idle weekend CI failed in 0-1s on
# `docker pull localhost/gitea-ci:latest` until someone re-ran this role and
# waited out a full rebuild. With a registry copy, a pruned image is simply
# re-pulled by the next job (force_pull stays false, so a present image is
# never re-pulled), and this role pulls instead of rebuilding when the
# Containerfile has not changed.
#
# Workflows that pin `container: image:` must use these registry paths too
# (esp-mg-tpms, skudak/esp32-stm32-vcu, skudak/esp32-web-interface).
gitea_ci_registry: git.debyl.io
# Namespace = the owner of gitea_registry_username / gitea_registry_token (vault).
gitea_ci_registry_namespace: gitbot
gitea_ci_image: "{{ gitea_ci_registry }}/{{ gitea_ci_registry_namespace }}/gitea-ci:latest"
# ESP-IDF firmware image tag tracks the upstream espressif/idf release we build from.
esp_idf_version: v5.4.1
gitea_ci_espidf_image: "localhost/gitea-ci-espidf:{{ esp_idf_version }}"
gitea_ci_espidf_image: "{{ gitea_ci_registry }}/{{ gitea_ci_registry_namespace }}/gitea-ci-espidf:{{ esp_idf_version }}"
# PlatformIO image for Arduino-framework ESP32 builds (esp32-web-interface).
# Tag tracks the pre-baked espressif32 platform version; both pins match the
# hardware-validated local build.
platformio_core_version: "6.1.19"
pio_espressif32_version: "7.0.1"
gitea_ci_platformio_image: "localhost/gitea-ci-platformio:{{ pio_espressif32_version }}"
gitea_ci_platformio_image: "{{ gitea_ci_registry }}/{{ gitea_ci_registry_namespace }}/gitea-ci-platformio:{{ pio_espressif32_version }}"
# Registry credentials for the gitea-runner user. The Docker-format path is read
# by both act_runner (to authenticate job image pulls) and podman (as its
# fallback auth file), so one login covers the runner and this role.
gitea_ci_registry_authfile: "{{ gitea_runner_home }}/.docker/config.json"
# Label stamped on the CI base images so the nightly prune skips them; must match
# podman_prune_ci_keep_label in roles/podman/defaults/main.yml. Without it the
# prune (whose `until` counts from build time, not pull time) would delete a
# re-pulled image again the next night -- a 7.8 GB ESP-IDF re-download after
# every idle day. Superseded tags (e.g. after an esp_idf_version bump) are
# therefore kept too; remove them by hand.
gitea_ci_keep_label: io.debyl.ci-base
gitea_ci_images:
- image: "{{ gitea_ci_image }}"
containerfile: Containerfile.ci
template: Containerfile.ci
- image: "{{ gitea_ci_espidf_image }}"
containerfile: Containerfile.espidf
template: Containerfile.espidf.j2
- image: "{{ gitea_ci_platformio_image }}"
containerfile: Containerfile.platformio
template: Containerfile.platformio.j2
# Default labels for every runner — map runs-on values to the local CI image.
# Firmware jobs opt into the ESP-IDF image per-job via `container:` in their workflow.
+54 -47
View File
@@ -1,4 +1,8 @@
---
# CI job images: stage each Containerfile, restore the image from the Gitea
# registry if the nightly prune removed it, rebuild only when the Containerfile
# changed, then push so the registry always holds what the runner uses.
# See gitea_ci_images in defaults/main.yml.
- name: create CI image build directory
become: true
become_user: "{{ gitea_runner_user }}"
@@ -8,71 +12,74 @@
mode: "0755"
tags: gitea-actions
- name: stage default CI Containerfile
- name: create gitea-runner registry auth directory
become: true
become_user: "{{ gitea_runner_user }}"
ansible.builtin.file:
path: "{{ gitea_ci_registry_authfile | dirname }}"
state: directory
mode: "0700"
tags: gitea-actions
- name: log gitea-runner in to the Gitea container registry
become: true
become_user: "{{ gitea_runner_user }}"
containers.podman.podman_login:
registry: "{{ gitea_ci_registry }}"
username: "{{ gitea_registry_username }}"
password: "{{ gitea_registry_token }}"
authfile: "{{ gitea_ci_registry_authfile }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
no_log: true
tags: gitea-actions
- name: stage CI Containerfiles
become: true
become_user: "{{ gitea_runner_user }}"
ansible.builtin.template:
src: Containerfile.ci
dest: "{{ gitea_runner_home }}/ci-images/Containerfile.ci"
src: "{{ item.template }}"
dest: "{{ gitea_runner_home }}/ci-images/{{ item.containerfile }}"
mode: "0644"
register: ci_containerfile
loop: "{{ gitea_ci_images }}"
loop_control:
label: "{{ item.containerfile }}"
register: ci_containerfiles
tags: gitea-actions
- name: stage ESP-IDF CI Containerfile
become: true
become_user: "{{ gitea_runner_user }}"
ansible.builtin.template:
src: Containerfile.espidf.j2
dest: "{{ gitea_runner_home }}/ci-images/Containerfile.espidf"
mode: "0644"
register: espidf_containerfile
tags: gitea-actions
- name: build default CI image ({{ gitea_ci_image }})
# A missing image here is normal (first push, or a new tag): the build below
# creates it. Anything already present locally is left untouched.
- name: restore CI images from the registry
become: true
become_user: "{{ gitea_runner_user }}"
containers.podman.podman_image:
name: "{{ gitea_ci_image }}"
path: "{{ gitea_runner_home }}/ci-images"
build:
file: "{{ gitea_runner_home }}/ci-images/Containerfile.ci"
force: "{{ ci_containerfile is changed }}"
name: "{{ item.image }}"
auth_file: "{{ gitea_ci_registry_authfile }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
loop: "{{ gitea_ci_images }}"
loop_control:
label: "{{ item.image }}"
when: not (ci_containerfiles.results | selectattr('item.image', 'equalto', item.image) | first).changed
failed_when: false
tags: gitea-actions
- name: stage PlatformIO CI Containerfile
become: true
become_user: "{{ gitea_runner_user }}"
ansible.builtin.template:
src: Containerfile.platformio.j2
dest: "{{ gitea_runner_home }}/ci-images/Containerfile.platformio"
mode: "0644"
register: platformio_containerfile
tags: gitea-actions
- name: build ESP-IDF CI image ({{ gitea_ci_espidf_image }})
- name: build and push CI images
become: true
become_user: "{{ gitea_runner_user }}"
containers.podman.podman_image:
name: "{{ gitea_ci_espidf_image }}"
name: "{{ item.image }}"
path: "{{ gitea_runner_home }}/ci-images"
build:
file: "{{ gitea_runner_home }}/ci-images/Containerfile.espidf"
force: "{{ espidf_containerfile is changed }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
tags: gitea-actions
- name: build PlatformIO CI image ({{ gitea_ci_platformio_image }})
become: true
become_user: "{{ gitea_runner_user }}"
containers.podman.podman_image:
name: "{{ gitea_ci_platformio_image }}"
path: "{{ gitea_runner_home }}/ci-images"
build:
file: "{{ gitea_runner_home }}/ci-images/Containerfile.platformio"
force: "{{ platformio_containerfile is changed }}"
file: "{{ gitea_runner_home }}/ci-images/{{ item.containerfile }}"
# Exempts the image from the nightly CI prune (gitea_ci_keep_label).
extra_args: "--label {{ gitea_ci_keep_label }}=true"
force: "{{ (ci_containerfiles.results | selectattr('item.image', 'equalto', item.image) | first).changed }}"
push: true
auth_file: "{{ gitea_ci_registry_authfile }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
loop: "{{ gitea_ci_images }}"
loop_control:
label: "{{ item.image }}"
tags: gitea-actions
+3
View File
@@ -311,6 +311,9 @@ podman_prune_ci_users:
- gitea-runner
- actions-runner
podman_prune_ci_until: 48h
# CI base images built by roles/gitea-actions carry this label (gitea_ci_keep_label
# there -- keep the two in sync) and are skipped by the CI image prune.
podman_prune_ci_keep_label: io.debyl.ci-base
# Daily rather than weekly: CI turns over many images a day, and a week of that
# is what let the store reach 113 GB between runs.
@@ -43,9 +43,10 @@ run() {
exec podman "$@"' _ "$@"
}
# prune_user <user> <until> <prune_containers: yes|no>
# prune_user <user> <until> <prune_containers: yes|no> [image prune filter...]
prune_user() {
local u=$1 keep=$2 do_containers=$3
shift 3
local before after img vol con
if ! id "$u" >/dev/null 2>&1; then
@@ -66,7 +67,7 @@ prune_user() {
con=$(run "$u" container prune -f --filter "until=$keep" 2>&1 | tail -1)
fi
img=$(run "$u" image prune -af --filter "until=$keep" 2>&1 | tail -1)
img=$(run "$u" image prune -af --filter "until=$keep" "$@" 2>&1 | tail -1)
vol=$(run "$u" volume prune -f 2>&1 | tail -1)
after=$(run "$u" system df --format '{{ '{{' }}.Size{{ '}}' }}' 2>/dev/null | head -1)
@@ -80,7 +81,10 @@ for u in {{ podman_prune_users | join(' ') }}; do
done
for u in {{ podman_prune_ci_users | join(' ') }}; do
prune_user "$u" "{{ podman_prune_ci_until }}" yes
# CI base images (gitea-ci, -espidf, -platformio) carry the keep label: they
# are rebuilt or re-pulled from the registry only when missing, so pruning
# them just forces a multi-GB re-download on the next job.
prune_user "$u" "{{ podman_prune_ci_until }}" yes --filter "label!={{ podman_prune_ci_keep_label }}"
done
log "status=ok"