devx — Reusable Development & CI/CD Tools
A Python package providing reusable development and CI/CD automation tools for oblachno-oss projects. devx consolidates release management, PR automation, wiki sync, badge generation, translation checks, documentation coverage, parallel test distribution, and more into a single installable package.
It was extracted from the GRM project to be reusable across all oblachno-oss repositories. Any project hosted on a Gitea instance with Gitea Actions can install devx and inherit a complete, opinionated CI/CD pipeline: conventional commits, automated versioning via git-cliff, squash-merge automation, Vikunja task tracking, wiki sync, and quality badges.
An open-source project from Oblachno (облачно means cloudy in Bulgarian).
Why devx?
Every oblachno-oss project shares the same CI/CD needs: automated releases, PR review, task tracking, documentation sync, and quality badges. Without a shared package, each repository duplicates this logic in shell scripts and workflow YAML, leading to drift, bugs, and maintenance burden.
devx solves this by providing a single, tested Python package that any
oblachno-oss project can install. The project declares its configuration via
environment variables and pyproject.toml, and devx handles the rest. Updates
to the CI/CD pipeline ship as new devx releases — consumer projects pick them
up by bumping their devx dependency.
Key features
- Automated releases — git-cliff-driven semver versioning, changelog generation, tagging, and publishing to a Gitea PyPI registry.
- PR automation — squash-merge with task ID validation, automated PR review with inline comments, and conventional commit enforcement.
- Smart change classification — user-facing vs workflow-only change detection so infrastructure-only changes skip releases.
- Documentation sync — push
docs/markdown to the Gitea wiki with integrity verification. - Quality badges — generate self-contained SVG badges for coverage, tests, docs, quality, version, and Python version.
- Translation checks — validate i18n keys against source code, detect dead keys and missing languages.
- Parallel test distribution — split test files or molecule scenarios across CI runners with cross-runner fail-fast.
- Developer tools — environment setup, CI tool installation, test speed enforcement, repository configuration.
- i18n — built-in translations for English, Bulgarian, German, Russian, Chinese, and Polish; projects can extend with their own keys.
Installation
devx 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)
pip install devx --index-url https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple
Persistent configuration (recommended)
Add the registry to ~/.pip/pip.conf so pip install devx works without
specifying --index-url every time:
[global]
extra-index-url = https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple
As a dependency in another project
To use devx as a dependency in your pyproject.toml, add the registry as an
extra index and list devx in your dependencies:
[project]
dependencies = [
"devx>=0.35.7",
]
[tool.pip]
extra-index-url = "https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple"
Then install normally:
pip install -e .
Note: If your project requires a specific devx version, pin it in
dependencies(for example,"devx==0.35.7") or use a version constraint (for example,"devx>=0.35.7,<0.36").
Optional extras
devx ships optional dependency groups for different use cases:
pip install "devx[ci,lint]" # CI runners and linting (pytest, ruff, pyright, bandit, build, twine)
pip install "devx[molecule]" # Molecule testing for Ansible projects
pip install "devx[dev]" # Full local development (ci + lint + build + twine)
Quick start
After installing devx, set the required environment variables (see
Configuration) and invoke modules via python -m devx.* or
the devx CLI.
CI/CD automation
CI/CD modules are invoked via python -m devx.ci.*. Each module is also
available as a devx ci <command> subcommand.
# Release automation (versioning, changelog, tagging)
python -m devx.ci.release
python -m devx.ci.release --dry-run # preview without changes
python -m devx.ci.release --verify # check tag/version/changelog alignment
# Publish a release to the Gitea PyPI registry
python -m devx.ci.publish v1.0.0 oblachno-oss/devx
python -m devx.ci.publish v1.0.0 oblachno-oss/devx --skip-build # Gitea release only
# Automated PR review (posts inline comments and structured review)
python -m devx.ci.pr_review 42 oblachno-oss/devx
# Auto-merge a PR (validates title, squash-merges)
python -m devx.ci.auto_merge feature-branch "DEVX-12: Add feature" oblachno-oss/devx 42
# Classify changes (user-facing vs workflow-only)
python -m devx.ci.classify_changes --base origin/master --head HEAD --github-output
# Sync documentation to Gitea wiki
python -m devx.ci.sync_wiki --repo oblachno-oss/devx --strict
# Generate and push quality badges
python -m devx.ci.push_badges
python -m devx.ci.push_badges --retries 3 # retry on git push failures
# Check translation completeness
python -m devx.ci.check_translations
python -m devx.ci.check_translations --translations path/to/translations.json
# Documentation coverage check
python -m devx.ci.doc_coverage --fail-on-missing
# Documentation lint (structure, links, headings, TODOs)
python -m devx.ci.lint_docs --root .
# Validate a commit message
python -m devx.ci.validate_commit_msg commit-msg.txt --branch master
# Detect whether the latest commit is a release commit
python -m devx.ci.detect_release_commit
# Notify on CI failure (creates a Gitea issue)
python -m devx.ci.notify_failure --repo oblachno-oss/devx --run-id 123 \
--workflow ci --commit abc123 --auto-login
# Discover available Gitea Actions runners
python -m devx.ci.discover_runners --owner oblachno-oss --repo devx --indices
# Distribute files across parallel runners (round-robin)
python -m devx.ci.distribute_files --pattern "tests/integration/test_*.py" \
--runner-index 1 --max-runners 3 --github-env
# Run pytest with cross-runner fail-fast
python -m devx.ci.integration_guard -- test_a.py test_b.py
Developer tools
Developer tooling modules are invoked via python -m devx.tools.* or the
devx tools <command> subcommand.
# Set up a development environment (venv, deps, hooks, tea login)
python -m devx.tools.setup --bin .venv/bin
python -m devx.tools.setup --bin .venv/bin --extras "ci,lint" --no-pre-commit
# Install CI tools (actionlint, git-cliff, act_runner, tea)
python -m devx.tools.install_tools
python -m devx.tools.install_tools --tool git-cliff --tool tea
python -m devx.tools.install_tools --list
# Install checkmake (Makefile linter)
python -m devx.tools.install_checkmake
# Check unit test speed
python -m devx.tools.check_test_speed --max-seconds 10
python -m devx.tools.check_test_speed --max-seconds 4 --max-single-seconds 0.5
# Configure repository (branch protection, labels)
python -m devx.tools.configure_repo --repo devx --owner oblachno-oss
# Generate badge SVG files locally
python -m devx.tools.generate_badges --output-dir .badges/
# Generate a cliff.toml with the correct task ID prefix
python -m devx.tools.generate_cliff_config --prefix GRM
python -m devx.tools.generate_cliff_config --prefix GRM --force # overwrite existing
Molecule testing (optional)
For projects with Ansible roles, devx provides molecule testing helpers via
python -m devx.molecule.* or devx molecule <command>.
# Distribute molecule scenarios across parallel runners
python -m devx.molecule.distribute_molecule --runner-index 1 --max-runners 3
python -m devx.molecule.distribute_molecule --list # list all scenarios
python -m devx.molecule.distribute_molecule --list-platforms # list platforms
# Run molecule tests with cross-runner fail-fast
python -m devx.molecule.molecule_ci_guard pair1 pair2
python -m devx.molecule.molecule_ci_guard --roles-root ansible/roles pair1 pair2
# Run all molecule scenarios locally (sequential)
python -m devx.molecule.molecule_all
python -m devx.molecule.molecule_all --bin .venv/bin
# Discover available Gitea Actions runners for molecule tests
python -m devx.molecule.discover_runners --indices
# Ensure Docker is available for molecule tests in CI
python -m devx.molecule.start_docker
OpenTofu helpers
devx provides reusable functions for extracting values from tofu output:
from devx.opentofu import get_tofu_output, get_tofu_vm_ip, get_tofu_vm_field
vms = get_tofu_output("customer_vms", cwd="tofu/environments/staging",
env={"HCLOUD_TOKEN": token})
ip = get_tofu_vm_ip("customer_vms", "oblachno", cwd="tofu/environments/staging",
env={"HCLOUD_TOKEN": token})
CLI commands overview
devx provides a devx CLI command with three command groups:
devx --help
devx --version
devx ci — CI/CD automation
| Command | Description |
|---|---|
devx ci auto-merge |
Squash-merge a PR with task ID validation |
devx ci check-translations |
Check translation files for gaps and dead keys |
devx ci classify-changes |
Classify git changes as user-facing or workflow-only |
devx ci detect-release-commit |
Detect whether the latest commit is a release commit |
devx ci discover-runners |
Discover available Gitea Actions runners |
devx ci distribute-files |
Distribute files across parallel runners (round-robin) |
devx ci doc-coverage |
Check documentation coverage for CLI commands and modules |
devx ci integration-guard |
Run pytest with cross-runner fail-fast |
devx ci notify-failure |
Create a Gitea issue when a CI workflow fails |
devx ci post-merge |
Update Vikunja task after a merge to master |
devx ci pr-review |
Run automated PR review |
devx ci publish |
Build package, publish to registry, create Gitea release |
devx ci push-badges |
Generate badge SVG files and push to the badges branch |
devx ci release |
Automated release: version, changelog, tag, push |
devx ci sync-wiki |
Sync documentation from docs/ to the Gitea wiki |
devx ci validate-commit-msg |
Validate commit messages for conventional format |
devx tools — Developer tools
| Command | Description |
|---|---|
devx tools check-test-speed |
Run unit tests and enforce execution-time budgets |
devx tools configure-repo |
Configure branch protection and labels via Gitea API |
devx tools generate-badges |
Generate self-contained SVG badge files |
devx tools generate-cliff-config |
Generate a cliff.toml with the correct task ID prefix |
devx tools install-checkmake |
Install checkmake (Makefile linter) |
devx tools install-tools |
Install actionlint, git-cliff, act_runner, tea |
devx tools setup |
Project setup: install deps, hooks, tea login |
devx molecule — Molecule testing (optional)
| Command | Description |
|---|---|
devx molecule all |
Run all molecule scenarios on all supported platforms |
devx molecule discover-runners |
Discover available Gitea Actions runners |
devx molecule distribute |
Distribute molecule test pairs across parallel runners |
devx molecule guard |
Run molecule tests with CI failure polling |
See CLI Commands in the wiki for full command documentation with examples.
Configuration
devx reads configuration from environment variables with .env file fallback.
The config system loads .env automatically via python-dotenv.
DEVX_ environment variables
| Variable | Default | Description |
|---|---|---|
DEVX_GITEA_API_URL |
https://git.oblachno.oblachno.fyi/api/v1 |
Gitea API base URL |
DEVX_VIKUNJA_API_URL |
https://work.oblachno.oblachno.fyi/api/v1 |
Vikunja API base URL |
DEVX_REPO_OWNER |
(none — must be set) | Repository owner for API calls |
DEVX_REPO_NAME |
(none — must be set) | Repository name (or owner/repo) |
DEVX_TASK_PREFIX |
DEVX |
Task ID prefix (GRM, OBL-INFRA, etc.) |
DEVX_VIKUNJA_PROJECT_ID |
6 |
Vikunja project ID |
DEVX_LANG |
en |
Language for i18n (en, bg, de, ru, zh, pl) |
DEVX_TRANSLATIONS_PATH |
— | Path to a custom JSON translations file |
DEVX_VERSION_FILE |
src/devx/__init__.py |
Version source file (used by release) |
DEVX_DOCS_DIR |
docs |
Documentation directory (used by sync_wiki) |
DEVX_STATUS_CHECKS |
CI / quality (pull_request) |
Comma-separated status check contexts |
DEVX_PYPI_REGISTRY_URL |
— | Gitea PyPI registry URL (used by publish) |
CI_GITEA_TOKEN |
— | Gitea API token (see scopes below) |
CI_GITEA_USERNAME |
— | Gitea username for registry authentication |
VIKUNJA_TOKEN |
— | Vikunja API token |
PYPI_TOKEN |
— | Standard PyPI token (takes precedence over Gitea registry) |
CI_GITEA_TOKEN scopes
The CI_GITEA_TOKEN is a single Gitea Personal Access Token used across all
workflows. It requires these scopes:
| Scope | Purpose |
|---|---|
read:repository |
Read repos, PRs, issues, branches |
write:repository |
Push commits, merge PRs, create tags/releases, create issues, set branch protection, push wiki |
read:package |
Pull packages from Gitea PyPI registry, pull Docker images |
write:package |
Publish packages to Gitea PyPI registry, push Docker images |
read:organization |
Query org-level runners for molecule test distribution |
Per-project overrides
Projects using devx can override the default API URLs and language by setting
DEVX_* environment variables or entries in their .env file. Copy
.env.example to .env and fill in your tokens:
cp .env.example .env
Change classification
Projects configure which file paths are infrastructure (no release needed) vs
user-facing (release needed) in pyproject.toml:
[tool.devx.classify]
# Merge with DEFAULT_INFRASTRUCTURE (CI workflows, tests, docs, config)
# use_defaults = true # (default)
# Project-specific infrastructure paths (merged with defaults)
infrastructure = []
# Files that would default to user-facing but are actually infrastructure
infrastructure_overrides = [
"src/myproject/__init__.py", # only contains __version__
]
# Safety override for broad infrastructure patterns
user_facing_overrides = []
# Tag patterns for CI conditional execution (orthogonal to release impact)
[tool.devx.classify.tags]
# ansible = ["ansible/**"]
Development
git clone https://git.oblachno.oblachno.fyi/oblachno-oss/devx.git
cd devx
make setup # Create venv, install deps, hooks, CI tools
make lint-all # ruff + pyright + bandit + actionlint
make pytest-cov # Unit tests with 100% coverage
make test-unit # Unit tests without coverage
make workflow-check # Static + dry-run validation of workflow YAML
make clean # Remove caches, build artifacts, coverage data
make setup automatically installs all development tools:
- Python deps via
python -m devx.tools.setup(pip install -e .[dev], pre-commit hooks) - actionlint, git-cliff, act_runner, tea via
python -m devx.tools.install_tools - tea CLI login via
python -m devx.tools.setup(configurestea loginfrom.env)
Make targets
| Target | Description |
|---|---|
make setup |
Full local development setup (venv, deps, hooks, CI tools) |
make setup-ci |
Lean setup for CI jobs (pytest + lint + runtime deps) |
make setup-quality |
Setup for quality job (lint + test deps, actionlint) |
make setup-release |
Setup for release jobs (git-cliff, tea, lint tools) |
make install-tools |
Install actionlint, git-cliff, act_runner, tea |
make install-hooks |
Install git hooks (pre-commit, pre-push) |
make lint |
ruff check + ruff format check + pyright + bandit |
make lint-ruff |
ruff check only |
make lint-format |
ruff format check only |
make typecheck |
pyright only |
make lint-bandit |
bandit security scan only |
make lint-all |
lint + workflow-lint (actionlint) |
make lint-deps |
pip-audit dependency vulnerability scan |
make test-unit |
Unit tests without coverage |
make pytest-cov |
Unit tests with 100% coverage enforcement |
make workflow-lint |
actionlint on .gitea/workflows/*.yml |
make workflow-dryrun |
act_runner exec --dryrun on all workflows |
make workflow-check |
workflow-lint + workflow-dryrun |
make clean |
Remove caches, build artifacts, coverage data |
See AGENTS.md for full project conventions, PR workflow, and architecture details.
Architecture overview
devx is a self-contained Python package under src/devx/. It never imports
from scripts outside the package. All tools are invoked via
python -m devx.ci.*, python -m devx.tools.*, or python -m devx.molecule.*.
src/devx/
├── __init__.py # Version (single source of truth, read by setuptools)
├── cli.py # Click-based CLI entry point (devx command)
├── config.py # Configuration system (DEVX_ env vars, .env loading)
├── api_clients.py # GiteaClient, VikunjaClient — HTTP API wrappers
├── gitea_cli.py # TeaCLI — wrapper around tea CLI with JSON parsing
├── i18n.py # Translation system (gettext-based, translations.json)
├── exceptions.py # Custom exception types (DevxError, APIError)
├── opentofu.py # OpenTofu output helpers
├── translations.json # Translation strings (en, bg, de, ru, zh, pl)
├── ci/ # CI/CD automation modules (run by workflows)
├── tools/ # Developer tooling modules (run locally or by CI)
└── molecule/ # Optional molecule testing helpers (for Ansible projects)
Design principles
- Self-contained package —
src/devx/never imports from scripts outside the package - Module-based invocation — All tools invoked via
python -m devx.ci.*orpython -m devx.tools.* - PYTHONPATH: src — Workflows set
PYTHONPATH: src(not.:srcsince there are no scripts at repo root) - Config via env vars —
DEVX_*environment variables with.envfile fallback - 100% test coverage — enforced by
--cov-fail-under=100 - i18n by default — all user-facing strings wrapped in
_()for translation
See Architecture and CI/CD Workflow in the wiki for detailed documentation.
Links
- Wiki: https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki
- Releases: https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases
- Actions: https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions
- Source: https://git.oblachno.oblachno.fyi/oblachno-oss/devx
- GRM (origin project): https://git.oblachno.oblachno.fyi/oblachno-oss/grm
License
GPL-3.0 — see LICENSE.