shields.io can't fetch JSON from our self-hosted Gitea instance (not
publicly reachable), so badges showed "unknown". Switched to generating
self-contained SVG badge files that are served directly by Gitea's raw
file API — no external service needed.
Changes:
- generate_badges.py: Added render_svg() to produce shields.io-style
SVG badges with gradient, rounded corners, and Verdana font
- Replaced xml.sax.saxutils.escape with a simple _xml_escape() to
avoid bandit B406 warning (no defusedxml dependency needed)
- CI workflow: Push .svg files instead of .json to badges branch
- README.md and docs/index.md: Updated badge URLs to use raw SVG
from the badges branch instead of shields.io endpoint
Also fixed:
- Coverage regex now handles 100% without decimal (was 100.00%)
- doc_coverage.py: Removed redundant % in pct variable that caused
double-percent (100%%) in output
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Add /docs/ directory with user and technical documentation extracted from README, AGENTS.md, and source code. Add scripts/sync_wiki.py to sync docs to Gitea wiki via API. Add scripts/doc_coverage.py to check CLI commands, modules, and CI scripts are documented. Add sync-wiki.yml workflow for auto-sync on merge and release. Slim down README.md to lean entry point. 28 new unit tests, 100% coverage maintained.
Closes GRM-36
Three major improvements:
1. Rootless Docker refactor: Removes docker/binary modes, unifies to
rootless Docker with per-runner system users. Each runner gets its
own rootless Docker daemon, systemd user service, and isolated
environment. Simplifies CLI (removes --mode option), Ansible role
(single code path), and molecule scenarios (removes binary scenario).
2. Auto-merge fix: Fixes status check context mismatch in branch
protection (was requiring "lint", "unit-tests", "molecule-tests" but
actual contexts are "CI / quality", "CI / molecule-tests*"). Adds
retry/wait logic to auto_merge.py that polls commit statuses for up
to 15 minutes before attempting merge, eliminating the chicken-and-egg
problem where auto-merge would fail because CI hadn't completed yet.
3. Molecule platform matrix: Adds OS platform matrix to CI — all 6
scenarios now run on all 4 supported OSes (ubuntu-2204, ubuntu-2404,
debian-12, archlinux) = 24 test pairs distributed across 3 parallel
runners. Updates distribute_molecule.py to distribute (scenario,
platform) pairs. Updates Makefile with molecule-all target for
local multi-platform testing.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Add CONTRIBUTING.md with branch naming, commit format, and PR workflow
- Add TROUBLESHOOTING.md with common issues and solutions
- Update README.md with CI badge and commit convention section
- Update pyproject.toml with pythonpath and coverage settings for scripts
- Change --ask-become-pass from opt-in to opt-out across all commands
(install, update, start, stop, enable, disable, status, remove)
- Use Click toggle pattern: --ask-become-pass/--no-ask-become-pass with
default=True so users are always prompted for sudo unless they
explicitly opt out
- Update i18n translations for both help texts
- Update all CLI tests to expect ask_become_pass=True as default and
add test for --no-ask-become-pass
- Update README: remove --ask-become-pass from examples, document
--no-ask-become-pass for passwordless-sudo setups
- 120 tests, 100% coverage, pyright clean, ruff clean
- Remove console StreamHandler from get_logger() — say() already handles
console output via click.echo(); having both caused every message to
appear twice
- Move GRM_LOG_LEVEL filtering into ui.say() via _console_level() so
console verbosity is still user-controllable while the log file always
captures everything at DEBUG
- Remove [GRM] prefix from say() calls — no longer needed without
duplicate logger output, giving cleaner user-facing messages
- Update test_logging_config.py: remove console handler tests and
_level_from_env tests (now in test_ui.py), expect 1 handler only
- Add test_ui.py coverage for _console_level and say() level filtering
- Update README to document single-path console output via click.echo
- 116 tests, 100% coverage, pyright clean, ruff clean
- Extend ui.say() with optional color parameter using click.style()
- Console output gets tinted; log file always stores plain text (no ANSI)
- executor.py: cyan for start, yellow for status, green for done, red for errors
- report.py: bright_cyan header, green completed, red failed, yellow in-progress,
white pending
- Update README with colorized output documentation
- 117 tests, 100% coverage, pyright clean, ruff clean
- Create logging_config.py with get_logger() providing dual handlers:
- Console handler (stderr) controlled by GRM_LOG_LEVEL env var (default INFO)
- File handler (~/.local/state/grm/logs/grm.log) capturing everything at DEBUG
- Replace all print() calls in executor.py and report.py with logger.info()/error()
- Add error logging before raising AnsibleError in executor.run()
- Add GRM_LOG_LEVEL to README configuration table and logging documentation
- Update all unit tests to mock logger instead of using capsys
- 114 tests, 100% coverage, pyright clean, ruff clean