diff --git a/.vale/styles/Google/Anthropomorphism.yml b/.vale/styles/Google/Anthropomorphism.yml new file mode 100644 index 0000000..36137a1 --- /dev/null +++ b/.vale/styles/Google/Anthropomorphism.yml @@ -0,0 +1,12 @@ +extends: existence +message: "Don't attribute human qualities to software or hardware ('%s')." +link: https://developers.google.com/style/anthropomorphism +level: suggestion +ignorecase: true +# Limited to the two verbs the guide itself names. Broader lists (wants, knows, +# thinks) can't tell a software subject from a human one: on a 950-file corpus +# they produced 8 false positives ('the customer wants', 'your audience knows') +# for every 2 real ones. +tokens: + - sees + - tells diff --git a/.vale/styles/Google/Colons.yml b/.vale/styles/Google/Colons.yml index 4a027c3..98972b9 100644 --- a/.vale/styles/Google/Colons.yml +++ b/.vale/styles/Google/Colons.yml @@ -1,8 +1,13 @@ extends: existence message: "'%s' should be in lowercase." link: 'https://developers.google.com/style/colons' -nonword: true level: warning scope: sentence +# The match is the word itself, not ': X', and `nonword` is off. Both are +# required for a project Vocab to work: Vale compares accept.txt entries +# against the matched text, and `nonword: true` opts out of that entirely. +# So a proper noun after a colon can be exempted by adding it to accept.txt. +# The guide's other exemption, notice labels, is handled by the lookbehinds; +# headings are already excluded by `scope: sentence`. See issue #20. tokens: - - '(?- + Healthcheck script uses 'prune -af' which removes ALL images + (including tagged runner images like ci-full). Use 'prune -f' + (dangling only) to preserve tagged images. diff --git a/ansible/roles/gitea_runner/tasks/integration_test.yml b/ansible/roles/gitea_runner/tasks/integration_test.yml index b0e65c3..36a8c39 100644 --- a/ansible/roles/gitea_runner/tasks/integration_test.yml +++ b/ansible/roles/gitea_runner/tasks/integration_test.yml @@ -18,7 +18,7 @@ else {} }} when: gitea_runner_file_stat.stat.exists | default(false) | bool -- name: Verify runner user service active +- name: Wait for runner user service to be active ansible.builtin.command: systemctl --user is-active gitea-runner become: true become_user: "{{ gitea_runner_service_user }}" @@ -26,6 +26,9 @@ XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}" register: gitea_runner_service_check changed_when: false + retries: 10 + delay: 2 + until: gitea_runner_service_check.stdout | default('') | trim == 'active' when: - gitea_runner_systemd_available.stat.exists - gitea_runner_docker_rootless_setup diff --git a/ansible/roles/gitea_runner/tasks/main.yml b/ansible/roles/gitea_runner/tasks/main.yml index 4973dd1..d887d03 100644 --- a/ansible/roles/gitea_runner/tasks/main.yml +++ b/ansible/roles/gitea_runner/tasks/main.yml @@ -17,6 +17,9 @@ - name: Include healthcheck setup ansible.builtin.include_tasks: healthcheck.yml +- name: Include pre-pull images + ansible.builtin.include_tasks: pre_pull_images.yml + - name: Include integration test ansible.builtin.include_tasks: integration_test.yml when: not gitea_runner_skip_registration diff --git a/ansible/roles/gitea_runner/tasks/pre_pull_images.yml b/ansible/roles/gitea_runner/tasks/pre_pull_images.yml new file mode 100644 index 0000000..f543928 --- /dev/null +++ b/ansible/roles/gitea_runner/tasks/pre_pull_images.yml @@ -0,0 +1,27 @@ +--- +# Pre-pull Docker images that CI runners need to avoid pulling them on +# every CI run. The runner container image (ci-full) is large (~3.3GB) +# and pulling it on every run causes timeouts and disk pressure. +# +# The healthcheck script's disk-space prune only removes dangling images +# (not tagged ones), so pre-pulled images persist between CI runs. +# +# Set gitea_runner_pre_pull_images to a list of image refs to pull, or +# empty list to skip pre-pulling. + +- name: Pre-pull Docker images for CI runner + ansible.builtin.command: "docker pull {{ item }}" + 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: 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 + delay: 5 + until: gitea_runner_pre_pull_result is success + loop: "{{ gitea_runner_pre_pull_images }}" + when: + - gitea_runner_docker_rootless_setup + - gitea_runner_pre_pull_images | length > 0 diff --git a/ansible/roles/gitea_runner/tasks/rootless_docker.yml b/ansible/roles/gitea_runner/tasks/rootless_docker.yml index ad17a6c..703cd8d 100644 --- a/ansible/roles/gitea_runner/tasks/rootless_docker.yml +++ b/ansible/roles/gitea_runner/tasks/rootless_docker.yml @@ -182,14 +182,16 @@ group: "{{ gitea_runner_service_user }}" when: gitea_runner_docker_rootless_setup -- name: Configure rootless Docker to use pasta with IPv6 +- name: Configure rootless Docker network driver ansible.builtin.copy: dest: "{{ gitea_runner_home }}/.config/systemd/user/docker.service.d/override.conf" 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 }}" @@ -207,14 +209,20 @@ - gitea_runner_docker_rootless_setup - gitea_runner_docker_network_override is changed -- name: Configure rootless Docker daemon with IPv6 enabled +- name: Configure rootless Docker daemon ansible.builtin.copy: dest: "{{ gitea_runner_home }}/.config/docker/daemon.json" content: | { + {% if gitea_runner_docker_rootless_net_driver == 'pasta' %} "ipv6": true, "ip6tables": true, - "fixed-cidr-v6": "{{ gitea_runner_docker_ipv6_cidr }}" + "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 }}" diff --git a/ansible/roles/gitea_runner/templates/runner-healthcheck.sh.j2 b/ansible/roles/gitea_runner/templates/runner-healthcheck.sh.j2 index da2a3f4..80bf83f 100644 --- a/ansible/roles/gitea_runner/templates/runner-healthcheck.sh.j2 +++ b/ansible/roles/gitea_runner/templates/runner-healthcheck.sh.j2 @@ -37,10 +37,10 @@ fi disk_pct=$(df -P / | awk 'NR==2 {gsub(/%/, "", $5); print $5}') if [[ "$disk_pct" -ge {{ gitea_runner_healthcheck_disk_threshold }} ]]; then echo "WARN: Disk usage at ${disk_pct}%, pruning all runner resources" - docker system prune -af --filter "label={{ gitea_runner_prune_label }}" --filter "until=1h" || true - docker volume prune -af --filter "label={{ gitea_runner_prune_label }}" || true - # Also prune dangling images (no label) - docker image prune -af || true + docker system prune -f --filter "label={{ gitea_runner_prune_label }}" --filter "until=1h" || true + 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 disk_pct=$(df -P / | awk 'NR==2 {gsub(/%/, "", $5); print $5}') echo "INFO: Disk usage after prune: ${disk_pct}%" fi diff --git a/pyproject.toml b/pyproject.toml index 5b31e48..5fc0e62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ ci = [ "build==1.5.1", "twine==6.2.0", # Reusable CI/CD and dev tools (auto-merge, pr-review, pre-push checks, etc.) - "devx @ git+https://git.oblachno.oblachno.fyi/oblachno-oss/devx.git@v0.47.2", + "devx @ git+https://git.oblachno.oblachno.fyi/oblachno-oss/devx.git@v0.47.8", ] # Lint and type-checking tools (validate job) lint = [ @@ -55,7 +55,7 @@ molecule = [ dev = [ "grm[ci,lint,molecule]", # Reusable CI/CD and dev tools (pre-push hooks, create-task, create-pr) - "devx @ git+https://git.oblachno.oblachno.fyi/oblachno-oss/devx.git@v0.47.2", + "devx @ git+https://git.oblachno.oblachno.fyi/oblachno-oss/devx.git@v0.47.8", # Non-Python dev dependency: checkmake (Makefile linter) # Install via: go install github.com/checkmake/checkmake/cmd/checkmake@latest ]