From d0d28657f5582896e4a66ecaf36eb3bc13e5eb5d Mon Sep 17 00:00:00 2001 From: Emil Simeonov Date: Mon, 3 Aug 2026 18:01:09 +0200 Subject: [PATCH 1/9] GRM-2: Bump devx to v0.48.0 - Update devx dependency to v0.48.0 - Add git auth to setup-image for git+https deps - Fix Vikunja project ID (6 -> 3) Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .gitea/workflows/ci.yml | 4 ++-- .gitea/workflows/post-merge.yml | 4 ++-- Makefile | 2 +- pyproject.toml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 91d2437..a7f4947 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -98,7 +98,7 @@ jobs: env: VIKUNJA_TOKEN: ${{ secrets.VIKUNJA_TOKEN }} DEVX_TASK_PREFIX: GRM - DEVX_VIKUNJA_PROJECT_ID: 6 + DEVX_VIKUNJA_PROJECT_ID: 3 HEAD_REF: ${{ github.head_ref }} PR_TITLE: ${{ github.event.pull_request.title }} REPOSITORY: ${{ github.repository }} @@ -255,7 +255,7 @@ jobs: CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} VIKUNJA_TOKEN: ${{ secrets.VIKUNJA_TOKEN }} DEVX_TASK_PREFIX: GRM - DEVX_VIKUNJA_PROJECT_ID: 6 + DEVX_VIKUNJA_PROJECT_ID: 3 HEAD_REF: ${{ github.head_ref }} PR_TITLE: ${{ github.event.pull_request.title }} REPOSITORY: ${{ github.repository }} diff --git a/.gitea/workflows/post-merge.yml b/.gitea/workflows/post-merge.yml index 7db9d88..6e5afe1 100644 --- a/.gitea/workflows/post-merge.yml +++ b/.gitea/workflows/post-merge.yml @@ -133,7 +133,7 @@ jobs: env: DEVX_VERSION_FILE: src/grm/__init__.py DEVX_TASK_PREFIX: GRM - DEVX_VIKUNJA_PROJECT_ID: 6 + DEVX_VIKUNJA_PROJECT_ID: 3 run: | . .venv/bin/activate 2>/dev/null || true export PATH="$HOME/.local/bin:$PATH" @@ -161,7 +161,7 @@ jobs: env: VIKUNJA_TOKEN: ${{ secrets.VIKUNJA_TOKEN }} DEVX_TASK_PREFIX: GRM - DEVX_VIKUNJA_PROJECT_ID: 6 + DEVX_VIKUNJA_PROJECT_ID: 3 run: | . .venv/bin/activate 2>/dev/null || true python3 -m devx.ci.post_merge --git-sha "${{ github.sha }}" diff --git a/Makefile b/Makefile index 79a8886..5aa6cea 100644 --- a/Makefile +++ b/Makefile @@ -88,7 +88,7 @@ setup-release: $(VENV)/bin/activate .env configure-gitea-pypi setup-image: @if [ -d /opt/venv ]; then ln -sf /opt/venv .venv; . .venv/bin/activate; \ _TOKEN="$$CI_GITEA_API_TOKEN"; [ -z "$$_TOKEN" ] && _TOKEN="$$DEVELOPER_GITEA_API_TOKEN"; [ -z "$$_TOKEN" ] && _TOKEN="$$CI_GITEA_TOKEN"; \ - if [ -n "$$_TOKEN" ]; then export PIP_EXTRA_INDEX_URL="https://$$CI_GITEA_USERNAME:$${_TOKEN}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"; fi; \ + if [ -n "$$_TOKEN" ]; then export PIP_EXTRA_INDEX_URL="https://$$CI_GITEA_USERNAME:$${_TOKEN}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"; git config --global url."https://$$CI_GITEA_USERNAME:$${_TOKEN}@git.oblachno.oblachno.fyi/".insteadOf "https://git.oblachno.oblachno.fyi/"; fi; \ pip install -e .$(if $(EXTRAS),[$(EXTRAS)],); \ else echo "[setup-image] /opt/venv not found — falling back to setup-ci"; $(MAKE) setup-ci; fi diff --git a/pyproject.toml b/pyproject.toml index 5b31e48..a2d2b81 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.48.0", ] # 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.48.0", # Non-Python dev dependency: checkmake (Makefile linter) # Install via: go install github.com/checkmake/checkmake/cmd/checkmake@latest ] -- 2.54.0 From 7cf6ce9afb8af36d631a5bbaec2daa1da20d9f33 Mon Sep 17 00:00:00 2001 From: Emil Simeonov Date: Tue, 4 Aug 2026 10:28:02 +0200 Subject: [PATCH 2/9] fix: switch default network driver to slirp4netns (pasta TCP RST bug) pasta's TCP proxy sends RST packets with wrong sequence numbers after the first data packet, breaking TCP connections from Docker containers to external hosts. slirp4netns doesn't have this bug. Changes: - Default gitea_runner_docker_rootless_net_driver changed to slirp4netns - Port driver: 'implicit' for pasta, 'builtin' for slirp4netns - daemon.json: DNS configured per driver (10.0.2.3 for pasta, 8.8.8.8 for slirp4netns) - IPv6 only enabled for pasta (slirp4netns doesn't support IPv6) - integration_test: retry service active check (handles slow startup) --- .vale/styles/Google/Anthropomorphism.yml | 12 ++++ .vale/styles/Google/Colons.yml | 9 ++- .vale/styles/Google/DateFormat.yml | 2 +- .vale/styles/Google/ExcessiveClaims.yml | 14 ++++ .vale/styles/Google/FirstPerson.yml | 10 +-- .vale/styles/Google/Headings.yml | 7 +- .vale/styles/Google/Jargon.yml | 13 ++++ .vale/styles/Google/Latin.yml | 8 ++- .vale/styles/Google/OxfordComma.yml | 10 ++- .vale/styles/Google/Parens.yml | 10 ++- .vale/styles/Google/Timeless.yml | 13 ++++ .vale/styles/Google/Units.yml | 6 +- .vale/styles/Google/WordList.yml | 57 +--------------- .vale/styles/Google/WordListCase.yml | 68 +++++++++++++++++++ ansible/roles/gitea_runner/defaults/main.yml | 9 ++- .../gitea_runner/tasks/integration_test.yml | 5 +- .../gitea_runner/tasks/rootless_docker.yml | 16 +++-- 17 files changed, 192 insertions(+), 77 deletions(-) create mode 100644 .vale/styles/Google/Anthropomorphism.yml create mode 100644 .vale/styles/Google/ExcessiveClaims.yml create mode 100644 .vale/styles/Google/Jargon.yml create mode 100644 .vale/styles/Google/Timeless.yml create mode 100644 .vale/styles/Google/WordListCase.yml 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: - - '(? Date: Tue, 4 Aug 2026 13:54:18 +0200 Subject: [PATCH 3/9] fix: healthcheck prune keeps tagged runner images The healthcheck script used 'docker image prune -af' which removes ALL unused images including tagged ones like ci-full:latest. When disk usage hit 85%, the healthcheck would delete the ci-full image, causing the next CI run to fail with 'No such image'. Fix: use 'docker image prune -f' (dangling only) and 'docker system prune -f' (without -a) to preserve tagged images. Also fix 'docker volume prune -f' (without -a) for consistency. --- .../roles/gitea_runner/templates/runner-healthcheck.sh.j2 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 -- 2.54.0 From 7215a5941231a591e626bfe545df5a33d6f70c5a Mon Sep 17 00:00:00 2001 From: Emil Simeonov Date: Tue, 4 Aug 2026 13:54:18 +0200 Subject: [PATCH 4/9] fix: healthcheck prune keeps tagged runner images The healthcheck script used 'docker image prune -af' which removes ALL unused images including tagged ones like ci-full:latest. When disk usage hit 85%, the healthcheck would delete the ci-full image, causing the next CI run to fail with 'No such image'. Fix: use 'docker image prune -f' (dangling only) and 'docker system prune -f' (without -a) to preserve tagged images. Also fix 'docker volume prune -f' (without -a) for consistency. --- ansible/roles/gitea_runner/defaults/main.yml | 6 +++++ .../molecule/template-content/verify.yml | 12 +++++++++ ansible/roles/gitea_runner/tasks/main.yml | 3 +++ .../gitea_runner/tasks/pre_pull_images.yml | 27 +++++++++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 ansible/roles/gitea_runner/tasks/pre_pull_images.yml diff --git a/ansible/roles/gitea_runner/defaults/main.yml b/ansible/roles/gitea_runner/defaults/main.yml index e6ec8a5..08bb745 100644 --- a/ansible/roles/gitea_runner/defaults/main.yml +++ b/ansible/roles/gitea_runner/defaults/main.yml @@ -73,3 +73,9 @@ gitea_runner_docker_rootless_net_driver: "slirp4netns" # IPv6 subnet for rootless Docker containers (ULA range, not routable on internet) gitea_runner_docker_ipv6_cidr: "fd00:dead:beef::/48" + +# Pre-pull Docker images that CI runners need (avoids pulling on every CI run). +# The runner container image (ci-full) is large (~3.3GB) and the healthcheck's +# 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: [] diff --git a/ansible/roles/gitea_runner/molecule/template-content/verify.yml b/ansible/roles/gitea_runner/molecule/template-content/verify.yml index 6c91b73..a2c0558 100644 --- a/ansible/roles/gitea_runner/molecule/template-content/verify.yml +++ b/ansible/roles/gitea_runner/molecule/template-content/verify.yml @@ -104,3 +104,15 @@ - "'docker system prune' 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" + + - name: Assert healthcheck script does NOT use aggressive prune (-af) + ansible.builtin.assert: + that: + - "'prune -af' not in healthcheck_script.content | b64decode" + - "'image prune -af' not in healthcheck_script.content | b64decode" + - "'system prune -af' not in healthcheck_script.content | b64decode" + - "'volume prune -af' not in healthcheck_script.content | b64decode" + fail_msg: >- + 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/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 -- 2.54.0 From 1da8900bc902347540d0b245b98b111c53a1d7e9 Mon Sep 17 00:00:00 2001 From: Emil Simeonov Date: Tue, 4 Aug 2026 14:40:33 +0200 Subject: [PATCH 5/9] fix: downgrade devx to v0.47.8 (v0.48.0 not yet released) --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a2d2b81..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.48.0", + "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.48.0", + "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 ] -- 2.54.0 From 565236f5b552fefbb56da8f16a61cd41ddd3a6a4 Mon Sep 17 00:00:00 2001 From: Emil Simeonov Date: Tue, 4 Aug 2026 14:44:44 +0200 Subject: [PATCH 6/9] chore: re-trigger CI after VIKUNJA_TOKEN update -- 2.54.0 From a2c8cae897f539d9bbbe08d35fdf6f3f45bec04e Mon Sep 17 00:00:00 2001 From: Emil Simeonov Date: Tue, 4 Aug 2026 15:10:58 +0200 Subject: [PATCH 7/9] fix: use correct Vikunja project ID (6 for grm, not 3 for infra) The grm workflows were using DEVX_VIKUNJA_PROJECT_ID=3 (infra project) instead of 6 (grm project). This caused pre-merge validation to fail with 'Could not find Vikunja task GRM-2 in project 3'. --- .gitea/workflows/ci.yml | 4 ++-- .gitea/workflows/post-merge.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index a7f4947..91d2437 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -98,7 +98,7 @@ jobs: env: VIKUNJA_TOKEN: ${{ secrets.VIKUNJA_TOKEN }} DEVX_TASK_PREFIX: GRM - DEVX_VIKUNJA_PROJECT_ID: 3 + DEVX_VIKUNJA_PROJECT_ID: 6 HEAD_REF: ${{ github.head_ref }} PR_TITLE: ${{ github.event.pull_request.title }} REPOSITORY: ${{ github.repository }} @@ -255,7 +255,7 @@ jobs: CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} VIKUNJA_TOKEN: ${{ secrets.VIKUNJA_TOKEN }} DEVX_TASK_PREFIX: GRM - DEVX_VIKUNJA_PROJECT_ID: 3 + DEVX_VIKUNJA_PROJECT_ID: 6 HEAD_REF: ${{ github.head_ref }} PR_TITLE: ${{ github.event.pull_request.title }} REPOSITORY: ${{ github.repository }} diff --git a/.gitea/workflows/post-merge.yml b/.gitea/workflows/post-merge.yml index 6e5afe1..7db9d88 100644 --- a/.gitea/workflows/post-merge.yml +++ b/.gitea/workflows/post-merge.yml @@ -133,7 +133,7 @@ jobs: env: DEVX_VERSION_FILE: src/grm/__init__.py DEVX_TASK_PREFIX: GRM - DEVX_VIKUNJA_PROJECT_ID: 3 + DEVX_VIKUNJA_PROJECT_ID: 6 run: | . .venv/bin/activate 2>/dev/null || true export PATH="$HOME/.local/bin:$PATH" @@ -161,7 +161,7 @@ jobs: env: VIKUNJA_TOKEN: ${{ secrets.VIKUNJA_TOKEN }} DEVX_TASK_PREFIX: GRM - DEVX_VIKUNJA_PROJECT_ID: 3 + DEVX_VIKUNJA_PROJECT_ID: 6 run: | . .venv/bin/activate 2>/dev/null || true python3 -m devx.ci.post_merge --git-sha "${{ github.sha }}" -- 2.54.0 From 82dd058a3d2d260821a9cb032df75b768072290a Mon Sep 17 00:00:00 2001 From: Emil Simeonov Date: Tue, 4 Aug 2026 15:32:43 +0200 Subject: [PATCH 8/9] chore: re-trigger CI after PR title fix -- 2.54.0 From 8d7e2fc404013a958e06b30b8fd31c04755dd163 Mon Sep 17 00:00:00 2001 From: Emil Simeonov Date: Tue, 4 Aug 2026 15:38:17 +0200 Subject: [PATCH 9/9] chore: re-trigger CI after setting CI_GITEA_USERNAME + MOLECULE_RUNNERS -- 2.54.0