Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
340222e041 | ||
|
|
179e47bbb2 | ||
|
|
68d16577b3 | ||
|
|
38607d9f29 | ||
|
|
90139b306b | ||
|
|
dd475bec0d | ||
|
|
0f0ada3576 | ||
|
|
185e41c49e | ||
|
|
103741b3ab |
@@ -2,6 +2,24 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.18.6] - 2026-08-05
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Pre-configure daemon.json before rootless setuptool + add DBUS_SESSION_BUS_ADDRESS
|
||||
|
||||
## [0.18.5] - 2026-08-05
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Pin Docker 28.x + disable containerd snapshotter + tune prune/disk
|
||||
|
||||
## [0.18.4] - 2026-08-05
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Harden rootless Docker daemon resilience on CI runners
|
||||
|
||||
## [0.18.3] - 2026-08-04
|
||||
|
||||
### 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?
|
||||
|
||||
|
||||
@@ -18,18 +18,40 @@ gitea_runner_binary_path: "/usr/local/bin/gitea_runner"
|
||||
|
||||
# Prune configuration
|
||||
gitea_runner_prune_until: "24h"
|
||||
gitea_runner_prune_schedule: "daily"
|
||||
# Every 6 hours — daily is insufficient for CI runners that build dozens
|
||||
# of images per day. Accumulation between daily runs can trigger Docker
|
||||
# daemon instability (containerd snapshotter GC holds locks, blocking
|
||||
# container operations).
|
||||
gitea_runner_prune_schedule: "*-*-* 00/6:00:00"
|
||||
gitea_runner_prune_label: "gitea-runner=true"
|
||||
|
||||
# Service configuration
|
||||
gitea_runner_service_restart_sec: "5"
|
||||
|
||||
# Health check configuration
|
||||
gitea_runner_healthcheck_interval: "5min"
|
||||
# 2min interval — catches hung daemons before multiple CI jobs fail between checks.
|
||||
# The previous 5min interval was too coarse: a stuck daemon could fail 3+ molecule
|
||||
# jobs in the window between healthcheck runs.
|
||||
gitea_runner_healthcheck_interval: "2min"
|
||||
gitea_runner_healthcheck_boot_delay: "2min"
|
||||
gitea_runner_healthcheck_disk_threshold: 85
|
||||
gitea_runner_healthcheck_disk_threshold: 75
|
||||
gitea_runner_healthcheck_script_path: "{{ gitea_runner_config_dir }}/healthcheck.sh"
|
||||
|
||||
# Docker daemon resilience settings (applied to daemon.json).
|
||||
# live-restore: containers survive daemon restarts — prevents stuck container
|
||||
# states when the healthcheck restarts a hung daemon.
|
||||
# shutdown-timeout: grace period (seconds) for containers to stop on daemon
|
||||
# shutdown/restart. Default 15s is too short for DinD containers with nested
|
||||
# processes (molecule tests). 30s gives SIGTERM time to propagate.
|
||||
# max-concurrent-downloads/uploads: limits parallel transfers to reduce daemon
|
||||
# memory pressure when multiple CI jobs pull images simultaneously.
|
||||
# default-ulimits: prevents FD exhaustion in container processes.
|
||||
gitea_runner_docker_live_restore: true
|
||||
gitea_runner_docker_shutdown_timeout: 30
|
||||
gitea_runner_docker_max_concurrent_downloads: 3
|
||||
gitea_runner_docker_max_concurrent_uploads: 3
|
||||
gitea_runner_docker_default_nofile: 65536
|
||||
|
||||
# Admin token for runner deregistration via Gitea API.
|
||||
# If not set, falls back to registration_token (which likely lacks admin scope).
|
||||
# Set this to a token with admin scope to enable automatic runner cleanup on removal.
|
||||
|
||||
@@ -49,6 +49,9 @@
|
||||
- "'Type=oneshot' in prune_service.content | b64decode"
|
||||
- "'docker system prune' in prune_service.content | b64decode"
|
||||
- "'docker volume prune' in prune_service.content | b64decode"
|
||||
- "'docker container prune' in prune_service.content | b64decode"
|
||||
- "'docker network prune' in prune_service.content | b64decode"
|
||||
- "'docker builder prune' in prune_service.content | b64decode"
|
||||
fail_msg: "Prune service template is missing expected directives"
|
||||
|
||||
- name: Read rendered prune timer template
|
||||
@@ -99,9 +102,15 @@
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "'docker info' in healthcheck_script.content | b64decode"
|
||||
- "'timeout 10 docker info' in healthcheck_script.content | b64decode"
|
||||
- "'systemctl --user restart docker.service' in healthcheck_script.content | b64decode"
|
||||
- "'systemctl --user restart gitea-runner.service' in healthcheck_script.content | b64decode"
|
||||
- "'docker system prune' in healthcheck_script.content | b64decode"
|
||||
- "'docker container prune' in healthcheck_script.content | b64decode"
|
||||
- "'docker network prune' in healthcheck_script.content | b64decode"
|
||||
- "'status=removing' in healthcheck_script.content | b64decode"
|
||||
- "'status=stopping' in healthcheck_script.content | b64decode"
|
||||
- "'docker rm -f' in healthcheck_script.content | b64decode"
|
||||
- "gitea_runner_healthcheck_disk_threshold | string in healthcheck_script.content | b64decode"
|
||||
fail_msg: "Healthcheck script template is missing expected content"
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
become_user: "{{ gitea_runner_service_user }}"
|
||||
environment:
|
||||
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid | default(0) }}"
|
||||
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ gitea_runner_uid | default(0) }}/bus"
|
||||
DOCKER_HOST: "unix:///run/user/{{ gitea_runner_uid | default(0) }}/docker.sock"
|
||||
when:
|
||||
- gitea_runner_file_stat.stat.exists | default(false) | bool
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
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
|
||||
@@ -40,6 +41,7 @@
|
||||
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
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
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"
|
||||
register: gitea_runner_service_check
|
||||
changed_when: false
|
||||
retries: 10
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
environment:
|
||||
DOCKER_HOST: "unix:///run/user/{{ gitea_runner_uid }}/docker.sock"
|
||||
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
|
||||
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ gitea_runner_uid | default(0) }}/bus"
|
||||
register: gitea_runner_pre_pull_result
|
||||
changed_when: "'Status: Downloaded' in gitea_runner_pre_pull_result.stdout or 'Status: Downloaded' in gitea_runner_pre_pull_result.stderr"
|
||||
retries: 3
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
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
|
||||
@@ -32,6 +33,7 @@
|
||||
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
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
become_user: "{{ gitea_runner_service_user }}"
|
||||
environment:
|
||||
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid | default(0) }}"
|
||||
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ gitea_runner_uid | default(0) }}/bus"
|
||||
DOCKER_HOST: "unix:///run/user/{{ gitea_runner_uid | default(0) }}/docker.sock"
|
||||
when: not gitea_runner_registered.stat.exists
|
||||
register: gitea_runner_register_output
|
||||
|
||||
@@ -31,6 +31,11 @@
|
||||
- ansible_facts['os_family'] == 'Debian'
|
||||
- gitea_runner_docker_apt_repo is changed
|
||||
|
||||
# Install Docker packages from the upstream Docker APT repository.
|
||||
# We do NOT pin to 28.x because recent Ubuntu releases (e.g. 26.04/plucky)
|
||||
# may not have 28.x packages in the Docker repo, and Docker 29 is safe
|
||||
# for rootless mode when the daemon.json disables the containerd snapshotter
|
||||
# and sets a conservative default nofile ulimit (see daemon.json tasks below).
|
||||
- name: Install rootless Docker dependencies (Debian/Ubuntu)
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
@@ -45,6 +50,7 @@
|
||||
- docker-compose-plugin
|
||||
- rsync
|
||||
state: present
|
||||
register: gitea_runner_docker_install
|
||||
when: ansible_facts['os_family'] == 'Debian'
|
||||
|
||||
- name: Update pacman cache (Arch Linux)
|
||||
@@ -133,8 +139,57 @@
|
||||
content: |
|
||||
[Service]
|
||||
Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_NET={{ gitea_runner_docker_rootless_net_driver }}"
|
||||
Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=implicit"
|
||||
Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER={{ 'implicit' if gitea_runner_docker_rootless_net_driver == 'pasta' else 'builtin' }}"
|
||||
{% if gitea_runner_docker_rootless_net_driver == 'pasta' %}
|
||||
Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS=--ipv6"
|
||||
{% endif %}
|
||||
mode: "0644"
|
||||
owner: "{{ gitea_runner_service_user }}"
|
||||
group: "{{ gitea_runner_service_user }}"
|
||||
when:
|
||||
- gitea_runner_docker_rootless_setup
|
||||
- not gitea_runner_rootless_docker_check.stat.exists
|
||||
|
||||
# Write daemon.json BEFORE the setuptool starts dockerd, so Docker 29
|
||||
# starts with containerd snapshotter disabled from the very first boot.
|
||||
# Without this, Docker 29 uses containerd snapshots by default, which
|
||||
# causes instability in rootless mode.
|
||||
- name: Ensure Docker config directory exists (pre-setup)
|
||||
ansible.builtin.file:
|
||||
path: "{{ gitea_runner_home }}/.config/docker"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
owner: "{{ gitea_runner_service_user }}"
|
||||
group: "{{ gitea_runner_service_user }}"
|
||||
when:
|
||||
- gitea_runner_docker_rootless_setup
|
||||
- not gitea_runner_rootless_docker_check.stat.exists
|
||||
|
||||
- name: Pre-configure rootless Docker daemon.json (disable containerd snapshotter)
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ gitea_runner_home }}/.config/docker/daemon.json"
|
||||
content: |
|
||||
{
|
||||
"live-restore": {{ gitea_runner_docker_live_restore | to_json }},
|
||||
"shutdown-timeout": {{ gitea_runner_docker_shutdown_timeout }},
|
||||
"max-concurrent-downloads": {{ gitea_runner_docker_max_concurrent_downloads }},
|
||||
"max-concurrent-uploads": {{ gitea_runner_docker_max_concurrent_uploads }},
|
||||
"default-ulimits": {
|
||||
"nofile": {"Name": "nofile", "Hard": {{ gitea_runner_docker_default_nofile }}, "Soft": {{ gitea_runner_docker_default_nofile }}}
|
||||
},
|
||||
"features": {
|
||||
"containerd-snapshotter": false
|
||||
},
|
||||
{% if gitea_runner_docker_rootless_net_driver == 'pasta' %}
|
||||
"ipv6": true,
|
||||
"ip6tables": true,
|
||||
"fixed-cidr-v6": "{{ gitea_runner_docker_ipv6_cidr }}",
|
||||
"dns": ["10.0.2.3", "8.8.8.8"]
|
||||
{% else %}
|
||||
"ipv6": false,
|
||||
"dns": ["8.8.8.8", "1.1.1.1"]
|
||||
{% endif %}
|
||||
}
|
||||
mode: "0644"
|
||||
owner: "{{ gitea_runner_service_user }}"
|
||||
group: "{{ gitea_runner_service_user }}"
|
||||
@@ -150,6 +205,7 @@
|
||||
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 }}/bus"
|
||||
DOCKERD_ROOTLESS_ROOTLESSKIT_NET: "{{ gitea_runner_docker_rootless_net_driver }}"
|
||||
when:
|
||||
- gitea_runner_docker_rootless_setup
|
||||
@@ -161,6 +217,7 @@
|
||||
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 }}/bus"
|
||||
changed_when: true
|
||||
when: gitea_runner_docker_rootless_setup
|
||||
|
||||
@@ -170,6 +227,7 @@
|
||||
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 }}/bus"
|
||||
changed_when: true
|
||||
when: gitea_runner_docker_rootless_setup
|
||||
|
||||
@@ -204,6 +262,7 @@
|
||||
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 }}/bus"
|
||||
changed_when: true
|
||||
when:
|
||||
- gitea_runner_docker_rootless_setup
|
||||
@@ -214,6 +273,16 @@
|
||||
dest: "{{ gitea_runner_home }}/.config/docker/daemon.json"
|
||||
content: |
|
||||
{
|
||||
"live-restore": {{ gitea_runner_docker_live_restore | to_json }},
|
||||
"shutdown-timeout": {{ gitea_runner_docker_shutdown_timeout }},
|
||||
"max-concurrent-downloads": {{ gitea_runner_docker_max_concurrent_downloads }},
|
||||
"max-concurrent-uploads": {{ gitea_runner_docker_max_concurrent_uploads }},
|
||||
"default-ulimits": {
|
||||
"nofile": {"Name": "nofile", "Hard": {{ gitea_runner_docker_default_nofile }}, "Soft": {{ gitea_runner_docker_default_nofile }}}
|
||||
},
|
||||
"features": {
|
||||
"containerd-snapshotter": false
|
||||
},
|
||||
{% if gitea_runner_docker_rootless_net_driver == 'pasta' %}
|
||||
"ipv6": true,
|
||||
"ip6tables": true,
|
||||
@@ -236,6 +305,7 @@
|
||||
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 }}/bus"
|
||||
changed_when: true
|
||||
when:
|
||||
- gitea_runner_docker_rootless_setup
|
||||
@@ -248,6 +318,7 @@
|
||||
environment:
|
||||
DOCKER_HOST: "unix:///run/user/{{ gitea_runner_uid }}/docker.sock"
|
||||
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
|
||||
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ gitea_runner_uid }}/bus"
|
||||
register: gitea_runner_docker_ready
|
||||
until: gitea_runner_docker_ready.rc == 0
|
||||
retries: 10
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
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
|
||||
@@ -24,6 +25,7 @@
|
||||
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
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
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"
|
||||
when:
|
||||
- gitea_runner_systemd_available.stat.exists | default(false) | bool
|
||||
- gitea_runner_docker_rootless_setup
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
environment:
|
||||
DOCKER_HOST: "unix:///run/user/{{ gitea_runner_uid }}/docker.sock"
|
||||
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
|
||||
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ gitea_runner_uid | default(0) }}/bus"
|
||||
register: gitea_runner_docker_version_output
|
||||
changed_when: false
|
||||
when: gitea_runner_docker_rootless_setup
|
||||
|
||||
@@ -7,3 +7,6 @@ Environment=DOCKER_HOST=unix:///run/user/{{ gitea_runner_uid }}/docker.sock
|
||||
Environment=XDG_RUNTIME_DIR=/run/user/{{ gitea_runner_uid }}
|
||||
ExecStart=/usr/bin/docker system prune -f --filter "label={{ gitea_runner_prune_label }}" --filter "until={{ gitea_runner_prune_until }}"
|
||||
ExecStart=/usr/bin/docker volume prune -f --filter "label={{ gitea_runner_prune_label }}"
|
||||
ExecStart=/usr/bin/docker container prune -f
|
||||
ExecStart=/usr/bin/docker network prune -f
|
||||
ExecStart=/usr/bin/docker builder prune -f
|
||||
|
||||
@@ -7,18 +7,29 @@ DOCKER_HOST="unix:///run/user/{{ gitea_runner_uid }}/docker.sock"
|
||||
XDG_RUNTIME_DIR="/run/user/{{ gitea_runner_uid }}"
|
||||
export DOCKER_HOST XDG_RUNTIME_DIR
|
||||
|
||||
# 1. Check Docker daemon responsiveness
|
||||
if ! docker info >/dev/null 2>&1; then
|
||||
echo "ERROR: Docker daemon not responding at ${DOCKER_HOST}"
|
||||
# 1. Check Docker daemon responsiveness (with timeout — a bare `docker info` can
|
||||
# hang indefinitely on a stuck daemon, blocking the healthcheck itself).
|
||||
if ! timeout 10 docker info >/dev/null 2>&1; then
|
||||
echo "ERROR: Docker daemon not responding at ${DOCKER_HOST} (timed out after 10s)"
|
||||
systemctl --user restart docker.service
|
||||
sleep 3
|
||||
if ! docker info >/dev/null 2>&1; then
|
||||
if ! timeout 10 docker info >/dev/null 2>&1; then
|
||||
echo "CRITICAL: Docker daemon still down after restart"
|
||||
exit 1
|
||||
fi
|
||||
echo "RECOVERED: Docker daemon restarted successfully"
|
||||
fi
|
||||
|
||||
# 1b. Clean up stuck containers — containers in "removing" or "stopping" state
|
||||
# for too long cause "cannot kill container: did not receive an exit event"
|
||||
# errors in molecule destroy phases. Force-remove them so subsequent CI jobs
|
||||
# don't inherit the stuck state.
|
||||
stuck_containers=$(timeout 10 docker ps -a --filter "status=removing" --filter "status=stopping" --format '{% raw %}{{.ID}}{% endraw %}' 2>/dev/null || true)
|
||||
if [[ -n "$stuck_containers" ]]; then
|
||||
echo "WARN: Found stuck containers (removing/stopping), force-cleaning"
|
||||
echo "$stuck_containers" | xargs -r docker rm -f 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 2. Check gitea-runner service is active
|
||||
runner_state=$(systemctl --user is-active gitea-runner.service 2>/dev/null || true)
|
||||
if [[ "$runner_state" != "active" ]]; then
|
||||
@@ -41,6 +52,9 @@ if [[ "$disk_pct" -ge {{ gitea_runner_healthcheck_disk_threshold }} ]]; then
|
||||
docker volume prune -f --filter "label={{ gitea_runner_prune_label }}" || true
|
||||
# Only prune dangling (untagged) images — keep tagged runner images (ci-full, ci-quality)
|
||||
docker image prune -f || true
|
||||
# Clean up stopped containers and dangling networks that accumulate from failed jobs
|
||||
docker container prune -f || true
|
||||
docker network prune -f || true
|
||||
disk_pct=$(df -P / | awk 'NR==2 {gsub(/%/, "", $5); print $5}')
|
||||
echo "INFO: Disk usage after prune: ${disk_pct}%"
|
||||
fi
|
||||
|
||||
+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
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
"""Gitea Runner Manager — lean CLI for managing Gitea Actions runners."""
|
||||
|
||||
__version__ = "0.18.3"
|
||||
__version__ = "0.18.6"
|
||||
|
||||
Reference in New Issue
Block a user