Compare commits

...
49 Commits
Author SHA1 Message Date
grm-ci-bot c339698603 release: v0.10.2
Post-merge / detect-type (push) Successful in 1m2s
Post-merge / validate-commit-msg (push) Has been skipped
Post-merge / release (push) Has been skipped
Post-merge / sync-wiki (push) Has been skipped
Post-merge / publish (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Post-merge / configure-repo (push) Has been skipped
Post-merge / badges (push) Successful in 57s
2026-06-27 16:31:58 +00:00
emil cbf082c78f GRM-102: fix: bump devx>=0.22.0 and remove REPO_TOKEN alias
Post-merge / detect-type (push) Successful in 48s
Post-merge / release (push) Successful in 1m0s
Post-merge / validate-commit-msg (push) Successful in 1m2s
Post-merge / vikunja (push) Successful in 1m0s
Post-merge / badges (push) Successful in 1m9s
Post-merge / sync-wiki (push) Successful in 1m38s
Post-merge / configure-repo (push) Successful in 1m3s
Post-merge / publish (push) Successful in 55s
2026-06-27 16:30:16 +00:00
gitea-actions-bot 4070135fda chore: update badge URLs to commit 5c243201 [skip ci] 2026-06-27 15:51:35 +00:00
emil ace0176e3a GRM-101: refactor: rename REPO_TOKEN to CI_GITEA_TOKEN, consolidate env vars
Post-merge / detect-type (push) Successful in 47s
Post-merge / release (push) Failing after 14s
Post-merge / validate-commit-msg (push) Successful in 59s
Post-merge / vikunja (push) Successful in 56s
Post-merge / publish (push) Has been skipped
Post-merge / configure-repo (push) Successful in 48s
Post-merge / badges (push) Successful in 1m8s
Post-merge / sync-wiki (push) Successful in 1m14s
2026-06-27 15:49:36 +00:00
gitea-actions-bot fda1d99d86 chore: update badge URLs to commit 42a9384e [skip ci] 2026-06-27 13:49:17 +00:00
emil 465f45d939 GRM-100: fix: gate auto-merge on release-dry-run and unmask failures
Post-merge / detect-type (push) Successful in 42s
Post-merge / release (push) Failing after 11s
Post-merge / validate-commit-msg (push) Successful in 48s
Post-merge / publish (push) Has been skipped
Post-merge / vikunja (push) Successful in 41s
Post-merge / badges (push) Successful in 56s
Post-merge / sync-wiki (push) Successful in 1m16s
Post-merge / configure-repo (push) Successful in 40s
2026-06-27 13:47:30 +00:00
gitea-actions-bot 103beaa0e8 chore: update badge URLs to commit 6b1270e1 [skip ci] 2026-06-27 13:30:20 +00:00
emil 2e97578269 GRM-99: fix: setup-image configures Gitea PyPI registry and shows pip errors
Post-merge / detect-type (push) Successful in 42s
Post-merge / release (push) Failing after 14s
Post-merge / validate-commit-msg (push) Successful in 53s
Post-merge / vikunja (push) Successful in 52s
Post-merge / publish (push) Has been skipped
Post-merge / configure-repo (push) Successful in 54s
Post-merge / badges (push) Successful in 1m16s
Post-merge / sync-wiki (push) Successful in 1m25s
2026-06-27 13:27:59 +00:00
gitea-actions-bot c31ec312ac chore: update badge URLs to commit b3f0d6da [skip ci] 2026-06-27 13:16:12 +00:00
emil c67b810e58 GRM-98: ci: add --auto-login to publish, bump devx>=0.21.0
Post-merge / release (push) Failing after 11s
Post-merge / detect-type (push) Successful in 45s
Post-merge / validate-commit-msg (push) Successful in 52s
Post-merge / publish (push) Has been skipped
Post-merge / vikunja (push) Successful in 52s
Post-merge / configure-repo (push) Successful in 51s
Post-merge / badges (push) Successful in 1m13s
Post-merge / sync-wiki (push) Successful in 1m14s
2026-06-27 13:14:13 +00:00
gitea-actions-bot ef16b07cdf chore: update badge URLs to commit 9addc544 [skip ci] 2026-06-27 12:34:11 +00:00
emil 2c849c7324 GRM-97: ci: use pre-built tier images for all CI workflows
Post-merge / detect-type (push) Successful in 37s
Post-merge / validate-commit-msg (push) Successful in 57s
Post-merge / release (push) Successful in 48s
Post-merge / publish (push) Has been skipped
Post-merge / vikunja (push) Successful in 46s
Post-merge / badges (push) Successful in 1m9s
Post-merge / configure-repo (push) Successful in 41s
Post-merge / sync-wiki (push) Successful in 1m11s
2026-06-27 12:32:13 +00:00
gitea-actions-bot 5804a18974 chore: update badge URLs to commit a7e1f518 [skip ci] 2026-06-27 00:24:48 +00:00
gitea-actions-bot 9dbe20ba73 chore: update badge URLs to commit 63466f88 [skip ci] 2026-06-27 00:21:48 +00:00
grm-ci-bot b92c87ba68 release: v0.10.1
Post-merge / detect-type (push) Successful in 1m58s
Post-merge / validate-commit-msg (push) Has been skipped
Post-merge / release (push) Has been skipped
Post-merge / sync-wiki (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Post-merge / configure-repo (push) Has been skipped
Post-merge / publish (push) Has been skipped
Post-merge / badges (push) Successful in 1m23s
2026-06-27 02:21:19 +02:00
emil dc4430d160 GRM-96: ci: add molecule test weights to pyproject.toml
Post-merge / detect-type (push) Successful in 1m12s
Post-merge / release (push) Successful in 1m38s
Post-merge / validate-commit-msg (push) Successful in 1m39s
Post-merge / badges (push) Successful in 2m8s
Post-merge / vikunja (push) Successful in 2m12s
Post-merge / sync-wiki (push) Successful in 2m48s
Post-merge / configure-repo (push) Successful in 1m30s
Post-merge / publish (push) Successful in 1m27s
2026-06-27 00:18:31 +00:00
gitea-actions-bot 7aa0ebaefe chore: update badge URLs to commit 182f11ca [skip ci] 2026-06-26 19:42:47 +00:00
emil e93da43219 GRM-95: refactor: consolidate publish.yml into post-merge.yml
Post-merge / detect-type (push) Successful in 1m14s
Post-merge / badges (push) Successful in 1m45s
Post-merge / vikunja (push) Successful in 1m42s
Post-merge / configure-repo (push) Successful in 1m41s
Post-merge / validate-commit-msg (push) Successful in 2m1s
Post-merge / release (push) Successful in 2m19s
Post-merge / sync-wiki (push) Successful in 2m29s
Post-merge / publish (push) Has been skipped
2026-06-26 19:39:50 +00:00
gitea-actions-bot b131a2872d chore: update badge URLs to commit 886112ce [skip ci] 2026-06-26 19:17:41 +00:00
emil e4dd8f308f GRM-94: refactor: replace duplicated Makefile targets with devx.mak aliases
Post-merge / detect-type (push) Successful in 1m12s
Post-merge / release (push) Successful in 1m29s
Post-merge / validate-commit-msg (push) Successful in 1m47s
Post-merge / badges (push) Successful in 1m54s
Post-merge / vikunja (push) Successful in 1m56s
Post-merge / sync-wiki (push) Successful in 2m4s
Post-merge / configure-repo (push) Successful in 1m19s
2026-06-26 19:14:33 +00:00
gitea-actions-bot 467e0d66e6 chore: update badge URLs to commit c2d3c4bb [skip ci] 2026-06-26 18:06:12 +00:00
emil 6ee5b74bb5 GRM-93: ci: use make setup-ci consistently, decouple vikunja/sync-wiki from release
Post-merge / detect-type (push) Successful in 1m18s
Post-merge / validate-commit-msg (push) Successful in 1m45s
Post-merge / badges (push) Successful in 1m53s
Post-merge / vikunja (push) Successful in 1m54s
Post-merge / release (push) Successful in 2m13s
Post-merge / configure-repo (push) Successful in 1m53s
Post-merge / sync-wiki (push) Successful in 2m29s
2026-06-26 18:03:02 +00:00
gitea-actions-bot 21cc89899f chore: update badge URLs to commit c29fbfe0 [skip ci] 2026-06-26 15:54:42 +00:00
emil 0382e155a6 GRM-92: fix: set PYTHONPATH=src in publish Install CI tools step
Post-merge / detect-type (push) Successful in 18s
Post-merge / validate-commit-msg (push) Successful in 17s
Post-merge / configure-repo (push) Successful in 40s
Post-merge / release (push) Successful in 1m48s
Post-merge / vikunja (push) Successful in 7s
Post-merge / badges (push) Successful in 1m25s
Post-merge / sync-wiki (push) Successful in 1m36s
2026-06-26 15:51:06 +00:00
gitea-actions-bot d87c0d7e9a chore: update badge URLs to commit b19d4b06 [skip ci] 2026-06-26 17:18:30 +02:00
gitea-actions-bot 4be480a18e chore: update badge URLs to commit f916dabb [skip ci] 2026-06-26 15:18:03 +00:00
grm-ci-bot de92f675ed release: v0.10.0
Post-merge / detect-type (push) Successful in 13s
Post-merge / validate-commit-msg (push) Has been skipped
Post-merge / release (push) Has been skipped
Post-merge / configure-repo (push) Has been skipped
Post-merge / sync-wiki (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Publish Release / publish (push) Failing after 16s
Post-merge / badges (push) Successful in 1m55s
2026-06-26 17:16:19 +02:00
emil b5803a8611 GRM-91: feat: adopt devx tools, devx.mak fragment, ci extra, remove legacy install-devx
Post-merge / detect-type (push) Successful in 10s
Post-merge / validate-commit-msg (push) Successful in 10s
Post-merge / configure-repo (push) Successful in 12s
Post-merge / release (push) Successful in 1m41s
Post-merge / vikunja (push) Successful in 12s
Post-merge / badges (push) Successful in 1m37s
Post-merge / sync-wiki (push) Successful in 2m0s
2026-06-26 15:14:33 +00:00
gitea-actions-bot ec22d20a15 chore: update badge URLs to commit 47c7e239 [skip ci] 2026-06-26 12:03:12 +02:00
emil e96012af7f GRM-90: ci: bump devx version to v0.14.1
Post-merge / detect-type (push) Successful in 9s
Post-merge / validate-commit-msg (push) Successful in 6s
Post-merge / configure-repo (push) Successful in 7s
Post-merge / release (push) Successful in 1m29s
Post-merge / vikunja (push) Successful in 13s
Post-merge / badges (push) Successful in 1m23s
Post-merge / sync-wiki (push) Successful in 2m0s
2026-06-26 10:00:03 +00:00
gitea-actions-bot addef7500c chore: update badge URLs to commit dd820c3d [skip ci] 2026-06-26 00:13:49 +02:00
emil 8a0d2c428b GRM-88: docs: fix AGENTS.md squash-merge format to use colon after task ID
Post-merge / detect-type (push) Successful in 10s
Post-merge / validate-commit-msg (push) Successful in 12s
Post-merge / configure-repo (push) Successful in 10s
Post-merge / release (push) Failing after 1m16s
Post-merge / sync-wiki (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Post-merge / badges (push) Successful in 1m8s
2026-06-25 22:11:11 +00:00
gitea-actions-bot d68fb6d272 chore: update badge URLs to commit 9b029d3a [skip ci] 2026-06-25 23:17:41 +02:00
emil 6721864b87 GRM-87: fix: always run publish in post-merge (idempotent) 2026-06-25 21:15:03 +00:00
gitea-actions-bot 06471d1403 chore: update badge URLs to commit c61f4cb6 [skip ci] 2026-06-25 01:23:53 +02:00
grm-ci-bot 1a04971622 release: v0.9.0 [skip ci] 2026-06-25 01:22:18 +02:00
emil 91440c1b0a GRM-86: feat: add Polish as officially supported language 2026-06-24 23:20:59 +00:00
gitea-actions-bot 3bb8d75748 chore: update badge URLs to commit ea0b5002 [skip ci] 2026-06-24 22:53:41 +00:00
emil d4a8172a25 GRM-85: docs: add Gitea PyPI registry instructions for pip install 2026-06-24 22:51:10 +00:00
gitea-actions-bot 2199ec0bfe chore: update badge URLs to commit b70a95f6 [skip ci] 2026-06-25 00:42:14 +02:00
emil 9ae9a76b11 GRM-84: feat: adopt devx v0.11.1 across Makefile and workflows 2026-06-24 22:39:35 +00:00
gitea-actions-bot 31d0eeae98 chore: update badge URLs to commit eaa85dd7 [skip ci] 2026-06-25 00:31:14 +02:00
grm-ci-bot acc768eaea release: v0.8.1 [skip ci] 2026-06-25 00:29:27 +02:00
emil cf2314c20f GRM-83: fix: add build/twine to ci deps, activate venv in notify_failure 2026-06-24 22:28:08 +00:00
gitea-actions-bot aac47e3472 chore: update badge URLs to commit a7a72d7d [skip ci] 2026-06-25 00:14:31 +02:00
emil 811e7a2309 GRM-82: fix: repair publish workflow and add publish step to post-merge 2026-06-24 22:11:48 +00:00
gitea-actions-bot cb68c85bb5 chore: update badge URLs to commit 123065d0 [skip ci] 2026-06-24 21:06:40 +00:00
emil 0ba30e09ea GRM-81: ci: update devx to v0.10.2 for badge testpaths fix 2026-06-24 21:03:53 +00:00
gitea-actions-bot 6a02581687 chore: update badge URLs to commit e32f1c05 [skip ci] 2026-06-24 22:45:35 +02:00
26 changed files with 672 additions and 386 deletions
+5 -1
View File
@@ -15,7 +15,7 @@ GITEA_REGISTRATION_TOKEN=your-registration-token
# If set, API checks are performed as a bonus but do NOT affect pass/fail.
# Required scopes: read:user, read:repository, read:admin (or just "admin")
# Generate token at: Settings → Applications → Generate New Token
# REPO_TOKEN=your-admin-api-token
# CI_GITEA_TOKEN=your-admin-api-token
# Integration test API retries (optional, default: 3).
# Number of times to retry API checks waiting for runner to appear.
@@ -37,6 +37,10 @@ GITEA_REGISTRATION_TOKEN=your-registration-token
# Supported: en, bg, de, ru, zh
# GRM_LANG=en
# Gitea PyPI registry username (for private package access)
# Used by PIP_INSTALL to configure PIP_EXTRA_INDEX_URL
CI_GITEA_USERNAME=emil
# devx configuration (GRM-specific overrides)
# Task prefix for Vikunja task IDs
DEVX_TASK_PREFIX=GRM
+58 -32
View File
@@ -8,13 +8,15 @@ on:
jobs:
quality:
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-quality:latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Set up environment
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: make setup-quality
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image EXTRAS=lint
- name: Lint all
run: |
. .venv/bin/activate
@@ -24,6 +26,10 @@ jobs:
run: |
. .venv/bin/activate
make pytest-cov
- name: Translation completeness check
run: |
. .venv/bin/activate
python3 -m devx.ci.check_translations --translations src/gitea_runner_manager/translations.json
- name: Check unit test speed
env:
PYTHONPATH: src
@@ -52,6 +58,7 @@ jobs:
needs: [quality, detect-changes]
if: needs.detect-changes.outputs.user-facing-changed == 'true'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-full:latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
@@ -59,8 +66,9 @@ jobs:
fetch-depth: 0
- name: Set up environment
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: make setup-release
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image EXTRAS=ci,lint
- name: Release dry-run validation
env:
PYTHONPATH: src
@@ -69,10 +77,11 @@ jobs:
run: |
. .venv/bin/activate
export PATH="$HOME/.local/bin:$PATH"
python3 -m devx.ci.release --dry-run || true
python3 -m devx.ci.release --dry-run
detect-changes:
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
outputs:
ansible-changed: ${{ steps.detect.outputs.ansible-changed }}
@@ -83,8 +92,9 @@ jobs:
fetch-depth: 0
- name: Set up environment
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: make setup-ci
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image
- name: Detect changed paths
id: detect
env:
@@ -101,6 +111,7 @@ jobs:
needs: [detect-changes]
if: needs.detect-changes.outputs.ansible-changed == 'true'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
outputs:
runner-count: ${{ steps.discover.outputs.runner-count }}
@@ -109,12 +120,13 @@ jobs:
- uses: actions/checkout@v4
- name: Set up environment
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: make setup-ci
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image
- name: Discover available runners
id: discover
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
MOLECULE_RUNNERS: ${{ vars.MOLECULE_RUNNERS }}
PYTHONPATH: src
run: |
@@ -128,6 +140,7 @@ jobs:
needs: [quality, detect-changes, discover-runners]
if: needs.detect-changes.outputs.ansible-changed == 'true'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-full:latest
timeout-minutes: 10
strategy:
matrix:
@@ -136,8 +149,13 @@ jobs:
- uses: actions/checkout@v4
- name: Set up environment
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: make setup-molecule
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image EXTRAS=ci,molecule
- name: Install Ansible collections
run: |
. .venv/bin/activate
python3 -m devx.tools.setup --skip-install --no-pre-commit --no-tea-login
- name: Discover assigned test pairs
env:
RUNNER_INDEX: ${{ matrix.runner-index }}
@@ -158,7 +176,7 @@ jobs:
python3 -m devx.molecule.molecule_ci_guard $TEST_PAIRS
env:
GITEA_URL: ${{ github.server_url }}
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
RUN_ID: ${{ github.run_id }}
JOB_NAME: ${{ github.job }}
MATRIX_INDEX: ${{ matrix.runner-index }}
@@ -168,54 +186,61 @@ jobs:
pr-review:
if: github.event_name == 'pull_request'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
. .env 2>/dev/null || true
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
- name: Set up environment
env:
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image
- name: Run automated PR review
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
PYTHONPATH: src
run: |
set -euo pipefail
. .venv/bin/activate
python3 -m devx.ci.pr_review \
"${{ github.event.number }}" \
"${{ github.repository }}"
auto-merge:
# Auto-merge runs after all CI checks pass. It reads the task ID
# from the branch name (falling back to .taskid file), validates
# the PR title, and squash-merges.
# from the branch name, validates the PR title, and squash-merges.
# Uses always() so it evaluates even when molecule-tests is skipped
# (Gitea Actions skips dependent jobs of skipped jobs by default).
needs: [quality, detect-changes, pr-review, molecule-tests]
needs: [quality, detect-changes, pr-review, molecule-tests, release-dry-run]
if: >-
always() &&
github.event_name == 'pull_request' &&
needs.quality.result == 'success' &&
needs.pr-review.result == 'success' &&
(needs.molecule-tests.result == 'success' || needs.molecule-tests.result == 'skipped')
(needs.molecule-tests.result == 'success' || needs.molecule-tests.result == 'skipped') &&
(needs.release-dry-run.result == 'success' || needs.release-dry-run.result == 'skipped')
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.REPO_TOKEN }}
- name: Install dependencies
run: |
. .env 2>/dev/null || true
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
token: ${{ secrets.CI_GITEA_TOKEN }}
- name: Set up environment
env:
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image
- name: Squash merge with task ID
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
VIKUNJA_TOKEN: ${{ secrets.VIKUNJA_TOKEN }}
PYTHONPATH: src
DEVX_TASK_PREFIX: GRM
@@ -225,6 +250,7 @@ jobs:
REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ github.event.number }}
run: |
. .venv/bin/activate
python3 -m devx.ci.auto_merge \
"$HEAD_REF" \
"$PR_TITLE" \
+119 -67
View File
@@ -1,30 +1,30 @@
name: Post-merge
# Runs on every push to master. A single workflow with conditional jobs
# replaces the previous 4 separate workflows (release.yml, post-merge.yml,
# sync-wiki.yml, and the badges job from ci.yml).
# for release, publish, wiki sync, badges, and Vikunja task updates.
#
# Job dependency graph:
#
# detect-type ──┬── release (skip if release commit)
# detect-type ──┬── validate-commit-msg (skip if release commit)
# ├── release (skip if release commit)
# │ └── publish (needs release — builds & publishes to PyPI)
# ├── badges (ALWAYS runs — even on release commits)
# ├── configure-repo (independent — skip if release commit)
# ├── sync-wiki (needs release — skip if release commit/fails)
# └── vikunja (needs release — skip if release commit/fails)
# ├── sync-wiki (skip if release commit — runs for ALL merges)
# └── vikunja (skip if release commit — runs for ALL merges)
#
# sync-wiki and vikunja depend on release succeeding so that the wiki
# and task tracker are only updated when the code is actually released.
# If release fails, they are skipped to avoid leaving the wiki or
# Vikunja in an inconsistent state with the codebase on master.
# sync-wiki and vikunja run for ALL non-release commits, not just when
# release succeeds. This ensures the wiki and task tracker are updated
# even for infrastructure-only changes (docs, CI config, etc.).
#
# The badges job depends on release so it picks up the latest version
# number. It uses `if: always()` with no is-release condition so it
# runs on every push to master, including release commits. This
# ensures badges (tests, coverage, version, etc.) are always current.
# The badges job uses `if: always()` with no is-release condition so it
# runs on every push to master, including release commits. This ensures
# badges (tests, coverage, version, etc.) are always current.
#
# When release.py creates a "release: vX.Y.Z" commit, the release
# commit's post-merge run still updates badges (version badge picks
# up the new version). Other jobs skip. The tag push triggers publish.yml.
# When release creates a "release: vX.Y.Z" commit and tag, the publish
# job (which depends on release) builds and publishes the package to the
# Gitea PyPI registry. The release commit's post-merge run still updates
# badges (version badge picks up the new version). Other jobs skip.
on:
push:
@@ -33,6 +33,7 @@ on:
jobs:
detect-type:
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
outputs:
is-release: ${{ steps.check.outputs.is-release }}
@@ -40,38 +41,40 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Install dependencies
run: |
. .env 2>/dev/null || true
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
- name: Set up environment
env:
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image
- name: Check if this is a release commit
id: check
env:
PYTHONPATH: src
run: python3 -m devx.ci.detect_release_commit
run: |
. .venv/bin/activate
python3 -m devx.ci.detect_release_commit
validate-commit-msg:
needs: [detect-type]
if: needs.detect-type.outputs.is-release == 'false'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Install dependencies
run: |
. .env 2>/dev/null || true
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
- name: Set up environment
env:
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image
- name: Validate latest commit message
env:
PYTHONPATH: src
DEVX_TASK_PREFIX: GRM
run: |
. .venv/bin/activate
git log -1 --format=%B > commit-msg.txt
python3 -m devx.ci.validate_commit_msg commit-msg.txt --branch master
rm -f commit-msg.txt
@@ -80,21 +83,26 @@ jobs:
needs: [detect-type]
if: needs.detect-type.outputs.is-release == 'false'
runs-on: docker
timeout-minutes: 10
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-full:latest
timeout-minutes: 15
outputs:
tag: ${{ steps.release-tag.outputs.tag }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.REPO_TOKEN }}
token: ${{ secrets.CI_GITEA_TOKEN }}
- name: Set up environment
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: make setup-release
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image EXTRAS=ci,lint
- name: Configure git
run: |
git config user.name "grm-ci-bot"
git config user.email "grm-ci-bot@oblachno.fyi"
- name: Run release
id: release-tag
env:
PYTHONPATH: src
DEVX_VERSION_FILE: src/gitea_runner_manager/__init__.py
@@ -107,21 +115,62 @@ jobs:
- name: Notify on failure
if: failure()
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
PYTHONPATH: src
run: |
. .venv/bin/activate 2>/dev/null || true
export PATH="$HOME/.local/bin:$PATH"
python3 -m devx.tools.install_tools --tool tea
python3 -m devx.ci.notify_failure --auto-login \
--repo "${{ github.repository }}" \
--run-id "${{ github.run_id }}" \
--workflow "post-merge/release" \
--commit "${{ github.sha }}"
publish:
needs: [release]
if: needs.release.outputs.tag != ''
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-full:latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ needs.release.outputs.tag }}
- name: Set up environment
env:
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image EXTRAS=ci,lint
- name: Build and publish release
env:
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
PYTHONPATH: src
run: |
. .venv/bin/activate
export PATH="$HOME/.local/bin:$PATH"
python3 -m devx.ci.publish \
"${{ needs.release.outputs.tag }}" \
"${{ github.repository }}" --auto-login
- name: Notify on failure
if: failure()
env:
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
PYTHONPATH: src
run: |
. .venv/bin/activate 2>/dev/null || true
export PATH="$HOME/.local/bin:$PATH"
python3 -m devx.ci.notify_failure --auto-login \
--repo "${{ github.repository }}" \
--run-id "${{ github.run_id }}" \
--workflow "post-merge/publish" \
--commit "${{ github.sha }}"
sync-wiki:
needs: [detect-type, release]
needs: [detect-type]
if: needs.detect-type.outputs.is-release == 'false'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
@@ -129,11 +178,12 @@ jobs:
fetch-depth: 0
- name: Set up environment
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: make setup-ci
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image
- name: Sync documentation to wiki
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
PYTHONPATH: src
run: |
. .venv/bin/activate
@@ -141,11 +191,10 @@ jobs:
- name: Notify on failure
if: failure()
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
PYTHONPATH: src
run: |
export PATH="$HOME/.local/bin:$PATH"
python3 -m devx.tools.install_tools --tool tea
python3 -m devx.ci.notify_failure --auto-login \
--repo "${{ github.repository }}" \
--run-id "${{ github.run_id }}" \
@@ -153,24 +202,26 @@ jobs:
--commit "${{ github.sha }}"
badges:
needs: [detect-type, release]
needs: [detect-type]
if: always()
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-quality:latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: master
token: ${{ secrets.REPO_TOKEN }}
token: ${{ secrets.CI_GITEA_TOKEN }}
- name: Fetch latest master
run: |
git fetch origin master
git reset --hard origin/master
- name: Set up environment
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: make setup-ci
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image EXTRAS=lint
- name: Generate and push badges
env:
PRE_COMMIT_ALLOW_NO_CONFIG: "1"
@@ -180,11 +231,10 @@ jobs:
- name: Notify on failure
if: failure()
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
PYTHONPATH: src
run: |
export PATH="$HOME/.local/bin:$PATH"
python3 -m devx.tools.install_tools --tool tea
python3 -m devx.ci.notify_failure --auto-login \
--repo "${{ github.repository }}" \
--run-id "${{ github.run_id }}" \
@@ -192,35 +242,36 @@ jobs:
--commit "${{ github.sha }}"
vikunja:
needs: [detect-type, release]
needs: [detect-type]
if: needs.detect-type.outputs.is-release == 'false'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
run: |
. .env 2>/dev/null || true
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
- name: Set up environment
env:
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image
- name: Update Vikunja task
env:
VIKUNJA_TOKEN: ${{ secrets.VIKUNJA_TOKEN }}
PYTHONPATH: src
DEVX_TASK_PREFIX: GRM
DEVX_VIKUNJA_PROJECT_ID: 6
run: python3 -m devx.ci.post_merge --git-sha "${{ github.sha }}"
run: |
. .venv/bin/activate
python3 -m devx.ci.post_merge --git-sha "${{ github.sha }}"
- name: Notify on failure
if: failure()
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
PYTHONPATH: src
run: |
export PATH="$HOME/.local/bin:$PATH"
python3 -m devx.tools.install_tools --tool tea
python3 -m devx.ci.notify_failure --auto-login \
--repo "${{ github.repository }}" \
--run-id "${{ github.run_id }}" \
@@ -231,31 +282,32 @@ jobs:
needs: [detect-type]
if: needs.detect-type.outputs.is-release == 'false'
runs-on: docker
container: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
. .env 2>/dev/null || true
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.10.1" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
- name: Set up environment
env:
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }}
run: make setup-image
- name: Ensure branch protection and labels
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
PYTHONPATH: src
DEVX_REPO_NAME: grm
DEVX_REPO_OWNER: oblachno-oss
DEVX_STATUS_CHECKS: "CI / quality (pull_request),CI / molecule-tests (1) (pull_request),CI / molecule-tests (2) (pull_request),CI / molecule-tests (3) (pull_request)"
run: python3 -m devx.tools.configure_repo
run: |
. .venv/bin/activate
python3 -m devx.tools.configure_repo
- name: Notify on failure
if: failure()
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
CI_GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
PYTHONPATH: src
run: |
export PATH="$HOME/.local/bin:$PATH"
python3 -m devx.tools.install_tools --tool tea
python3 -m devx.ci.notify_failure --auto-login \
--repo "${{ github.repository }}" \
--run-id "${{ github.run_id }}" \
-57
View File
@@ -1,57 +0,0 @@
name: Publish Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to publish (e.g. v0.7.0)'
required: true
type: string
jobs:
publish:
runs-on: docker
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install CI tools
run: |
. .env 2>/dev/null || true
python3 -m pip install --break-system-packages --target=src "devx==0.9.12" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.9.12" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m pip install --break-system-packages --target=src "devx==0.9.12" --extra-index-url "https://emil:${{ secrets.REPO_TOKEN }}@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
python3 -m devx.tools.install_tools --tool git-cliff --tool tea
- name: Install build tools
run: python3 -m pip install --break-system-packages build twine
- name: Configure tea login
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: |
export PATH="$HOME/.local/bin:$PATH"
- name: Build and publish release
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
PYTHONPATH: src
run: |
export PATH="$HOME/.local/bin:$PATH"
python3 -m devx.ci.publish \
"${{ github.event.inputs.tag || github.ref_name }}" \
"${{ github.repository }}"
- name: Notify on failure
if: failure()
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
PYTHONPATH: src
run: |
export PATH="$HOME/.local/bin:$PATH"
python3 -m devx.ci.notify_failure --auto-login \
--repo "${{ github.repository }}" \
--run-id "${{ github.run_id }}" \
--workflow "publish" \
--commit "${{ github.sha }}"
+30 -24
View File
@@ -17,11 +17,10 @@ make workflow-check # workflow-lint + workflow-dryrun
```
`make setup` automatically installs all development tools:
- **Python deps** via `devx.tools.setup` (pip install -e .[dev], ansible-galaxy, pre-commit hooks)
- **devx package** via `make install-devx` (installs the devx package from git, providing all CI/CD tools)
- **Python deps** via `pip install -e .[dev]` (includes devx from Gitea PyPI registry, configured by `make configure-gitea-pypi`)
- **Post-install setup** via `devx.tools.setup --skip-install` (ansible-galaxy, pre-commit hooks, tea CLI login)
- **checkmake** via `devx.tools.install_checkmake` (Makefile linter)
- **actionlint, git-cliff, act_runner, tea** via `devx.tools.install_tools` (CI/CD tools to ~/.local/bin)
- **tea CLI login** via `devx.tools.setup` (configures `tea login` from `.env` `REPO_TOKEN`)
## Workflow Verification (Before Push)
@@ -69,7 +68,7 @@ The auto-merge workflow enforces the APPROVE review check programmatically
as a defense-in-depth measure, but branch protection is the primary gate.
### 1. Create Vikunja Task
Create a task in Vikunja project 6 to get a `GRM-N` identifier.
Create a task in Vikunja project 6 via `make create-task -- --title "Task title" --description "<h2>...</h2>"` (requires `VIKUNJA_TOKEN` in `.env`). This prints the `GRM-N` identifier and next-step instructions.
### 2. Create Branch
```bash
@@ -91,7 +90,9 @@ docs: update README
```
### 5. Push and Create PR
- **PR title format**: `GRM-N: <vikunja task title>` (must match the Vikunja task title exactly)
- Push: `git push -u origin HEAD` (pre-push hook validates Vikunja task existence via `devx.tools.pre_push_check`)
- Create PR: `make create-pr` (creates a PR with title `GRM-N: <vikunja task title>`, auto-derived from the branch name and Vikunja task)
- Or both in one step: `make push-with-pr`
- PR body: summary of changes, `Closes GRM-N`
- Add `ready-to-merge` label **only after review is complete**
@@ -129,7 +130,7 @@ the **[manual]** items by reviewing the full diff
Post review comments using `devx.ci.pr_review` (run as `python -m devx.ci.pr_review`):
```bash
REPO_TOKEN=<token> python -m devx.ci.pr_review <pr_number> <owner/repo> \
CI_GITEA_TOKEN=<token> python -m devx.ci.pr_review <pr_number> <owner/repo> \
--event REQUEST_CHANGES \
--body "Review summary" \
--comments-json comments.json
@@ -142,7 +143,7 @@ Fix each comment one by one, commit, and push. Re-review until satisfied.
Once all checklist items are verified and comments are addressed, post
an approval review with `--checklist-confirmed` and `--checklist-categories`:
```bash
REPO_TOKEN=<token> python -m devx.ci.pr_review <pr_number> <owner/repo> \
CI_GITEA_TOKEN=<token> python -m devx.ci.pr_review <pr_number> <owner/repo> \
--event APPROVE --checklist-confirmed \
--checklist-categories 1,2,3,4,5,6,7,8,9,10,11,12,13 \
--body "All 13 REVIEW_CHECKLIST.md categories verified. Architecture: <summary>. Security: <summary>. Tests: <summary>. Docs: <summary>."
@@ -160,13 +161,13 @@ Then add the `ready-to-merge` label. The auto-merge workflow will:
1. **Validate** PR title format (`GRM-N: <vikunja task title>`) and match against Vikunja task title
2. **Check** that at least one substantive APPROVE review exists (body > 20 chars or has inline comments)
3. Wait for all CI checks to pass (including the `pr-review` job)
4. Squash-merge with title: `GRM-N <conventional commit message>` (space-separated, no colon after GRM-N)
4. Squash-merge with title: `GRM-N: <conventional commit message>`
5. The post-merge workflow marks the Vikunja task as done
6. The release workflow automatically versions, tags, and publishes (see below)
> **IMPORTANT**: Never manually merge PRs via the API. Always use the auto-merge
> workflow by adding the `ready-to-merge` label. Manual merges bypass the
> `GRM-N <conventional>` format enforcement, producing incorrectly named commits.
> `GRM-N: <conventional>` format enforcement, producing incorrectly named commits.
> The auto-merge script validates the PR title matches the Vikunja task ID
> and conventional commit format before merging.
@@ -220,17 +221,22 @@ Vikunja task updates:
- `--skip-tests` flag bypasses test verification (emergency use only, not recommended)
- Loops are prevented by `has_unreleased_changes` — after a release commit is tagged, the next run finds no unreleased changes and exits
3. **sync-wiki** — Syncs documentation to the Gitea wiki.
3. **sync-wiki** — Syncs documentation to the Gitea wiki. Runs for ALL
non-release commits (not just when release succeeds), so docs-only
changes still update the wiki.
4. **badges** — Generates and pushes quality badge SVGs to the `badges` branch.
Runs **after** the release job (even if release fails or is skipped) so the
version badge always reflects the latest state. The script fetches the
latest master before generating badges to pick up any release commits.
Uses `if: always()` so it runs on every push, including release commits.
The script fetches the latest master before generating badges to pick up
any release commits.
5. **vikunja** — Marks the corresponding Vikunja task as done.
5. **vikunja** — Marks the corresponding Vikunja task as done. Runs for ALL
non-release commits (not just when release succeeds), so infrastructure-only
changes still update the task tracker.
The tag push triggers the **publish workflow** (`.gitea/workflows/publish.yml`)
which builds and publishes the package to PyPI.
6. **publish** — Runs after release succeeds (needs: release). Builds and
publishes the package to the Gitea PyPI registry. Gets the tag from the
release job's `tag` output.
### Smart CI: User-Facing vs Workflow-Only Changes
@@ -303,7 +309,7 @@ The codebase enforces strict separation between the GRM tool and the devx packag
### tea CLI Integration
The `tea` Gitea CLI tool is used for Gitea API interactions in devx. It is installed by `devx.tools.install_tools` and configured by `devx.tools.setup` (login profile from `.env` `REPO_TOKEN`).
The `tea` Gitea CLI tool is used for Gitea API interactions in devx. It is installed by `devx.tools.install_tools` and configured by `devx.tools.setup` (login profile from `.env` `CI_GITEA_TOKEN`).
**`devx.gitea_cli`** — Python wrapper around `tea` CLI with JSON output parsing:
- `TeaCLI.create_issue()` — Create issues with labels
@@ -351,18 +357,18 @@ platform matrix. Both `devx.molecule.distribute_molecule` (CI) and
`devx.molecule.molecule_all` (dev tool) import `PLATFORMS` from it — this
avoids dev tools importing directly from CI modules.
2. **Publish workflow** (`.gitea/workflows/publish.yml`):
- Triggers on tag push (`v*`)
- Validates `PYPI_TOKEN` is set (warns if missing)
2. **Publish job** (in `post-merge.yml`, needs: release):
- Runs after the release job creates a tag
- Gets the tag from `needs.release.outputs.tag`
- Builds the Python package
- Optionally publishes to PyPI (if `PYPI_TOKEN` is set)
- Publishes to the Gitea PyPI registry
- Creates a Gitea release with git-cliff-generated release notes
- On failure, creates a Gitea issue via `devx.ci.notify_failure`
### git-cliff Commit Preprocessing
Merge commits on master have the format `GRM-N <conventional commit>`. The
`GRM-N ` prefix is not a valid conventional commit prefix, so `cliff.toml`
Merge commits on master have the format `GRM-N: <conventional commit>`. The
`GRM-N: ` prefix is not a valid conventional commit prefix, so `cliff.toml`
includes a `commit_preprocessors` entry that strips it before parsing. This
ensures all merged work appears in the changelog.
@@ -384,7 +390,7 @@ The version source is `__version__` in `src/gitea_runner_manager/__init__.py`, r
| Branch name | `GRM-N-short-description` | `GRM-33-add-pr-review-step` |
| Branch commits | `<conventional commit>` | `feat: add review script` |
| PR title | `GRM-N: <vikunja task title>` | `GRM-33: Add mandatory PR review step` |
| Merge commit | `GRM-N <conventional commit>` | `GRM-33 feat: add review script` |
| Merge commit | `GRM-N: <conventional commit>` | `GRM-33: feat: add review script` |
### Configuration
+47
View File
@@ -2,6 +2,53 @@
All notable changes to this project will be documented in this file.
## [0.10.2] - 2026-06-27
### Bug Fixes
- Setup-image configures Gitea PyPI registry and shows pip errors
- Gate auto-merge on release-dry-run and unmask failures
- Bump devx>=0.22.0 and remove REPO_TOKEN alias
### Refactor
- Rename REPO_TOKEN to CI_GITEA_TOKEN, consolidate env vars
## [0.10.1] - 2026-06-27
### Bug Fixes
- Set PYTHONPATH=src in publish Install CI tools step
### Refactor
- Replace duplicated Makefile targets with devx.mak aliases
- Consolidate publish.yml into post-merge.yml
## [0.10.0] - 2026-06-26
### Features
- Adopt devx tools, devx.mak fragment, ci extra, remove legacy install-devx
### Bug Fixes
- Always run publish in post-merge (idempotent)
## [0.9.0] - 2026-06-24
### Features
- Adopt devx v0.11.1 across Makefile and workflows
- Add Polish as officially supported language
## [0.8.1] - 2026-06-24
### Bug Fixes
- Repair publish workflow and add publish step to post-merge
- Add build/twine to ci deps, activate venv in notify_failure
## [0.8.0] - 2026-06-24
### Features
+86 -81
View File
@@ -1,4 +1,6 @@
.PHONY: all setup setup-ci setup-quality setup-molecule setup-release install-devx install update lint ansible-lint makefile-lint lint-all test test-unit pytest-cov molecule molecule-all test-all clean workflow-lint workflow-dryrun workflow-check install-tools
.PHONY: all setup setup-ci setup-quality setup-molecule setup-release setup-image install update lint ansible-lint makefile-lint lint-all lint-ruff lint-format lint-bandit lint-deps typecheck checkmake install-hooks test test-unit pytest-cov molecule molecule-all test-all clean workflow-lint workflow-dryrun workflow-check install-tools
.PHONY: configure-gitea-pypi
.PHONY: create-task create-pr push-with-pr git-push
PYTHON := python3
VENV := .venv
@@ -7,45 +9,80 @@ CHECKMAKE := $(shell command -v checkmake 2>/dev/null || echo $(HOME)/go/bin/che
all: setup
# Pinned devx version — update this when upgrading devx.
# All workflow files (.gitea/workflows/*.yml) must be updated to match.
DEVX_VERSION := v0.10.1
# --- devx.mak include (shared Makefile targets) -------------------------------
# Set DEVX_PYTHON before including devx.mak so it uses the venv Python.
DEVX_PYTHON := $(BIN)/python
DEVX_VENV := $(VENV)
DEVX_BIN := $(BIN)
DEVX_COV_PKG := src/gitea_runner_manager
install-devx: $(VENV)/bin/activate
@# REPO_TOKEN may come from .env (local) or environment (CI secrets)
@if [ -z "$$REPO_TOKEN" ]; then . ./.env 2>/dev/null; fi; \
if [ -z "$$REPO_TOKEN" ]; then echo "REPO_TOKEN not set (check .env or environment)"; exit 1; fi; \
$(BIN)/pip install "devx==$(shell echo $(DEVX_VERSION) | sed 's/^v//')" \
--extra-index-url "https://emil:$$REPO_TOKEN@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
# Include shared targets from devx package (create-task, create-pr, push-with-pr,
# check-config, workflow-lint, lint-ruff, clean, venv, .env, activate-scripts,
# install-hooks, install-tools, configure-gitea-pypi, checkmake, etc.)
# Silent if devx not installed yet — run 'make setup' first.
DEVX_MAK := $(shell $(BIN)/python -c \
"from pathlib import Path; import devx; print(Path(devx.__file__).parent / 'make' / 'devx.mak')" \
2>/dev/null)
-include $(DEVX_MAK)
# Full setup for local development (all deps, tools, collections, hooks)
# install-devx must run before install-tools (which uses devx modules)
setup: $(VENV)/bin/activate .env activate-scripts install-devx checkmake install-tools
# devx is installed via pip install -e .[dev] (devx is in dev extra)
setup: $(VENV)/bin/activate .env activate-scripts configure-gitea-pypi
@$(PIP_INSTALL) install -e '.[dev]'
@$(BIN)/python -m devx.tools.install_checkmake
@$(BIN)/python -m devx.tools.install_tools
@export PATH="$(HOME)/.local/bin:$$PATH"; \
$(BIN)/python -m devx.tools.setup --bin "$(BIN)"
$(BIN)/python -m devx.tools.setup --bin "$(BIN)" --skip-install
# Lean setup for CI jobs that need pytest + lint tools + runtime deps
# (detect-changes, discover-runners, pr-review, sync-wiki, badges)
# badges job runs generate_badges.py which needs ruff, pyright, bandit
setup-ci: $(VENV)/bin/activate .env install-devx
@$(BIN)/python -m devx.tools.setup --bin "$(BIN)" --extras "ci,lint" --no-ansible-collections --no-pre-commit --no-tea-login
setup-ci: $(VENV)/bin/activate .env configure-gitea-pypi
@$(PIP_INSTALL) install -e '.[ci,lint]'
@$(BIN)/python -m devx.tools.setup --bin "$(BIN)" --skip-install --no-ansible-collections --no-pre-commit --no-tea-login
# Setup for the quality job (lint + test deps, actionlint tool)
# install-devx must run before install-tools (which uses devx modules)
setup-quality: $(VENV)/bin/activate .env install-devx install-tools
setup-quality: $(VENV)/bin/activate .env configure-gitea-pypi
@$(PIP_INSTALL) install -e '.[ci,lint]'
@$(BIN)/python -m devx.tools.install_tools
@export PATH="$(HOME)/.local/bin:$$PATH"; \
$(BIN)/python -m devx.tools.setup --bin "$(BIN)" --extras "ci,lint" --no-ansible-collections --no-pre-commit --no-tea-login
$(BIN)/python -m devx.tools.setup --bin "$(BIN)" --skip-install --no-ansible-collections --no-pre-commit --no-tea-login
# Full setup for molecule testing (needs ansible, molecule, collections)
setup-molecule: $(VENV)/bin/activate .env install-devx install-tools
setup-molecule: $(VENV)/bin/activate .env configure-gitea-pypi
@$(PIP_INSTALL) install -e '.[ci,molecule]'
@$(BIN)/python -m devx.tools.install_tools
@export PATH="$(HOME)/.local/bin:$$PATH"; \
$(BIN)/python -m devx.tools.setup --bin "$(BIN)" --extras "ci,molecule" --no-pre-commit --no-tea-login
$(BIN)/python -m devx.tools.setup --bin "$(BIN)" --skip-install --no-pre-commit --no-tea-login
# Setup for release jobs (needs git-cliff, tea, and lint tools for release.py)
setup-release: $(VENV)/bin/activate .env install-devx
setup-release: $(VENV)/bin/activate .env configure-gitea-pypi
@$(PIP_INSTALL) install -e '.[ci,lint]'
@$(BIN)/python -m devx.tools.install_tools --tool git-cliff --tool tea
@export PATH="$(HOME)/.local/bin:$$PATH"; \
$(BIN)/python -m devx.tools.setup --bin "$(BIN)" --extras "ci,lint" --no-ansible-collections --no-pre-commit
$(BIN)/python -m devx.tools.setup --bin "$(BIN)" --skip-install --no-ansible-collections --no-pre-commit
# Setup for pre-built image jobs (deps already in image, just link venv + install project)
# Usage: make setup-image (runtime + ci deps, incl devx)
# make setup-image EXTRAS=lint (runtime + lint deps, e.g. ansible-lint)
# make setup-image EXTRAS=ci,lint (runtime + ci + lint deps)
setup-image:
@if [ -d /opt/venv ]; then ln -sf /opt/venv .venv; . .venv/bin/activate; \
if [ -n "$$CI_GITEA_TOKEN" ]; then export PIP_EXTRA_INDEX_URL="https://$$CI_GITEA_USERNAME:$$CI_GITEA_TOKEN@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"; fi; \
pip install -e .$(if $(EXTRAS),[$(EXTRAS)],[ci]); \
else echo "[setup-image] /opt/venv not found — falling back to setup-ci"; $(MAKE) setup-ci; fi
# Helper: run pip install with Gitea registry configured
# Usage: $(PIP_INSTALL) install -e '.[ci,lint]'
PIP_INSTALL := if [ -z "$$CI_GITEA_TOKEN" ]; then . ./.env 2>/dev/null; fi; \
CI_GITEA_TOKEN="$$CI_GITEA_TOKEN"; \
if [ -n "$$CI_GITEA_TOKEN" ]; then export PIP_EXTRA_INDEX_URL="https://$$CI_GITEA_USERNAME:$$CI_GITEA_TOKEN@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"; fi; \
$(BIN)/pip
$(VENV)/bin/activate:
@python3 -c "import sys; v=sys.version_info; assert v >= (3, 12), f'Python 3.12+ required, found {v.major}.{v.minor}'; print(f'Python {v.major}.{v.minor}.{v.micro} OK')"
$(PYTHON) -m venv $(VENV)
$(BIN)/pip install --upgrade pip setuptools wheel
.env:
@if [ ! -f .env ]; then \
@@ -53,27 +90,11 @@ setup-release: $(VENV)/bin/activate .env install-devx
echo "Created .env from .env.example — please edit it with your credentials."; \
fi
$(VENV)/bin/activate:
@python3 -c "import sys; v=sys.version_info; assert v >= (3, 12), f'Python 3.12+ required, found {v.major}.{v.minor}'; print(f'Python {v.major}.{v.minor}.{v.micro} OK')"
$(PYTHON) -m venv $(VENV)
$(BIN)/pip install --upgrade pip setuptools wheel
activate-scripts: $(VENV)/bin/activate
@test -f activate.sh || (echo '#!/usr/bin/env bash' > activate.sh && echo 'source "$$(cd "$$(dirname "$${BASH_SOURCE[0]}")" && pwd)/.venv/bin/activate"' >> activate.sh && chmod +x activate.sh)
@test -f activate.fish || (echo '#!/usr/bin/env fish' > activate.fish && echo 'set -l script_dir (dirname (status --current-filename))' >> activate.fish && echo 'source "$$script_dir/.venv/bin/activate.fish"' >> activate.fish && chmod +x activate.fish)
@test -f activate.zsh || (echo '#!/usr/bin/env zsh' > activate.zsh && echo '0="$${ZERO:-$${0:#$$ZSH_ARGZERO}}"' >> activate.zsh && echo '0="$${$${(M)0:#/*}:-$$PWD/$$0}"' >> activate.zsh && echo 'source "$${0:A:h}/.venv/bin/activate"' >> activate.zsh && chmod +x activate.zsh)
install-hooks:
@cp hooks/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
@cp hooks/pre-push .git/hooks/pre-push && chmod +x .git/hooks/pre-push
@echo "Git hooks installed."
checkmake: install-devx
@$(BIN)/python -m devx.tools.install_checkmake
install-tools: install-devx
@$(BIN)/python -m devx.tools.install_tools
install:
@if [ -z "$(HOST)" ]; then echo "HOST is required. Example: make install HOST=192.168.1.10"; exit 1; fi
$(BIN)/grm install $(HOST) $(if $(USER),--user $(USER),) $(if $(KEY),--key $(KEY),) $(if $(NAME),--name $(NAME),) $(if $(TOKEN),--token $(TOKEN),) $(if $(ASK_BECOME_PASS),--ask-become-pass,)
@@ -106,25 +127,28 @@ remove:
@if [ -z "$(HOST)" ]; then echo "HOST is required. Example: make remove HOST=192.168.1.10"; exit 1; fi
$(BIN)/grm remove $(NAME) $(if $(HOST),--host $(HOST),) $(if $(USER),--user $(USER),) $(if $(TOKEN),--token $(TOKEN),) $(if $(ASK_BECOME_PASS),--ask-become-pass,)
lint-ruff:
$(BIN)/ruff check src/ tests/
# --- Aliases to devx.mak targets ----------------------------------------------
lint-ruff: devx-lint-ruff
lint-format: devx-lint-format
typecheck: devx-typecheck
lint-bandit: devx-lint-bandit
lint-deps: devx-lint-deps
lint: devx-lint
checkmake: devx-checkmake
install-tools: devx-install-tools
install-hooks: devx-install-hooks
clean: devx-clean
test-unit: devx-test-unit
pytest-cov: devx-pytest-cov
workflow-lint: devx-workflow-lint
workflow-dryrun: devx-workflow-dryrun
workflow-check: devx-workflow-check
lint-format:
$(BIN)/ruff format --check src/ tests/
typecheck:
$(BIN)/pyright
lint: lint-ruff lint-format typecheck lint-bandit
lint-bandit:
$(BIN)/bandit -r src/
lint-deps:
@echo "Checking dependencies for known vulnerabilities..."
@.venv/bin/python -m ensurepip 2>/dev/null || true
@PIPAPI_PYTHON_LOCATION=$$(pwd)/.venv/bin/python \
.venv/bin/pip-audit --desc --skip-editable 2>&1 || true
configure-gitea-pypi:
@if [ -z "$$CI_GITEA_TOKEN" ]; then . ./.env 2>/dev/null; fi; \
CI_GITEA_TOKEN="$$CI_GITEA_TOKEN"; \
if [ -z "$$CI_GITEA_TOKEN" ]; then echo "[configure-gitea-pypi] CI_GITEA_TOKEN not set — skipping (devx must be on public PyPI)"; exit 0; fi; \
echo "[configure-gitea-pypi] Gitea PyPI registry configured (CI_GITEA_TOKEN present)."
ansible-lint:
PATH="$(PWD)/$(BIN):$$PATH" $(BIN)/ansible-lint ansible/
@@ -138,30 +162,9 @@ makefile-lint:
lint-all: lint ansible-lint makefile-lint workflow-lint
workflow-lint:
@command -v actionlint >/dev/null 2>&1 || { \
echo "actionlint not found. Install: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)"; \
exit 1; \
}
actionlint -config-file .gitea/actionlint.yaml .gitea/workflows/*.yml
workflow-dryrun:
@command -v act_runner >/dev/null 2>&1 || { echo "act_runner not found. Install: https://gitea.com/gitea/act_runner/releases"; exit 1; }
@echo "Dry-running all workflows (no Docker containers started)..."
act_runner exec --dryrun -W .gitea/workflows/ 2>&1 | grep -E 'DRYRUN|ERROR|FAIL|Job'
workflow-check: workflow-lint workflow-dryrun
@echo "Workflow checks passed (static lint + dry-run)."
test-unit:
$(BIN)/pytest tests/unit/ -v --no-cov
test-integration:
$(BIN)/pytest tests/integration/ -v --no-cov
pytest-cov:
$(BIN)/pytest tests/ -v --cov=src/gitea_runner_manager --cov-report=term-missing --cov-fail-under=100
MOLECULE := $(realpath $(BIN))/molecule
MOLECULE_BASE := cd $(CURDIR)/ansible/roles/gitea-runner && ANSIBLE_ALLOW_BROKEN_CONDITIONALS=true ANSIBLE_INJECT_INVOCATION=1 $(MOLECULE)
@@ -177,7 +180,9 @@ test: test-all
test-all: pytest-cov molecule
clean:
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
find . -type f -name "*.pyc" -delete 2>/dev/null || true
rm -rf .coverage htmlcov/ .molecule/
# --- Vikunja task and PR management (via devx.mak fragment) -------------------
# Aliases for project-specific target names
create-task: devx-create-task
create-pr: devx-create-pr
push-with-pr: devx-push-with-pr
git-push: devx-push
+34 -10
View File
@@ -8,12 +8,12 @@ Each runner runs in an isolated **rootless Docker** environment under a dedicate
[![CI](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions/workflows/ci.yml/badge.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/branch/badges/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/branch/badges/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/branch/badges/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/branch/badges/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/branch/badges/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/branch/badges/python.svg)](https://www.python.org/downloads/)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5c243201a5c2795cb11880ad0f4094f8da623669/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5c243201a5c2795cb11880ad0f4094f8da623669/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5c243201a5c2795cb11880ad0f4094f8da623669/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5c243201a5c2795cb11880ad0f4094f8da623669/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5c243201a5c2795cb11880ad0f4094f8da623669/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5c243201a5c2795cb11880ad0f4094f8da623669/python.svg)](https://www.python.org/downloads/)
## Why GRM?
@@ -35,7 +35,7 @@ Key problems GRM solves:
- **Automatic integration testing** — Every installation runs an integration test that verifies the `.runner` registration file and systemd service state.
- **Docker prune automation** — A systemd user timer automatically prunes old Docker images and volumes on a daily schedule.
- **Local runner registry** — Connection details are stored in `~/.local/share/grm/runners.json`, so lifecycle commands work by runner name alone.
- **Internationalisation** — Console messages support English, Bulgarian, German, Russian, and Chinese via the `GRM_LANG` environment variable.
- **Internationalisation** — Console messages support English, Bulgarian, German, Russian, Chinese, and Polish via the `GRM_LANG` environment variable.
- **Security-conscious** — Secrets (registration tokens) are passed via temporary JSON files with `0600` permissions, never on the command line (CWE-214).
- **Comprehensive CI/CD** — 100% test coverage, automated releases via conventional commits and git-cliff, Molecule tests across 4 OS platforms.
@@ -92,11 +92,35 @@ source .venv/bin/activate
### Option 2: Via pip (for using GRM without the full repo)
GRM is published to the Gitea PyPI registry at
`https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple`.
The registry is publicly readable — no authentication required to install.
**Quick install (one-off):**
```bash
pip install gitea-runner-manager --index-url https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple
```
**Persistent configuration (recommended):**
Add the registry to `~/.pip/pip.conf` so future `pip install` commands find
GRM automatically:
```ini
[global]
extra-index-url = https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple
```
Then install normally:
```bash
pip install gitea-runner-manager
```
This installs the `grm` CLI and its Python dependencies. Note that the Ansible playbooks and role are bundled with the package, so `grm install` will work out of the box. However, for development or access to Make targets, clone the repository.
This installs the `grm` CLI and its Python dependencies. The Ansible playbooks
and role are bundled with the package, so `grm install` works out of the box.
For development or access to Make targets, clone the repository (Option 1).
### Post-install configuration
@@ -145,7 +169,7 @@ GRM reads configuration from a `.env` file in the current directory (loaded auto
| Variable | Default | Description |
|----------|---------|-------------|
| `REPO_TOKEN` | — | Gitea admin API token for optional post-install API verification |
| `CI_GITEA_TOKEN` | — | Gitea admin API token for optional post-install API verification |
| `GITEA_INTEGRATION_RETRIES` | `3` | Number of API check retries during integration test |
| `GITEA_RUNNER_USER` | current login | Default SSH user (overrides `--user`) |
| `GITEA_RUNNER_KEY` | — | Default SSH key path (overrides `--key`) |
@@ -268,7 +292,7 @@ grm install <host>
| `runner_manager.py` | Ansible orchestration + registry integration |
| `executor.py` | Ansible subprocess execution with log capture |
| `registry.py` | Local JSON runner registry at `~/.local/share/grm/runners.json` |
| `i18n.py` | Internationalisation (en, bg, de, ru, zh) |
| `i18n.py` | Internationalisation (en, bg, de, ru, zh, pl) |
| `exceptions.py` | Custom exceptions (`GRMError`, `AnsibleError`, `APIError`) |
| `config.py` | Configuration constants (API URLs, repo owner/name) |
| `logging_config.py` | Logging to `~/.local/state/grm/logs/grm.log` |
+1 -1
View File
@@ -9,7 +9,7 @@
| Vikunja task not updated after merge | VIKUNJA_TOKEN expired or task ID missing from commit | Regenerate token; verify merge commit has `GRM-N:` prefix |
| Post-merge can't find Vikunja task | Task not in project 6 or identifier mismatch | Verify task exists in Vikunja project 6 with correct identifier |
| `make pytest-cov` fails | Coverage below 100% | Add tests for new code paths |
| `devx.tools.configure_repo` fails | REPO_TOKEN missing or invalid | Set token with repo admin scope and re-run |
| `devx.tools.configure_repo` fails | CI_GITEA_TOKEN missing or invalid | Set token with repo admin scope and re-run |
| `configure_repo` sets wrong status checks | Stale `BRANCH_PROTECTION_CONFIG` | Updated to include `(pull_request)` suffix; re-run `configure_repo` |
| Token visible in `ps aux` during install | Old version passed tokens via command line | Fixed: tokens now passed via temp file with `0600` permissions |
| `remove-runner.yml` leaves lingering enabled | Old version didn't disable lingering | Fixed: now runs `loginctl disable-linger` and removes subuid/subgid |
+7 -7
View File
@@ -8,12 +8,12 @@ Each runner runs in an isolated **rootless Docker** environment under a dedicate
[![CI](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions/workflows/ci.yml/badge.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/branch/badges/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/branch/badges/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/branch/badges/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/branch/badges/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/branch/badges/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/branch/badges/python.svg)](https://www.python.org/downloads/)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5c243201a5c2795cb11880ad0f4094f8da623669/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5c243201a5c2795cb11880ad0f4094f8da623669/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5c243201a5c2795cb11880ad0f4094f8da623669/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5c243201a5c2795cb11880ad0f4094f8da623669/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5c243201a5c2795cb11880ad0f4094f8da623669/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5c243201a5c2795cb11880ad0f4094f8da623669/python.svg)](https://www.python.org/downloads/)
## Overview
@@ -27,7 +27,7 @@ GRM is a two-layer tool: a Python CLI (built with Click) that delegates to an id
- **Idempotent Ansible role** — Safe to re-run; second run produces zero changes.
- **Automatic integration testing** — Every installation verifies the `.runner` registration file and systemd service state.
- **Local runner registry** — Connection details stored locally; manage runners by name after installation.
- **Internationalisation** — Console messages in English, Bulgarian, German, Russian, and Chinese.
- **Internationalisation** — Console messages in English, Bulgarian, German, Russian, Chinese, and Polish.
- **Security-conscious** — Secrets passed via temporary JSON files with `0600` permissions (CWE-214).
### Supported operating systems
+1 -1
View File
@@ -209,7 +209,7 @@ The Python CLI layer (`src/gitea_runner_manager/`) consists of the following mod
| `runner_manager.py` | Ansible orchestration + registry integration — delegates to executor and manages runner lifecycle |
| `executor.py` | Ansible subprocess execution — runs `ansible-playbook` with extra-vars via temp JSON files, streams output to log files |
| `registry.py` | Local JSON runner registry at `~/.local/share/grm/runners.json` — stores connection metadata |
| `i18n.py` | Internationalisation translations (en, bg, de, ru, zh) — opt-in via `GRM_LANG` environment variable |
| `i18n.py` | Internationalisation translations (en, bg, de, ru, zh, pl) — opt-in via `GRM_LANG` environment variable |
| `exceptions.py` | Custom exceptions (`GRMError`, `AnsibleError`, `APIError`) |
| `config.py` | Configuration constants (API URLs, repo owner/name, project IDs) — overridable via environment variables |
| `logging_config.py` | Logging configuration — writes all messages to `~/.local/state/grm/logs/grm.log` at DEBUG level |
+2 -2
View File
@@ -74,7 +74,7 @@ Review the full diff (`git diff master...HEAD`) focusing on:
Post review comments using `devx.ci.pr_review`:
```bash
REPO_TOKEN=<token> python -m devx.ci.pr_review <pr_number> <owner/repo> \
CI_GITEA_TOKEN=<token> python -m devx.ci.pr_review <pr_number> <owner/repo> \
--event REQUEST_CHANGES \
--body "Review summary" \
--comments-json comments.json
@@ -89,7 +89,7 @@ Fix each comment one by one, commit, and push. Re-review until satisfied.
Once all comments are addressed:
```bash
REPO_TOKEN=<token> python -m devx.ci.pr_review <pr_number> <owner/repo> \
CI_GITEA_TOKEN=<token> python -m devx.ci.pr_review <pr_number> <owner/repo> \
--event APPROVE \
--body "All comments addressed. LGTM."
```
+2 -2
View File
@@ -137,7 +137,7 @@ Review the full diff (`git diff master...HEAD`) focusing on:
Post review comments using `devx.ci.review_pr`:
```bash
REPO_TOKEN=<token> python -m devx.ci.review_pr <pr_number> <owner/repo> \
CI_GITEA_TOKEN=<token> python -m devx.ci.review_pr <pr_number> <owner/repo> \
--event REQUEST_CHANGES \
--body "Review summary" \
--comments-json comments.json
@@ -152,7 +152,7 @@ Fix each comment one by one, commit, and push. Re-review until satisfied.
Once all comments are addressed, post an approval review:
```bash
REPO_TOKEN=<token> python -m devx.ci.review_pr <pr_number> <owner/repo> \
CI_GITEA_TOKEN=<token> python -m devx.ci.review_pr <pr_number> <owner/repo> \
--event APPROVE --checklist-confirmed \
--checklist-categories 1,2,3,4,5,6,7,8,9,10,11,12,13 \
--body "All 13 REVIEW_CHECKLIST.md categories verified."
+2 -2
View File
@@ -9,7 +9,7 @@
│ ├── runner_manager.py # Ansible orchestration + registry integration
│ ├── executor.py # Ansible subprocess execution
│ ├── registry.py # Local JSON runner registry
│ ├── i18n.py # Translations (en, bg, de, ru, zh)
│ ├── i18n.py # Translations (en, bg, de, ru, zh, pl)
│ ├── exceptions.py # Custom exceptions
│ ├── config.py # Configuration constants
│ ├── logging_config.py # Logging to ~/.local/state/grm/logs/
@@ -108,7 +108,7 @@ cp .env.example .env
#### Admin API token (optional)
Set `REPO_TOKEN` to enable informational API checks during integration test. This is **optional** — the test primarily verifies the runner by checking:
Set `CI_GITEA_TOKEN` to enable informational API checks during integration test. This is **optional** — the test primarily verifies the runner by checking:
1. **`.runner` registration file** exists and contains valid JSON (proves successful registration)
2. **Systemd user service** is active (proves daemon is polling for jobs)
+2 -2
View File
@@ -51,7 +51,7 @@ grm install <host> [options]
| `--name` | `-n` | hostname | Gitea Runner name |
| `--token` | `-t` | `GITEA_REGISTRATION_TOKEN` env | Registration token |
| `--url` | — | `GITEA_URL` env | Gitea URL |
| `--admin-token` | `-a` | `REPO_TOKEN` env | Gitea admin API token for integration test |
| `--admin-token` | `-a` | `CI_GITEA_TOKEN` env | Gitea admin API token for integration test |
| `--integration-retries` | `-r` | `3` (`GITEA_INTEGRATION_RETRIES` env) | Integration test API retries |
| `--labels` | `-l` | `GITEA_RUNNER_LABELS` env | Runner labels for Gitea Actions. Example: `docker:docker://alpine:latest` |
| `--ask-become-pass/--no-ask-become-pass` | — | `--ask-become-pass` | Prompt for sudo password (default) or skip it |
@@ -294,7 +294,7 @@ All CLI options can be set via environment variables (loaded from `.env` via pyt
|----------|---------|-------------|
| `GITEA_URL` | `install`, `disable`, `remove` | Gitea instance URL |
| `GITEA_REGISTRATION_TOKEN` | `install`, `disable`, `remove` | Runner registration token |
| `REPO_TOKEN` | `install` | Admin API token for integration test |
| `CI_GITEA_TOKEN` | `install` | Admin API token for integration test |
| `GITEA_INTEGRATION_RETRIES` | `install` | API check retries (default: 3) |
| `GITEA_RUNNER_USER` | `install`, `update` | Default SSH user |
| `GITEA_RUNNER_KEY` | `install`, `update` | Default SSH key path |
+2 -2
View File
@@ -10,9 +10,9 @@ There are three levels of registration tokens, depending on which repositories t
Set the token as `GITEA_REGISTRATION_TOKEN` in your `.env` file or pass it via `--token` on the command line.
### What is the REPO_TOKEN and do I need it?
### What is the CI_GITEA_TOKEN and do I need it?
`REPO_TOKEN` is a Gitea admin API token used for optional post-install verification. When set, GRM queries the Gitea API after installation to confirm the runner appears in the runner list. This is purely informational — the integration test passes/fails based on the `.runner` file and systemd service, not the API check.
`CI_GITEA_TOKEN` is a Gitea admin API token used for optional post-install verification. When set, GRM queries the Gitea API after installation to confirm the runner appears in the runner list. This is purely informational — the integration test passes/fails based on the `.runner` file and systemd service, not the API check.
To generate one: Settings → Applications → Generate New Token, with the `admin` scope (or at minimum `read:user`, `read:repository`, `read:admin`).
+3 -3
View File
@@ -84,7 +84,7 @@ GITEA_URL=https://git.example.com
GITEA_REGISTRATION_TOKEN=GRxxxxxxxxxxxxxxxxxx
# Admin API token from Step 2 (optional)
REPO_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
CI_GITEA_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
### Environment Variables Reference
@@ -93,7 +93,7 @@ REPO_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|----------|----------|---------|-------------|
| `GITEA_URL` | Yes | — | Gitea instance URL (e.g., `https://git.example.com`) |
| `GITEA_REGISTRATION_TOKEN` | Yes | — | Runner registration token from Gitea admin panel |
| `REPO_TOKEN` | No | — | Admin API token for post-install verification |
| `CI_GITEA_TOKEN` | No | — | Admin API token for post-install verification |
| `GITEA_INTEGRATION_RETRIES` | No | `3` | API check retries (default: 3) |
| `GITEA_RUNNER_USER` | No | current login | Default SSH user (overrides `--user`) |
| `GITEA_RUNNER_KEY` | No | — | Default SSH key path (overrides `--key`) |
@@ -150,7 +150,7 @@ The installer performs an automated integration test that verifies:
You can also check the Gitea UI under **Actions → Runners** to confirm the runner appears as **Online**.
Optional: If `REPO_TOKEN` is set, the installer will also query the Gitea API and report whether the runner appears in the admin or repo runners list. This is purely informational.
Optional: If `CI_GITEA_TOKEN` is set, the installer will also query the Gitea API and report whether the runner appears in the admin or repo runners list. This is purely informational.
### Check runner status via CLI
+24 -1
View File
@@ -51,11 +51,34 @@ source .venv/bin/activate
### Method 2: Via pip
GRM is published to the Gitea PyPI registry at
`https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple`.
The registry is publicly readable — no authentication required to install.
**Quick install (one-off):**
```bash
pip install gitea-runner-manager --index-url https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple
```
**Persistent configuration (recommended):**
Add the registry to `~/.pip/pip.conf`:
```ini
[global]
extra-index-url = https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple
```
Then install normally:
```bash
pip install gitea-runner-manager
```
This installs the `grm` CLI and its Python dependencies. The Ansible playbooks and role are bundled with the package, so `grm install` will work out of the box. However, for development or access to Make targets, clone the repository.
This installs the `grm` CLI and its Python dependencies. The Ansible playbooks
and role are bundled with the package, so `grm install` works out of the box.
For development or access to Make targets, clone the repository (Method 1).
### Post-install configuration
+2 -2
View File
@@ -98,7 +98,7 @@ The test checks two things:
### API verification shows error status
If `REPO_TOKEN` is set, the integration test queries the Gitea API. If the API returns `401` or `403`, the token does not have sufficient permissions. This is **informational only** and does not affect pass/fail. The test passes as long as the `.runner` file exists and the systemd service is active.
If `CI_GITEA_TOKEN` is set, the integration test queries the Gitea API. If the API returns `401` or `403`, the token does not have sufficient permissions. This is **informational only** and does not affect pass/fail. The test passes as long as the `.runner` file exists and the systemd service is active.
## Logging and Diagnostics
@@ -191,7 +191,7 @@ If Docker is not installed, install it via your package manager or [Docker's off
| Vikunja task not updated after merge | VIKUNJA_TOKEN expired or task ID missing from commit | Regenerate token; verify merge commit has `GRM-N:` prefix |
| Post-merge can't find Vikunja task | Task not in project 6 or identifier mismatch | Verify task exists in Vikunja project 6 with correct identifier |
| `make pytest-cov` fails | Coverage below 100% | Add tests for new code paths |
| `devx.tools.configure_repo` fails | REPO_TOKEN missing or invalid | Set token with repo admin scope and re-run |
| `devx.tools.configure_repo` fails | CI_GITEA_TOKEN missing or invalid | Set token with repo admin scope and re-run |
| `configure_repo` sets wrong status checks | Stale `BRANCH_PROTECTION_CONFIG` | Updated to include `(pull_request)` suffix; re-run `configure_repo` |
| Token visible in `ps aux` during install | Old version passed tokens via command line | Fixed: tokens now passed via temp file with `0600` permissions |
| `remove-runner.yml` leaves lingering enabled | Old version didn't disable lingering | Fixed: now runs `loginctl disable-linger` and removes subuid/subgid |
+60 -5
View File
@@ -1,6 +1,61 @@
#!/usr/bin/env bash
# pre-push hook: fail if unit tests are too slow.
# Checks both total suite time (10s) and per-test time (0.5s).
# Aligned with CI (ci.yml uses same thresholds).
set -e
python3 -m devx.tools.check_test_speed --max-seconds 4 --max-single-seconds 0.5
# pre-push hook: validate Vikunja task exists and tests are fast.
#
# This catches issues that would otherwise only surface in CI:
# - Branch name missing task ID (e.g., GRM-N)
# - Vikunja task does not exist for the task ID in the branch name
# - Unit tests too slow (total > 4s, per-test > 0.5s)
#
# Uses devx.tools.pre_push_check for reusable validation logic.
# Project config (task prefix, Vikunja project ID) is read from
# [tool.devx] in pyproject.toml by devx.config — no hardcoded values here.
#
# Bootstrap resilience: if devx is not importable (e.g., during devx
# upgrades), the hook prints a warning and allows the push.
# Determine the branch being pushed
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
if [ -z "$BRANCH" ] || [ "$BRANCH" = "master" ] || [ "$BRANCH" = "main" ]; then
exit 0
fi
# Load .env if present (for VIKUNJA_TOKEN)
if [ -f .env ]; then
set -a
# shellcheck disable=SC1091
. .env
set +a
fi
# Find the Python interpreter with devx installed
if [ -f .venv/bin/python ]; then
PY=.venv/bin/python
elif [ -x "${HOME}/.pyenv/bin/pyenv" ]; then
export PYENV_ROOT="${HOME}/.pyenv"
export PATH="${PYENV_ROOT}/bin:${PYENV_ROOT}/shims:${PATH}"
eval "$("${PYENV_ROOT}/bin/pyenv" init -)" 2>/dev/null || true
eval "$("${PYENV_ROOT}/bin/pyenv" virtualenv-init -)" 2>/dev/null || true
pyenv activate gitea-runner-manager 2>/dev/null || true
PY=python3
else
PY=python3
fi
# Bootstrap resilience: if devx is not importable, warn but allow the push
if ! $PY -c "import devx.tools.pre_push_check" 2>/dev/null; then
echo "WARNING: devx not installed — pre-push check skipped."
echo "Run 'make setup' to install devx."
exit 0
fi
# Run pre-push validation via devx
$PY -m devx.tools.pre_push_check --branch "$BRANCH" || {
echo ""
echo "Pre-push validation failed. Fix the issues above before pushing."
echo "To bypass (NOT recommended): git push --no-verify"
exit 1
}
# Check test speed (aligned with CI thresholds)
$PY -m devx.tools.check_test_speed --max-seconds 4 --max-single-seconds 0.5
+25
View File
@@ -32,6 +32,10 @@ version = {attr = "gitea_runner_manager.__version__"}
ci = [
"pytest>=9.1.0",
"pytest-cov>=7.1.0",
"build>=1.5.0",
"twine>=6.2.0",
# Reusable CI/CD and dev tools (auto-merge, pr-review, pre-push checks, etc.)
"devx>=0.22.0",
]
# Lint and type-checking tools (quality job)
lint = [
@@ -50,6 +54,8 @@ molecule = [
# Full dev environment (local development, includes everything)
dev = [
"gitea-runner-manager[ci,lint,molecule]",
# Reusable CI/CD and dev tools (pre-push hooks, create-task, create-pr)
"devx>=0.22.0",
# Non-Python dev dependency: checkmake (Makefile linter)
# Install via: go install github.com/checkmake/checkmake/cmd/checkmake@latest
]
@@ -89,6 +95,25 @@ strict = ["src/gitea_runner_manager"]
# Change classification — determines which changes trigger a release
# ---------------------------------------------------------------------------
# The framework provides DEFAULT_INFRASTRUCTURE (CI workflows, tests, docs,
# Project-specific devx configuration (read by devx.config)
[tool.devx]
task_prefix = "GRM"
vikunja_project_id = 6
repo_owner = "oblachno-oss"
repo_name = "gitea-runner-manager"
# Molecule test weights for LPT scheduling.
# GRM has a single role (gitea-runner) with 7 scenarios.
# Weights are estimates — recalibrate from CI logs after next run.
[tool.devx.molecule.weights]
"multi-instance" = 8
"lifecycle" = 6
"update" = 5
"default" = 4
"deregister" = 3
"remove" = 3
"template-content" = 2
# lint config, etc.) that applies to any Python project. We only specify
# what's different about GRM.
[tool.devx.classify]
+1 -1
View File
@@ -1,3 +1,3 @@
"""Gitea Runner Manager — lean CLI for managing Gitea Actions runners."""
__version__ = "0.8.0"
__version__ = "0.10.2"
+2 -2
View File
@@ -77,8 +77,8 @@ def cli() -> None:
@click.option(
"--admin-token",
"-a",
default=lambda: os.getenv("REPO_TOKEN"),
help=_("Gitea admin API token for integration test (env: REPO_TOKEN)"),
default=lambda: os.getenv("CI_GITEA_TOKEN"),
help=_("Gitea admin API token for integration test (env: CI_GITEA_TOKEN)"),
)
@click.option(
"--integration-retries",
+2 -2
View File
@@ -1,7 +1,7 @@
"""Simple i18n for GRM console messages.
Set GRM_LANG environment variable to override the default English.
Supported: en, bg, de, ru, zh.
Supported: en, bg, de, ru, zh, pl.
"""
from __future__ import annotations
@@ -22,7 +22,7 @@ def _(key: str, **kwargs: object) -> str:
If unset, English is always returned regardless of system locale.
"""
lang = os.getenv("GRM_LANG", "en")
if lang not in ("en", "bg", "de", "ru", "zh"):
if lang not in ("en", "bg", "de", "ru", "zh", "pl"):
lang = "en"
template = TRANSLATIONS.get(key, {}).get(lang, key)
return template.format(**kwargs)
+149 -75
View File
@@ -1,487 +1,561 @@
{
"=== Operation Report ===": {
"en": "=== Operation Report ===",
"bg": "=== Отчет за операцията ===",
"de": "=== Operationsbericht ===",
"en": "=== Operation Report ===",
"pl": "=== Raport operacji ===",
"ru": "=== Отчет об операции ===",
"zh": "=== 操作报告 ==="
},
"Ad-hoc command failed on {host}: {stderr}": {
"en": "Ad-hoc command failed on {host}: {stderr}",
"bg": "Ad-hoc командата неуспешна на {host}: {stderr}",
"de": "Ad-hoc-Befehl auf {host} fehlgeschlagen: {stderr}",
"en": "Ad-hoc command failed on {host}: {stderr}",
"pl": "Polecenie ad-hoc nie powiodło się na {host}: {stderr}",
"ru": "Ad-hoc команда не удалась на {host}: {stderr}",
"zh": "Ad-hoc 命令在 {host} 上失败: {stderr}"
},
"Ansible failed with exit code {code}. See full log: {log_file}": {
"en": "Ansible failed with exit code {code}. See full log: {log_file}",
"bg": "Ansible е неуспешен с код {code}. Вижте пълния лог: {log_file}",
"de": "Ansible fehlgeschlagen mit Exit-Code {code}. Siehe Log: {log_file}",
"en": "Ansible failed with exit code {code}. See full log: {log_file}",
"pl": "Ansible zakończone niepowodzeniem z kodem {code}. Pełny log: {log_file}",
"ru": "Ansible завершился с кодом {code}. См. лог: {log_file}",
"zh": "Ansible 失败,退出码 {code}。查看日志: {log_file}"
},
"Check the status of a registered Gitea Runner.": {
"en": "Check the status of a registered Gitea Runner.",
"bg": "Проверка на състоянието на регистриран Gitea Runner.",
"de": "Status eines registrierten Gitea Runners prüfen.",
"en": "Check the status of a registered Gitea Runner.",
"pl": "Sprawdź status zarejestrowanego Gitea Runner.",
"ru": "Проверить состояние зарегистрированного Gitea Runner.",
"zh": "检查已注册的 Gitea Runner 状态。"
},
"Checking status of Gitea Runner {name} on {host}": {
"en": "Checking status of Gitea Runner {name} on {host}",
"bg": "Проверка на състоянието на Gitea Runner {name} на {host}",
"de": "Prüfe Status von Gitea Runner {name} auf {host}",
"en": "Checking status of Gitea Runner {name} on {host}",
"pl": "Sprawdzanie statusu Gitea Runner {name} na {host}",
"ru": "Проверка состояния Gitea Runner {name} на {host}",
"zh": "正在检查 {host} 上 Gitea Runner {name} 的状态"
},
"Checking status of Gitea Runner {name} on {host} as {user} (sudo required)": {
"en": "Checking status of Gitea Runner {name} on {host} as {user} (sudo required)",
"bg": "Проверка на състоянието на Gitea Runner {name} на {host} като {user} (необходим е sudo)",
"de": "Prüfe Status von Gitea Runner {name} auf {host} als {user} (sudo erforderlich)",
"en": "Checking status of Gitea Runner {name} on {host} as {user} (sudo required)",
"pl": "Sprawdzanie statusu Gitea Runner {name} na {host} jako {user} (wymagane sudo)",
"ru": "Проверка состояния Gitea Runner {name} на {host} как {user} (требуется sudo)",
"zh": "正在检查 {host} 上 Gitea Runner {name} 的状态(用户 {user},需要 sudo"
},
"Command: {cmd}": {
"en": "Command: {cmd}",
"bg": "Команда: {cmd}",
"de": "Befehl: {cmd}",
"en": "Command: {cmd}",
"pl": "Polecenie: {cmd}",
"ru": "Команда: {cmd}",
"zh": "命令: {cmd}"
},
"Disable a registered Gitea Runner and deregister it.": {
"en": "Disable a registered Gitea Runner and deregister it.",
"bg": "Деактивиране на регистриран Gitea Runner и дерегистриране.",
"de": "Einen registrierten Gitea Runner deaktivieren und abmelden.",
"en": "Disable a registered Gitea Runner and deregister it.",
"pl": "Wyłącz zarejestrowanego Gitea Runner i wyrejestruj go.",
"ru": "Отключить и дерегистрировать зарегистрированный Gitea Runner.",
"zh": "禁用并注销已注册的 Gitea Runner。"
},
"Disable failed: {error}": {
"en": "Disable failed: {error}",
"bg": "Деактивирането неуспешно: {error}",
"de": "Deaktivierung fehlgeschlagen: {error}",
"en": "Disable failed: {error}",
"pl": "Wyłączanie nie powiodło się: {error}",
"ru": "Отключение не удалось: {error}",
"zh": "禁用失败: {error}"
},
"Disabling Gitea Runner {name} on {host}": {
"en": "Disabling Gitea Runner {name} on {host}",
"bg": "Деактивиране на Gitea Runner {name} на {host}",
"de": "Deaktiviere Gitea Runner {name} auf {host}",
"en": "Disabling Gitea Runner {name} on {host}",
"pl": "Wyłączanie Gitea Runner {name} na {host}",
"ru": "Отключение Gitea Runner {name} на {host}",
"zh": "正在 {host} 上禁用 Gitea Runner {name}"
},
"Done. See full log: {log_file}": {
"en": "Done. See full log: {log_file}",
"bg": "Готово. Вижте пълния лог: {log_file}",
"de": "Fertig. Siehe vollständiges Log: {log_file}",
"en": "Done. See full log: {log_file}",
"pl": "Gotowe. Pełny log: {log_file}",
"ru": "Готово. См. полный лог: {log_file}",
"zh": "完成。查看完整日志: {log_file}"
},
"Enable a registered Gitea Runner to start on boot.": {
"en": "Enable a registered Gitea Runner to start on boot.",
"bg": "Разрешаване на регистриран Gitea Runner да стартира при зареждане.",
"de": "Einen registrierten Gitea Runner für den Autostart aktivieren.",
"en": "Enable a registered Gitea Runner to start on boot.",
"pl": "Włącz zarejestrowanego Gitea Runner, aby startował przy uruchomieniu.",
"ru": "Включить автозапуск зарегистрированного Gitea Runner.",
"zh": "启用已注册的 Gitea Runner 开机自启。"
},
"Enable failed: {error}": {
"en": "Enable failed: {error}",
"bg": "Активирането неуспешно: {error}",
"de": "Aktivierung fehlgeschlagen: {error}",
"en": "Enable failed: {error}",
"pl": "Włączanie nie powiodło się: {error}",
"ru": "Включение не удалось: {error}",
"zh": "启用失败: {error}"
},
"Enabling Gitea Runner {name} on {host}": {
"en": "Enabling Gitea Runner {name} on {host}",
"bg": "Активиране на Gitea Runner {name} на {host}",
"de": "Aktiviere Gitea Runner {name} auf {host}",
"en": "Enabling Gitea Runner {name} on {host}",
"pl": "Włączanie Gitea Runner {name} na {host}",
"ru": "Включение Gitea Runner {name} на {host}",
"zh": "正在 {host} 上启用 Gitea Runner {name}"
},
"Full log: {log_file}": {
"en": "Full log: {log_file}",
"bg": "Пълен лог: {log_file}",
"de": "Vollständiges Log: {log_file}",
"en": "Full log: {log_file}",
"pl": "Pełny log: {log_file}",
"ru": "Полный лог: {log_file}",
"zh": "完整日志: {log_file}"
},
"GITEA_REGISTRATION_TOKEN must be set (or pass --token)": {
"en": "GITEA_REGISTRATION_TOKEN must be set (or pass --token)",
"bg": "GITEA_REGISTRATION_TOKEN трябва да е зададен (или подайте --token)",
"de": "GITEA_REGISTRATION_TOKEN muss gesetzt sein (oder --token übergeben)",
"en": "GITEA_REGISTRATION_TOKEN must be set (or pass --token)",
"pl": "GITEA_REGISTRATION_TOKEN musi być ustawiony (lub podaj --token)",
"ru": "GITEA_REGISTRATION_TOKEN должен быть задан (или передайте --token)",
"zh": "必须设置 GITEA_REGISTRATION_TOKEN(或传递 --token"
},
"GITEA_URL must be set (or pass --url)": {
"en": "GITEA_URL must be set (or pass --url)",
"bg": "GITEA_URL трябва да е зададен (или подайте --url)",
"de": "GITEA_URL muss gesetzt sein (oder --url übergeben)",
"en": "GITEA_URL must be set (or pass --url)",
"pl": "GITEA_URL musi być ustawiony (lub podaj --url)",
"ru": "GITEA_URL должен быть задан (или передайте --url)",
"zh": "必须设置 GITEA_URL(或传递 --url"
},
"Gitea Runner Manager — manage Gitea Actions runners.": {
"en": "Gitea Runner Manager — manage Gitea Actions runners.",
"bg": "Gitea Runner Manager — управление на Gitea Actions runners.",
"de": "Gitea Runner Manager — Gitea Actions Runner verwalten.",
"en": "Gitea Runner Manager — manage Gitea Actions runners.",
"pl": "Gitea Runner Manager — zarządzaj runnerami Gitea Actions.",
"ru": "Gitea Runner Manager — управление Gitea Actions runners.",
"zh": "Gitea Runner Manager — 管理 Gitea Actions runners。"
},
"Gitea Runner name (default: host)": {
"en": "Gitea Runner name (default: host)",
"bg": "Име на Gitea Runner (по подразбиране: host)",
"de": "Gitea Runner-Name (Standard: host)",
"en": "Gitea Runner name (default: host)",
"pl": "Nazwa Gitea Runner (domyślnie: host)",
"ru": "Имя Gitea Runner (по умолчанию: host)",
"zh": "Gitea Runner 名称(默认: host"
},
"Gitea URL (env: GITEA_URL)": {
"en": "Gitea URL (env: GITEA_URL)",
"bg": "Gitea URL (env: GITEA_URL)",
"de": "Gitea-URL (env: GITEA_URL)",
"en": "Gitea URL (env: GITEA_URL)",
"pl": "URL Gitea (env: GITEA_URL)",
"ru": "URL Gitea (env: GITEA_URL)",
"zh": "Gitea URL(环境变量: GITEA_URL"
},
"Gitea admin API token for integration test (env: REPO_TOKEN)": {
"en": "Gitea admin API token for integration test (env: REPO_TOKEN)",
"bg": "Gitea admin API токен за интеграционен тест (env: REPO_TOKEN)",
"de": "Gitea-Admin-API-Token für Integrationstest (env: REPO_TOKEN)",
"ru": "Токен админ API Gitea для интеграционного теста (env: REPO_TOKEN)",
"zh": "Gitea 管理员 API 令牌,用于集成测试(环境变量: REPO_TOKEN"
"Gitea admin API token for integration test (env: CI_GITEA_TOKEN)": {
"bg": "Gitea admin API токен за интеграционен тест (env: CI_GITEA_TOKEN)",
"de": "Gitea-Admin-API-Token für Integrationstest (env: CI_GITEA_TOKEN)",
"en": "Gitea admin API token for integration test (env: CI_GITEA_TOKEN)",
"pl": "Token API administratora Gitea do testów integracyjnych (env: CI_GITEA_TOKEN)",
"ru": "Токен админ API Gitea для интеграционного теста (env: CI_GITEA_TOKEN)",
"zh": "Gitea 管理员 API 令牌,用于集成测试(环境变量: CI_GITEA_TOKEN"
},
"HOST": {
"en": "HOST",
"bg": "ХОСТ",
"de": "HOST",
"en": "HOST",
"pl": "HOST",
"ru": "ХОСТ",
"zh": "主机"
},
"Install and configure a Gitea Runner on a remote host.": {
"en": "Install and configure a Gitea Runner on a remote host.",
"bg": "Инсталиране и конфигуриране на Gitea Runner на отдалечен хост.",
"de": "Gitea Runner auf einem Remote-Host installieren und konfigurieren.",
"en": "Install and configure a Gitea Runner on a remote host.",
"pl": "Zainstaluj i skonfiguruj Gitea Runner na zdalnym hoście.",
"ru": "Установить и настроить Gitea Runner на удалённом хосте.",
"zh": "在远程主机上安装并配置 Gitea Runner。"
},
"Installation failed: {error}": {
"en": "Installation failed: {error}",
"bg": "Инсталацията неуспешна: {error}",
"de": "Installation fehlgeschlagen: {error}",
"en": "Installation failed: {error}",
"pl": "Instalacja nie powiodła się: {error}",
"ru": "Установка не удалась: {error}",
"zh": "安装失败: {error}"
},
"Installing Gitea Runner on {host}": {
"en": "Installing Gitea Runner on {host}",
"bg": "Инсталиране на Gitea Runner на {host}",
"de": "Gitea Runner wird auf {host} installiert",
"en": "Installing Gitea Runner on {host}",
"pl": "Instalowanie Gitea Runner na {host}",
"ru": "Установка Gitea Runner на {host}",
"zh": "正在 {host} 上安装 Gitea Runner"
},
"Integration test API retries (default: 3, env: GITEA_INTEGRATION_RETRIES)": {
"en": "Integration test API retries (default: 3, env: GITEA_INTEGRATION_RETRIES)",
"bg": "Повторни опити за интеграционен тест API (по подразбиране: 3, env: GITEA_INTEGRATION_RETRIES)",
"de": "API-Wiederholungen für Integrationstest (Standard: 3, env: GITEA_INTEGRATION_RETRIES)",
"en": "Integration test API retries (default: 3, env: GITEA_INTEGRATION_RETRIES)",
"pl": "Powtórzenia API testów integracyjnych (domyślnie: 3, env: GITEA_INTEGRATION_RETRIES)",
"ru": "Повторы API интеграционного теста (по умолчанию: 3, env: GITEA_INTEGRATION_RETRIES)",
"zh": "集成测试 API 重试次数(默认: 3,环境变量: GITEA_INTEGRATION_RETRIES"
},
"LABELS": {
"en": "LABELS",
"bg": "ЕТИКЕТИ",
"de": "LABELS",
"en": "LABELS",
"pl": "ETYKIETY",
"ru": "МЕТКИ",
"zh": "标签"
},
"List all registered runners with live status.": {
"en": "List all registered runners with live status.",
"bg": "Списък на всички регистрирани runners с актуално състояние.",
"de": "Alle registrierten Runner mit Live-Status auflisten.",
"en": "List all registered runners with live status.",
"pl": "Wyświetl wszystkie zarejestrowane runnery z aktualnym statusem.",
"ru": "Список всех зарегистрированных runners с текущим статусом.",
"zh": "列出所有已注册 runners 的实时状态。"
},
"List failed: {error}": {
"en": "List failed: {error}",
"bg": "Списъкът неуспешен: {error}",
"de": "Auflistung fehlgeschlagen: {error}",
"en": "List failed: {error}",
"pl": "Wyświetlanie nie powiodło się: {error}",
"ru": "Ошибка списка: {error}",
"zh": "列表失败: {error}"
},
"NAME": {
"en": "NAME",
"bg": "ИМЕ",
"de": "NAME",
"en": "NAME",
"pl": "NAZWA",
"ru": "ИМЯ",
"zh": "名称"
},
"No runners registered. Use 'grm install' to add one.": {
"en": "No runners registered. Use 'grm install' to add one.",
"bg": "Няма регистрирани runners. Използвайте 'grm install', за да добавите.",
"de": "Keine Runner registriert. Verwenden Sie 'grm install', um einen hinzuzufügen.",
"en": "No runners registered. Use 'grm install' to add one.",
"pl": "Brak zarejestrowanych runnerów. Użyj 'grm install', aby dodać jeden.",
"ru": "Нет зарегистрированных runners. Используйте 'grm install' чтобы добавить.",
"zh": "没有已注册的 runners。使用 'grm install' 添加一个。"
},
"Override SSH key from registry": {
"en": "Override SSH key from registry",
"bg": "Замяна на SSH ключа от регистъра",
"de": "SSH-Schlüssel aus Registrierung überschreiben",
"en": "Override SSH key from registry",
"pl": "Nadpisz klucz SSH z rejestru",
"ru": "Переопределить SSH ключ из реестра",
"zh": "覆盖注册表中的 SSH 密钥"
},
"Override host from registry": {
"en": "Override host from registry",
"bg": "Замяна на хоста от регистъра",
"de": "Host aus Registrierung überschreiben",
"en": "Override host from registry",
"pl": "Nadpisz host z rejestru",
"ru": "Переопределить хост из реестра",
"zh": "覆盖注册表中的主机"
},
"Override user from registry": {
"en": "Override user from registry",
"bg": "Замяна на потребителя от регистъра",
"de": "Benutzer aus Registrierung überschreiben",
"en": "Override user from registry",
"pl": "Nadpisz użytkownika z rejestru",
"ru": "Переопределить пользователя из реестра",
"zh": "覆盖注册表中的用户"
},
"Path to SSH private key": {
"en": "Path to SSH private key",
"bg": "Път към SSH частен ключ",
"de": "Pfad zum SSH-Private-Key",
"en": "Path to SSH private key",
"pl": "Ścieżka do klucza prywatnego SSH",
"ru": "Путь к SSH приватному ключу",
"zh": "SSH 私钥路径"
},
"Playbook not found: {playbook}": {
"en": "Playbook not found: {playbook}",
"bg": "Playbook не е намерен: {playbook}",
"de": "Playbook nicht gefunden: {playbook}",
"en": "Playbook not found: {playbook}",
"pl": "Playbook nie znaleziony: {playbook}",
"ru": "Playbook не найден: {playbook}",
"zh": "未找到 Playbook: {playbook}"
},
"Prompt for sudo password (default)": {
"en": "Prompt for sudo password (default)",
"bg": "Подканване за sudo парола (по подразбиране)",
"de": "Nach sudo-Passwort fragen (Standard)",
"en": "Prompt for sudo password (default)",
"pl": "Zapytaj o hasło sudo (domyślnie)",
"ru": "Запросить пароль sudo (по умолчанию)",
"zh": "提示输入 sudo 密码(默认)"
},
"Registration token (env: GITEA_REGISTRATION_TOKEN)": {
"en": "Registration token (env: GITEA_REGISTRATION_TOKEN)",
"bg": "Регистрационен токен (env: GITEA_REGISTRATION_TOKEN)",
"de": "Registrierungstoken (env: GITEA_REGISTRATION_TOKEN)",
"en": "Registration token (env: GITEA_REGISTRATION_TOKEN)",
"pl": "Token rejestracji (env: GITEA_REGISTRATION_TOKEN)",
"ru": "Токен регистрации (env: GITEA_REGISTRATION_TOKEN)",
"zh": "注册令牌(环境变量: GITEA_REGISTRATION_TOKEN"
},
"Remove a registered Gitea Runner completely.": {
"en": "Remove a registered Gitea Runner completely.",
"bg": "Пълно премахване на регистриран Gitea Runner.",
"de": "Einen registrierten Gitea Runner vollständig entfernen.",
"en": "Remove a registered Gitea Runner completely.",
"pl": "Usuń zarejestrowanego Gitea Runner całkowicie.",
"ru": "Полностью удалить зарегистрированный Gitea Runner.",
"zh": "完全移除已注册的 Gitea Runner。"
},
"Remove failed: {error}": {
"en": "Remove failed: {error}",
"bg": "Премахването неуспешно: {error}",
"de": "Entfernung fehlgeschlagen: {error}",
"en": "Remove failed: {error}",
"pl": "Usuwanie nie powiodło się: {error}",
"ru": "Удаление не удалось: {error}",
"zh": "移除失败: {error}"
},
"Remove runner '{name}' from local registry": {
"en": "Remove runner '{name}' from local registry",
"bg": "Премахване на runner '{name}' от локалния регистър",
"de": "Runner '{name}' aus lokaler Registrierung entfernen",
"en": "Remove runner '{name}' from local registry",
"pl": "Usuń runner '{name}' z lokalnego rejestru",
"ru": "Удалить runner '{name}' из локального реестра",
"zh": "从本地注册表移除 runner '{name}'"
},
"Removing Gitea Runner {name} from {host}": {
"en": "Removing Gitea Runner {name} from {host}",
"bg": "Премахване на Gitea Runner {name} от {host}",
"de": "Entferne Gitea Runner {name} von {host}",
"en": "Removing Gitea Runner {name} from {host}",
"pl": "Usuwanie Gitea Runner {name} z {host}",
"ru": "Удаление Gitea Runner {name} с {host}",
"zh": "正在从 {host} 移除 Gitea Runner {name}"
},
"Runner '{name}' not found in registry.": {
"en": "Runner '{name}' not found in registry."
"bg": "Runner '{name}' не е намерен в регистъра.",
"de": "Runner '{name}' nicht in der Registrierung gefunden.",
"en": "Runner '{name}' not found in registry.",
"pl": "Runner '{name}' nie znaleziony w rejestrze.",
"ru": "Runner '{name}' не найден в реестре.",
"zh": "在注册表中未找到运行器 '{name}'。"
},
"Runner '{name}' not found in registry. Use 'grm install' first or provide --host and --user.": {
"en": "Runner '{name}' not found in registry. Use 'grm install' first or provide --host and --user.",
"bg": "Runner '{name}' не е намерен в регистъра. Използвайте 'grm install' първо или подайте --host и --user.",
"de": "Runner '{name}' nicht in Registrierung gefunden. Verwenden Sie zuerst 'grm install' oder geben Sie --host und --user an.",
"en": "Runner '{name}' not found in registry. Use 'grm install' first or provide --host and --user.",
"pl": "Runner '{name}' nie znaleziony w rejestrze. Użyj najpierw 'grm install' lub podaj --host i --user.",
"ru": "Runner '{name}' не найден в реестре. Сначала используйте 'grm install' или укажите --host и --user.",
"zh": "注册表中未找到 Runner '{name}'。请先使用 'grm install' 或提供 --host 和 --user。"
},
"Runner labels for Gitea Actions (env: GITEA_RUNNER_LABELS). Example: docker:docker://alpine:latest": {
"en": "Runner labels for Gitea Actions (env: GITEA_RUNNER_LABELS). Example: docker:docker://alpine:latest",
"bg": "Етикети на runner за Gitea Actions (env: GITEA_RUNNER_LABELS). Пример: docker:docker://alpine:latest",
"de": "Runner-Labels für Gitea Actions (env: GITEA_RUNNER_LABELS). Beispiel: docker:docker://alpine:latest",
"en": "Runner labels for Gitea Actions (env: GITEA_RUNNER_LABELS). Example: docker:docker://alpine:latest",
"pl": "Etykiety runnera dla Gitea Actions (env: GITEA_RUNNER_LABELS). Przykład: docker:docker://alpine:latest",
"ru": "Метки runner для Gitea Actions (env: GITEA_RUNNER_LABELS). Пример: docker:docker://alpine:latest",
"zh": "Gitea Actions 的 runner 标签(环境变量: GITEA_RUNNER_LABELS)。示例: docker:docker://alpine:latest"
},
"Running Ansible playbook": {
"en": "Running Ansible playbook",
"bg": "Изпълнение на Ansible playbook",
"de": "Ansible-Playbook wird ausgeführt",
"en": "Running Ansible playbook",
"pl": "Uruchamianie playbook Ansible",
"ru": "Выполнение Ansible playbook",
"zh": "正在运行 Ansible playbook"
},
"SSH user": {
"en": "SSH user",
"bg": "SSH потребител",
"de": "SSH-Benutzer",
"en": "SSH user",
"pl": "Użytkownik SSH",
"ru": "SSH пользователь",
"zh": "SSH 用户"
},
"STATUS": {
"en": "STATUS",
"bg": "СТАТУС",
"de": "STATUS",
"en": "STATUS",
"pl": "STATUS",
"ru": "СТАТУС",
"zh": "状态"
},
"Save runner '{name}' to local registry": {
"en": "Save runner '{name}' to local registry",
"bg": "Запазване на runner '{name}' в локалния регистър",
"de": "Runner '{name}' in lokaler Registrierung speichern",
"en": "Save runner '{name}' to local registry",
"pl": "Zapisz runner '{name}' do lokalnego rejestru",
"ru": "Сохранить runner '{name}' в локальном реестре",
"zh": "将 runner '{name}' 保存到本地注册表"
},
"Skip remote cleanup and only remove the local registry entry": {
"en": "Skip remote cleanup and only remove the local registry entry",
"bg": "Пропуснете отдалеченото почистване и премахнете само локалния запис",
"de": "Remote-Bereinigung überspringen und nur den lokalen Registrierungseintrag entfernen",
"en": "Skip remote cleanup and only remove the local registry entry",
"pl": "Pomiń zdalne czyszczenie i usuń tylko wpis w lokalnym rejestrze",
"ru": "Пропустить удаленную очистку и удалить только локальную запись реестра",
"zh": "跳过远程清理,仅删除本地注册表条目"
},
"Specific Gitea Runner version": {
"en": "Specific Gitea Runner version",
"bg": "Конкретна версия на Gitea Runner",
"de": "Spezifische Gitea Runner-Version",
"en": "Specific Gitea Runner version",
"pl": "Konkretna wersja Gitea Runner",
"ru": "Конкретная версия Gitea Runner",
"zh": "指定 Gitea Runner 版本"
},
"Start a registered Gitea Runner.": {
"en": "Start a registered Gitea Runner.",
"bg": "Стартиране на регистриран Gitea Runner.",
"de": "Einen registrierten Gitea Runner starten.",
"en": "Start a registered Gitea Runner.",
"pl": "Uruchom zarejestrowanego Gitea Runner.",
"ru": "Запустить зарегистрированный Gitea Runner.",
"zh": "启动已注册的 Gitea Runner。"
},
"Start failed: {error}": {
"en": "Start failed: {error}",
"bg": "Стартирането неуспешно: {error}",
"de": "Start fehlgeschlagen: {error}",
"en": "Start failed: {error}",
"pl": "Uruchamianie nie powiodło się: {error}",
"ru": "Запуск не удался: {error}",
"zh": "启动失败: {error}"
},
"Starting Gitea Runner {name} on {host}": {
"en": "Starting Gitea Runner {name} on {host}",
"bg": "Стартиране на Gitea Runner {name} на {host}",
"de": "Starte Gitea Runner {name} auf {host}",
"en": "Starting Gitea Runner {name} on {host}",
"pl": "Uruchamianie Gitea Runner {name} na {host}",
"ru": "Запуск Gitea Runner {name} на {host}",
"zh": "正在 {host} 上启动 Gitea Runner {name}"
},
"Status check failed: {error}": {
"en": "Status check failed: {error}",
"bg": "Проверката на състоянието неуспешна: {error}",
"de": "Statusprüfung fehlgeschlagen: {error}",
"en": "Status check failed: {error}",
"pl": "Sprawdzanie statusu nie powiodło się: {error}",
"ru": "Проверка состояния не удалась: {error}",
"zh": "状态检查失败: {error}"
},
"Stop a registered Gitea Runner.": {
"en": "Stop a registered Gitea Runner.",
"bg": "Спиране на регистриран Gitea Runner.",
"de": "Einen registrierten Gitea Runner stoppen.",
"en": "Stop a registered Gitea Runner.",
"pl": "Zatrzymaj zarejestrowanego Gitea Runner.",
"ru": "Остановить зарегистрированный Gitea Runner.",
"zh": "停止已注册的 Gitea Runner。"
},
"Stop failed: {error}": {
"en": "Stop failed: {error}",
"bg": "Спирането неуспешно: {error}",
"de": "Stop fehlgeschlagen: {error}",
"en": "Stop failed: {error}",
"pl": "Zatrzymywanie nie powiodło się: {error}",
"ru": "Остановка не удалась: {error}",
"zh": "停止失败: {error}"
},
"Stopping Gitea Runner {name} on {host}": {
"en": "Stopping Gitea Runner {name} on {host}",
"bg": "Спиране на Gitea Runner {name} на {host}",
"de": "Stoppe Gitea Runner {name} auf {host}",
"en": "Stopping Gitea Runner {name} on {host}",
"pl": "Zatrzymywanie Gitea Runner {name} na {host}",
"ru": "Остановка Gitea Runner {name} на {host}",
"zh": "正在 {host} 上停止 Gitea Runner {name}"
},
"USER": {
"en": "USER",
"bg": "ПОТРЕБИТЕЛ",
"de": "BENUTZER",
"en": "USER",
"pl": "UŻYTKOWNIK",
"ru": "ПОЛЬЗОВАТЕЛЬ",
"zh": "用户"
},
"Update failed: {error}": {
"en": "Update failed: {error}",
"bg": "Актуализацията неуспешна: {error}",
"de": "Update fehlgeschlagen: {error}",
"en": "Update failed: {error}",
"pl": "Aktualizacja nie powiodła się: {error}",
"ru": "Обновление не удалось: {error}",
"zh": "更新失败: {error}"
},
"Update the Gitea Runner binary on a remote host.": {
"en": "Update the Gitea Runner binary on a remote host.",
"bg": "Актуализиране на Gitea Runner двоичния файл на отдалечен хост.",
"de": "Gitea Runner-Binary auf einem Remote-Host aktualisieren.",
"en": "Update the Gitea Runner binary on a remote host.",
"pl": "Zaktualizuj binaria Gitea Runner na zdalnym hoście.",
"ru": "Обновить бинарный файл Gitea Runner на удалённом хосте.",
"zh": "在远程主机上更新 Gitea Runner 二进制文件。"
},
"Updating Gitea Runner on {host}": {
"en": "Updating Gitea Runner on {host}",
"bg": "Актуализиране на Gitea Runner на {host}",
"de": "Gitea Runner wird auf {host} aktualisiert",
"en": "Updating Gitea Runner on {host}",
"pl": "Aktualizowanie Gitea Runner na {host}",
"ru": "Обновление Gitea Runner на {host}",
"zh": "正在 {host} 上更新 Gitea Runner"
},
"active": {
"en": "active",
"bg": "активен",
"de": "aktiv",
"en": "active",
"pl": "aktywny",
"ru": "активен",
"zh": "活跃"
},
"completed": {
"en": "completed",
"bg": "завършено",
"de": "abgeschlossen",
"en": "completed",
"pl": "ukończony",
"ru": "завершено",
"zh": "已完成"
},
"exit code {code}": {
"en": "exit code {code}",
"bg": "код за изход {code}",
"de": "Exit-Code {code}",
"en": "exit code {code}",
"pl": "kod wyjścia {code}",
"ru": "код выхода {code}",
"zh": "退出代码 {code}"
},
"failed": {
"en": "failed",
"bg": "неуспешен",
"de": "fehlgeschlagen",
"en": "failed",
"pl": "nieudany",
"ru": "сбой",
"zh": "失败"
},
"in_progress": {
"en": "in progress",
"bg": "в процес",
"de": "in Bearbeitung",
"en": "in progress",
"pl": "w toku",
"ru": "в процессе",
"zh": "进行中"
},
"inactive": {
"en": "inactive",
"bg": "неактивен",
"de": "inaktiv",
"en": "inactive",
"pl": "nieaktywny",
"ru": "неактивен",
"zh": "不活跃"
},
"pending": {
"en": "pending",
"bg": "чакащо",
"de": "ausstehend",
"en": "pending",
"pl": "oczekujący",
"ru": "в ожидании",
"zh": "待处理"
},
"unknown": {
"en": "unknown",
"bg": "неизвестен",
"de": "unbekannt",
"en": "unknown",
"pl": "nieznany",
"ru": "неизвестно",
"zh": "未知"
}
+6 -4
View File
@@ -7,6 +7,8 @@ from click.testing import CliRunner
from gitea_runner_manager import __version__
from gitea_runner_manager.cli import cli
_TEST_ENV = {"GITEA_URL": "https://git.example.com", "CI_GITEA_TOKEN": "", "GITEA_RUNNER_LABELS": ""}
class TestCLI:
def test_cli_version(self) -> None:
@@ -20,7 +22,7 @@ class TestCLI:
mock_manager = MagicMock()
mock_manager_class.return_value = mock_manager
runner = CliRunner(env={"GITEA_URL": "https://git.example.com", "REPO_TOKEN": "", "GITEA_RUNNER_LABELS": ""})
runner = CliRunner(env=_TEST_ENV)
result = runner.invoke(cli, ["install", "host1", "--user", "ubuntu", "--token", "tok"])
assert result.exit_code == 0
mock_manager.install.assert_called_once_with(
@@ -41,7 +43,7 @@ class TestCLI:
mock_manager = MagicMock()
mock_manager_class.return_value = mock_manager
runner = CliRunner(env={"GITEA_URL": "https://git.example.com", "REPO_TOKEN": "", "GITEA_RUNNER_LABELS": ""})
runner = CliRunner(env=_TEST_ENV)
result = runner.invoke(cli, ["install", "host1", "--user", "ubuntu", "--token", "tok", "--no-ask-become-pass"])
assert result.exit_code == 0
mock_manager.install.assert_called_once_with(
@@ -115,7 +117,7 @@ class TestCLI:
mock_manager = MagicMock()
mock_manager_class.return_value = mock_manager
runner = CliRunner(env={"GITEA_URL": "https://git.example.com", "REPO_TOKEN": "", "GITEA_RUNNER_LABELS": ""})
runner = CliRunner(env=_TEST_ENV)
result = runner.invoke(
cli,
[
@@ -150,7 +152,7 @@ class TestCLI:
mock_manager = MagicMock()
mock_manager_class.return_value = mock_manager
runner = CliRunner(env={"GITEA_URL": "https://git.example.com", "REPO_TOKEN": "", "GITEA_RUNNER_LABELS": ""})
runner = CliRunner(env=_TEST_ENV)
result = runner.invoke(cli, ["install", "host1", "--user", "ubuntu", "--token", "tok", "--ask-become-pass"])
assert result.exit_code == 0
mock_manager.install.assert_called_once_with(