diff --git a/Architecture.md b/Architecture.md index c79bec1..18572e5 100644 --- a/Architecture.md +++ b/Architecture.md @@ -1 +1,600 @@ # Architecture + +devx is a reusable Python package providing development and CI/CD tools for +oblachno-oss projects. It is self-contained under `src/devx/` and never imports +from scripts outside the package. + +## Package structure + +```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 (JSON-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) +│ ├── __init__.py +│ ├── _shared.py # Shared utilities (get_latest_tag) +│ ├── release.py # Automated versioning, tagging, changelog +│ ├── publish.py # Build and publish to Gitea PyPI registry +│ ├── auto_merge.py # Squash-merge PRs with task ID validation +│ ├── classify_changes.py # User-facing vs workflow-only change detection +│ ├── detect_release_commit.py # Detect release commits on master +│ ├── validate_commit_msg.py # Conventional commit validation +│ ├── pr_review.py # Automated PR review +│ ├── post_merge.py # Vikunja task updates after merge +│ ├── sync_wiki.py # Sync documentation to Gitea wiki +│ ├── push_badges.py # Generate and push quality badges +│ ├── notify_failure.py # Create Gitea issues on CI failures +│ ├── distribute_files.py # Distribute files across parallel runners +│ ├── integration_guard.py # Run pytest with cross-runner fail-fast +│ ├── discover_runners.py # Dynamic Gitea runner discovery +│ ├── check_translations.py # Translation completeness check +│ └── doc_coverage.py # Documentation coverage check +├── tools/ # Developer tooling modules (run locally or by CI) +│ ├── __init__.py +│ ├── setup.py # Environment setup (venv, deps, hooks, tea login) +│ ├── install_tools.py # Install actionlint, git-cliff, act_runner, tea +│ ├── check_test_speed.py # Measure unit test execution time +│ ├── check_test_isolation.py # Pytest plugin: detect un-hermetic test patterns +│ ├── configure_repo.py # Branch protection and label setup +│ ├── generate_badges.py # Badge SVG generation +│ ├── generate_cliff_config.py # Generate cliff.toml with correct prefix +│ └── install_checkmake.py # Install checkmake (Makefile linter) +└── molecule/ # Optional molecule testing helpers (Ansible projects) + ├── __init__.py + ├── discover_runners.py # Dynamic Gitea runner discovery + ├── distribute_molecule.py # Distribute scenarios across runners + ├── molecule_ci_guard.py # Run molecule with cross-runner fail-fast + ├── molecule_all.py # Run all molecule scenarios locally + ├── start_docker.py # Ensure Docker is available for molecule + └── platforms.py # Supported molecule platforms +``` + +## Core modules + +### `__init__.py` + +Contains only `__version__`, the single source of truth for the package +version. Read by setuptools via `dynamic = ["version"]` in `pyproject.toml`. +Updated automatically by `devx.ci.release` during the release process. Treated +as infrastructure (not user-facing) by the change classifier since it is a +release artifact, not user code. + +### `cli.py` + +Click-based CLI entry point. Provides three command groups: `devx ci`, +`devx tools`, and `devx molecule`. Each subcommand delegates to the +corresponding module via `_run_module()`, which imports the module, sets +`sys.argv`, and calls its `main()` function. This design keeps all logic in +the modules themselves — `cli.py` is purely a router. + +The CLI is registered as a console script via `pyproject.toml`: +```toml +[project.scripts] +devx = "devx.cli:cli" +``` + +### `config.py` + +Shared configuration constants for all devx modules. All defaults can be +overridden via environment variables with the `DEVX_` prefix. Provides: + +- `GITEA_API_URL` / `VIKUNJA_API_URL` — API endpoints +- `REPO_OWNER` — repository owner (must be set per-project) +- `TASK_PREFIX` / `TASK_ID_RE` — task ID prefix and regex (for example, `DEVX-N`) +- `VIKUNJA_PROJECT_ID` — Vikunja project for task tracking +- `DEFAULT_TIMEOUT`, `DEFAULT_PER_PAGE` — HTTP client defaults +- `MAX_RETRIES`, `RETRY_BACKOFF_BASE`, `RETRY_STATUS_CODES` — retry config +- `CONVENTIONAL_RE` — conventional commit format regex + +### `exceptions.py` + +Custom exception hierarchy: + +- `DevxError` — base exception for all devx errors +- `APIError(DevxError)` — raised when a REST API call returns an HTTP error. + Carries `status` (HTTP status code) and `message` (error message). + +### `i18n.py` + +Simple i18n system using a JSON translations file (`translations.json`). +Supports six languages: `en`, `bg`, `de`, `pl`, `ru`, `zh`. The `_()` function +wraps user-facing strings for translation. + +Projects can extend translations by setting `DEVX_TRANSLATIONS_PATH` to a +custom JSON file. Keys from the project's file are merged on top of devx's +built-in translations, allowing projects to override or add keys without +modifying the package. + +### `api_clients.py` + +Reusable HTTP API clients with connection pooling and retry logic. Both +clients retry on transient errors (429, 5xx, connection errors) with +exponential backoff (2s, 4s, 8s). + +**`GiteaClient`** — Gitea REST API wrapper: +- Branch protection (get, create, update) +- Labels (list, create, add to issues) +- Issues (create, list) +- Pull requests (get commits, merge, create review) +- Releases (list, create idempotent) +- Actions (list runs, list jobs, get job logs) +- Actions variables (get, set idempotent) +- Wiki pages (list, fetch, create, update, delete) + +**`VikunjaClient`** — Vikunja REST API wrapper: +- Tasks (list project tasks, get, update, mark done) +- Comments (create) + +### `gitea_cli.py` + +Thin Python wrapper around the `tea` Gitea CLI tool. Parses JSON output for +structured data. Used by CI scripts for Gitea API operations that tea handles +well, avoiding hand-rolled HTTP requests. + +**`TeaCLI`** operations: +- `create_issue()` — Create issues with labels +- `list_labels()` / `create_label()` / `add_label()` — Label management +- `create_pr()` / `merge_pr()` / `review_pr()` — Pull request operations +- `create_release()` / `list_releases()` — Release management +- `list_branches()` — Branch listing + +Operations NOT supported via tea (still use `GiteaClient`): +- Wiki page management +- Commit status checks +- Runner discovery +- PR file/commit listing (tea has limited support) +- Branch protection with detailed config + +### `opentofu.py` + +OpenTofu output helpers for CI/CD deployment scripts. Provides reusable +functions for extracting values from `tofu output` in a structured way, +eliminating duplicated `subprocess.run` boilerplate: + +- `get_tofu_output(output_name, cwd, env)` — Run `tofu output -json` and return parsed JSON +- `get_tofu_vm_ip(output_name, vm_name, cwd, env)` — Extract a VM's IP address +- `get_tofu_vm_field(output_name, vm_name, field, cwd, env)` — Extract a VM field + +## CI/CD modules (`devx.ci`) + +Modules in this package are run by Gitea Actions workflows. They may import +from `devx.api_clients`, `devx.config`, `devx.gitea_cli`, and `devx.i18n`. + +### `release.py` + +Automated release using git-cliff. Calculates the next semver version from +conventional commits since the last tag, updates `__version__` in +`__init__.py` and `CHANGELOG.md`, runs lint and tests to verify the release +is healthy, commits with `release: vX.Y.Z [skip ci]`, creates an annotated +tag, and pushes both to master. + +Idempotent: if there are no new conventional commits since the last tag, it +exits without doing anything. If the tag already exists, it skips tag creation +and only pushes. Includes a `--verify` mode that checks tag/version/changelog +alignment without making changes. + +### `publish.py` + +Builds the Python package with `python -m build`, publishes to a Gitea PyPI +registry (or standard PyPI if `PYPI_TOKEN` is set), and creates a Gitea +release with git-cliff-generated notes. Supports `--skip-build` for non-Python +repos that only need a Gitea release. + +### `auto_merge.py` + +Auto-merges a PR when all CI checks pass. Reads the task ID from the branch +name, validates the PR title format against +the Vikunja task title, extracts the conventional commit message from PR +commits, and squash-merges with title `{PREFIX}-N `. + +If the head branch is behind master (HTTP 405), it automatically pulls master, +rebases, force-pushes, and retries the merge. + +### `classify_changes.py` + +Classifies git changes between two refs as user-facing or workflow-only. Uses +a layered rule system configured in `pyproject.toml` under +`[tool.devx.classify]`: + +1. **User-facing overrides** (highest priority — safety override) +2. **Infrastructure overrides** (explicit per-file) +3. **Infrastructure patterns** (DEFAULT_INFRASTRUCTURE + project-specific) +4. **Default**: user-facing (safe default — any unknown file triggers release) + +Also supports custom tags (orthogonal to release impact) for CI conditional +execution (for example, `ansible` tag to trigger molecule tests). + +### `pr_review.py` + +Automated PR review. Fetches the PR diff via the Gitea API and runs a series +of checks, posting a structured review with `COMMENT` (no issues) or +`REQUEST_CHANGES` (issues found): + +- Architecture compliance (no subprocess in CLI, no hardcoded URLs) +- Best practices (no `print()`, no bare `except`, no `TODO`/`FIXME`, no + functions > 50 lines) +- Security (no hardcoded secrets, no `shell=True`, no `eval`/`exec`) +- i18n (no raw strings in `click.echo()` without `_()` wrapper) +- Resource management (no `open()` without `with`, no `Popen()` without cleanup) +- Documentation (source changes must include doc updates) +- Test coverage (source changes must include test updates) +- Commit conventions (conventional commit format on PR commits) + +### `sync_wiki.py` + +Syncs documentation from `docs/` to the Gitea wiki via the API. Reads +`docs/mapping.json` to map file paths to wiki page titles, then creates or +updates pages. Supports `--dry-run`, `--verify` (check content), and +`--strict` (full integrity check: page count, missing pages, stale pages, +content match). + +### `push_badges.py` + +Generates SVG badge files using `devx.tools.generate_badges`, pushes them to +an orphan `badges` branch, and updates `README.md` and `docs/index.md` on +master with cache-busting `raw/commit//badge.svg` URLs (Gitea caches +`raw/branch/` URLs for 6 hours). Fetches latest master before generating +badges so the version badge reflects the current state. Supports `--retries` +for retrying on git push failures. + +### `notify_failure.py` + +Creates a Gitea issue when a CI workflow fails. Uses the `tea` CLI for issue +creation with failure labels. Supports `--auto-login` to configure the tea +CLI login profile from `CI_GITEA_TOKEN` and `DEVX_GITEA_API_URL` before creating +the issue. + +### `post_merge.py` + +Updates the Vikunja task after a merge to master. Extracts the task ID from +the commit message, marks the task as done, and posts a comment with the +merge SHA. + +### `validate_commit_msg.py` + +Validates commit messages. On feature branches: conventional commits only +(no `{PREFIX}-N` prefix). On master: must have `{PREFIX}-N` prefix from +auto-merge, followed by a conventional commit message. + +### `detect_release_commit.py` + +Detects whether the latest git commit is a release commit +(`release: vX.Y.Z [skip ci]`). Writes `is-release=true` or `is-release=false` +to `$GITHUB_OUTPUT` for use in CI workflow conditionals. + +### `check_translations.py` + +Validates translation files against the Python source code. Checks for +missing keys (used in code but not in translations), dead keys (defined but +not used), and missing languages (a key exists but is missing one of the five +supported languages). Supports checking additional translation sets via +`--translations`. + +### `doc_coverage.py` + +Checks documentation coverage for CLI commands and major modules. Parses +Click commands from `cli.py` and verifies each has documentation in +`docs/user/cli-commands.md`. Checks that core modules are documented in +`architecture.md` and CI scripts in `ci-cd-workflow.md`. Supports +`--fail-on-missing` to enforce 100% coverage. + +### `discover_runners.py` + +Discovers available Gitea Actions runners at three levels: repository, +organization, and instance (admin). Falls back to the `MOLECULE_RUNNERS` repo +variable or `DEFAULT_MAX_RUNNERS` (3). Outputs runner count or a JSON index +array for use as a dynamic matrix in Gitea Actions. + +### `distribute_files.py` + +Distributes files matching a glob pattern across N parallel runners +(round-robin). Writes the assigned file list for the current runner to +`$GITHUB_ENV`. Used for splitting test suites across CI runners. + +### `integration_guard.py` + +Runs pytest with the same cross-runner failure detection mechanism used by +`molecule_ci_guard`. If any other integration-tests matrix runner reports +failure, the current pytest subprocess is killed and this runner exits early. + +## Developer tools (`devx.tools`) + +Modules in this package are run locally or by CI setup jobs. They may import +from `devx.api_clients`, `devx.config`, and `devx.gitea_cli`. + +### `setup.py` + +Project setup: installs Python dependencies (editable mode with extras), +Ansible Galaxy collections (if `ansible/requirements.yml` exists in the target repo), pre-commit +hooks (pre-commit, commit-msg, pre-push), and configures the `tea` CLI login +profile from `.env`. Supports `--extras` to specify dependency groups, +`--no-pre-commit` to skip hook installation, and `--no-tea-login` to skip tea +configuration. + +### `install_tools.py` + +Installs CI/CD development tools that are not Python packages: actionlint, +git-cliff, act_runner, and tea. Each tool is installed to `~/.local/bin` if +not already on PATH. Idempotent: skips tools that are already available. +Supports `--tool` to install specific tools and `--list` to show status. + +### `check_test_speed.py` + +Runs unit tests and enforces execution-time budgets. Two quality gates: +total suite time must not exceed `--max-seconds` (default: 10s), and no +individual test may exceed `--max-single-seconds` (default: 0.5s, 0 to +disable). Runs `make test-unit` with `PYTEST_ADDOPTS=--durations=0`. + +### `check_test_isolation.py` + +Pytest plugin (auto-discovered via `pytest11` entry point) that +statically analyzes test files for un-hermetic patterns causing slow +or flaky tests: unpatched `subprocess.run`/`time.sleep` calls, known +subprocess-spawning helpers called without `@patch`, and excessive +loop iterations (>100). Also available as a standalone CLI for CI +gates and pre-commit hooks. See ADR-0001 for design rationale. + +### `configure_repo.py` + +Configures repository branch protection and labels via the Gitea REST API. +Sets up master branch protection (required status checks, block on rejected +reviews, block on outdated branch) and creates standard labels. Status check +contexts are read from `DEVX_STATUS_CHECKS` or default to +`CI / validate (pull_request)`. + +### `generate_badges.py` + +Generates self-contained SVG badge files from project metrics. Runs +pytest-cov, doc-coverage, lint checks, and version extraction, then writes +SVG files that can be served as static files from the Gitea raw file API. +Badges generated: coverage, tests, docs, quality, version, python. + +### `generate_cliff_config.py` + +Generates a `cliff.toml` configuration file with the correct task ID prefix +preprocessor. Eliminates the need to manually duplicate and maintain +`cliff.toml` across repos that use devx. Supports `--prefix` to set the task +ID prefix and `--force` to overwrite an existing file. + +### `install_checkmake.py` + +Installs checkmake (Makefile linter) if not already present. Tries +`go install` first if Go is available, otherwise downloads the latest +pre-built Linux binary from the official GitHub releases. + +## Molecule modules (`devx.molecule`) + +Optional modules for projects with Ansible roles. Requires the `molecule` +extra (`pip install devx[molecule]`). + +### `distribute_molecule.py` + +Distributes molecule (scenario, platform) pairs across N parallel runners. +Discovers scenarios under `ansible/roles/*/molecule/` and crosses them with +the supported OS platform matrix. Supports `--roles-root` for multi-role +repositories, `--list` to list scenarios, and `--list-platforms` to list +platforms. + +### `molecule_ci_guard.py` + +Runs molecule tests sequentially while polling the Gitea API for other runner +failures. If any other molecule matrix runner reports failure, the current +molecule subprocess is killed and this runner exits early. Supports both +single-role (4-part) and multi-role (5-part) pair encoding. + +### `molecule_all.py` + +Runs all molecule scenarios on all supported OS platforms sequentially. +Intended for local development; CI uses the parallel matrix instead. + +### `molecule/discover_runners.py` + +Discovers available Gitea Actions runners for molecule tests. Same logic as +`devx.ci.discover_runners` but intended for molecule-specific workflows. + +### `start_docker.py` + +Ensures Docker is available for molecule tests in CI. Verifies Docker is +accessible and sets `DOCKER_HOST` explicitly. If the host socket is not +available, tries the rootless socket, then starts a local `dockerd` with the +vfs storage driver (requires privileged container). + +### `platforms.py` + +Single source of truth for the supported OS platform matrix. Each entry maps +a short name to (image, command). Uses the project's pre-built +molecule-test-base image with `sleep infinity` (not systemd) to avoid cgroup +v2 failures. Supports loading custom platforms from a JSON file. + +## Design principles + +- **Self-contained package** — `src/devx/` never imports from scripts outside + the package. This allows devx to be installed and used as a dependency + without requiring a specific repo layout in the consumer. +- **Module-based invocation** — All tools invoked via `python -m devx.ci.*`, + `python -m devx.tools.*`, or `python -m devx.molecule.*`. The `devx` CLI is + a thin router that delegates to module `main()` functions. +- **PYTHONPATH: src** — Workflows set `PYTHONPATH: src` (not `.:src` since + there are no scripts at repo root). The `src` directory is the sole import + root. +- **Config via env vars** — `DEVX_*` environment variables with `.env` file + fallback. Projects override defaults via environment or `.env`, never by + editing package code. +- **100% test coverage** — enforced by `--cov-fail-under=100` in pytest. +- **i18n by default** — all user-facing strings wrapped in `_()` for + translation. Five languages supported out of the box. +- **Safe-by-default classification** — any file that doesn't match an + infrastructure pattern defaults to user-facing, triggering a release. This + prevents new file types from accidentally skipping releases. +- **Secrets via environment** — secrets are passed via environment variables, + never on the command line. + +## Import rules + +1. **`src/devx/` is self-contained** — the package never imports from outside `src/` +2. **CI modules** (`devx.ci.*`) may import from `devx.api_clients`, + `devx.config`, `devx.gitea_cli`, `devx.i18n` +3. **Tool modules** (`devx.tools.*`) may import from `devx.api_clients`, + `devx.config`, `devx.gitea_cli` +4. **Cross-module imports** within `devx.ci.*` or `devx.tools.*` are allowed + but must be documented (for example, `release.py` imports from + `classify_changes.py`) + +## Data flow + +### PR lifecycle + +```text +Developer creates Vikunja task (DEVX-N) + │ + ▼ +Developer creates branch (DEVX-N-short-description) + │ + ▼ +Developer commits (conventional commits, no DEVX-N prefix) + │ + ▼ +Developer pushes and creates PR (title: "DEVX-N: ") + │ + ▼ +CI workflow (ci.yml) triggers: + │ + ├── validate (single job: quality + detect-changes + + │ release-dry-run + pr-review + pre-merge validation) + │ ├── quality steps (lint, tests, coverage, test speed, doc coverage, + │ │ translation check, dependency scan, workflow dry-run) + │ ├── detect-changes (classify_changes.py → user-facing or workflow-only) + │ │ └── if user-facing → release-dry-run (release.py --dry-run) + │ ├── pre-merge validation (check_auto_merge_ready.py) + │ └── pr-review (pr_review.py → posts COMMENT or REQUEST_CHANGES) + │ + └── auto-merge (auto_merge.py) + ├── validate PR title format + ├── validate PR title matches Vikunja task title + ├── extract conventional commit message from PR commits + ├── squash-merge with "DEVX-N " title + └── push to master + │ + ▼ + Post-merge workflow triggers (see below) +``` + +### Post-merge flow + +```text +Push to master (squash-merge commit: "DEVX-N ") + │ + ▼ +Post-merge workflow (post-merge.yml) triggers: + │ + ├── detect-and-configure (single job) + │ ├── configure-repo (configure_repo.py) + │ ├── detect-type (detect_release_commit.py) + │ │ └── is-release? → skip all steps except badges + │ └── validate-commit-msg (validate_commit_msg.py --branch master) + │ + └── release-and-maintain (needs detect-and-configure) + ├── release (release.py) [skip if release commit or workflow-only] + │ ├── classify_changes.py → skip if workflow-only + │ ├── git-cliff → calculate next version + │ ├── update __version__ in __init__.py + │ ├── update CHANGELOG.md + │ ├── run make lint-ruff && make pytest-cov + │ ├── commit "release: vX.Y.Z [skip ci]" + │ ├── create annotated tag vX.Y.Z + │ └── push commit + tag to master + │ │ + │ ▼ + │ publish (publish.py) [if release created a tag] + │ ├── build package (python -m build) + │ ├── publish to Gitea PyPI registry (twine upload) + │ │ OR publish to standard PyPI (if PYPI_TOKEN set) + │ │ OR skip publish (if --skip-build) + │ └── create Gitea release with git-cliff notes + │ + ├── sync-wiki (sync_wiki.py --strict) [skip if automated] + │ └── sync docs/ to Gitea wiki with integrity check + │ + ├── vikunja (post_merge.py) [skip if automated] + │ ├── extract task ID from commit message + │ ├── mark Vikunja task as done + │ └── post comment with merge SHA + │ + └── badges (push_badges.py) [ALWAYS runs, even on release commits] + ├── fetch latest master + ├── generate_badges.py → SVG files + ├── push to orphan badges branch + └── update README.md + docs/index.md with cache-busting URLs +``` + +### Publish flow + +```text +Within release-and-maintain job (after release step creates a tag): + │ + ├── install build, twine, git-cliff, tea + ├── configure tea login + ├── checkout release tag + │ + └── publish (publish.py) + ├── build package (python -m build) + ├── publish to Gitea PyPI registry (twine upload) + │ OR publish to standard PyPI (if PYPI_TOKEN set) + │ OR skip publish (if --skip-build) + └── create Gitea release with git-cliff notes +``` + +### Badge generation flow + +```text +push_badges.py: + │ + ├── fetch_latest_master() → git fetch + reset --hard origin/master + │ + ├── generate_badges() → devx.tools.generate_badges + │ ├── run pytest-cov → parse coverage % + │ ├── run pytest → parse test count + │ ├── run doc_coverage → parse doc coverage % + │ ├── run lint → quality status + │ ├── read __version__ from __init__.py + │ └── write SVG files to .badges/ + │ + ├── push_to_badges_branch() + │ ├── git checkout --orphan badges + │ ├── git rm -rf . + │ ├── copy SVG files to root + │ ├── git commit "Update badges [skip ci]" + │ ├── git push origin badges --force + │ └── return commit SHA + │ + └── update_readme_with_badge_sha() + ├── git checkout master + ├── replace raw/branch/badges/ URLs with raw/commit// URLs + ├── git commit "chore: update badge URLs [skip ci]" + └── git push origin master +``` + +## tea CLI integration + +The `tea` Gitea CLI tool is used for Gitea API interactions where tea provides +reliable, official support. It is installed by +`python -m devx.tools.install_tools` and configured by +`python -m devx.tools.setup` (login profile from `.env` `CI_GITEA_TOKEN`). + +`devx.gitea_cli.TeaCLI` wraps tea with JSON output parsing. Operations that +tea does not support (wiki management, commit status, runner discovery, +detailed branch protection) fall back to `GiteaClient` (direct HTTP). + +## Version source + +The version source is `__version__` in `src/devx/__init__.py`, read by +setuptools via `dynamic = ["version"]` in `pyproject.toml`. The release +script updates this file, commits it, and tags the commit. This ensures the +package version, git tag, and changelog always stay aligned. diff --git a/CI-CD-Workflow.-.md b/CI-CD-Workflow.-.md index 2be3e5a..094dbbc 100644 --- a/CI-CD-Workflow.-.md +++ b/CI-CD-Workflow.-.md @@ -1 +1,581 @@ -# CI-CD-Workflow +# CI/CD Workflow + +devx uses Gitea Actions for CI/CD automation. Two workflows implement a +complete pipeline: pull request validation and post-merge release +automation (including publishing). + +## Workflow overview + +```text +PR opened/synchronized ──► CI (ci.yml) + │ ├── validate (quality + detect-changes + + │ │ release-dry-run + pr-review + + │ │ pre-merge validation) + │ └── auto-merge ──► squash-merge to master + │ │ + ▼ ▼ +Push to master ──► Post-merge (post-merge.yml) + ├── detect-and-configure (detect-type + + │ validate-commit-msg + + │ configure-repo) + └── release-and-maintain + ├── release ──► tag vX.Y.Z + ├── publish ──► Gitea PyPI registry + Gitea release + ├── sync-wiki + ├── vikunja + └── badges (always runs) +``` + +## CI workflow (`ci.yml`) + +Runs on pull requests (opened and synchronize) and manual dispatch. + +### Jobs + +#### `validate` + +The single validation job. Consolidates the former `quality`, +`detect-changes`, `release-dry-run`, `pr-review`, and `pre-merge-check` +jobs into one job to save checkout+setup overhead. Runs on every PR. + +**Quality steps** + +The main quality gate: + +1. **Lint all** — ruff check, ruff format check, pyright, bandit, actionlint + (via `make lint-all`) +2. **Unit tests with 100% coverage** — `make pytest-cov` +3. **Check unit test speed** — `python -m devx.tools.check_test_speed + --max-seconds 4 --max-single-seconds 0.5` +4. **Documentation coverage check** — `python -m devx.ci.doc_coverage + --fail-on-missing` +5. **Translation completeness check** — `python -m devx.ci.check_translations` +6. **Dependency security scan** — `pip-audit --desc --skip-editable` + (best-effort, non-blocking) +7. **Workflow dry-run validation** — `make workflow-dryrun` via act_runner + (best-effort, skipped if act_runner is not installed) + +**`detect-changes` step** + +Classifies changes between `origin/master` and the PR head as user-facing or +workflow-only using `python -m devx.ci.classify_changes --github-output`. +Writes `user-facing-changed=true|false` to the job output for use by +downstream steps. + +**`release-dry-run` step** + +Only runs if the detect-changes step detected user-facing changes. Runs +`python -m devx.ci.release --dry-run` to validate that the release script +can calculate the next version and generate the changelog without making +changes. Non-blocking (uses `|| true`). + +**`pr-review` step** + +Runs on every pull request. Executes `python -m devx.ci.pr_review` with the +PR number and repository. Fetches the PR diff via the Gitea API and runs +automated checks, posting a structured review: + +- `COMMENT` — no issues found +- `REQUEST_CHANGES` — issues found that must be addressed + +Checks performed: +1. Architecture compliance — no subprocess in CLI, no hardcoded URLs +2. Best practices — no `print()`, no bare `except`, no `TODO`/`FIXME`, + no functions > 50 lines +3. Security — no hardcoded secrets, no `shell=True`, no `eval`/`exec` +4. i18n — no raw strings in `click.echo()` without `_()` wrapper +5. Resource management — no `open()` without `with`, no `Popen()` without + cleanup +6. Documentation — source changes must include doc updates +7. Test coverage — source changes must include test updates +8. Commit conventions — conventional commit format on PR commits + +**Pre-merge validation step** + +Runs on every pull request. Executes +`python -m devx.ci.check_auto_merge_ready` with the branch name, PR title, +repository, and PR number. Validates auto-merge preconditions before the +`auto-merge` job runs: + +1. **Branch name** — must contain a valid task ID (for example, + `DEVX-12-fix-foo` → `DEVX-12`) +2. **PR title format** — must be `{PREFIX}-N: ` +3. **Vikunja task** — must exist and the title must match the PR title +4. **Branch state** — must not be behind master + +#### `auto-merge` + +Depends on `validate`. The final job in the CI workflow. Runs +`python -m devx.ci.auto_merge` with the branch name, PR title, repository, +and PR number: + +1. **Read task ID** from branch name (for example, `DEVX-12-fix-foo` → `DEVX-12`) +2. **Validate PR title format** — must be `{PREFIX}-N: ` +3. **Validate PR title matches Vikunja task** — fetches the Vikunja task and + compares the title +4. **Extract conventional commit message** from PR commits (newest matching + conventional format) +5. **Squash-merge** with title `{PREFIX}-N ` +6. If the head branch is behind master (HTTP 405), automatically pulls master, + rebases, force-pushes, and retries the merge + +The merge commit push to master triggers the post-merge workflow. + +### Smart CI: user-facing vs workflow-only changes + +Not all changes require a new release. The `detect-changes` step in the +`validate` job classifies changes using +`python -m devx.ci.classify_changes`: + +**Workflow-only paths** (infrastructure — no release needed): +- `.gitea/**` — Gitea Actions workflows +- `tests/**` — Test files +- `AGENTS.md`, `README.md`, `CHANGELOG.md` — Project docs +- `Makefile`, `cliff.toml`, `.pre-commit-config.yaml` — Config +- `.env.example`, `.gitignore` — Config +- `hooks/**` — Git hooks +- `src/devx/__init__.py` — Only contains `__version__` (release artifact) + +**User-facing paths** (tool changes — release needed) — everything else: +- `src/devx/**` — Python package source (except `__init__.py`) +- `pyproject.toml` — Package metadata +- Any new file type not in the allowlist + +Classification is configured in `pyproject.toml` under +`[tool.devx.classify]`. The framework provides `DEFAULT_INFRASTRUCTURE` — a +curated list of paths that are infrastructure for any Python project. Projects +inherit these automatically and only specify what is different. + +Rule priority (first match wins): +1. `user_facing_overrides` — safety override (highest priority) +2. `infrastructure_overrides` — explicit per-file +3. `infrastructure` — DEFAULT_INFRASTRUCTURE + project-specific patterns +4. Default: user-facing (safe — any unknown file triggers release) + +## Post-merge workflow (`post-merge.yml`) + +Runs on every push to master. Consolidated into 2 jobs (from 7) to reduce +runner overhead: `detect-and-configure` (detect-type + validate-commit-msg + +configure-repo) and `release-and-maintain` (release + publish + sync-wiki + +badges + vikunja). Individual steps within `release-and-maintain` are +conditional on the `detect-and-configure` job's outputs. + +### Job dependency graph + +```text +detect-and-configure + ├── configure-repo (independent, skip if release commit) + ├── detect-type → is-release? is-automated? + └── validate-commit-msg (skip if release commit) + │ + ▼ +release-and-maintain (needs detect-and-configure) + ├── release (skip if release commit or workflow-only) + │ └── publish (if release created a tag) + ├── sync-wiki (skip if automated) + ├── vikunja (skip if automated) + └── badges (always runs) +``` + +`sync-wiki` and `vikunja` run only on non-automated commits (that is, real PR +merges) so that the wiki and task tracker are only updated when a human +change lands. They skip on release commits and automated commits. + +The `badges` step always runs (even on release commits) so badges (tests, +coverage, version, etc.) are always current. It runs last so it picks up +any version bump the release step created. + +When `release` creates a `release: vX.Y.Z` commit, the release commit's +post-merge run still updates badges (the version badge picks up the new +version). Other steps skip. The `publish` step builds and publishes the +package to the Gitea PyPI registry within the same `release-and-maintain` +job (it checks out the release tag). + +### Post-merge jobs + +#### `detect-and-configure` + +The first post-merge job. Consolidates the former `detect-type`, +`validate-commit-msg`, and `configure-repo` jobs. Outputs `is-release`, +`is-automated`, and `user-facing-changed` for the `release-and-maintain` +job. + +**`detect-type` step** + +Checks if the latest commit is a release commit (`release: vX.Y.Z [skip ci]`) +using `python -m devx.ci.detect_release_commit`. Writes `is-release=true` or +`is-release=false` (and `is-automated`) to the job output. The +`release-and-maintain` job uses these to conditionally skip steps for +release commits. + +**`validate-commit-msg` step** + +Skips for release/automated commits. Validates the latest commit message +using `python -m devx.ci.validate_commit_msg --branch master`. On master, +commits must follow `{PREFIX}-N: ` format (added by +auto-merge). + +**`configure-repo` step** + +Ensures branch protection and labels are configured using +`python -m devx.tools.configure_repo --repo --owner `: + +- Sets up master branch protection (required status checks, block on rejected + reviews, block on outdated branch) +- Creates standard labels +- Status check contexts read from `DEVX_STATUS_CHECKS` or default to + `CI / validate (pull_request)` + +On failure, the `notify_failure` step creates a Gitea issue. + +#### `release-and-maintain` + +Depends on `detect-and-configure`. The second post-merge job. Consolidates +the former `release`, `publish`, `sync-wiki`, `badges`, and `vikunja` jobs. +Individual steps are conditional on the `detect-and-configure` job's outputs. + +**`release` step** + +Skips for release commits and workflow-only changes. The core release +automation step. Runs `python -m devx.ci.release`: + +1. **Classify changes** — calls `classify_changes.py` to check for user-facing + changes. If only infrastructure files changed, exits without releasing. +2. **Calculate next version** — uses git-cliff to determine the next semver + version from conventional commits since the last tag +3. **Update version file** — updates `__version__` in `src/devx/__init__.py` +4. **Update changelog** — prepends the new version section to `CHANGELOG.md` + using git-cliff output +5. **Run tests** — executes `make lint-ruff` and `make pytest-cov` to verify + the release is healthy. If either fails, the release is aborted — no + commit, no tag. Use `--skip-tests` only for emergency releases. +6. **Commit** — stages the version file and changelog, commits with + `release: vX.Y.Z [skip ci]` (uses `--no-verify` to bypass the commit-msg + hook since release commits are a special case) +7. **Create tag** — creates an annotated tag `vX.Y.Z` with the changelog as + the tag message +8. **Push** — pushes both the commit and tag to master + +The script is idempotent: if there are no new conventional commits since the +last tag, it exits without doing anything. If the tag already exists (for example, +from a partial previous run), it skips tag creation and only pushes. + +**Tag consistency**: Before releasing, the script fetches remote tags and +verifies all existing tags point to commits whose message matches the tag +version. This prevents duplicate release commits and ensures +tag/version/commit alignment. + +**Version bumping rules** (git-cliff): + +| Commit type | Version bump | +|-------------|-------------| +| `feat:` | minor (0.X.0) | +| `fix:` | patch (0.0.X) | +| `feat!:` or `BREAKING CHANGE` | minor (pre-1.0) | +| `chore:`, `ci:`, `docs:` | no bump (excluded by cliff.toml) | + +On failure, the `notify_failure` step creates a Gitea issue via +`python -m devx.ci.notify_failure`. + +**`sync-wiki` step** + +Skips for automated commits. Syncs documentation from `docs/` to the Gitea +wiki using `python -m devx.ci.sync_wiki --repo --strict`: + +1. Reads `docs/mapping.json` to map file paths to wiki page titles +2. Lists existing wiki pages via the Gitea API +3. For each mapped file, reads content and creates or updates the wiki page +4. `--strict` runs a full integrity check: verifies page count, missing + pages, stale pages, and content match. Fails if any page is empty or + content doesn't match. + +Pages that exist in the wiki but not in the mapping are left untouched (not +deleted). + +On failure, the `notify_failure` step creates a Gitea issue. + +**`badges` step** + +Always runs (even on release commits). Generates and pushes quality badges +using `python -m devx.ci.push_badges`: + +1. **Fetch latest master** — `git fetch origin master && git reset --hard + origin/master` (ensures the version badge reflects the current state, + even if the release step recently pushed a new version) +2. **Generate badges** — calls `devx.tools.generate_badges` which runs + pytest-cov, doc-coverage, lint checks, and version extraction, then writes + SVG files: `coverage.svg`, `tests.svg`, `docs.svg`, `quality.svg`, + `version.svg`, `python.svg` +3. **Push to badges branch** — creates an orphan `badges` branch, copies SVG + files, commits, and force-pushes +4. **Update README/docs** — switches back to master, replaces + `raw/branch/badges/.svg` URLs with `raw/commit//.svg` + URLs (cache-busting — Gitea caches `raw/branch/` URLs for 6 hours), + commits, and pushes + +Supports `--retries` for retrying on git push failures (fetches latest master +and waits 10s between attempts). + +On failure, the `notify_failure` step creates a Gitea issue. + +**`vikunja` step** + +Skips for automated commits. Updates the Vikunja task after a merge using +`python -m devx.ci.post_merge --git-sha `: + +1. Extracts the task ID from the first line of the commit message +2. Marks the corresponding Vikunja task as done +3. Posts a comment with the merge SHA + +On failure, the `notify_failure` step creates a Gitea issue. + +**`publish` step** + +Only runs if the `release` step created a tag. Builds and publishes the +package within the same `release-and-maintain` job (checks out the release +tag). Runs `python -m devx.ci.publish `: + +1. **Install dependencies** — build, twine, requests, python-dotenv, click, + and the project itself +2. **Install CI tools** — git-cliff and tea via + `python -m devx.tools.install_tools` +3. **Configure tea login** — `tea login add` using `CI_GITEA_TOKEN` +4. **Build and publish** — `python -m devx.ci.publish `: + - Build the package with `python -m build` + - Publish to the Gitea PyPI registry (default) using `twine upload + --repository-url -u -p ` + - OR publish to standard PyPI if `PYPI_TOKEN` is set + - OR skip publishing if `--skip-build` is passed (non-Python repos) + - Create a Gitea release with git-cliff-generated release notes via + `tea create release` + +Publishing destination resolution (checked in order): +1. **Gitea PyPI registry** — if `--registry-url` is given, or + `DEVX_PYPI_REGISTRY_URL` env var is set, or derived from `GITEA_API_URL` +2. **Standard PyPI** — if `PYPI_TOKEN` is set (takes precedence over Gitea + registry) +3. **Skip** — if neither is configured, only the Gitea release is created + +On failure, the `notify_failure` step creates a Gitea issue. + +## CI scripts + +### `auto_merge.py` + +Auto-merge PR when all CI checks pass. Reads task ID from the branch name +(for example, `DEVX-12-fix-foo` → `DEVX-12`). Validates PR title format, checks the +Vikunja task exists and the title matches, extracts the conventional commit +message from PR commits, and squash-merges with +`{PREFIX}-N ` title. + +```bash +python -m devx.ci.auto_merge +``` + +### `release.py` + +Automated release using git-cliff. Calculates next semver version from +conventional commits, updates `__version__` and `CHANGELOG.md`, runs lint and +tests, commits with `release: vX.Y.Z [skip ci]`, creates annotated tag, and +pushes. Idempotent — exits if no unreleased changes. + +```bash +python -m devx.ci.release [--dry-run] [--skip-tests] [--verify] +``` + +- `--dry-run` — preview without making changes +- `--skip-tests` — skip lint and test verification (emergency only) +- `--verify` — check tag/version/changelog alignment and exit + +### `publish.py` + +Builds package, publishes to Gitea PyPI registry or standard PyPI, and +creates a Gitea release with git-cliff-generated notes. + +```bash +python -m devx.ci.publish [--registry-url ] [--skip-build] +``` + +### `pr_review.py` + +Automated PR review. Fetches the PR diff via the Gitea API, runs automated +checks (architecture, best practices, security, i18n, resource management, +documentation, test coverage, commit conventions), and posts a structured +review with inline comments. + +```bash +python -m devx.ci.pr_review +``` + +### `notify_failure.py` + +Creates a Gitea issue when a CI workflow fails. Uses the tea CLI for issue +creation with failure labels. Supports `--auto-login` to configure the tea +CLI login profile from `CI_GITEA_TOKEN`. + +```bash +python -m devx.ci.notify_failure --repo --run-id \ + --workflow --commit [--auto-login] +``` + +### `post_merge.py` + +Updates Vikunja task after a merge to master. Extracts task ID from the +commit message, marks the task as done, and posts a comment with the merge SHA. + +```bash +python -m devx.ci.post_merge [--commit-sha ] [--git-sha ] +``` + +### `classify_changes.py` + +Classifies git changes as user-facing or workflow-only. Uses a layered rule +system configured in `pyproject.toml`. Safe-by-default: any unknown file +defaults to user-facing. + +```bash +python -m devx.ci.classify_changes [--base ] [--head ] \ + [--quiet] [--check ] [--github-output] +``` + +### `discover_runners.py` + +Discovers available Gitea Actions runners at repository, organization, and +instance levels. Falls back to `MOLECULE_RUNNERS` repo variable or +`DEFAULT_MAX_RUNNERS` (3). + +```bash +python -m devx.ci.discover_runners --owner --repo [--count] [--indices] +``` + +### `detect_release_commit.py` + +Detects whether the latest git commit is a release commit. Writes +`is-release=true|false` to `$GITHUB_OUTPUT`. + +```bash +python -m devx.ci.detect_release_commit +``` + +### `push_badges.py` + +Generates SVG badge files, pushes them to the `badges` branch, and updates +README.md and docs/index.md with cache-busting `raw/commit//` URLs. + +```bash +python -m devx.ci.push_badges [--output-dir ] [--branch ] \ + [--no-readme-update] [--retries ] +``` + +### `distribute_molecule.py` + +Distributes molecule (scenario, platform) pairs across N parallel runners. +Discovers scenarios under `ansible/roles/*/molecule/`. + +```bash +python -m devx.molecule.distribute_molecule --runner-index --max-runners +python -m devx.molecule.distribute_molecule --list +python -m devx.molecule.distribute_molecule --list-platforms +``` + +### `molecule_ci_guard.py` + +Runs molecule tests sequentially while polling the Gitea API for other runner +failures. Aborts early if another runner fails the same job. + +```bash +python -m devx.molecule.molecule_ci_guard [--roles-root ] pair1 pair2 ... +``` + +### `validate_commit_msg.py` + +Validates commit messages. On feature branches: conventional commits only +(no `{PREFIX}-N` prefix). On master: must have `{PREFIX}-N` prefix from +auto-merge, followed by a conventional commit message. + +```bash +python -m devx.ci.validate_commit_msg [--branch ] +``` + +### `sync_wiki.py` + +Syncs documentation from `docs/` to the Gitea wiki via the API. Reads +`docs/mapping.json` for file-to-page mapping. Supports `--dry-run`, +`--verify`, and `--strict` (full integrity check). + +```bash +python -m devx.ci.sync_wiki [--dry-run] [--repo ] [--verify] [--strict] +``` + +### `check_translations.py` + +Validates translation files against the Python source code. Checks for +missing keys, dead keys, and missing languages. + +```bash +python -m devx.ci.check_translations [--translations ]... +``` + +### `doc_coverage.py` + +Checks documentation coverage for CLI commands and major modules. Parses +Click commands from `cli.py` and verifies documentation exists. + +```bash +python -m devx.ci.doc_coverage [--docs-dir ] [--fail-on-missing] +``` + +### `distribute_files.py` + +Distributes files matching a glob pattern across N parallel runners +(round-robin). Writes the assigned file list to `$GITHUB_ENV`. + +```bash +python -m devx.ci.distribute_files --pattern --runner-index \ + --max-runners [--github-env] [--skip-if-excess] +``` + +### `integration_guard.py` + +Runs pytest with cross-runner failure detection. If any other +integration-tests matrix runner reports failure, the current pytest +subprocess is killed and this runner exits early. + +```bash +python -m devx.ci.integration_guard -- +``` + +## Release process summary + +The complete release process from PR to published package: + +1. **PR merged** — `auto-merge` squash-merges the PR to master with + `{PREFIX}-N ` title +2. **Post-merge triggers** — the merge push triggers `post-merge.yml` +3. **detect-and-configure** — detects release commit, validates commit + message, and ensures branch protection/labels +4. **release** (step in `release-and-maintain`) — `release.py` calculates + the next version, updates files, runs tests, commits + `release: vX.Y.Z [skip ci]`, creates tag `vX.Y.Z`, and pushes to master +5. **publish** (step in `release-and-maintain`) — `publish.py` builds the + package, publishes to the Gitea PyPI registry, and creates a Gitea + release with git-cliff notes (checks out the release tag within the + same job) +6. **sync-wiki** (step in `release-and-maintain`) — documentation is synced + to the Gitea wiki +7. **vikunja** (step in `release-and-maintain`) — the corresponding Vikunja + task is marked as done +8. **badges** (step in `release-and-maintain`) — quality badges are + regenerated and pushed to the `badges` branch; README and docs/index.md + are updated with cache-busting URLs + +The release commit's post-merge run skips all steps except `badges` (which +picks up the new version number). This prevents infinite loops. + +## Failure handling + +Every job in the CI and post-merge workflows has a `notify_failure` step +that runs `if: failure()`. This creates a Gitea issue with the workflow name, +run ID, and commit SHA, ensuring failures that would otherwise go unnoticed +in the Actions tab are surfaced as issues. The issue is created via the tea +CLI with a `bug` label if available. diff --git a/CLI-Commands.-.md b/CLI-Commands.-.md index 9c47afa..453a39c 100644 --- a/CLI-Commands.-.md +++ b/CLI-Commands.-.md @@ -1 +1,559 @@ -# CLI-Commands +# CLI Commands + +devx provides a CLI with three command groups: `ci`, `tools`, and `molecule`. +Each subcommand delegates to the corresponding Python module via +`python -m devx.*`, so `devx ci release` is equivalent to +`python -m devx.ci.release`. + +```bash +devx --help # show all command groups +devx --version # show package version +devx ci --help # show CI commands +devx tools --help # show tools commands +devx molecule --help # show molecule commands +``` + +## CI Commands + +### `devx ci auto-merge` + +Auto-merge a PR when all CI checks pass. Reads the task ID from the branch +name, validates the PR title format against +the Vikunja task title, extracts the conventional commit message from PR +commits, and squash-merges with `{PREFIX}-N ` title. + +If the head branch is behind master (HTTP 405), automatically pulls master, +rebases, force-pushes, and retries the merge. + +```bash +devx ci auto-merge +# Example: +devx ci auto-merge DEVX-12-add-feature "DEVX-12: Add feature" oblachno-oss/devx 42 +``` + +### `devx ci check-translations` + +Check translation files for gaps, dead keys, and missing languages. Validates +translation files against the Python source code that uses them. By default, +checks `src/devx/translations.json` against `src/devx/**/*.py`. + +Checks performed: +- **Missing keys** — a `_()` call in code has no entry in the translations file +- **Dead keys** — a key in the translations file is not used in any code +- **Missing languages** — a key exists but is missing one of the six + supported languages (en, bg, de, ru, zh, pl) + +```bash +devx ci check-translations +devx ci check-translations --translations path/to/translations.json +``` + +### `devx ci classify-changes` + +Classify git changes as user-facing or workflow-only. Used to skip releases +for infrastructure-only changes. Classification rules are configured in +`pyproject.toml` under `[tool.devx.classify]`. + +```bash +devx ci classify-changes --base origin/master --head HEAD +devx ci classify-changes --base origin/master --head HEAD --github-output +devx ci classify-changes --quiet --check user-facing +devx ci classify-changes --check ansible # custom tag from pyproject.toml +``` + +Options: +- `--base ` — base ref (default: latest tag) +- `--head ` — head ref (default: HEAD) +- `--quiet` — only output true/false +- `--check ` — check specific category: `all` (default), + `user-facing`, or any tag name defined in `[tool.devx.classify.tags]` +- `--github-output` — write results to `$GITHUB_OUTPUT` for CI workflow steps + +Exit code 2 indicates workflow-only changes (no release needed). + +### `devx ci detect-release-commit` + +Detect whether the latest git commit is a release commit +(`release: vX.Y.Z [skip ci]`). Writes `is-release=true` or `is-release=false` +to `$GITHUB_OUTPUT` for use in CI workflow conditionals. + +```bash +devx ci detect-release-commit +``` + +### `devx ci discover-runners` + +Discover available Gitea Actions runners for dynamic job distribution. +Queries the Gitea API for registered runners at repository, organization, and +instance (admin) levels. Falls back to `MOLECULE_RUNNERS` repo variable or +`DEFAULT_MAX_RUNNERS` (3). + +```bash +devx ci discover-runners --owner oblachno-oss --repo devx +devx ci discover-runners --owner oblachno-oss --repo devx --count +devx ci discover-runners --owner oblachno-oss --repo devx --indices +``` + +Options: +- `--count` — print the number of available runners +- `--indices` — print a JSON array `[0, 1, ..., N-1]` for use as a dynamic + matrix in Gitea Actions + +### `devx ci distribute-files` + +Distribute files across parallel runners (round-robin). Discovers files +matching a glob pattern, sorts them for deterministic ordering, then assigns +them round-robin to `max_runners` groups. The assigned group for +`runner_index` is written to `$GITHUB_ENV`. + +```bash +devx ci distribute-files --pattern "tests/integration/test_*.py" \ + --runner-index 1 --max-runners 3 --github-env +``` + +Options: +- `--pattern ` — glob pattern for files to distribute +- `--runner-index ` — current runner index (0-based) +- `--max-runners ` — total number of runners (default: 3) +- `--github-env` — write file list to `$GITHUB_ENV` +- `--skip-if-excess` — skip if fewer files than runners + +### `devx ci doc-coverage` + +Check documentation coverage for CLI commands and major modules. Parses +Click commands from `cli.py` and checks if each has documentation in +`docs/user/cli-commands.md`. Verifies core modules are documented in +`architecture.md` and CI scripts in `ci-cd-workflow.md`. + +```bash +devx ci doc-coverage +devx ci doc-coverage --docs-dir docs/ --source-dir src/ --fail-on-missing +``` + +Options: +- `--docs-dir ` — path to the docs directory (default: `docs/`) +- `--source-dir ` — path to the source directory (default: auto-detect) +- `--fail-on-missing` — exit with non-zero status if any documentation is + missing + +### `devx ci lint-docs` + +Lint documentation files for structure, broken links, heading hierarchy, +duplicate headings, TODO/FIXME markers, and trailing whitespace. + +```bash +devx ci lint-docs +devx ci lint-docs --root . --fix +devx ci lint-docs --no-check-links --no-check-stale +``` + +Options: +- `--root ` — repository root directory (default: `.`) +- `--docs-dir ` — docs directory (default: `/docs`) +- `--check-links/--no-check-links` — check internal links (default: yes) +- `--check-headings/--no-check-headings` — check heading hierarchy (default: yes) +- `--check-todo/--no-check-todo` — check for TODO/FIXME markers (default: yes) +- `--check-stale/--no-check-stale` — check for stale docs (default: no) +- `--check-trailing/--no-check-trailing` — check trailing whitespace (default: yes) +- `--check-duplicates/--no-check-duplicates` — check duplicate headings (default: yes) +- `--fix` — auto-fix trailing whitespace + +### `devx ci integration-guard` + +Run pytest with cross-runner failure detection. If any +other integration-tests matrix runner reports failure, the current pytest +subprocess is killed and this runner exits early with code 1. + +```bash +devx ci integration-guard -- test_a.py test_b.py +devx ci integration-guard -- -x -v --tb=short test_a.py +``` + +Environment variables: +- `GITEA_URL` — base URL of the Gitea instance +- `CI_GITEA_TOKEN` — API token with repo access +- `RUN_ID` — workflow run ID (`GITHUB_RUN_ID`) +- `JOB_NAME` — base job name (`GITHUB_JOB`) +- `MATRIX_INDEX` — current matrix index (runner-index) +- `GITEA_REPOSITORY` — repository in `owner/repo` format + +### `devx ci notify-failure` + +Create a Gitea issue when a CI workflow fails. Uses the tea CLI for issue +creation with a `bug` label if available. + +```bash +devx ci notify-failure --repo oblachno-oss/devx --run-id 123 \ + --workflow ci --commit abc123def456 +devx ci notify-failure --repo oblachno-oss/devx --run-id 123 \ + --workflow post-merge/release --commit abc123def456 --auto-login +``` + +Options: +- `--repo ` — repository (required) +- `--run-id ` — CI run ID (required) +- `--workflow ` — workflow name (required) +- `--commit ` — commit SHA (required) +- `--auto-login` — configure tea CLI login from `CI_GITEA_TOKEN` before creating + the issue + +### `devx ci post-merge` + +Update Vikunja task after a merge to master. Extracts the task ID from the +commit message, marks the task as done, and posts a comment with the merge SHA. + +```bash +devx ci post-merge "DEVX-12 feat: add feature" --git-sha abc123def456 +``` + +### `devx ci pr-review` + +Run automated PR review. Fetches the PR diff via the Gitea API and runs a +series of checks, posting a structured review (`COMMENT` or +`REQUEST_CHANGES`). + +Checks: architecture compliance, best practices, security, i18n, resource +management, documentation, test coverage, and commit conventions. + +```bash +devx ci pr-review 42 oblachno-oss/devx +``` + +### `devx ci publish` + +Build package, publish to Gitea PyPI registry (or standard PyPI), and create +a Gitea release with git-cliff-generated notes. + +```bash +devx ci publish v1.0.0 oblachno-oss/devx +devx ci publish v1.0.0 oblachno-oss/devx --registry-url https://git.example.com/api/packages/owner/pypi +devx ci publish v1.0.0 oblachno-oss/devx --skip-build # Gitea release only +``` + +Options: +- `--registry-url ` — Gitea PyPI registry URL. Defaults to + `DEVX_PYPI_REGISTRY_URL` env var or a URL derived from `GITEA_API_URL`. + When set, publishes to Gitea PyPI instead of standard PyPI (unless + `PYPI_TOKEN` is also set). +- `--skip-build` — skip package build and PyPI publish (for non-Python repos + that only need a Gitea release) + +### `devx ci push-badges` + +Generate badge SVG files and push them to the `badges` branch. Also updates +`README.md` and `docs/index.md` on master with cache-busting +`raw/commit//` URLs. + +```bash +devx ci push-badges +devx ci push-badges --output-dir .badges/ --branch master +devx ci push-badges --no-readme-update # skip README update (local testing) +devx ci push-badges --retries 3 # retry on git push failures +``` + +Options: +- `--output-dir ` — temporary directory for badge files (default: + `.badges/`) +- `--branch ` — branch to sync before generating badges (default: + `master`) +- `--no-readme-update` — skip updating README with cache-busting URLs +- `--retries ` — number of attempts on git push failures (default: 1). + Between attempts, fetches latest master and waits 10s. + +### `devx ci release` + +Automated release: calculate next version, update files, tag, and push. Uses +git-cliff to determine the next semver version from conventional commits. + +```bash +devx ci release +devx ci release --dry-run # preview without making changes +devx ci release --skip-tests # skip lint and tests (emergency only) +devx ci release --verify # check tag/version/changelog alignment +``` + +Options: +- `--dry-run` — show what would happen without making changes +- `--skip-tests` — skip lint and test verification (NOT recommended — only + for emergency releases) +- `--verify` — verify tag/version/changelog alignment and exit (no changes + made) + +### `devx ci sync-wiki` + +Sync documentation from `docs/` to the Gitea wiki. Reads `docs/mapping.json` +for file-to-page mapping. Pages that exist in the wiki but not in the mapping +are left untouched. + +```bash +devx ci sync-wiki --repo oblachno-oss/devx +devx ci sync-wiki --repo oblachno-oss/devx --dry-run +devx ci sync-wiki --repo oblachno-oss/devx --verify +devx ci sync-wiki --repo oblachno-oss/devx --strict +``` + +Options: +- `--dry-run` — show what would happen without making changes +- `--repo ` — repository (auto-detected if omitted) +- `--verify` — after syncing, verify each page has non-empty content. Exit 1 + if any page is empty or mismatched. +- `--strict` — full integrity check: verify page count, missing pages, stale + pages, and content. Implies `--verify`. + +### `devx ci validate-commit-msg` + +Validate commit messages for conventional commit format. On feature branches: +conventional commits only (no `{PREFIX}-N` prefix). On master: must have +`{PREFIX}-N` prefix from auto-merge, followed by a conventional commit +message. + +```bash +devx ci validate-commit-msg commit-msg.txt +devx ci validate-commit-msg commit-msg.txt --branch master +``` + +Options: +- `--branch ` — override branch detection (for CI use) + +## Tools Commands + +### `devx tools check-test-speed` + +Run unit tests and enforce execution-time budgets. Two quality gates: + +- **Total suite time** must not exceed `--max-seconds` (default: 10s) +- **Per-test time** — no individual test may exceed `--max-single-seconds` + (default: 0.5s, 0 to disable) + +Runs `make test-unit` with `PYTEST_ADDOPTS=--durations=0` so pytest emits +per-test timing lines. + +```bash +devx tools check-test-speed +devx tools check-test-speed --max-seconds 10 +devx tools check-test-speed --max-seconds 4 --max-single-seconds 0.5 +``` + +### `devx tools check-test-isolation` + +Statically analyze test files for un-hermetic patterns that cause slow +or flaky tests. Also available as a **pytest plugin** (auto-discovered +via the `pytest11` entry point when devx is installed — runs +automatically on every `pytest` invocation and **fails on violations**). + +Detected patterns (hard errors — exit non-zero): + +- **unpatched-subprocess**: `subprocess.run/call/Popen/check_call/check_output` + called in a test function without `@patch` or `with patch(...)` +- **unpatched-sleep**: `time.sleep` called without `@patch` +- **unpatched-helper**: known subprocess-spawning helpers (`update_doc_versions`, + `run_cmd`, `run_tests`) called without `@patch` or patching their internal deps +- **excessive-iterations**: `for _ in range(N)` where N > 100 +- **heavy-module-import**: `httpx`, `ansible`, etc. imported at module level +- **reload-without-cleanup**: `importlib.reload()` called an odd number of times + +Advisory patterns (exit 0 — runtime audit is authoritative): + +- **transitive-subprocess**: `CliRunner.invoke(target)` where `target` + transitively calls `subprocess.run` without being patched. Detected via + static call-graph analysis. The runtime subprocess audit catches actual + leaks — if a real subprocess runs without `@patch`, the test fails. + +```bash +devx tools check-test-isolation +devx tools check-test-isolation --test-path tests/ +devx tools check-test-isolation --categories unpatched-subprocess,transitive-subprocess +devx tools check-test-isolation --max-loop-iterations 50 +devx tools check-test-isolation --src-dir src/ +``` + +Pytest plugin options (automatic when devx is installed): + +- `--no-test-isolation` — disable static analysis and runtime subprocess audit +- `--test-isolation-max-loop N` — max iterations per loop (default: 100) + +### `devx tools configure-repo` + +Configure repository: branch protection and labels via the Gitea REST API. +Sets up master branch protection (required status checks, block on rejected +reviews, block on outdated branch) and creates standard labels. + +```bash +devx tools configure-repo --repo devx --owner oblachno-oss +``` + +Status check contexts are read from `DEVX_STATUS_CHECKS` (comma-separated) or +default to `CI / quality (pull_request)`. + +### `devx tools generate-badges` + +Generate self-contained SVG badge files from project metrics. Runs +pytest-cov, doc-coverage, lint checks, and version extraction, then writes +SVG files that can be served as static files from the Gitea raw file API. + +Badges generated: `coverage.svg`, `tests.svg`, `docs.svg`, `quality.svg`, +`version.svg`, `python.svg`. + +```bash +devx tools generate-badges +devx tools generate-badges --output-dir .badges/ +``` + +### `devx tools generate-cliff-config` + +Generate a `cliff.toml` configuration file with the correct task ID prefix +preprocessor. Eliminates the need to manually duplicate and maintain +`cliff.toml` across repos that use devx. + +```bash +devx tools generate-cliff-config --prefix GRM +devx tools generate-cliff-config --prefix GRM --output cliff.toml +devx tools generate-cliff-config --prefix GRM --force # overwrite existing +``` + +Options: +- `--prefix ` — task ID prefix (default: `DEVX_TASK_PREFIX` env var + or `DEVX`) +- `--output ` — output file path (default: `cliff.toml`) +- `--force` — overwrite existing file + +### `devx tools install-checkmake` + +Install checkmake (Makefile linter) if not already present. Tries +`go install` first if Go is available, otherwise downloads the latest +pre-built Linux binary from the official GitHub releases. + +```bash +devx tools install-checkmake +``` + +### `devx tools install-tools` + +Install CI/CD development tools that are not Python packages: actionlint, +git-cliff, act_runner, and tea. Each tool is installed to `~/.local/bin` if +not already on PATH. Idempotent: skips tools that are already available. + +```bash +devx tools install-tools # install all +devx tools install-tools --tool actionlint # install one +devx tools install-tools --tool git-cliff --tool tea # install specific +devx tools install-tools --list # list status +``` + +### `devx tools setup` + +Project setup: install Python dependencies (editable mode with extras), +Ansible Galaxy collections (if `ansible/requirements.yml` exists in the target repo), pre-commit +hooks (pre-commit, commit-msg, pre-push), and configure the tea CLI login +profile from `.env`. + +```bash +devx tools setup --bin .venv/bin +devx tools setup --bin .venv/bin --extras "ci,lint" +devx tools setup --bin .venv/bin --no-pre-commit --no-tea-login +``` + +Options: +- `--bin ` — virtualenv bin directory (required) +- `--extras ` — pip extras to install (default: `dev`) +- `--no-pre-commit` — skip pre-commit hook installation +- `--no-tea-login` — skip tea CLI login configuration + +### `devx tools rebase` + +Rebase the current branch onto `origin/master` and force-push with +`--force-with-lease`. Checks if the branch is behind master first — +if up-to-date, exits without doing anything. + +```bash +devx tools rebase # rebase + force-push +devx tools rebase -- --no-push # rebase locally only +``` + +Options (pass after `--`): +- `--no-push` — rebase locally without pushing + +### `devx tools pr-rebase` + +Rebase a pull request's head branch onto master via the Gitea API +(server-side). This triggers a new `pull_request synchronize` event, +which starts a new CI run. Useful when you don't have the branch +checked out locally. + +```bash +devx tools pr-rebase -- --pr 42 # rebase PR #42 +devx tools pr-rebase # auto-detect PR from current branch +``` + +Options (pass after `--`): +- `--pr ` — PR number (auto-detected from current branch if omitted) + +## Molecule Commands + +Molecule commands require the `molecule` extra (`pip install devx[molecule]`). + +### `devx molecule all` + +Run all molecule scenarios on all supported OS platforms. Sequential +execution — CI uses the parallel matrix instead. + +```bash +devx molecule all +devx molecule all --bin .venv/bin +``` + +### `devx molecule discover-runners` + +Discover available Gitea Actions runners for molecule tests. Same logic as +`devx ci discover-runners` but intended for molecule-specific workflows. + +```bash +devx molecule discover-runners --owner oblachno-oss --repo devx --indices +``` + +### `devx molecule distribute` + +Distribute molecule (scenario, platform) pairs across N parallel runners. +Discovers scenarios under `ansible/roles/*/molecule/` and crosses them with +the supported OS platform matrix. + +```bash +devx molecule distribute --runner-index 1 --max-runners 3 +devx molecule distribute --list # list all scenarios +devx molecule distribute --list-platforms # list platforms +devx molecule distribute --roles-root ansible/roles # multi-role repos +``` + +Options: +- `--runner-index ` — current runner index (0-based) +- `--max-runners ` — total number of runners (default: 3) +- `--list` — list all scenarios, one per line +- `--list-platforms` — list all platforms, one per line +- `--roles-root ` — roles root directory for multi-role repos (default: + `ansible/roles`) + +### `devx molecule guard` + +Run molecule tests sequentially with CI failure polling. A background thread +polls the Gitea API. If any other molecule matrix runner reports failure, the +current molecule subprocess is killed and this runner exits early with code 1. + +```bash +devx molecule guard pair1 pair2 pair3 +devx molecule guard --roles-root ansible/roles pair1 pair2 +``` + +Each pair is encoded as: +- **Single-role (4-part):** `scenario|platform_name|platform_image|platform_command` +- **Multi-role (5-part):** `role|scenario|platform_name|platform_image|platform_command` + +Options: +- `--roles-root ` — roles root directory for multi-role repos + +Environment variables: +- `GITEA_URL` — base URL of the Gitea instance +- `CI_GITEA_TOKEN` — API token with repo access +- `RUN_ID` — workflow run ID (`GITHUB_RUN_ID`) +- `JOB_NAME` — base job name (`GITHUB_JOB`) +- `MATRIX_INDEX` — current matrix index (runner-index) +- `GITEA_REPOSITORY` — repository in `owner/repo` format diff --git a/Getting-Started.-.md b/Getting-Started.-.md index 14dd306..d1c6f76 100644 --- a/Getting-Started.-.md +++ b/Getting-Started.-.md @@ -1 +1,161 @@ -# Getting-Started +# Getting Started with devx + +This guide walks you through installing devx, configuring it for your project, +and setting up a complete CI/CD pipeline. + +## Prerequisites + +- **Python 3.12+** +- **A Gitea instance** with Actions enabled +- **A Gitea API token** with repo, workflow, and organization scopes +- **(Optional) Vikunja API token** for task tracking integration + +## Installation + +devx is published to the Gitea PyPI registry. Configure pip to use it: + +```bash +# Configure Gitea PyPI registry +pip config set global.extra-index-url https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple + +# Install devx +pip install devx +``` + +Or install from source: + +```bash +git clone https://git.oblachno.oblachno.fyi/oblachno-oss/devx.git +cd devx +make setup +``` + +## Quick Start + +### 1. Configure environment variables + +Create a `.env` file in your project root: + +```bash +CI_GITEA_TOKEN=your_gitea_api_token +VIKUNJA_TOKEN=your_vikunja_api_token # optional +``` + +### 2. Add devx to your project + +Add devx to your `pyproject.toml`: + +```toml +[project] +dependencies = [ + "devx>=0.47.5", +] + +[project.optional-dependencies] +dev = [ + "devx>=0.47.5", +] +``` + +### 3. Set up the Makefile + +devx provides a shared Makefile fragment. Add this to your `Makefile`: + +```makefile +include devx.mak +``` + +Run `devx tools setup` to install all development tools (actionlint, git-cliff, +tea CLI, etc.) and configure pre-commit hooks. + +### 4. Create the docs structure + +devx expects a `docs/` directory with at minimum: + +```text +docs/ +├── index.md # Documentation home page +├── mapping.json # Wiki page title mappings +├── user/ # User-facing documentation +│ └── cli-commands.md +└── tech/ # Technical documentation + ├── architecture.md + └── ci-cd-workflow.md +``` + +Example `docs/mapping.json`: + +```json +{ + "index.md": "Home", + "user/cli-commands.md": "CLI-Commands", + "tech/architecture.md": "Architecture", + "tech/ci-cd-workflow.md": "CI-CD-Workflow" +} +``` + +### 5. Set up CI workflows + +Create `.gitea/workflows/ci.yml` and `.gitea/workflows/post-merge.yml` in your +project. See the [CI/CD Workflow guide](ci-cd-workflow) for details. + +### 6. Configure release settings + +Add a `cliff.toml` for git-cliff-based versioning: + +```bash +devx tools generate-cliff-config +``` + +Add `[tool.devx]` section to `pyproject.toml` for project-specific config: + +```toml +[tool.devx] +# Vikunja project ID for task tracking +vikunja_project_id = 6 + +[tool.devx.classify] +# File patterns that are infrastructure (no release needed) +infrastructure = [ + ".gitea/**", + "docs/**", + "tests/**", + "AGENTS.md", + "README.md", + "CHANGELOG.md", +] +``` + +## Available Tools + +### CI/CD Automation (`devx.ci.*`) + +- `devx.ci.release` — Automated semver versioning and tagging +- `devx.ci.publish` — Package publishing to Gitea PyPI registry +- `devx.ci.auto_merge` — Squash-merge automation with task ID validation +- `devx.ci.pr_review` — Automated PR review with inline comments +- `devx.ci.classify_changes` — User-facing vs workflow-only change detection +- `devx.ci.sync_wiki` — Push docs/ to Gitea wiki +- `devx.ci.doc_coverage` — Documentation coverage checker +- `devx.ci.lint_docs` — Documentation linter (structure, links, headings) +- `devx.ci.check_translations` — i18n translation completeness checker +- `devx.ci.notify_failure` — Create Gitea issues on CI failures +- `devx.ci.distribute_files` — Parallel test file distribution +- `devx.ci.distribute_items` — Parallel item distribution across runners +- `devx.ci.discover_runners` — Dynamic runner discovery via Gitea API + +### Development Tools (`devx.tools.*`) + +- `devx.tools.setup` — Environment setup (venv, deps, hooks, tools) +- `devx.tools.install_tools` — Install CI/CD tools (actionlint, git-cliff, tea) +- `devx.tools.create_task` — Create Vikunja tasks +- `devx.tools.create_pr` — Create Gitea PRs with task ID in title +- `devx.tools.configure_repo` — Configure branch protection and labels +- `devx.tools.generate_badges` — Generate quality badge SVGs +- `devx.tools.check_test_speed` — Enforce test execution speed limits + +## Next Steps + +- Read the [CLI Commands reference](cli-commands) for all available commands +- Read the [Architecture guide](architecture) to understand internals +- Read the [CI/CD Workflow guide](ci-cd-workflow) for pipeline details diff --git a/Home.md b/Home.md index e69de29..1067c29 100644 --- a/Home.md +++ b/Home.md @@ -0,0 +1,175 @@ +# 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. + +> An open-source project from **Oblachno** (облачно means *cloudy* in Bulgarian). + +[![CI](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions/workflows/ci.yml/badge.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions) +[![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE) +[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/82b4caf3bcf5fb7058654abab87cd2fea339d882/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions) +[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/82b4caf3bcf5fb7058654abab87cd2fea339d882/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions) +[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/82b4caf3bcf5fb7058654abab87cd2fea339d882/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki) +[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/82b4caf3bcf5fb7058654abab87cd2fea339d882/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions) +[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/82b4caf3bcf5fb7058654abab87cd2fea339d882/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases) +[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/82b4caf3bcf5fb7058654abab87cd2fea339d882/python.svg)](https://www.python.org/downloads/) + +## Overview + +devx provides a complete, opinionated CI/CD pipeline for any project hosted on +a Gitea instance with Gitea Actions. Install the package, declare configuration +via environment variables and `pyproject.toml`, and inherit: + +- **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** — 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`: + +```ini +[global] +extra-index-url = https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple +``` + +Then `pip install devx` works without specifying `--index-url`. + +### As a dependency in another project + +Add devx to your `pyproject.toml` dependencies and configure the registry: + +```toml +[project] +dependencies = [ + "devx>=0.47.5", +] + +[tool.pip] +extra-index-url = "https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple" +``` + +Pin a specific version if needed: `"devx==0.47.5"` or `"devx>=0.47.5,<0.48"`. + +### Optional extras + +```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) +``` + +## Architecture + +devx is a self-contained Python package under `src/devx/`: + +- **Core modules** — `config.py`, `exceptions.py`, `i18n.py`, `api_clients.py`, + `gitea_cli.py`, `cli.py`, `opentofu.py` +- **CI automation** (`devx.ci`) — release, publish, auto_merge, pr_review, + classify_changes, sync_wiki, push_badges, check_translations, doc_coverage, + validate_commit_msg, detect_release_commit, notify_failure, post_merge, + discover_runners, distribute_files, integration_guard +- **Dev tools** (`devx.tools`) — setup, install_tools, check_test_speed, + configure_repo, generate_badges, generate_cliff_config, install_checkmake +- **Molecule tools** (`devx.molecule`) — Optional, for projects with Ansible + roles: distribute_molecule, molecule_ci_guard, molecule_all, discover_runners, + start_docker, platforms + +See [Architecture](Architecture) for the full package structure, module +descriptions, design principles, and data flow diagrams. + +## CI/CD pipeline + +devx uses Gitea Actions with three workflows: + +- **CI** (`ci.yml`) — runs on pull requests: quality checks, change detection, + release dry-run, automated PR review, and auto-merge. +- **Post-merge** (`post-merge.yml`) — runs on every push to master: release + versioning, wiki sync, badge generation, Vikunja task updates, and repo + configuration. +- **Publish** (`publish.yml`) — runs on tag pushes: builds the package, + publishes to the Gitea PyPI registry, and creates a Gitea release. + +See [CI/CD Workflow](CI-CD-Workflow) for the full pipeline documentation, +including the post-merge job graph, release process, badge generation, and +wiki sync details. + +## CLI commands + +devx provides a `devx` CLI with three command groups: + +- `devx ci ` — CI/CD automation (17 commands) +- `devx tools ` — Developer tools (9 commands) +- `devx molecule ` — Molecule testing (4 commands, optional) + +See [CLI Commands](CLI-Commands) for full command documentation with examples. + +## Configuration + +devx reads configuration from `DEVX_*` environment variables with `.env` file +fallback. Key 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` | **(must be set)** | Repository owner | +| `DEVX_REPO_NAME` | **(must be set)** | Repository name | +| `DEVX_TASK_PREFIX` | `DEVX` | Task ID prefix (GRM, OBL-INFRA, etc.) | +| `DEVX_LANG` | `en` | Language for i18n (en, bg, de, ru, zh, pl) | +| `CI_GITEA_TOKEN` | — | Gitea API token | +| `VIKUNJA_TOKEN` | — | Vikunja API token | + +See [AGENTS.md](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/AGENTS.md) +for the full configuration reference, PR workflow, and project conventions. + +## Wiki pages + +- [Home](Home) — This page +- [Getting Started](Getting-Started) — Installation, configuration, and quick start guide +- [CLI Commands](CLI-Commands) — Full CLI command documentation with examples +- [Architecture](Architecture) — Package structure, module descriptions, design principles +- [CI/CD Workflow](CI-CD-Workflow) — Pipeline documentation, workflows, and CI scripts + +## Links + +- **Source**: [https://git.oblachno.oblachno.fyi/oblachno-oss/devx](https://git.oblachno.oblachno.fyi/oblachno-oss/devx) +- **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) +- **GRM (origin project)**: [https://git.oblachno.oblachno.fyi/oblachno-oss/grm](https://git.oblachno.oblachno.fyi/oblachno-oss/grm) + +## License + +GPL-3.0