Public Access
472 lines
21 KiB
Markdown
472 lines
21 KiB
Markdown
# 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](https://git.oblachno.oblachno.fyi/oblachno-oss/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).
|
|
|
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE)
|
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
|
[](https://www.python.org/downloads/)
|
|
|
|
## 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)
|
|
|
|
```bash
|
|
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:
|
|
|
|
```ini
|
|
[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:
|
|
|
|
```toml
|
|
[project]
|
|
dependencies = [
|
|
"devx>=0.49.0",
|
|
]
|
|
|
|
[tool.pip]
|
|
extra-index-url = "https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple"
|
|
```
|
|
|
|
Then install normally:
|
|
|
|
```bash
|
|
pip install -e .
|
|
```
|
|
|
|
> **Note:** If your project requires a specific devx version, pin it in
|
|
> `dependencies` (for example, `"devx==0.49.0"`) or use a version constraint
|
|
> (for example, `"devx>=0.49.0,<0.50"`).
|
|
|
|
### Optional extras
|
|
|
|
devx ships optional dependency groups for different use cases:
|
|
|
|
```bash
|
|
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](#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.
|
|
|
|
```bash
|
|
# 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.
|
|
|
|
```bash
|
|
# 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>`.
|
|
|
|
```bash
|
|
# 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 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`:
|
|
|
|
```python
|
|
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:
|
|
|
|
```bash
|
|
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 |
|
|
|
|
See [CLI Commands](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki/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:
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
```
|
|
|
|
### Change classification
|
|
|
|
Projects configure which file paths are infrastructure (no release needed) vs
|
|
user-facing (release needed) in `pyproject.toml`:
|
|
|
|
```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", # example only — 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
|
|
|
|
```bash
|
|
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` (configures `tea login` from `.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](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.*`.
|
|
|
|
```text
|
|
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.*` or `python -m devx.tools.*`
|
|
- **PYTHONPATH: src** — Workflows set `PYTHONPATH: src` (not `.:src` since there are no scripts at repo root)
|
|
- **Config via env vars** — `DEVX_*` environment variables with `.env` file fallback
|
|
- **100% test coverage** — enforced by `--cov-fail-under=100`
|
|
- **i18n by default** — all user-facing strings wrapped in `_()` for translation
|
|
|
|
See [Architecture](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki/Architecture)
|
|
and [CI/CD Workflow](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki/CI-CD-Workflow)
|
|
in the wiki for detailed documentation.
|
|
|
|
## Links
|
|
|
|
- **Wiki**: [https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
|
- **Releases**: [https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
|
- **Actions**: [https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
|
- **Source**: [https://git.oblachno.oblachno.fyi/oblachno-oss/devx](https://git.oblachno.oblachno.fyi/oblachno-oss/devx)
|
|
- **GRM (origin project)**: [https://git.oblachno.oblachno.fyi/oblachno-oss/grm](https://git.oblachno.oblachno.fyi/oblachno-oss/grm)
|
|
|
|
## License
|
|
|
|
GPL-3.0 — see [LICENSE](LICENSE).
|