Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c339698603 | ||
|
|
cbf082c78f | ||
|
|
4070135fda | ||
|
|
ace0176e3a | ||
|
|
fda1d99d86 | ||
|
|
465f45d939 | ||
|
|
103beaa0e8 | ||
|
|
2e97578269 | ||
|
|
c31ec312ac | ||
|
|
c67b810e58 | ||
|
|
ef16b07cdf | ||
|
|
2c849c7324 | ||
|
|
5804a18974 | ||
|
|
9dbe20ba73 | ||
|
|
b92c87ba68 | ||
|
|
dc4430d160 | ||
|
|
7aa0ebaefe | ||
|
|
e93da43219 | ||
|
|
b131a2872d | ||
|
|
e4dd8f308f | ||
|
|
467e0d66e6 | ||
|
|
6ee5b74bb5 | ||
|
|
21cc89899f | ||
|
|
0382e155a6 | ||
|
|
d87c0d7e9a | ||
|
|
4be480a18e | ||
|
|
de92f675ed | ||
|
|
b5803a8611 | ||
|
|
ec22d20a15 | ||
|
|
e96012af7f | ||
|
|
addef7500c | ||
|
|
8a0d2c428b | ||
|
|
d68fb6d272 | ||
|
|
6721864b87 | ||
|
|
06471d1403 | ||
|
|
1a04971622 | ||
|
|
91440c1b0a | ||
|
|
3bb8d75748 | ||
|
|
d4a8172a25 | ||
|
|
2199ec0bfe | ||
|
|
9ae9a76b11 | ||
|
|
31d0eeae98 | ||
|
|
acc768eaea | ||
|
|
cf2314c20f | ||
|
|
aac47e3472 | ||
|
|
811e7a2309 | ||
|
|
cb68c85bb5 | ||
|
|
0ba30e09ea | ||
|
|
6a02581687 |
+5
-1
@@ -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
@@ -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
@@ -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 }}" \
|
||||
|
||||
@@ -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 }}"
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,12 +8,12 @@ Each runner runs in an isolated **rootless Docker** environment under a dedicate
|
||||
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
|
||||
## Why GRM?
|
||||
|
||||
@@ -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
@@ -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
@@ -8,12 +8,12 @@ Each runner runs in an isolated **rootless Docker** environment under a dedicate
|
||||
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
|
||||
[](https://www.python.org/downloads/)
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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."
|
||||
```
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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`).
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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,3 +1,3 @@
|
||||
"""Gitea Runner Manager — lean CLI for managing Gitea Actions runners."""
|
||||
|
||||
__version__ = "0.8.0"
|
||||
__version__ = "0.10.2"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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": "未知"
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user