Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2acc1c0eb6 | ||
|
|
a31af2e236 | ||
|
|
68a763b942 | ||
|
|
d9dae396bc | ||
|
|
7816733e5e |
@@ -2,6 +2,12 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.23.0] - 2026-08-28
|
||||
|
||||
### Features
|
||||
|
||||
- Add pre-cache timer, force_pull, and Docker socket options to runner config
|
||||
|
||||
## [0.22.1] - 2026-08-26
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -8,12 +8,12 @@ Each runner runs in an isolated **rootless Docker** environment under a dedicate
|
||||
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
|
||||
## Why GRM?
|
||||
|
||||
|
||||
@@ -90,6 +90,22 @@ gitea_runner_remove_user: true
|
||||
gitea_runner_log_level: "info"
|
||||
gitea_runner_container_label: "gitea-runner=true"
|
||||
gitea_runner_file: ".runner"
|
||||
# force_pull: when false (default), the runner reuses locally cached images
|
||||
# instead of pulling on every job. Pre-cached images (via the pre-cache timer
|
||||
# or pre_pull_images task) eliminate registry thundering-herd when all runners
|
||||
# start jobs simultaneously.
|
||||
gitea_runner_force_pull: false
|
||||
# Container options passed to `docker run` for CI job containers.
|
||||
# Mounts the host rootless Docker socket as /run/host-docker.sock so
|
||||
# start_docker.py inside the container can detect and use the host daemon
|
||||
# (full disk, no nested DinD) instead of starting an inner dockerd.
|
||||
gitea_runner_container_options: "-v /run/user/{{ gitea_runner_uid }}/docker.sock:/run/host-docker.sock"
|
||||
# Volumes allowed in CI job containers (validated by the runner against
|
||||
# container.options and job-level volumes). Must include the host Docker
|
||||
# socket mount target.
|
||||
gitea_runner_valid_volumes:
|
||||
- "/run/host-docker.sock"
|
||||
- "/run/user/{{ gitea_runner_uid }}/docker.sock"
|
||||
|
||||
# Containerd version pinning — Docker 28.x vendors containerd v2.1.x internally.
|
||||
# containerd.io >= 2.3 ships a shim that returns a protobuf BootstrapResult which
|
||||
@@ -135,3 +151,11 @@ gitea_runner_docker_ipv6_cidr: "fd00:dead:beef::/48"
|
||||
# disk-space prune only removes dangling images, so pre-pulled tagged images persist.
|
||||
# Set to [] to skip pre-pulling. Images are pulled as the runner user via rootless Docker.
|
||||
gitea_runner_pre_pull_images: []
|
||||
|
||||
# Pre-cache timer: periodically pulls the runner container image so it stays
|
||||
# fresh in the local Docker cache. This prevents thundering-herd registry
|
||||
# timeouts when all runners start CI jobs simultaneously with empty caches.
|
||||
# Runs every 6 hours (aligned with prune schedule). Set to empty string to
|
||||
# disable the timer.
|
||||
gitea_runner_pre_cache_schedule: "*-*-* 00/6:30:00"
|
||||
gitea_runner_pre_cache_images: "{{ gitea_runner_pre_pull_images }}"
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
- name: Include pre-pull images
|
||||
ansible.builtin.include_tasks: pre_pull_images.yml
|
||||
|
||||
- name: Include pre-cache timer
|
||||
ansible.builtin.include_tasks: pre_cache.yml
|
||||
|
||||
- name: Include integration test
|
||||
ansible.builtin.include_tasks: integration_test.yml
|
||||
when: not gitea_runner_skip_registration
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
# Periodic timer that pre-pulls CI runner images into the local Docker cache.
|
||||
# Prevents thundering-herd registry timeouts when all runners start jobs
|
||||
# simultaneously with empty/stale caches. Runs every 6 hours (configurable).
|
||||
# The prune timer removes dangling images but NOT tagged ones, so pre-pulled
|
||||
# images persist between runs.
|
||||
|
||||
- name: Create docker-pull-images user service file
|
||||
ansible.builtin.template:
|
||||
src: docker-pull-images.service.j2
|
||||
dest: "{{ gitea_runner_home }}/.config/systemd/user/docker-pull-images.service"
|
||||
owner: "{{ gitea_runner_service_user }}"
|
||||
group: "{{ gitea_runner_service_user }}"
|
||||
mode: "0644"
|
||||
register: gitea_runner_pre_cache_service
|
||||
|
||||
- name: Create docker-pull-images user timer file
|
||||
ansible.builtin.template:
|
||||
src: docker-pull-images.timer.j2
|
||||
dest: "{{ gitea_runner_home }}/.config/systemd/user/docker-pull-images.timer"
|
||||
owner: "{{ gitea_runner_service_user }}"
|
||||
group: "{{ gitea_runner_service_user }}"
|
||||
mode: "0644"
|
||||
register: gitea_runner_pre_cache_timer
|
||||
|
||||
- name: Reload systemd user daemon for pre-cache timer
|
||||
ansible.builtin.command: systemctl --user daemon-reload
|
||||
become: true
|
||||
become_user: "{{ gitea_runner_service_user }}"
|
||||
environment:
|
||||
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
|
||||
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ gitea_runner_uid | default(0) }}/bus"
|
||||
changed_when: true
|
||||
when:
|
||||
- gitea_runner_systemd_available.stat.exists
|
||||
- gitea_runner_docker_rootless_setup
|
||||
- gitea_runner_pre_cache_service is changed or gitea_runner_pre_cache_timer is changed
|
||||
- gitea_runner_pre_cache_schedule | length > 0
|
||||
- gitea_runner_pre_cache_images | length > 0
|
||||
|
||||
- name: Enable and start docker-pull-images user timer
|
||||
ansible.builtin.command: systemctl --user enable --now docker-pull-images.timer
|
||||
become: true
|
||||
become_user: "{{ gitea_runner_service_user }}"
|
||||
environment:
|
||||
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
|
||||
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ gitea_runner_uid | default(0) }}/bus"
|
||||
changed_when: true
|
||||
when:
|
||||
- gitea_runner_systemd_available.stat.exists
|
||||
- gitea_runner_docker_rootless_setup
|
||||
- gitea_runner_pre_cache_schedule | length > 0
|
||||
- gitea_runner_pre_cache_images | length > 0
|
||||
|
||||
- name: Disable and stop docker-pull-images timer (no images or schedule)
|
||||
ansible.builtin.command: systemctl --user disable --now docker-pull-images.timer
|
||||
become: true
|
||||
become_user: "{{ gitea_runner_service_user }}"
|
||||
environment:
|
||||
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
|
||||
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ gitea_runner_uid | default(0) }}/bus"
|
||||
changed_when: true
|
||||
failed_when: false
|
||||
when:
|
||||
- gitea_runner_systemd_available.stat.exists
|
||||
- gitea_runner_docker_rootless_setup
|
||||
- gitea_runner_pre_cache_schedule | length == 0 or gitea_runner_pre_cache_images | length == 0
|
||||
@@ -8,6 +8,15 @@
|
||||
#
|
||||
# Set gitea_runner_pre_pull_images to a list of image refs to pull, or
|
||||
# empty list to skip pre-pulling.
|
||||
#
|
||||
# IMPORTANT: Do NOT use this mechanism for:
|
||||
# - CI runner container images (e.g. ci-full) — these are already
|
||||
# cached by the runner setup task and pulling them here is redundant.
|
||||
# - Images that molecule tests pull themselves — molecule prepare/converge
|
||||
# steps handle their own image pulls; pre-pulling them here wastes time
|
||||
# and disk space.
|
||||
# This mechanism is intended only for images that are needed by the runner
|
||||
# itself but not pulled by any molecule scenario or runner setup step.
|
||||
|
||||
- name: Pre-pull Docker images for CI runner
|
||||
ansible.builtin.command: "docker pull {{ item }}"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=Pre-pull Docker images for CI runner cache
|
||||
After=docker.service
|
||||
Wants=docker.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
Environment=DOCKER_HOST=unix:///run/user/{{ gitea_runner_uid }}/docker.sock
|
||||
Environment=XDG_RUNTIME_DIR=/run/user/{{ gitea_runner_uid }}
|
||||
# Pull each image quietly. docker pull exits 0 if image is already up-to-date,
|
||||
# so this is idempotent. Errors are non-fatal (image may already be cached).
|
||||
{% for image in gitea_runner_pre_cache_images %}
|
||||
ExecStart=/usr/bin/docker pull -q {{ image }}
|
||||
{% endfor %}
|
||||
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Periodic Docker image pre-cache for CI runner
|
||||
|
||||
[Timer]
|
||||
OnCalendar={{ gitea_runner_pre_cache_schedule }}
|
||||
Persistent=true
|
||||
RandomizedDelaySec=300
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -9,3 +9,13 @@ runner:
|
||||
container:
|
||||
label: "{{ gitea_runner_container_label }}"
|
||||
docker_host: "unix:///run/user/{{ gitea_runner_uid }}/docker.sock"
|
||||
force_pull: {{ gitea_runner_force_pull | lower }}
|
||||
{% if gitea_runner_container_options | length > 0 %}
|
||||
options: "{{ gitea_runner_container_options }}"
|
||||
{% endif %}
|
||||
{% if gitea_runner_valid_volumes | length > 0 %}
|
||||
valid_volumes:
|
||||
{% for volume in gitea_runner_valid_volumes %}
|
||||
- "{{ volume }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
+6
-6
@@ -8,12 +8,12 @@ Each runner runs in an isolated **rootless Docker** environment under a dedicate
|
||||
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# GRM-162: Add pre-cache timer, force_pull, and Docker socket options to runner config
|
||||
|
||||
## Problem
|
||||
CI containers were not using the host's rootless Docker daemon, leading to
|
||||
"no space left on device" errors. The runner config template was missing
|
||||
`force_pull`, `options` (host Docker socket mount), and `valid_volumes`
|
||||
fields. Additionally, no pre-cache timer existed to prevent thundering-herd
|
||||
registry timeouts when all runners pull images simultaneously.
|
||||
|
||||
## Approach
|
||||
REQ-1: Add `force_pull: false` to runner config template (explicit default
|
||||
so the runner reuses locally cached images instead of pulling on every job)
|
||||
REQ-2: Add `options` field to mount host rootless Docker socket as
|
||||
`/run/host-docker.sock` so `start_docker.py` inside CI containers can detect
|
||||
and use the host daemon (full disk, no nested DinD)
|
||||
REQ-3: Add `valid_volumes` list for the socket mount targets (validated by
|
||||
the runner against `container.options` and job-level volumes)
|
||||
REQ-4: Add `pre_cache.yml` task with a systemd user timer that pre-pulls CI
|
||||
images every 6 hours (configurable via `gitea_runner_pre_cache_schedule`)
|
||||
REQ-5: Add `docker-pull-images.service.j2` and `docker-pull-images.timer.j2`
|
||||
templates for the pre-cache timer
|
||||
REQ-6: Timer is disabled when `gitea_runner_pre_cache_schedule` is empty or
|
||||
`gitea_runner_pre_cache_images` is empty (graceful degradation)
|
||||
|
||||
## Test Plan
|
||||
- `make lint-ci` passes (ansible-lint on new task/template files)
|
||||
- `make molecule` converges successfully with the new pre-cache tasks
|
||||
- Verify the runner config template renders correctly with and without
|
||||
container options/valid_volumes
|
||||
|
||||
## Deploy Plan
|
||||
- Merge to master → post-merge auto-publishes package
|
||||
- Infra dependency PR auto-created to bump pinned grm version
|
||||
- Runners pick up the new config on next `make setup` or ansible apply
|
||||
|
||||
## Rollback Plan
|
||||
- Revert the merge commit
|
||||
- Set `gitea_runner_pre_cache_schedule: ""` to disable the timer without
|
||||
reverting
|
||||
|
||||
## Acceptance Criteria
|
||||
- [x] REQ-1: `force_pull: false` in runner config template
|
||||
- [x] REQ-2: `options` field mounts host Docker socket as `/run/host-docker.sock`
|
||||
- [x] REQ-3: `valid_volumes` list includes both socket mount targets
|
||||
- [x] REQ-4: `pre_cache.yml` task creates and manages systemd user timer
|
||||
- [x] REQ-5: Service and timer templates created
|
||||
- [x] REQ-6: Timer disabled gracefully when schedule or images empty
|
||||
@@ -0,0 +1,38 @@
|
||||
# GRM-172: Audit and document pre-pull image usage guidelines
|
||||
|
||||
## Problem
|
||||
The grm repo contains a runner-level `pre_pull_images.yml` task file that
|
||||
pre-pulls Docker images to avoid repeated pulls on every CI run. However,
|
||||
there was no audit confirming that molecule `prepare.yml` files are not
|
||||
also redundantly pre-pulling images that the runner setup already caches.
|
||||
Wasteful pre-pulling wastes CI time and disk space.
|
||||
|
||||
## Approach
|
||||
Audit all molecule `prepare.yml` files in the grm repo for pre-pull tasks.
|
||||
The audit found NO molecule prepare.yml files contain pre-pull tasks, so no
|
||||
code removal is needed. Document the audit findings in a spec and add a
|
||||
comment to the runner-level `pre_pull_images.yml` task file clarifying that
|
||||
it should not be used for images that molecule tests pull themselves (to
|
||||
avoid redundant pulls).
|
||||
|
||||
REQ-1: Audit all molecule prepare.yml files for pre-pull tasks and confirm none exist
|
||||
REQ-2: Add documentation comment to pre_pull_images.yml stating it should not be used for CI runner container images (already cached by runner setup) or images molecule tests pull themselves
|
||||
REQ-3: Confirm gitea_runner_pre_pull_images default remains empty ([]) which is correct
|
||||
|
||||
## Test Plan
|
||||
- Grep all molecule prepare.yml files for pre-pull patterns confirms zero matches
|
||||
- Verify pre_pull_images.yml comment is present and accurate
|
||||
- Verify gitea_runner_pre_pull_images default is [] in defaults/main.yml
|
||||
- Run make lint-ci to confirm no lint regressions
|
||||
|
||||
## Deploy Plan
|
||||
- Merge to master via auto-merge workflow
|
||||
- No runtime changes; documentation-only
|
||||
|
||||
## Rollback Plan
|
||||
- Revert the merge commit; comments are removed, no functional impact
|
||||
|
||||
## Acceptance Criteria
|
||||
- [x] REQ-1: No molecule prepare.yml files in the grm repo contain pre-pull tasks (audit confirmed via grep)
|
||||
- [x] REQ-2: pre_pull_images.yml contains a comment documenting it should not be used for CI runner container images or images molecule tests pull themselves
|
||||
- [x] REQ-3: gitea_runner_pre_pull_images default remains empty ([]) in defaults/main.yml
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
"""Gitea Runner Manager — lean CLI for managing Gitea Actions runners."""
|
||||
|
||||
__version__ = "0.22.1"
|
||||
__version__ = "0.23.0"
|
||||
|
||||
Reference in New Issue
Block a user