refactor: rootless Docker, fix auto-merge, molecule platform matrix

Three major improvements:

1. Rootless Docker refactor: Removes docker/binary modes, unifies to
   rootless Docker with per-runner system users. Each runner gets its
   own rootless Docker daemon, systemd user service, and isolated
   environment. Simplifies CLI (removes --mode option), Ansible role
   (single code path), and molecule scenarios (removes binary scenario).

2. Auto-merge fix: Fixes status check context mismatch in branch
   protection (was requiring "lint", "unit-tests", "molecule-tests" but
   actual contexts are "CI / quality", "CI / molecule-tests*"). Adds
   retry/wait logic to auto_merge.py that polls commit statuses for up
   to 15 minutes before attempting merge, eliminating the chicken-and-egg
   problem where auto-merge would fail because CI hadn't completed yet.

3. Molecule platform matrix: Adds OS platform matrix to CI — all 6
   scenarios now run on all 4 supported OSes (ubuntu-2204, ubuntu-2404,
   debian-12, archlinux) = 24 test pairs distributed across 3 parallel
   runners. Updates distribute_molecule.py to distribute (scenario,
   platform) pairs. Updates Makefile with molecule-all target for
   local multi-platform testing.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Emil Simeonov
2026-06-20 21:02:52 +02:00
co-authored by Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent 0c6c735000
commit 55c2746569
71 changed files with 2444 additions and 1277 deletions
+8 -5
View File
@@ -1,10 +1,14 @@
---
runner_mode: "docker"
gitea_runner_version: "1.0.8"
gitea_runner_docker_image: "gitea/runner"
runner_labels: "ubuntu-latest:docker://runner-images:ubuntu-22.04"
runner_labels: "docker,ubuntu-latest:docker://runner-images:ubuntu-22.04"
skip_runner_registration: false
# Per-runner user (rootless isolation)
gitea_runner_user_prefix: "grm-"
gitea_runner_base_home: "/home"
gitea_runner_service_user: "{{ gitea_runner_user_prefix }}{{ runner_name }}"
gitea_runner_home: "{{ gitea_runner_base_home }}/{{ gitea_runner_service_user }}"
# Base paths (instance-scoped via runner_name)
gitea_runner_base_data_dir: "/var/lib/gitea-runner"
gitea_runner_base_config_dir: "/etc/gitea-runner"
@@ -19,12 +23,11 @@ gitea_runner_prune_label: "gitea-runner=true"
# Service configuration
gitea_runner_service_restart_sec: "5"
gitea_runner_service_user: "{{ ansible_user | default('root') }}"
# Runner configuration
gitea_runner_log_level: "info"
gitea_runner_container_label: "gitea-runner=true"
gitea_runner_file: ".runner"
# Docker installation
# Docker installation (for rootless dependencies)
docker_gpg_key_path: "/etc/apt/keyrings/docker.asc"
+6 -3
View File
@@ -5,7 +5,10 @@
when: ansible_facts is defined and ansible_facts['service_mgr'] | default('') == 'systemd'
- name: Restart gitea-runner
ansible.builtin.systemd:
name: "gitea-runner-{{ runner_mode }}@{{ runner_name }}"
state: restarted
ansible.builtin.command: systemctl --user restart gitea-runner
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
changed_when: true
when: ansible_facts is defined and ansible_facts['service_mgr'] | default('') == 'systemd'
@@ -1,12 +0,0 @@
---
- name: Converge
hosts: all
become: true
vars:
gitea_url: "http://localhost:3000"
registration_token: "fake-token-for-testing"
runner_name: "molecule-test-runner"
runner_mode: "binary"
skip_runner_registration: true
roles:
- role: gitea-runner
@@ -1,39 +0,0 @@
---
driver:
name: docker
platforms:
- name: ${MOLECULE_PLATFORM_NAME:-ubuntu-2204}
image: ${MOLECULE_PLATFORM_IMAGE:-ubuntu:22.04}
command: ${MOLECULE_PLATFORM_COMMAND:-sleep infinity}
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
cgroupns_mode: host
privileged: true
pre_build_image: false
provisioner:
name: ansible
playbooks:
converge: converge.yml
prepare: ../common/prepare.yml
env:
ANSIBLE_ROLES_PATH: "../../.."
scenario:
test_sequence:
- dependency
- cleanup
- destroy
- syntax
- create
- prepare
- converge
- idempotence
- side_effect
- verify
- cleanup
- destroy
verifier:
name: ansible
@@ -1,72 +0,0 @@
---
- name: Verify
hosts: all
become: true
vars:
runner_name: "molecule-test-runner"
pre_tasks:
- name: Load role defaults
ansible.builtin.include_vars:
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults"
- name: Override runner mode for binary scenario
ansible.builtin.set_fact:
runner_mode: "binary"
tasks:
- name: Check gitea_runner binary exists
ansible.builtin.stat:
path: "{{ gitea_runner_binary_path }}"
register: gitea_runner_stat
- name: Assert gitea_runner binary exists
ansible.builtin.assert:
that:
- gitea_runner_stat.stat.exists
fail_msg: "gitea_runner binary is missing at {{ gitea_runner_binary_path }}"
- name: Check Docker is installed
ansible.builtin.command: docker --version
changed_when: false
- name: Check systemd template unit exists
ansible.builtin.stat:
path: "/etc/systemd/system/gitea-runner-{{ runner_mode | default('docker') }}@.service"
register: service_stat
- name: Assert template unit exists
ansible.builtin.assert:
that:
- service_stat.stat.exists
fail_msg: "Systemd template unit is missing"
- name: Check instance data directory exists
ansible.builtin.stat:
path: "{{ gitea_runner_data_dir }}"
register: data_dir_stat
- name: Assert instance data directory exists
ansible.builtin.assert:
that:
- data_dir_stat.stat.exists
fail_msg: "Instance data directory is missing"
- name: Check prune timer exists
ansible.builtin.stat:
path: /etc/systemd/system/docker-prune.timer
register: timer_stat
- name: Assert prune timer exists
ansible.builtin.assert:
that:
- timer_stat.stat.exists
fail_msg: "Docker prune timer is missing"
- name: Check config file exists
ansible.builtin.stat:
path: "{{ gitea_runner_config_dir }}/config.yaml"
register: config_stat
- name: Assert config file exists
ansible.builtin.assert:
that:
- config_stat.stat.exists
fail_msg: "Config file is missing"
@@ -19,16 +19,3 @@
community.general.pacman:
update_cache: true
when: ansible_facts['os_family'] == 'Archlinux'
- name: Ensure /etc/docker directory exists
ansible.builtin.file:
path: /etc/docker
state: directory
mode: "0755"
- name: Configure Docker daemon for vfs storage driver (DinD)
ansible.builtin.copy:
dest: /etc/docker/daemon.json
content: |
{"storage-driver": "vfs"}
mode: "0644"
@@ -9,42 +9,39 @@
ansible.builtin.include_vars:
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults"
tasks:
- name: Check Docker is installed
ansible.builtin.command: docker --version
changed_when: false
- name: Check runner user exists
ansible.builtin.user:
name: "{{ gitea_runner_service_user }}"
register: user_info
check_mode: true
- name: Check runner image was pulled
ansible.builtin.command: docker images gitea/runner --format '{{ '{{' }} .Repository {{ '}}' }}:{{ '{{' }} .Tag {{ '}}' }}'
register: image_list
changed_when: false
- name: Assert runner image exists locally
- name: Assert runner user exists
ansible.builtin.assert:
that:
- "'gitea/runner' in image_list.stdout"
fail_msg: "Runner Docker image was not pulled"
- user_info.state == "present"
fail_msg: "Runner system user was not created"
- name: Check prune timer exists
- name: Check runner binary exists
ansible.builtin.stat:
path: /etc/systemd/system/docker-prune.timer
register: timer_stat
path: "{{ gitea_runner_binary_path }}"
register: binary_stat
- name: Assert prune timer exists
- name: Assert runner binary exists
ansible.builtin.assert:
that:
- timer_stat.stat.exists
fail_msg: "Docker prune timer is missing"
- binary_stat.stat.exists
fail_msg: "Gitea runner binary is missing"
- name: Check systemd template unit exists
- name: Check systemd user service exists
ansible.builtin.stat:
path: "/etc/systemd/system/gitea-runner-{{ runner_mode | default('docker') }}@.service"
path: "{{ gitea_runner_home }}/.config/systemd/user/gitea-runner.service"
register: service_stat
- name: Assert template unit exists
- name: Assert user service exists
ansible.builtin.assert:
that:
- service_stat.stat.exists
fail_msg: "Systemd template unit is missing"
fail_msg: "Systemd user service is missing"
- name: Check instance data directory exists
ansible.builtin.stat:
@@ -57,9 +54,9 @@
- data_dir_stat.stat.exists
fail_msg: "Instance data directory is missing"
- name: Check config file exists in data directory
- name: Check config file exists in config directory
ansible.builtin.stat:
path: "{{ gitea_runner_data_dir }}/config.yaml"
path: "{{ gitea_runner_config_dir }}/config.yaml"
register: config_stat
- name: Assert config file exists
@@ -67,3 +64,14 @@
that:
- config_stat.stat.exists
fail_msg: "Config file is missing"
- name: Check prune timer exists
ansible.builtin.stat:
path: "{{ gitea_runner_home }}/.config/systemd/user/docker-prune.timer"
register: timer_stat
- name: Assert prune timer exists
ansible.builtin.assert:
that:
- timer_stat.stat.exists
fail_msg: "Docker prune timer is missing"
@@ -4,12 +4,18 @@
become: true
vars:
runner_name: "deregister-test-runner"
pre_tasks:
- name: Load role defaults
ansible.builtin.include_vars:
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults"
tasks:
- name: Ensure fake .runner file exists
ansible.builtin.copy:
dest: "/var/lib/gitea-runner/{{ runner_name }}/.runner"
dest: "{{ gitea_runner_data_dir }}/.runner"
content: |
{"id": 1, "uuid": "test-uuid-1234", "name": "{{ runner_name }}", "address": "http://localhost:3000"}
owner: "{{ gitea_runner_service_user }}"
group: "{{ gitea_runner_service_user }}"
mode: "0644"
- name: Deregister runner
@@ -19,7 +25,6 @@
runner_name: "deregister-test-runner"
registration_token: "fake-token-for-testing"
gitea_url: "http://localhost:3000"
runner_mode: "docker"
skip_runner_registration: false
tasks:
- name: Include deregistration tasks
@@ -9,7 +9,7 @@
tasks:
- name: Check registration file was removed
ansible.builtin.stat:
path: "/var/lib/gitea-runner/deregister-test-runner/.runner"
path: "{{ gitea_runner_data_dir }}/.runner"
register: runner_file_stat
- name: Assert registration file no longer exists
@@ -18,13 +18,13 @@
- not runner_file_stat.stat.exists
fail_msg: "Registration file (.runner) was not removed by deregistration"
- name: Check systemd template still exists
- name: Check systemd user service still exists
ansible.builtin.stat:
path: "/etc/systemd/system/gitea-runner-{{ runner_mode | default('docker') }}@.service"
path: "{{ gitea_runner_home }}/.config/systemd/user/gitea-runner.service"
register: service_stat
- name: Assert systemd template still exists after deregister
- name: Assert user service still exists after deregister
ansible.builtin.assert:
that:
- service_stat.stat.exists
fail_msg: "Systemd template unit was incorrectly removed"
fail_msg: "Systemd user service was incorrectly removed"
@@ -5,24 +5,17 @@
vars:
runner_name: "lifecycle-test-runner"
pre_tasks:
- name: Check if systemd is available
ansible.builtin.stat:
path: /run/systemd/system
register: systemd_available
- name: Load role defaults
ansible.builtin.include_vars:
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults"
tasks:
- name: Stop gitea-runner systemd instance
ansible.builtin.systemd:
name: "gitea-runner-docker@{{ runner_name }}"
state: stopped
daemon_reload: true
when: systemd_available.stat.exists
- name: Stop gitea-runner user service
ansible.builtin.command: "sudo -u {{ gitea_runner_service_user }} systemctl --user stop gitea-runner"
changed_when: true
- name: Disable gitea-runner systemd instance
ansible.builtin.systemd:
name: "gitea-runner-docker@{{ runner_name }}"
enabled: false
daemon_reload: true
when: systemd_available.stat.exists
- name: Disable gitea-runner user service
ansible.builtin.command: "sudo -u {{ gitea_runner_service_user }} systemctl --user disable gitea-runner"
changed_when: true
- name: Re-enable and start runner
hosts: all
@@ -30,21 +23,14 @@
vars:
runner_name: "lifecycle-test-runner"
pre_tasks:
- name: Check if systemd is available
ansible.builtin.stat:
path: /run/systemd/system
register: systemd_available
- name: Load role defaults
ansible.builtin.include_vars:
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults"
tasks:
- name: Enable gitea-runner systemd instance
ansible.builtin.systemd:
name: "gitea-runner-docker@{{ runner_name }}"
enabled: true
daemon_reload: true
when: systemd_available.stat.exists
- name: Enable gitea-runner user service
ansible.builtin.command: "sudo -u {{ gitea_runner_service_user }} systemctl --user enable gitea-runner"
changed_when: true
- name: Start gitea-runner systemd instance
ansible.builtin.systemd:
name: "gitea-runner-docker@{{ runner_name }}"
state: started
daemon_reload: true
when: systemd_available.stat.exists
- name: Start gitea-runner user service
ansible.builtin.command: "sudo -u {{ gitea_runner_service_user }} systemctl --user start gitea-runner"
changed_when: true
@@ -9,38 +9,20 @@
ansible.builtin.include_vars:
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults"
tasks:
- name: Check systemd template unit exists
- name: Check systemd user service exists
ansible.builtin.stat:
path: "/etc/systemd/system/gitea-runner-{{ runner_mode | default('docker') }}@.service"
register: template_stat
path: "{{ gitea_runner_home }}/.config/systemd/user/gitea-runner.service"
register: service_stat
- name: Assert template unit exists
- name: Assert user service exists
ansible.builtin.assert:
that:
- template_stat.stat.exists
fail_msg: "Systemd template unit is missing"
- name: Check if systemd is available
ansible.builtin.stat:
path: /run/systemd/system
register: systemd_available
- name: Check instance is enabled
ansible.builtin.systemd:
name: "gitea-runner-{{ runner_mode | default('docker') }}@lifecycle-test-runner"
register: service_status
when: systemd_available.stat.exists
- name: Assert instance is enabled
ansible.builtin.assert:
that:
- service_status.status.LoadState == 'loaded'
fail_msg: "Systemd instance is not loaded"
when: systemd_available.stat.exists
- service_stat.stat.exists
fail_msg: "Systemd user service is missing"
- name: Check instance data directory exists after lifecycle
ansible.builtin.stat:
path: "{{ gitea_runner_base_data_dir }}/{{ runner_name }}"
path: "{{ gitea_runner_data_dir }}"
register: data_dir_stat
- name: Assert instance data directory exists
@@ -6,7 +6,6 @@
gitea_url: "http://localhost:3000"
registration_token: "fake-token-for-testing"
runner_name: "molecule-runner-a"
runner_mode: "binary"
skip_runner_registration: true
roles:
- role: gitea-runner
@@ -18,7 +17,6 @@
gitea_url: "http://localhost:3000"
registration_token: "fake-token-for-testing"
runner_name: "molecule-runner-b"
runner_mode: "binary"
skip_runner_registration: true
roles:
- role: gitea-runner
@@ -6,24 +6,32 @@
- name: Load role defaults
ansible.builtin.include_vars:
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults"
- name: Override runner mode for multi-instance scenario
ansible.builtin.set_fact:
runner_mode: "binary"
tasks:
- name: Check systemd template unit exists
- name: Check first runner user exists
ansible.builtin.stat:
path: "/etc/systemd/system/gitea-runner-{{ runner_mode | default('docker') }}@.service"
register: template_stat
path: "{{ gitea_runner_base_home }}/grm-molecule-runner-a"
register: home_a_stat
- name: Assert template unit exists
- name: Assert first runner user home exists
ansible.builtin.assert:
that:
- template_stat.stat.exists
fail_msg: "Systemd template unit is missing"
- home_a_stat.stat.exists
fail_msg: "First runner user home is missing"
- name: Check second runner user exists
ansible.builtin.stat:
path: "{{ gitea_runner_base_home }}/grm-molecule-runner-b"
register: home_b_stat
- name: Assert second runner user home exists
ansible.builtin.assert:
that:
- home_b_stat.stat.exists
fail_msg: "Second runner user home is missing"
- name: Check first instance data directory exists
ansible.builtin.stat:
path: "/var/lib/gitea-runner/molecule-runner-a"
path: "{{ gitea_runner_base_data_dir }}/molecule-runner-a"
register: data_a_stat
- name: Assert first instance data directory exists
@@ -34,7 +42,7 @@
- name: Check second instance data directory exists
ansible.builtin.stat:
path: "/var/lib/gitea-runner/molecule-runner-b"
path: "{{ gitea_runner_base_data_dir }}/molecule-runner-b"
register: data_b_stat
- name: Assert second instance data directory exists
@@ -45,7 +53,7 @@
- name: Check first instance config exists
ansible.builtin.stat:
path: "/etc/gitea-runner/molecule-runner-a/config.yaml"
path: "{{ gitea_runner_base_config_dir }}/molecule-runner-a/config.yaml"
register: config_a_stat
- name: Assert first instance config exists
@@ -56,7 +64,7 @@
- name: Check second instance config exists
ansible.builtin.stat:
path: "/etc/gitea-runner/molecule-runner-b/config.yaml"
path: "{{ gitea_runner_base_config_dir }}/molecule-runner-b/config.yaml"
register: config_b_stat
- name: Assert second instance config exists
@@ -1,23 +1,11 @@
---
- name: Converge Docker mode
- name: Converge
hosts: all
become: true
vars:
gitea_url: "http://localhost:3000"
registration_token: "fake-token-for-testing"
runner_name: "template-docker-runner"
skip_runner_registration: true
roles:
- role: gitea-runner
- name: Converge Binary mode
hosts: all
become: true
vars:
gitea_url: "http://localhost:3000"
registration_token: "fake-token-for-testing"
runner_name: "template-binary-runner"
runner_mode: "binary"
runner_name: "template-test-runner"
skip_runner_registration: true
roles:
- role: gitea-runner
@@ -2,65 +2,42 @@
- name: Verify
hosts: all
become: true
vars:
runner_name: "template-test-runner"
pre_tasks:
- name: Load role defaults
ansible.builtin.include_vars:
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults"
tasks:
- name: Check Docker mode systemd template exists
- name: Check systemd user service exists
ansible.builtin.stat:
path: "/etc/systemd/system/gitea-runner-docker@.service"
register: docker_template_stat
path: "{{ gitea_runner_home }}/.config/systemd/user/gitea-runner.service"
register: service_stat
- name: Assert Docker mode systemd template exists
- name: Assert user service exists
ansible.builtin.assert:
that:
- docker_template_stat.stat.exists
fail_msg: "Docker systemd template is missing"
- service_stat.stat.exists
fail_msg: "Systemd user service is missing"
- name: Read rendered Docker systemd template
- name: Read rendered user service template
ansible.builtin.slurp:
src: "/etc/systemd/system/gitea-runner-docker@.service"
register: docker_template
src: "{{ gitea_runner_home }}/.config/systemd/user/gitea-runner.service"
register: service_template
- name: Assert Docker mode template contains expected directives
- name: Assert user service template contains expected directives
ansible.builtin.assert:
that:
- "'Type=oneshot' in docker_template.content | b64decode"
- "'RemainAfterExit=yes' in docker_template.content | b64decode"
- "'Requires=docker.service' in docker_template.content | b64decode"
- "'docker run -d' in docker_template.content | b64decode"
- "'ExecStop=/usr/bin/docker stop' in docker_template.content | b64decode"
- "'Restart=on-failure' in docker_template.content | b64decode"
fail_msg: "Docker systemd template is missing expected directives"
- name: Check Binary mode systemd template exists
ansible.builtin.stat:
path: "/etc/systemd/system/gitea-runner-binary@.service"
register: binary_template_stat
- name: Assert Binary mode systemd template exists
ansible.builtin.assert:
that:
- binary_template_stat.stat.exists
fail_msg: "Binary systemd template is missing"
- name: Read rendered Binary systemd template
ansible.builtin.slurp:
src: "/etc/systemd/system/gitea-runner-binary@.service"
register: binary_template
- name: Assert Binary mode template contains expected directives
ansible.builtin.assert:
that:
- "'Type=simple' in binary_template.content | b64decode"
- "'ExecStart={{ gitea_runner_binary_path }}' in binary_template.content | b64decode"
- "'Restart=on-failure' in binary_template.content | b64decode"
fail_msg: "Binary systemd template is missing expected directives"
- "'Type=simple' in service_template.content | b64decode"
- "'ExecStart={{ gitea_runner_binary_path }}' in service_template.content | b64decode"
- "'Restart=on-failure' in service_template.content | b64decode"
- "'DOCKER_HOST=unix:///run/user' in service_template.content | b64decode"
- "'XDG_RUNTIME_DIR=/run/user' in service_template.content | b64decode"
fail_msg: "User service template is missing expected directives"
- name: Read rendered prune service template
ansible.builtin.slurp:
src: "/etc/systemd/system/docker-prune.service"
src: "{{ gitea_runner_home }}/.config/systemd/user/docker-prune.service"
register: prune_service
- name: Assert prune service contains expected directives
@@ -69,17 +46,16 @@
- "'Type=oneshot' in prune_service.content | b64decode"
- "'docker system prune' in prune_service.content | b64decode"
- "'docker volume prune' in prune_service.content | b64decode"
- "'label=gitea-runner=true' in prune_service.content | b64decode"
fail_msg: "Prune service template is missing expected directives"
- name: Read rendered prune timer template
ansible.builtin.slurp:
src: "/etc/systemd/system/docker-prune.timer"
src: "{{ gitea_runner_home }}/.config/systemd/user/docker-prune.timer"
register: prune_timer
- name: Assert prune timer contains expected directives
ansible.builtin.assert:
that:
- "'OnCalendar=daily' in prune_timer.content | b64decode"
- "'OnCalendar={{ gitea_runner_prune_schedule }}' in prune_timer.content | b64decode"
- "'Persistent=true' in prune_timer.content | b64decode"
fail_msg: "Prune timer template is missing expected directives"
@@ -1,23 +1,11 @@
---
- name: Converge Docker mode
- name: Converge
hosts: all
become: true
vars:
gitea_url: "http://localhost:3000"
registration_token: "fake-token-for-testing"
runner_name: "update-docker-runner"
skip_runner_registration: true
roles:
- role: gitea-runner
- name: Converge Binary mode
hosts: all
become: true
vars:
gitea_url: "http://localhost:3000"
registration_token: "fake-token-for-testing"
runner_name: "update-binary-runner"
runner_mode: "binary"
runner_name: "update-test-runner"
skip_runner_registration: true
roles:
- role: gitea-runner
@@ -1,24 +1,11 @@
---
- name: Update Docker mode runner
- name: Update runner
hosts: all
become: true
vars:
runner_name: "update-docker-runner"
runner_mode: "docker"
runner_name: "update-test-runner"
tasks:
- name: Include Docker update tasks
- name: Include update tasks
ansible.builtin.include_role:
name: gitea-runner
tasks_from: docker_update.yml
- name: Update Binary mode runner
hosts: all
become: true
vars:
runner_name: "update-binary-runner"
runner_mode: "binary"
tasks:
- name: Include binary update tasks
ansible.builtin.include_role:
name: gitea-runner
tasks_from: binary_update.yml
tasks_from: update_runner.yml
@@ -2,23 +2,14 @@
- name: Verify
hosts: all
become: true
vars:
runner_name: "update-test-runner"
pre_tasks:
- name: Load role defaults
ansible.builtin.include_vars:
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults"
tasks:
- name: Check Docker runner image still exists after update
ansible.builtin.command: docker images gitea/runner --format '{{ '{{' }} .Repository {{ '}}' }}:{{ '{{' }} .Tag {{ '}}' }}'
register: image_list
changed_when: false
- name: Assert runner image exists after update
ansible.builtin.assert:
that:
- "'gitea/runner' in image_list.stdout"
fail_msg: "Runner Docker image missing after update"
- name: Check binary runner executable still exists after update
- name: Check runner binary still exists after update
ansible.builtin.stat:
path: "{{ gitea_runner_binary_path }}"
register: binary_stat
@@ -27,48 +18,26 @@
ansible.builtin.assert:
that:
- binary_stat.stat.exists
fail_msg: "Binary runner executable missing after update"
fail_msg: "Runner binary missing after update"
- name: Check Docker mode systemd template still exists
- name: Check systemd user service still exists
ansible.builtin.stat:
path: "/etc/systemd/system/gitea-runner-docker@.service"
register: docker_service_stat
path: "{{ gitea_runner_home }}/.config/systemd/user/gitea-runner.service"
register: service_stat
- name: Assert Docker mode systemd template still exists after update
- name: Assert user service exists after update
ansible.builtin.assert:
that:
- docker_service_stat.stat.exists
fail_msg: "Docker mode systemd template missing after update"
- service_stat.stat.exists
fail_msg: "Systemd user service missing after update"
- name: Check Binary mode systemd template still exists
- name: Check instance data directory still exists
ansible.builtin.stat:
path: "/etc/systemd/system/gitea-runner-binary@.service"
register: binary_service_stat
path: "{{ gitea_runner_data_dir }}"
register: data_stat
- name: Assert Binary mode systemd template still exists after update
- name: Assert data directory exists after update
ansible.builtin.assert:
that:
- binary_service_stat.stat.exists
fail_msg: "Binary mode systemd template missing after update"
- name: Check Docker mode data directory still exists
ansible.builtin.stat:
path: "/var/lib/gitea-runner/update-docker-runner"
register: docker_data_stat
- name: Assert Docker mode data directory exists after update
ansible.builtin.assert:
that:
- docker_data_stat.stat.exists
fail_msg: "Docker runner data directory missing after update"
- name: Check Binary mode data directory still exists
ansible.builtin.stat:
path: "/var/lib/gitea-runner/update-binary-runner"
register: binary_data_stat
- name: Assert Binary mode data directory exists after update
ansible.builtin.assert:
that:
- binary_data_stat.stat.exists
fail_msg: "Binary runner data directory missing after update"
- data_stat.stat.exists
fail_msg: "Runner data directory missing after update"
@@ -1,10 +0,0 @@
---
- name: Include gitea_runner download
ansible.builtin.include_tasks: download_gitea_runner.yml
- name: Restart gitea-runner systemd instance
ansible.builtin.systemd:
name: "gitea-runner-{{ runner_mode }}@{{ runner_name }}"
state: restarted
daemon_reload: true
when: systemd_available is defined and systemd_available.stat is defined and systemd_available.stat.exists
@@ -27,34 +27,18 @@
--no-interactive
args:
chdir: "{{ gitea_runner_data_dir }}"
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
DOCKER_HOST: "unix:///run/user/{{ gitea_runner_uid }}/docker.sock"
when:
- runner_file_stat.stat.exists | default(false) | bool
- not skip_runner_registration
- runner_mode == 'binary'
register: deregister_output
changed_when: deregister_output.rc == 0
failed_when: false
- name: Deregister runner with Gitea via Docker
ansible.builtin.command: >
docker run --rm
--entrypoint /usr/local/bin/gitea-runner
-v {{ gitea_runner_data_dir }}:/data
-w /data
{{ gitea_runner_docker_image }}:{{ gitea_runner_version | regex_replace('^v', '') }}
delete
--token {{ registration_token }}
--name {{ runner_name }}
--instance {{ gitea_url }}
--no-interactive
when:
- runner_file_stat.stat.exists | default(false) | bool
- not skip_runner_registration
- runner_mode == 'docker'
register: deregister_docker_output
changed_when: deregister_docker_output.rc == 0
failed_when: false
- name: Remove runner registration file
ansible.builtin.file:
path: "{{ gitea_runner_data_dir }}/.runner"
-100
View File
@@ -1,100 +0,0 @@
---
- name: Check if Docker is already installed
ansible.builtin.command: docker --version
register: docker_check
changed_when: false
failed_when: false
- name: Install Docker (Debian/Ubuntu)
when:
- ansible_facts['os_family'] == 'Debian'
- docker_check.rc != 0
block:
- name: Remove old Docker apt repository files
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- /etc/apt/sources.list.d/docker.list
- /etc/apt/keyrings/docker.gpg
- name: Install prerequisite packages
ansible.builtin.apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
state: present
update_cache: true
cache_valid_time: 0
- name: Add Docker GPG key
ansible.builtin.get_url:
url: https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }}/gpg
dest: "{{ docker_gpg_key_path }}"
mode: "0644"
- name: Add Docker repository
ansible.builtin.deb822_repository:
name: docker
types: deb
uris: https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }}
suites: "{{ ansible_facts['distribution_release'] }}"
components: stable
signed_by: "{{ docker_gpg_key_path }}"
state: present
- name: Install Docker packages
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
state: present
update_cache: true
cache_valid_time: 0
- name: Install Docker (Arch Linux)
when:
- ansible_facts['os_family'] == 'Archlinux'
- docker_check.rc != 0
block:
- name: Install Docker packages
community.general.pacman:
name:
- docker
- docker-compose
state: present
update_cache: true
- name: Ensure Docker service is running (systemd)
ansible.builtin.systemd:
name: docker
state: started
enabled: true
when: systemd_available.stat.exists
- name: Start Docker daemon manually (no systemd)
ansible.builtin.shell: nohup dockerd > /var/log/dockerd.log 2>&1 &
when:
- not systemd_available.stat.exists
- docker_check.rc != 0
changed_when: true
- name: Wait for Docker daemon to be ready
ansible.builtin.command: docker version
register: docker_ready
until: docker_ready.rc == 0
retries: 10
delay: 2
changed_when: false
when: not systemd_available.stat.exists
- name: Add user to docker group
ansible.builtin.user:
name: "{{ ansible_user | default(ansible_facts['user_id']) }}"
groups: docker
append: true
when: ansible_user is defined or ansible_facts['user_id'] is defined
@@ -1,55 +0,0 @@
---
- name: Include Docker installation
ansible.builtin.include_tasks: docker.yml
- name: Ensure runner data directory exists
ansible.builtin.file:
path: "{{ gitea_runner_data_dir }}"
state: directory
mode: "0755"
- name: Create gitea_runner config file in data directory
ansible.builtin.template:
src: gitea-runner-config.yaml.j2
dest: "{{ gitea_runner_data_dir }}/config.yaml"
mode: "0644"
- name: Pull gitea_runner image
ansible.builtin.command: docker pull {{ gitea_runner_docker_image }}:{{ gitea_runner_version | regex_replace('^v', '') }}
changed_when: "'Downloaded newer image' in pull_result.stdout or 'Status: Downloaded' in pull_result.stdout"
register: pull_result
- name: Check if runner is already registered
ansible.builtin.stat:
path: "{{ gitea_runner_data_dir }}/.runner"
register: runner_registered
- name: Register runner with Gitea via Docker
ansible.builtin.command: >
docker run --rm
--entrypoint /usr/local/bin/gitea-runner
-v {{ gitea_runner_data_dir }}:/data
-w /data
{{ gitea_runner_docker_image }}:{{ gitea_runner_version | regex_replace('^v', '') }}
register
--token {{ registration_token }}
--name {{ runner_name }}
--instance {{ gitea_url }}
--labels {{ runner_labels }}
--no-interactive
when:
- not runner_registered.stat.exists
- not skip_runner_registration
register: register_output
changed_when: "'already exists' not in register_output.stdout | default('')"
timeout: 60
- name: Restart gitea-runner service after registration
ansible.builtin.systemd:
name: "gitea-runner-docker@{{ runner_name }}"
state: restarted
daemon_reload: true
when: systemd_available.stat.exists | default(false) | bool
- name: Include service setup
ansible.builtin.include_tasks: service.yml
@@ -1,12 +0,0 @@
---
- name: Pull gitea_runner image
ansible.builtin.command: docker pull {{ gitea_runner_docker_image }}:{{ gitea_runner_version | regex_replace('^v', '') }}
changed_when: "'Downloaded newer image' in pull_result.stdout or 'Status: Downloaded' in pull_result.stdout"
register: pull_result
- name: Restart gitea-runner systemd instance
ansible.builtin.systemd:
name: "gitea-runner-{{ runner_mode }}@{{ runner_name }}"
state: restarted
daemon_reload: true
when: systemd_available is defined and systemd_available.stat is defined and systemd_available.stat.exists
@@ -1,18 +1,14 @@
---
- name: Include Docker installation
ansible.builtin.include_tasks: docker.yml
- name: Include gitea_runner download
ansible.builtin.include_tasks: download_gitea_runner.yml
- name: Ensure runner data directory exists
ansible.builtin.file:
path: "{{ gitea_runner_data_dir }}"
state: directory
mode: "0755"
- name: Include config creation
ansible.builtin.include_tasks: config.yml
- name: Create gitea_runner config file
ansible.builtin.template:
src: gitea-runner-config.yaml.j2
dest: "{{ gitea_runner_config_dir }}/config.yaml"
owner: "{{ gitea_runner_service_user }}"
group: "{{ gitea_runner_service_user }}"
mode: "0644"
- name: Include validation
ansible.builtin.include_tasks: validate.yml
@@ -18,19 +18,14 @@
else {} }}
when: runner_file_stat.stat.exists | default(false) | bool
- name: Verify runner container running (Docker mode)
ansible.builtin.shell: |
docker ps --filter "name=gitea-runner-{{ runner_name }}" --format "{{ '{{.Status}}' }}"
register: docker_check
changed_when: false
when: runner_mode == 'docker'
- name: Verify runner service active (Binary mode)
ansible.builtin.systemd:
name: "gitea-runner-{{ runner_mode }}@{{ runner_name }}"
state: started
- name: Verify runner user service active
ansible.builtin.command: systemctl --user is-active gitea-runner
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
register: service_check
when: runner_mode == 'binary'
changed_when: false
- name: Validate runner installation
ansible.builtin.fail:
@@ -39,16 +34,12 @@
{% if not (runner_file_stat.stat.exists | default(false)) %}
- Registration file (.runner) is missing. Registration may have failed.
{% endif %}
{% if runner_mode == 'docker' and not (docker_check.stdout | default('')) %}
- Docker container is not running.
{% endif %}
{% if runner_mode == 'binary' and not (service_check.status.ActiveState | default('')) == 'active' %}
- Systemd service is not active.
{% if not (service_check.stdout | default('') | trim) == 'active' %}
- Systemd user service is not active.
{% endif %}
when: >
not (runner_file_stat.stat.exists | default(false))
or (runner_mode == 'docker' and not (docker_check.stdout | default('')))
or (runner_mode == 'binary' and not (service_check.status.ActiveState | default('')) == 'active')
or not (service_check.stdout | default('') | trim) == 'active'
- name: Report runner status
ansible.builtin.debug:
@@ -58,8 +49,7 @@
{% if runner_reg.id is defined %}Runner ID: {{ runner_reg.id }}{% endif %}
{% if runner_reg.uuid is defined %}UUID: {{ runner_reg.uuid }}{% endif %}
{% if runner_reg.address is defined %}Gitea: {{ runner_reg.address }}{% endif %}
{% if runner_mode == 'docker' %}Container: {{ docker_check.stdout | default('unknown') }}{% endif %}
{% if runner_mode == 'binary' %}Service: {{ service_check.status.ActiveState | default('unknown') }}{% endif %}
Service: {{ service_check.stdout | default('unknown') | trim }}
- name: Optional Gitea API verification
when:
+7 -6
View File
@@ -2,13 +2,14 @@
- name: Include systemd availability check
ansible.builtin.include_tasks: systemd_check.yml
- name: Install runner (Docker mode)
ansible.builtin.include_tasks: docker_mode.yml
when: runner_mode == 'docker'
- name: Include user setup
ansible.builtin.include_tasks: user_setup.yml
- name: Install runner (Binary mode)
ansible.builtin.include_tasks: binary_mode.yml
when: runner_mode == 'binary'
- name: Include rootless Docker setup
ansible.builtin.include_tasks: rootless_docker.yml
- name: Include runner install
ansible.builtin.include_tasks: install_runner.yml
- name: Include prune setup
ansible.builtin.include_tasks: prune.yml
+23 -10
View File
@@ -1,20 +1,33 @@
---
- name: Create docker-prune service file
- name: Create docker-prune user service file
ansible.builtin.template:
src: docker-prune.service.j2
dest: /etc/systemd/system/docker-prune.service
dest: "{{ gitea_runner_home }}/.config/systemd/user/docker-prune.service"
owner: "{{ gitea_runner_service_user }}"
group: "{{ gitea_runner_service_user }}"
mode: "0644"
- name: Create docker-prune timer file
- name: Create docker-prune user timer file
ansible.builtin.template:
src: docker-prune.timer.j2
dest: /etc/systemd/system/docker-prune.timer
dest: "{{ gitea_runner_home }}/.config/systemd/user/docker-prune.timer"
owner: "{{ gitea_runner_service_user }}"
group: "{{ gitea_runner_service_user }}"
mode: "0644"
- name: Enable and start docker-prune timer
ansible.builtin.systemd:
name: docker-prune.timer
state: started
enabled: true
daemon_reload: true
- name: Reload systemd user daemon for prune 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 }}"
changed_when: true
- name: Enable and start docker-prune user timer
ansible.builtin.command: systemctl --user enable --now docker-prune.timer
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
changed_when: true
when: systemd_available.stat.exists
@@ -3,6 +3,8 @@
ansible.builtin.file:
path: "{{ gitea_runner_data_dir }}"
state: directory
owner: "{{ gitea_runner_service_user }}"
group: "{{ gitea_runner_service_user }}"
mode: "0755"
- name: Check if runner is already registered
@@ -12,7 +14,7 @@
- name: Register runner with Gitea
ansible.builtin.command: >
"{{ gitea_runner_binary_path }}" register
{{ gitea_runner_binary_path }} register
--token {{ registration_token }}
--name {{ runner_name }}
--instance {{ gitea_url }}
@@ -20,14 +22,12 @@
--no-interactive
args:
chdir: "{{ gitea_runner_data_dir }}"
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
DOCKER_HOST: "unix:///run/user/{{ gitea_runner_uid }}/docker.sock"
when: not runner_registered.stat.exists
register: register_output
changed_when: "'already exists' not in register_output.stdout | default('')"
timeout: 60
- name: Restart gitea-runner service after registration
ansible.builtin.systemd:
name: "gitea-runner-binary@{{ runner_name }}"
state: restarted
daemon_reload: true
when: systemd_available.stat.exists | default(false) | bool
@@ -0,0 +1,73 @@
---
- name: Install rootless Docker dependencies (Debian/Ubuntu)
ansible.builtin.apt:
name:
- uidmap
- slirp4netns
- fuse-overlayfs
- docker-ce
- docker-ce-cli
- docker-ce-rootless-extras
- containerd.io
- docker-compose-plugin
- rsync
state: present
update_cache: true
cache_valid_time: 0
when: ansible_facts['os_family'] == 'Debian'
- name: Install rootless Docker dependencies (Arch Linux)
community.general.pacman:
name:
- docker
- docker-compose
- slirp4netns
- fuse-overlayfs
- rsync
state: present
update_cache: true
when: ansible_facts['os_family'] == 'Archlinux'
- name: Check if rootless Docker is already set up
ansible.builtin.stat:
path: "{{ gitea_runner_home }}/.config/systemd/user/docker.service"
register: rootless_docker_check
- name: Set up rootless Docker for runner user
ansible.builtin.command: dockerd-rootless-setuptool.sh install
args:
creates: "{{ gitea_runner_home }}/.config/systemd/user/docker.service"
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
when: not rootless_docker_check.stat.exists
- name: Start rootless Docker daemon (systemd user service)
ansible.builtin.command: systemctl --user start docker
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
changed_when: true
- name: Enable rootless Docker daemon (systemd user service)
ansible.builtin.command: systemctl --user enable docker
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
changed_when: true
- name: Wait for rootless Docker daemon to be ready
ansible.builtin.command: docker version
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
DOCKER_HOST: "unix:///run/user/{{ gitea_runner_uid }}/docker.sock"
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
register: docker_ready
until: docker_ready.rc == 0
retries: 10
delay: 2
changed_when: false
+20 -12
View File
@@ -1,17 +1,25 @@
---
- name: Create systemd template service file
- name: Create systemd user service file
ansible.builtin.template:
src: gitea-runner@.service.j2
dest: "/etc/systemd/system/gitea-runner-{{ runner_mode }}@.service"
src: gitea-runner-user.service.j2
dest: "{{ gitea_runner_home }}/.config/systemd/user/gitea-runner.service"
owner: "{{ gitea_runner_service_user }}"
group: "{{ gitea_runner_service_user }}"
mode: "0644"
notify:
- Reload systemd
- Restart gitea-runner
- name: Enable and start gitea-runner instance
ansible.builtin.systemd:
name: "gitea-runner-{{ runner_mode }}@{{ runner_name }}"
state: started
enabled: true
daemon_reload: true
- name: Reload systemd user daemon
ansible.builtin.command: systemctl --user daemon-reload
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
changed_when: true
- name: Enable and start gitea-runner user service
ansible.builtin.command: systemctl --user enable --now gitea-runner
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
changed_when: true
when: systemd_available.stat.exists
@@ -0,0 +1,12 @@
---
- name: Include gitea_runner download
ansible.builtin.include_tasks: download_gitea_runner.yml
- name: Restart gitea-runner user service
ansible.builtin.command: systemctl --user restart gitea-runner
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
when: systemd_available.stat.exists | default(false) | bool
changed_when: true
@@ -0,0 +1,65 @@
---
- name: Create per-runner system user
ansible.builtin.user:
name: "{{ gitea_runner_service_user }}"
home: "{{ gitea_runner_home }}"
shell: /bin/bash
system: true
create_home: true
register: runner_user
- name: Set runner UID fact
ansible.builtin.set_fact:
gitea_runner_uid: "{{ runner_user.uid }}"
- name: Enable lingering for runner user
ansible.builtin.command: loginctl enable-linger {{ gitea_runner_service_user }}
changed_when: true
- name: Ensure subuid entry for runner user
ansible.builtin.lineinfile:
path: /etc/subuid
regexp: "^{{ gitea_runner_service_user }}:"
line: "{{ gitea_runner_service_user }}:100000:65536"
create: true
mode: "0644"
- name: Ensure subgid entry for runner user
ansible.builtin.lineinfile:
path: /etc/subgid
regexp: "^{{ gitea_runner_service_user }}:"
line: "{{ gitea_runner_service_user }}:100000:65536"
create: true
mode: "0644"
- name: Ensure XDG_RUNTIME_DIR exists
ansible.builtin.file:
path: "/run/user/{{ gitea_runner_uid }}"
state: directory
owner: "{{ gitea_runner_service_user }}"
group: "{{ gitea_runner_service_user }}"
mode: "0700"
- name: Ensure runner data directory exists
ansible.builtin.file:
path: "{{ gitea_runner_data_dir }}"
state: directory
owner: "{{ gitea_runner_service_user }}"
group: "{{ gitea_runner_service_user }}"
mode: "0755"
- name: Ensure runner config directory exists
ansible.builtin.file:
path: "{{ gitea_runner_config_dir }}"
state: directory
owner: "{{ gitea_runner_service_user }}"
group: "{{ gitea_runner_service_user }}"
mode: "0755"
- name: Ensure systemd user directory exists
ansible.builtin.file:
path: "{{ gitea_runner_home }}/.config/systemd/user"
state: directory
owner: "{{ gitea_runner_service_user }}"
group: "{{ gitea_runner_service_user }}"
mode: "0755"
@@ -14,7 +14,12 @@
register: gitea_runner_version_output
changed_when: false
- name: Verify Docker connectivity
- name: Verify rootless Docker connectivity
ansible.builtin.command: docker version
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
DOCKER_HOST: "unix:///run/user/{{ gitea_runner_uid }}/docker.sock"
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
register: docker_version_output
changed_when: false
@@ -1,9 +1,9 @@
[Unit]
Description=Docker prune for Gitea runner resources
After=docker.service
Requires=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 }}
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 }}" --filter "until={{ gitea_runner_prune_until }}"
@@ -8,3 +8,4 @@ runner:
container:
label: "{{ gitea_runner_container_label }}"
docker_host: "unix:///run/user/{{ gitea_runner_uid }}/docker.sock"
@@ -0,0 +1,17 @@
[Unit]
Description=Gitea Actions Runner (rootless)
After=docker.service
[Service]
Type=simple
ExecStart={{ gitea_runner_binary_path }} daemon --config {{ gitea_runner_config_dir }}/config.yaml
WorkingDirectory={{ gitea_runner_data_dir }}
Environment=DOCKER_HOST=unix:///run/user/{{ gitea_runner_uid }}/docker.sock
Environment=XDG_RUNTIME_DIR=/run/user/{{ gitea_runner_uid }}
ExecStop=/bin/kill -TERM $MAINPID
TimeoutStopSec=30
Restart=on-failure
RestartSec={{ gitea_runner_service_restart_sec }}
[Install]
WantedBy=default.target
@@ -1,35 +0,0 @@
[Unit]
Description=Gitea Actions Runner %i ({% if runner_mode == 'docker' %}docker{% else %}binary{% endif %})
After=network.target{% if runner_mode == 'docker' %} docker.service
Requires=docker.service{% endif %}
[Service]
{% if runner_mode == 'docker' %}
Type=oneshot
RemainAfterExit=yes
ExecStartPre=-/usr/bin/docker rm -f gitea-runner-%i
ExecStart=/usr/bin/docker run -d --name gitea-runner-%i \
--restart=no \
-v /var/run/docker.sock:/var/run/docker.sock \
-v {{ gitea_runner_base_data_dir }}/%i:/data \
-w /data \
{{ gitea_runner_docker_image }}:{{ gitea_runner_version | regex_replace('^v', '') }} \
daemon --config /data/config.yaml
ExecStop=/usr/bin/docker stop -t 30 gitea-runner-%i
ExecStopPost=-/usr/bin/docker rm -f gitea-runner-%i
Restart=on-failure
RestartSec={{ gitea_runner_service_restart_sec }}
{% else %}
Type=simple
ExecStart={{ gitea_runner_binary_path }} daemon --config {{ gitea_runner_base_config_dir }}/%i/config.yaml
WorkingDirectory={{ gitea_runner_base_data_dir }}/%i
ExecStop=/bin/kill -TERM $MAINPID
TimeoutStopSec=30
Restart=on-failure
RestartSec={{ gitea_runner_service_restart_sec }}
User={{ gitea_runner_service_user }}
Group=docker
{% endif %}
[Install]
WantedBy=multi-user.target