Compare commits

...

75 Commits
v4.0.0 ... main

Author SHA1 Message Date
renovate[bot]
29320b6b96 chore(docker): update python:3.12-alpine docker digest to d24ed56 2024-06-08 03:37:15 +02:00
8e042c739e
feat: migrate to dynaconf to handle multi-source configuration (#708) 2024-06-07 21:51:10 +02:00
9b20c11660
fix: exclude tags from exclude_tags during rendering (#711) 2024-06-07 21:30:10 +02:00
renovate[bot]
5760ee0832 chore(docker): update python:3.12-alpine docker digest to 32385e6 2024-06-06 03:51:22 +02:00
renovate[bot]
db94c07396
chore(deps): lock file maintenance (#707)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-05 09:26:04 +02:00
renovate[bot]
73bbd746d3 chore(deps): update dependency ruff to v0.4.7 2024-06-03 03:31:33 +02:00
ab0372bef5
chore: unifi jinja template syntax and add linting (#704) 2024-06-02 09:00:07 +02:00
3df7e465db
docs: add documentation for tabulated vars option (#705) 2024-06-02 00:08:33 +02:00
89c6a11be4
fix: fix sysexit with custom error (#703) 2024-06-02 00:08:25 +02:00
Chip Selden
4051d2915d
feat: add option for tabulating variables (#693) 2024-06-01 22:05:16 +02:00
renovate[bot]
fe4e4e5f7a
chore(deps): lock file maintenance (#702)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-01 13:18:57 +02:00
renovate[bot]
172e4f4380 chore(deps): update dependency ruff to v0.4.5 2024-05-27 04:31:24 +02:00
renovate[bot]
fada900568
fix(deps): update dependency ansible-core to v2.14.17 (#698)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-24 09:44:19 +02:00
renovate[bot]
013f760c8a chore(docker): update python:3.12-alpine docker digest to 5365725 2024-05-23 05:49:37 +02:00
renovate[bot]
00adc389a2 chore(deps): update dependency pytest to v8.2.1 2024-05-20 03:47:49 +02:00
renovate[bot]
84fdc06315
chore(deps): lock file maintenance (#695)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-15 11:06:23 +02:00
renovate[bot]
db68e80372
chore(deps): update quay.io/thegeeklab/hugo docker tag to v0.125.7 (#696)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-14 14:24:17 +02:00
renovate[bot]
af702628eb chore(deps): update dependency ruff to v0.4.4 2024-05-13 03:05:31 +02:00
renovate[bot]
81d4e97af6
fix(deps): update dependency jinja2 to v3.1.4 (#692)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-06 21:29:22 +02:00
renovate[bot]
a33f3c53bb chore(deps): update dependency ruff to v0.4.3 2024-05-06 02:37:46 +02:00
renovate[bot]
ccc2d249f8
fix(deps): update dependency jsonschema to v4.22.0 (#689)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-01 12:36:45 +02:00
renovate[bot]
f7ff5fd624
chore(deps): lock file maintenance (#683)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-29 10:28:49 +02:00
renovate[bot]
acee6e1285 chore(deps): update devdeps non-major 2024-04-29 04:15:22 +02:00
Julien Rottenberg
2375ad118d
fix: install extra group when using pre-commit (#687) 2024-04-24 08:54:48 +02:00
renovate[bot]
a2f02527d9
fix(deps): update dependency ansible-core to v2.14.16 (#686)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-23 21:18:13 +02:00
renovate[bot]
0bf59ac34f chore(deps): update dependency ruff to v0.4.1 2024-04-22 03:46:05 +02:00
renovate[bot]
94ec1a632b chore(deps): update dependency ruff to v0.3.7 2024-04-15 02:40:29 +02:00
renovate[bot]
075e1f91ca
fix(deps): update dependency ansible-core to v2.14.15 (#681)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-12 09:59:00 +02:00
4cf63bf2fe
separate minor-patch for ansible deps 2024-04-12 09:23:00 +02:00
renovate[bot]
e3f797d5e3
chore(deps): lock file maintenance (#677)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-12 09:18:07 +02:00
renovate[bot]
3a0c5ae35f chore(docker): update python:3.12-alpine docker digest to ef09762 2024-04-11 04:03:51 +02:00
renovate[bot]
9f7f943c93 chore(deps): update dependency ruff to v0.3.5 2024-04-08 03:24:24 +02:00
renovate[bot]
b38c4aa2b8 chore(deps): update dependency thegeeklab/hugo-geekdoc to v0.45.0 2024-04-08 03:24:09 +02:00
renovate[bot]
ed167d1443 chore(deps): update dependency thegeeklab/hugo-geekdoc to v0.44.3 2024-04-01 03:24:41 +02:00
renovate[bot]
da6fd26c6d
chore(deps): update quay.io/thegeeklab/wp-docker-buildx docker tag to v4 (#674)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-27 08:42:10 +01:00
renovate[bot]
522c21f8fc chore(docker): update python:3.12-alpine docker digest to c7eb5c9 2024-03-27 01:21:36 +01:00
renovate[bot]
28e39055e3
chore(deps): lock file maintenance (#669)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-26 21:39:41 +01:00
renovate[bot]
894965286b
chore(deps): update dependency pytest-cov to v5 (#671)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-25 08:34:20 +01:00
renovate[bot]
c2e0f787ce chore(deps): update devdeps non-major 2024-03-25 01:35:05 +01:00
renovate[bot]
d524537fd3
chore(deps): update quay.io/thegeeklab/hugo docker tag to v0.124.1 (#670)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-21 08:48:14 +01:00
renovate[bot]
a559b654ca chore(deps): update dependency ruff to v0.3.3 2024-03-18 03:00:26 +01:00
renovate[bot]
aa78adf912 chore(docker): update python:3.12-alpine docker digest to 25a82f6 2024-03-17 01:49:43 +01:00
6e88c18375
ci: fix deprecated ruff command 2024-03-12 20:52:52 +01:00
7b9ba09f1d fix linting 2024-03-11 09:44:16 +01:00
renovate[bot]
08883952c1 chore(deps): update devdeps non-major 2024-03-11 09:44:16 +01:00
renovate[bot]
e1ef4937dd chore(deps): update dependency thegeeklab/hugo-geekdoc to v0.44.2 2024-03-11 02:43:51 +01:00
renovate[bot]
571222f6f5
chore(deps): lock file maintenance (#663)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-07 11:04:59 +01:00
renovate[bot]
6d50525021
fix(deps): update dependency environs to v11 (#664)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-07 11:04:46 +01:00
renovate[bot]
3ade4698e7 chore(deps): update devdeps non-major 2024-03-04 02:18:33 +01:00
renovate[bot]
ee81c8ee73
chore(deps): lock file maintenance (#659)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-26 10:26:11 +01:00
renovate[bot]
03df5bd79b
chore(deps): lock file maintenance (#657)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-26 10:14:18 +01:00
renovate[bot]
1e32a8f87a chore(deps): update dependency pytest to v8.0.2 2024-02-26 02:21:25 +01:00
renovate[bot]
dab9bc8691 chore(deps): update devdeps non-major 2024-02-19 02:20:31 +01:00
e2eaa81c4f
[skip ci] revert renovate automerge config 2024-02-15 12:23:07 +01:00
renovate[bot]
732f588aa9
chore(deps): update quay.io/thegeeklab/hugo docker tag to v0.122.0 (#655)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-11 16:53:09 +01:00
6619351fbd
enable renovate on automerge branches 2024-02-09 23:08:29 +01:00
renovate[bot]
8f6f444931 chore(docker): update python:3.12-alpine docker digest to 1a05012 2024-02-09 05:43:21 +01:00
renovate[bot]
49b861cabc
fix(deps): update dependency ruamel.yaml to v0.18.6 (#653)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-07 22:36:59 +01:00
renovate[bot]
59b497d745
fix(deps): update dependency ansible-core to v2.14.14 (#652)
This CVE does not affect ansible-doctor and is therefore not treated as a security update.

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-07 08:38:43 +01:00
b2f4fd2bd8
chore: bump ruff to v0.2.1 (#651) 2024-02-06 09:34:17 +01:00
renovate[bot]
864af95606
chore(deps): update dependency pytest to v8 (#648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-31 09:05:49 +01:00
renovate[bot]
758c87ee80 chore(docker): update python:3.12-alpine docker digest to 14cfc61 2024-01-29 06:35:05 +01:00
renovate[bot]
9b0edda70a
chore(deps): update quay.io/thegeeklab/wp-docker-buildx docker tag to v3 (#647)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-22 11:27:39 +01:00
renovate[bot]
1d32f7548a chore(deps): update dependency ruff to v0.1.14 2024-01-22 02:14:07 +01:00
renovate[bot]
704cdb9d7c
fix(deps): update dependency jsonschema to v4.21.1 (#645)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-20 15:13:09 +01:00
renovate[bot]
c7f3fe57a0 chore(docker): update python:3.12-alpine docker digest to 801b54e 2024-01-19 23:32:46 +01:00
renovate[bot]
1270d7cb7d chore(docker): update python:3.12-alpine docker digest to 4a156f7 2024-01-19 05:23:41 +01:00
renovate[bot]
461eeb2d74 chore(docker): update python:3.12-alpine docker digest to 67990ec 2024-01-19 02:54:58 +01:00
renovate[bot]
0817646004
fix(deps): update dependency jsonschema to v4.21.0 (#641)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-18 10:21:51 +01:00
renovate[bot]
5242fd882a chore(deps): update dependency thegeeklab/hugo-geekdoc to v0.44.1 2024-01-16 00:19:56 +01:00
renovate[bot]
052c668d92
fix(deps): update dependency anyconfig to v0.14.0 (#638)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-15 21:56:46 +01:00
1e8a8beef7 fix linting 2024-01-15 15:24:52 +01:00
renovate[bot]
c124460c11 chore(deps): update dependency ruff to v0.1.13 2024-01-15 15:24:52 +01:00
renovate[bot]
38bd53f7bc
fix(deps): update dependency environs to v10.3.0 (#637)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-12 08:30:19 +01:00
renovate[bot]
dbf9c979ac
fix(deps): update dependency jinja2 to v3.1.3 (#636)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-11 08:29:35 +01:00
27 changed files with 1000 additions and 836 deletions

View File

@ -5,3 +5,6 @@ MD041: False
MD024: False
MD004:
style: dash
MD033:
allowed_elements:
- "br"

View File

@ -6,3 +6,5 @@
language: python
pass_filenames: False
always_run: True
additional_dependencies:
- .[ansible-core]

View File

@ -13,7 +13,7 @@ steps:
- poetry build
- name: dryrun
image: quay.io/thegeeklab/wp-docker-buildx:2
image: quay.io/thegeeklab/wp-docker-buildx:4
settings:
containerfile: Containerfile.multiarch
dry_run: true
@ -26,7 +26,7 @@ steps:
- event: [pull_request]
- name: publish-dockerhub
image: quay.io/thegeeklab/wp-docker-buildx:2
image: quay.io/thegeeklab/wp-docker-buildx:4
group: container
settings:
auto_tag: true
@ -47,7 +47,7 @@ steps:
- ${CI_REPO_DEFAULT_BRANCH}
- name: publish-quay
image: quay.io/thegeeklab/wp-docker-buildx:2
image: quay.io/thegeeklab/wp-docker-buildx:4
group: container
settings:
auto_tag: true

View File

@ -32,7 +32,7 @@ steps:
- lychee --no-progress --format detailed docs/content README.md
- name: build
image: quay.io/thegeeklab/hugo:0.121.2
image: quay.io/thegeeklab/hugo:0.125.7
commands:
- hugo --panicOnWarning -s docs/

View File

@ -20,6 +20,15 @@ steps:
commands:
- pip install poetry poetry-dynamic-versioning -qq
- poetry install -E ansible-core
- poetry run ruff ./${CI_REPO_NAME//-/}
- poetry run ruff check ./${CI_REPO_NAME//-/}
environment:
PY_COLORS: "1"
- name: check-jinja
image: docker.io/library/python:3.12
commands:
- pip install poetry poetry-dynamic-versioning -qq
- poetry install -E ansible-core
- poetry run j2lint ansibledoctor/templates/ -i jinja-statements-indentation jinja-statements-delimiter
environment:
PY_COLORS: "1"

View File

@ -1,4 +1,4 @@
FROM python:3.12-alpine@sha256:c793b92fd9e0e2a0b611756788a033d569ca864b733461c8fb30cfd14847dbcf
FROM python:3.12-alpine@sha256:d24ed567ee3b972478a232ceff84b0d002e18ee9f5d38234ecbffece23dfa084
LABEL maintainer="Robert Kaussow <mail@thegeeklab.de>"
LABEL org.opencontainers.image.authors="Robert Kaussow <mail@thegeeklab.de>"
@ -12,7 +12,7 @@ ENV TZ=UTC
ADD dist/ansible_doctor-*.whl /
RUN apk --update add --virtual .build-deps build-base libffi-dev openssl-dev && \
RUN apk --update add --virtual .build-deps build-base libffi-dev openssl-dev git && \
pip install --upgrade --no-cache-dir pip && \
pip install --no-cache-dir $(find / -name "ansible_doctor-*.whl")[ansible-core] && \
rm -f ansible_doctor-*.whl && \

View File

@ -1,5 +1,5 @@
# renovate: datasource=github-releases depName=thegeeklab/hugo-geekdoc
THEME_VERSION := v0.44.0
THEME_VERSION := v0.45.0
THEME := hugo-geekdoc
BASEDIR := docs
THEMEDIR := $(BASEDIR)/themes

View File

@ -18,11 +18,17 @@ class AnsibleDoctor:
def __init__(self):
self.log = SingleLog()
self.logger = self.log.logger
self.args = self._cli_args()
self.config = self._get_config()
try:
self.config = SingleConfig()
self.config.load(args=self._parse_args())
self.log.register_hanlers(json=self.config.config.logging.json)
except ansibledoctor.exception.ConfigError as e:
self.log.sysexit_with_message(e)
self._execute()
def _cli_args(self):
def _parse_args(self):
"""
Use argparse for parsing CLI arguments.
@ -33,28 +39,40 @@ class AnsibleDoctor:
description="Generate documentation from annotated Ansible roles using templates"
)
parser.add_argument(
"base_dir", nargs="?", help="base directory (default: current working directory)"
"base_dir",
nargs="?",
default=self.config.config.base_dir,
help="base directory (default: current working directory)",
)
parser.add_argument(
"-c", "--config", dest="config_file", help="path to configuration file"
"-c",
"--config",
dest="config_file",
help="path to configuration file",
)
parser.add_argument(
"-o", "--output", dest="output_dir", action="store", help="output directory"
"-o",
"--output",
dest="renderer__dest",
action="store",
default=self.config.config.renderer.dest,
help="output directory",
metavar="OUTPUT_DIR",
)
parser.add_argument(
"-r",
"--recursive",
dest="recursive",
action="store_true",
default=None,
default=self.config.config.recursive,
help="run recursively over the base directory subfolders",
)
parser.add_argument(
"-f",
"--force",
dest="force_overwrite",
dest="renderer.force_overwrite",
action="store_true",
default=None,
default=self.config.config.renderer.force_overwrite,
help="force overwrite output file",
)
parser.add_argument(
@ -62,7 +80,7 @@ class AnsibleDoctor:
"--dry-run",
dest="dry_run",
action="store_true",
default=None,
default=self.config.config.dry_run,
help="dry run without writing",
)
parser.add_argument(
@ -70,49 +88,58 @@ class AnsibleDoctor:
"--no-role-detection",
dest="role_detection",
action="store_false",
default=None,
default=self.config.config.role.autodetect,
help="disable automatic role detection",
)
parser.add_argument(
"-v", dest="logging.level", action="append_const", const=-1, help="increase log level"
"-v",
dest="logging.level",
action="append_const",
const=-1,
help="increase log level",
)
parser.add_argument(
"-q", dest="logging.level", action="append_const", const=1, help="decrease log level"
"-q",
dest="logging.level",
action="append_const",
const=1,
help="decrease log level",
)
parser.add_argument(
"--version",
action="version",
version=f"%(prog)s {__version__}",
)
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
return parser.parse_args().__dict__
def _get_config(self):
try:
config = SingleConfig(args=self.args)
except ansibledoctor.exception.ConfigError as e:
self.log.sysexit_with_message(e)
return config
def _execute(self):
cwd = self.config.base_dir
cwd = os.path.abspath(self.config.config.base_dir)
walkdirs = [cwd]
if self.config.recursive:
if self.config.config.recursive:
walkdirs = [f.path for f in os.scandir(cwd) if f.is_dir()]
for item in walkdirs:
os.chdir(item)
self.config.set_config(base_dir=os.getcwd())
try:
self.log.set_level(self.config.config["logging"]["level"])
self.config.load(root_path=os.getcwd())
self.log.register_hanlers(json=self.config.config.logging.json)
except ansibledoctor.exception.ConfigError as e:
self.log.sysexit_with_message(e)
try:
self.log.set_level(self.config.config.logging.level)
except ValueError as e:
self.log.sysexit_with_message(f"Can not set log level.\n{e!s}")
self.logger.info(f"Using config file: {self.config.config_file}")
self.logger.info(f"Using config file: {self.config.config_files}")
self.logger.debug(f"Using working dir: {item}")
self.logger.debug(f"Using working directory: {os.path.relpath(item, self.log.ctx)}")
if self.config.config["role_detection"]:
if self.config.is_role:
self.logger.info(f"Ansible role detected: {self.config.config['role_name']}")
if self.config.config.role.autodetect:
if self.config.is_role():
self.logger.info(f"Ansible role detected: {self.config.config.role_name}")
else:
self.log.sysexit_with_message("No Ansible role detected")
else:

View File

@ -3,127 +3,15 @@
import os
import anyconfig
import environs
import jsonschema.exceptions
import ruamel.yaml
from appdirs import AppDirs
from jsonschema._utils import format_as_index
from dynaconf import Dynaconf, ValidationError, Validator
import ansibledoctor.exception
from ansibledoctor.utils import Singleton
config_dir = AppDirs("ansible-doctor").user_config_dir
default_config_file = os.path.join(config_dir, "config.yml")
default_envs_prefix = "ANSIBLE_DOCTOR_"
class Config:
"""
Create an object with all necessary settings.
Settings are loade from multiple locations in defined order (last wins):
- default settings defined by `self._get_defaults()`
- yaml config file, defaults to OS specific user config dir (https://pypi.org/project/appdirs/)
- provides cli parameters
"""
SETTINGS = {
"config_file": {
"default": default_config_file,
"env": "CONFIG_FILE",
"type": environs.Env().str,
},
"base_dir": {
"default": os.getcwd(),
"refresh": os.getcwd,
"env": "BASE_DIR",
"type": environs.Env().str,
},
"role_name": {
"default": "",
"env": "ROLE_NAME",
"type": environs.Env().str,
},
"dry_run": {
"default": False,
"env": "DRY_RUN",
"file": True,
"type": environs.Env().bool,
},
"logging.level": {
"default": "WARNING",
"env": "LOG_LEVEL",
"file": True,
"type": environs.Env().str,
},
"logging.json": {
"default": False,
"env": "LOG_JSON",
"file": True,
"type": environs.Env().bool,
},
"output_dir": {
"default": os.getcwd(),
"refresh": os.getcwd,
"env": "OUTPUT_DIR",
"file": True,
"type": environs.Env().str,
},
"recursive": {
"default": False,
"env": "RECURSIVE",
"type": environs.Env().bool,
},
"template_dir": {
"default": os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates"),
"env": "TEMPLATE_DIR",
"file": True,
"type": environs.Env().str,
},
"template": {
"default": "readme",
"env": "TEMPLATE",
"file": True,
"type": environs.Env().str,
},
"template_autotrim": {
"default": True,
"env": "TEMPLATE_AUTOTRIM",
"file": True,
"type": environs.Env().bool,
},
"force_overwrite": {
"default": False,
"env": "FORCE_OVERWRITE",
"file": True,
"type": environs.Env().bool,
},
"custom_header": {
"default": "",
"env": "CUSTOM_HEADER",
"file": True,
"type": environs.Env().str,
},
"exclude_files": {
"default": [],
"env": "EXCLUDE_FILES",
"file": True,
"type": environs.Env().list,
},
"exclude_tags": {
"default": [],
"env": "EXCLUDE_TAGS",
"file": True,
"type": environs.Env().list,
},
"role_detection": {
"default": True,
"env": "ROLE_DETECTION",
"file": True,
"type": environs.Env().bool,
},
}
"""Create configuration object."""
ANNOTATIONS = {
"meta": {
@ -158,183 +46,167 @@ class Config:
},
}
def __init__(self, args=None):
"""
Initialize a new settings class.
def __init__(self):
self.config_files = [
os.path.join(AppDirs("ansible-doctor").user_config_dir, "config.yml"),
".ansibledoctor",
".ansibledoctor.yml",
".ansibledoctor.yaml",
]
self.config_merge = True
self.args = {}
self.load()
:param args: An optional dict of options, arguments and commands from the CLI.
:param config_file: An optional path to a yaml config file.
:returns: None
def load(self, root_path=None, args=None):
if args:
if args.get("config_file"):
self.config_merge = False
self.config_files = [os.path.abspath(args.get("config_file"))]
args.pop("config_file")
"""
if args is None:
self._args = {}
else:
self._args = args
self._schema = None
self.config = None
self.is_role = False
self.set_config()
self.args = args
def _get_args(self, args):
cleaned = dict(filter(lambda item: item[1] is not None, args.items()))
self.config = Dynaconf(
envvar_prefix="ANSIBLE_DOCTOR",
merge_enabled=self.config_merge,
core_loaders=["YAML"],
root_path=root_path,
settings_files=self.config_files,
fresh_vars=["base_dir", "output_dir"],
validators=[
Validator(
"base_dir",
default=os.getcwd(),
apply_default_on_none=True,
is_type_of=str,
),
Validator(
"dry_run",
default=False,
is_type_of=bool,
),
Validator(
"recursive",
default=False,
is_type_of=bool,
),
Validator(
"exclude_files",
default=[],
is_type_of=list,
),
Validator(
"exclude_tags",
default=[],
is_type_of=list,
),
Validator(
"role.name",
is_type_of=str,
),
Validator(
"role.autodetect",
default=True,
is_type_of=bool,
),
Validator(
"logging.level",
default="WARNING",
is_in=[
"DEBUG",
"INFO",
"WARNING",
"ERROR",
"CRITICAL",
"debug",
"info",
"warning",
"error",
"critical",
],
),
Validator(
"logging.json",
default=False,
is_type_of=bool,
),
Validator(
"recursive",
default=False,
is_type_of=bool,
),
Validator(
"template.src",
default=os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates"),
is_type_of=str,
),
Validator(
"template.name",
default="readme",
is_type_of=str,
),
Validator(
"template.options.tabulate_variables",
default=False,
is_type_of=bool,
),
Validator(
"renderer.autotrim",
default=True,
is_type_of=bool,
),
Validator(
"renderer.include_header",
default="",
is_type_of=str,
),
Validator(
"renderer.dest",
default=os.path.relpath(os.getcwd()),
is_type_of=str,
),
Validator(
"renderer.force_overwrite",
default=False,
is_type_of=bool,
),
],
)
normalized = {}
for key, value in cleaned.items():
normalized = self._add_dict_branch(normalized, key.split("."), value)
self.validate()
# Override correct log level from argparse
levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
log_level = levels.index(self.SETTINGS["logging.level"]["default"])
if normalized.get("logging"):
for adjustment in normalized["logging"]["level"]:
log_level = min(len(levels) - 1, max(log_level + adjustment, 0))
normalized["logging"]["level"] = levels[log_level]
log_level = levels.index(self.config.logging.level.upper())
if self.args.get("logging.level") and isinstance(self.args["logging.level"], list):
for lvl in self.args["logging.level"]:
log_level = min(len(levels) - 1, max(log_level + lvl, 0))
return normalized
self.args["logging__level"] = levels[log_level]
def _get_defaults(self):
normalized = {}
for key, item in self.SETTINGS.items():
if item.get("refresh"):
item["default"] = item["refresh"]()
normalized = self._add_dict_branch(normalized, key.split("."), item["default"])
if root_path:
self.args["base_dir"] = root_path
self.schema = anyconfig.gen_schema(normalized)
return normalized
self.config.update(self.args)
self.validate()
def _get_envs(self):
normalized = {}
for key, item in self.SETTINGS.items():
if item.get("env"):
envname = f"{default_envs_prefix}{item['env']}"
def validate(self):
try:
value = item["type"](envname)
normalized = self._add_dict_branch(normalized, key.split("."), value)
except environs.EnvError as e:
if f'"{envname}" not set' in str(e):
pass
else:
raise ansibledoctor.exception.ConfigError(
"Unable to read environment variable", str(e)
) from e
self.config.validators.validate_all()
except ValidationError as e:
raise ansibledoctor.exception.ConfigError("Configuration error", e.message) from e
return normalized
def set_config(self, base_dir=None):
args = self._get_args(self._args)
envs = self._get_envs()
defaults = self._get_defaults()
self.recursive = defaults.get("recursive")
if envs.get("recursive"):
self.recursive = envs.get("recursive")
if args.get("recursive"):
self.recursive = args.get("recursive")
if "recursive" in defaults:
defaults.pop("recursive")
self.config_file = defaults.get("config_file")
if envs.get("config_file"):
self.config_file = self._normalize_path(envs.get("config_file"))
if args.get("config_file"):
self.config_file = self._normalize_path(args.get("config_file"))
if "config_file" in defaults:
defaults.pop("config_file")
self.base_dir = defaults.get("base_dir")
if envs.get("base_dir"):
self.base_dir = self._normalize_path(envs.get("base_dir"))
if args.get("base_dir"):
self.base_dir = self._normalize_path(args.get("base_dir"))
if base_dir:
self.base_dir = base_dir
if "base_dir" in defaults:
defaults.pop("base_dir")
self.is_role = os.path.isdir(os.path.join(self.base_dir, "tasks"))
# compute role_name default
defaults["role_name"] = os.path.basename(self.base_dir)
source_files = []
source_files.append((self.config_file, False))
source_files.append((os.path.join(os.getcwd(), ".ansibledoctor"), True))
source_files.append((os.path.join(os.getcwd(), ".ansibledoctor.yml"), True))
source_files.append((os.path.join(os.getcwd(), ".ansibledoctor.yaml"), True))
for config, first_found in source_files:
if config and os.path.exists(config):
with open(config, encoding="utf8") as stream:
s = stream.read()
try:
file_dict = ruamel.yaml.YAML(typ="safe", pure=True).load(s)
except (
ruamel.yaml.composer.ComposerError,
ruamel.yaml.scanner.ScannerError,
) as e:
message = f"{e.context} {e.problem}"
raise ansibledoctor.exception.ConfigError(
f"Unable to read config file: {config}", message
) from e
if self._validate(file_dict):
anyconfig.merge(defaults, file_dict, ac_merge=anyconfig.MS_DICTS)
defaults["logging"]["level"] = defaults["logging"]["level"].upper()
self.config_file = config
if first_found:
break
if self._validate(envs):
anyconfig.merge(defaults, envs, ac_merge=anyconfig.MS_DICTS)
if self._validate(args):
anyconfig.merge(defaults, args, ac_merge=anyconfig.MS_DICTS)
fix_files = ["output_dir", "template_dir", "custom_header"]
for filename in fix_files:
if defaults[filename] and defaults[filename] != "":
defaults[filename] = self._normalize_path(defaults[filename])
defaults["logging"]["level"] = defaults["logging"]["level"].upper()
self.config = defaults
def _normalize_path(self, path):
if not os.path.isabs(path):
base = os.path.join(os.getcwd(), path)
return os.path.abspath(os.path.expanduser(os.path.expandvars(base)))
return path
def _validate(self, config):
try:
anyconfig.validate(config, self.schema, ac_schema_safe=False)
except jsonschema.exceptions.ValidationError as e:
schema_error = "Failed validating '{validator}' in schema{schema}\n{message}".format(
validator=e.validator,
schema=format_as_index(list(e.relative_schema_path)[:-1]),
message=e.message,
def is_role(self):
self.config.role_name = self.config.get(
"role_name", os.path.basename(self.config.base_dir)
)
raise ansibledoctor.exception.ConfigError("Configuration error", schema_error) from e
return True
def _add_dict_branch(self, tree, vector, value):
key = vector[0]
tree[key] = (
value
if len(vector) == 1
else self._add_dict_branch(tree[key] if key in tree else {}, vector[1:], value)
)
return tree
return os.path.isdir(os.path.join(self.config.base_dir, "tasks"))
def get_annotations_definition(self, automatic=True):
annotations = {}
if automatic:
for k, item in self.ANNOTATIONS.items():
if "automatic" in item and item["automatic"]:
if item.get("automatic"):
annotations[k] = item
return annotations
@ -342,7 +214,7 @@ class Config:
annotations = []
if automatic:
for k, item in self.ANNOTATIONS.items():
if "automatic" in item and item["automatic"]:
if item.get("automatic"):
annotations.append(k)
return annotations
@ -352,9 +224,9 @@ class Config:
:return: str abs path
"""
template_dir = self.config.get("template_dir")
template = self.config.get("template")
return os.path.realpath(os.path.join(template_dir, template))
template_base = self.config.get("template.src")
template_name = self.config.get("template.name")
return os.path.realpath(os.path.join(template_base, template_name))
class SingleConfig(Config, metaclass=Singleton):

View File

@ -36,14 +36,14 @@ class Generator:
:return: None
"""
template_dir = self.config.get_template()
if os.path.isdir(template_dir):
self.logger.info(f"Using template dir: {template_dir}")
template = self.config.get_template()
if os.path.isdir(template):
self.logger.info(f"Using template: {os.path.relpath(template, self.log.ctx)}")
else:
self.log.sysexit_with_message(f"Can not open template dir {template_dir}")
self.log.sysexit_with_message(f"Can not open template directory {template}")
for file in glob.iglob(template_dir + "/**/*." + self.extension, recursive=True):
relative_file = file[len(template_dir) + 1 :]
for file in glob.iglob(template + "/**/*." + self.extension, recursive=True):
relative_file = file[len(template) + 1 :]
if ntpath.basename(file)[:1] != "_":
self.logger.debug(f"Found template file: {relative_file}")
self.template_files.append(relative_file)
@ -56,19 +56,19 @@ class Generator:
os.makedirs(directory, exist_ok=True)
self.logger.info(f"Creating dir: {directory}")
except FileExistsError as e:
self.log.sysexit_with_message(str(e))
self.log.sysexit_with_message(e)
def _write_doc(self):
files_to_overwite = []
for file in self.template_files:
doc_file = os.path.join(
self.config.config.get("output_dir"), os.path.splitext(file)[0]
self.config.config.get("renderer.dest"), os.path.splitext(file)[0]
)
if os.path.isfile(doc_file):
files_to_overwite.append(doc_file)
header_file = self.config.config.get("custom_header")
header_file = self.config.config.get("renderer.include_header")
role_data = self._parser.get_data()
header_content = ""
if bool(header_file):
@ -81,7 +81,7 @@ class Generator:
if (
len(files_to_overwite) > 0
and self.config.config.get("force_overwrite") is False
and self.config.config.get("renderer.force_overwrite") is False
and not self.config.config["dry_run"]
):
files_to_overwite_string = "\n".join(files_to_overwite)
@ -98,11 +98,14 @@ class Generator:
for file in self.template_files:
doc_file = os.path.join(
self.config.config.get("output_dir"), os.path.splitext(file)[0]
self.config.config.get("renderer.dest"), os.path.splitext(file)[0]
)
source_file = self.config.get_template() + "/" + file
self.logger.debug(f"Writing doc output to: {doc_file} from: {source_file}")
self.logger.debug(
f"Writing renderer output to: {os.path.relpath(doc_file, self.log.ctx)} "
f"from: {os.path.dirname(os.path.relpath(source_file, self.log.ctx))}"
)
# make sure the directory exists
self._create_dir(os.path.dirname(doc_file))
@ -123,7 +126,10 @@ class Generator:
jenv.filters["safe_join"] = self._safe_join
# keep the old name of the function to not break custom templates.
jenv.filters["save_join"] = self._safe_join
data = jenv.from_string(data).render(role_data, role=role_data)
template_options = self.config.config.get("template.options")
data = jenv.from_string(data).render(
role_data, role=role_data, options=template_options
)
if not self.config.config["dry_run"]:
with open(doc_file, "wb") as outfile:
outfile.write(header_content.encode("utf-8"))
@ -167,12 +173,12 @@ class Generator:
normalized = jinja2.filters.do_join(eval_ctx, value, d, attribute=None)
if self.config.config["template_autotrim"]:
if self.config.config.renderer.autotrim:
for s in [r" +(\n|\t| )", r"(\n|\t) +"]:
normalized = re.sub(s, "\\1", normalized)
return jinja2.filters.do_mark_safe(normalized)
def render(self):
self.logger.info(f"Using output dir: {self.config.config.get('output_dir')}")
self.logger.info(f"Using renderer destination: {self.config.config.get('renderer.dest')}")
self._write_doc()

View File

@ -72,12 +72,15 @@ class Parser:
except YAMLError as e:
self.log.sysexit_with_message(f"Unable to read yaml file {rfile}\n{e}")
tags = [
task.get("tags")
for task in raw
if task.get("tags")
and task.get("tags") not in self.config.config["exclude_tags"]
]
tags = []
for task in raw:
task_tags = task.get("tags")
if isinstance(task_tags, str):
task_tags = [task_tags]
for tag in task_tags:
if tag not in self.config.config["exclude_tags"]:
tags.append(tag)
for tag in flatten(tags):
self._data["tag"][tag] = {"value": tag}

View File

@ -21,7 +21,8 @@ class Registry:
def __init__(self):
self._doc = []
self.config = SingleConfig()
self.log = SingleLog().logger
self.log = SingleLog()
self.logger = self.log.logger
self._scan_for_yamls()
def get_files(self):
@ -35,19 +36,19 @@ class Registry:
:return: None
"""
extensions = YAML_EXTENSIONS
base_dir = self.config.base_dir
role_name = os.path.basename(base_dir)
base_dir = self.config.config.base_dir
role_name = self.config.config.role_name
excludes = self.config.config.get("exclude_files")
excludespec = pathspec.PathSpec.from_lines("gitwildmatch", excludes)
self.log.debug(f"Scan for files: {base_dir}")
self.logger.debug(f"Scan for files: {os.path.relpath(base_dir,self.log.ctx)}")
for extension in extensions:
pattern = os.path.join(base_dir, "**/*." + extension)
for filename in glob.iglob(pattern, recursive=True):
if not excludespec.match_file(filename):
self.log.debug(
f"Adding file to '{role_name}': {os.path.relpath(filename, base_dir)}"
f"Adding file to role '{role_name}': {os.path.relpath(filename, base_dir)}"
)
self._doc.append(filename)
else:

View File

@ -2,10 +2,12 @@
{% set var = role.var | default({}) %}
{% if var %}
- [Default Variables](#default-variables)
{% if not options.tabulate_vars %}
{% for key, item in var | dictsort %}
- [{{ key }}](#{{ key }})
{% endfor %}
{% endif %}
{% endif %}
{% if tag %}
- [Discovered Tags](#discovered-tags)
{% endif %}

View File

@ -0,0 +1,49 @@
{% set var = role.var | default({}) %}
{% if var %}
## Default Variables
{% set columns = ["variable", "default", "description", "type", "deprecated", "example"] %}
{% set found_columns = ["variable", "default"] + var.values() | map("list") | sum(start=["key"]) | unique | list %}
{% for c in columns %}
{% if c in found_columns %}
|{{ c | capitalize -}}
{% endif %}
{% endfor %}
|
{% for c in columns %}
{% if c in found_columns %}
|{{ "-" * (c | length) -}}
{% endif %}
{% endfor %}
|
{% for key, item in var | dictsort %}
|{{ key -}}
|{{ (item.value | default({}))[key] | default -}}
{% if "description" in found_columns %}
|{{ item.description | default([]) | safe_join("<br />") | replace("\n", "<br />") | replace("|", "\|") -}}
{% endif %}
{% if "type" in found_columns %}
|{{ item.type | default([]) | join("<br />") -}}
{% endif %}
{% if "deprecated" in found_columns %}
|
{%- if "deprecated" in found_columns %}
{% if item.deprecated is defined %}
{% set deprecated = [item.deprecated] if item.deprecated is string else item.deprecated %}
{% set deprecated_string = deprecated | map("replace", "\n", "<br />") | safe_join("<br />") %}
{% if deprecated_string -%}
{{ deprecated_string }}
{%- else -%}
True
{%- endif %}
{%- else -%}
False
{%- endif %}
{% endif %}
{% endif %}
{% if "example" in found_columns %}
|{{ item.example | default([]) | safe_join("<br />") | replace("\n", "<br />") | replace("|", "\|") -}}
{% endif %}
|
{% endfor %}
{% endif %}

View File

@ -23,7 +23,11 @@ summary: {{ meta.summary.value | safe_join(" ") }}
{% include '_requirements.j2' %}
{# Vars #}
{% if options.tabulate_vars %}
{% include '_vars_tabulated.j2' %}
{% else %}
{% include '_vars.j2' %}
{% endif %}
{# Tag #}
{% include '_tag.j2' %}

View File

@ -15,7 +15,11 @@
{% include '_requirements.j2' %}
{# Vars #}
{% if options.tabulate_vars %}
{% include '_vars_tabulated.j2' %}
{% else %}
{% include '_vars.j2' %}
{% endif %}
{# Tag #}
{% include '_tag.j2' %}

View File

@ -4,10 +4,12 @@
{% set var = role.var | default({}) %}
{% if var %}
- [Default Variables](#default-variables)
{% if not options.tabulate_vars %}
{% for key, item in var | dictsort %}
- [{{ key }}](#{{ key }})
{% endfor %}
{% endif %}
{% endif %}
{% if tag %}
- [Discovered Tags](#discovered-tags)
{% endif %}

View File

@ -0,0 +1,49 @@
{% set var = role.var | default({}) %}
{% if var %}
## Default Variables
{% set columns = ["variable", "default", "description", "type", "deprecated", "example"] %}
{% set found_columns = ["variable", "default"] + var.values() | map("list") | sum(start=["key"]) | unique | list %}
{% for c in columns %}
{% if c in found_columns %}
|{{ c | capitalize -}}
{% endif %}
{% endfor %}
|
{% for c in columns %}
{% if c in found_columns %}
|{{ "-" * (c | length) -}}
{% endif %}
{% endfor %}
|
{% for key, item in var | dictsort %}
|{{ key -}}
|{{ (item.value | default({}))[key] | default -}}
{% if "description" in found_columns %}
|{{ item.description | default([]) | safe_join("<br />") | replace("\n", "<br />") | replace("|", "\|") -}}
{% endif %}
{% if "type" in found_columns %}
|{{ item.type | default([]) | join("<br />") -}}
{% endif %}
{% if "deprecated" in found_columns %}
|
{%- if "deprecated" in found_columns %}
{% if item.deprecated is defined %}
{% set deprecated = [item.deprecated] if item.deprecated is string else item.deprecated %}
{% set deprecated_string = deprecated | map("replace", "\n", "<br />") | safe_join("<br />") %}
{% if deprecated_string -%}
{{ deprecated_string }}
{%- else -%}
True
{%- endif %}
{%- else -%}
False
{%- endif %}
{% endif %}
{% endif %}
{% if "example" in found_columns %}
|{{ item.example | default([]) | safe_join("<br />") | replace("\n", "<br />") | replace("|", "\|") -}}
{% endif %}
|
{% endfor %}
{% endif %}

View File

@ -128,7 +128,7 @@ class LogFilter:
class MultilineFormatter(logging.Formatter):
"""Reset color after newline characters."""
def format(self, record): # noqa
def format(self, record):
record.msg = record.msg.strip().replace("\n", f"\n{colorama.Style.RESET_ALL}... ")
return logging.Formatter.format(self, record)
@ -136,7 +136,7 @@ class MultilineFormatter(logging.Formatter):
class MultilineJsonFormatter(jsonlogger.JsonFormatter):
"""Remove newline characters."""
def format(self, record): # noqa
def format(self, record):
record.msg = record.msg.replace("\n", " ")
return jsonlogger.JsonFormatter.format(self, record)
@ -145,13 +145,10 @@ class Log:
"""Handle logging."""
def __init__(self, level=logging.WARNING, name="ansibledoctor", json=False):
self.ctx = os.getcwd()
self.logger = logging.getLogger(name)
self.logger.setLevel(level)
self.logger.addHandler(self._get_error_handler(json=json))
self.logger.addHandler(self._get_warning_handler(json=json))
self.logger.addHandler(self._get_info_handler(json=json))
self.logger.addHandler(self._get_critical_handler(json=json))
self.logger.addHandler(self._get_debug_handler(json=json))
self.register_hanlers(json=json)
self.logger.propagate = False
def _get_error_handler(self, json=False):
@ -249,8 +246,24 @@ class Log:
return handler
def register_hanlers(self, json=False):
"""
Enable or disable JSON logging.
:param enable: True to enable JSON logging, False to disable
"""
# Remove all existing handlers
for handler in self.logger.handlers[:]:
self.logger.removeHandler(handler)
self.logger.addHandler(self._get_error_handler(json=json))
self.logger.addHandler(self._get_warning_handler(json=json))
self.logger.addHandler(self._get_info_handler(json=json))
self.logger.addHandler(self._get_critical_handler(json=json))
self.logger.addHandler(self._get_debug_handler(json=json))
def set_level(self, s):
self.logger.setLevel(s)
self.logger.setLevel(s.upper())
def debug(self, msg):
"""Format info messages and return string."""
@ -287,7 +300,7 @@ class Log:
sys.exit(code)
def sysexit_with_message(self, msg, code=1):
self.logger.critical(str(msg.strip()))
self.logger.critical(str(msg).strip())
self.sysexit(code)

View File

@ -20,36 +20,17 @@ Configuration options can be set in different places, which are processed in the
---
# Default is the current working directory.
base_dir:
# Default is the basename of 'role_name'.
role_name:
# Auto-detect if the given directory is a role, can be disabled
# to parse loose files instead.
role_detection: True
# Don't write anything to file system
role:
# Default is the basename of 'role_name'.
name:
# Auto-detect if the given directory is a role, can be disabled
# to parse loose files instead.
autodetect: True
# Don't write anything to file system.
dry_run: False
logging:
# Possible options debug | info | warning | error | critical
level: "warning"
# Json logging can be enabled if a parsable output is required
json: False
# Path to write rendered template file. Default is the current working directory.
output_dir:
# Default is built-in templates directory.
template_dir:
template: readme
# By default, double spaces, spaces before and after line breaks or tab characters, etc.
# are automatically removed before the template is rendered. As a result, indenting
# with spaces does not work. If you want to use spaces to indent text, you must disable
# this option.
template_autotrim: True
# Don't ask to overwrite if output file exists.
force_overwrite: False
# Load custom header from given file and append template output to it before write.
custom_header: ""
exclude_files: []
# Examples
# exclude_files:
@ -59,6 +40,36 @@ exclude_files: []
# Exclude tags from automatic detection. Configured tags are only skipped
# if the tag is not used in an annotation.
exclude_tags: []
logging:
# Possible options: debug|info|warning| error|critical
level: "warning"
# JSON logging can be enabled if a parsable output is required.
json: False
template:
name: readme
# Default is built-in templates directory.
src:
options:
# Configures whether to tabulate variables in the output. When set to `True`,
# variables will be displayed in a tabular format intsead of plain marktdown sections.
# NOTE: This option does not support rendering multiline code blocks.
tabulate_vars: False
renderer:
# By default, double spaces, spaces before and after line breaks or tab characters, etc.
# are automatically removed before the template is rendered. As a result, indenting
# with spaces does not work. If you want to use spaces to indent text, you must disable
# this option.
autotrim: True
# Load custom header from given file and append template output to it before write.
include_header: ""
# Path to write rendered template file. Default is the current working directory.
dest:
# Don't ask to overwrite if output file exists.
force_overwrite: False
```
## CLI
@ -91,22 +102,25 @@ options:
## Environment Variables
```Shell
ANSIBLE_DOCTOR_CONFIG_FILE=
ANSIBLE_DOCTOR_ROLE_DETECTION=true
ANSIBLE_DOCTOR_BASE_DIR=
ANSIBLE_DOCTOR_RECURSIVE=false
ANSIBLE_DOCTOR_ROLE_NAME=
ANSIBLE_DOCTOR_DRY_RUN=false
ANSIBLE_DOCTOR_LOG_LEVEL=warning
ANSIBLE_DOCTOR_LOG_JSON=false
ANSIBLE_DOCTOR_OUTPUT_DIR=
ANSIBLE_DOCTOR_TEMPLATE_DIR=
ANSIBLE_DOCTOR_TEMPLATE=readme
ANSIBLE_DOCTOR_TEMPLATE_AUTOTRIM=true
ANSIBLE_DOCTOR_FORCE_OVERWRITE=false
ANSIBLE_DOCTOR_CUSTOM_HEADER=
ANSIBLE_DOCTOR_DRY_RUN=False
ANSIBLE_DOCTOR_EXCLUDE_FILES=
ANSIBLE_DOCTOR_EXCLUDE_FILES=molecule/,files/**/*.py
ANSIBLE_DOCTOR_EXCLUDE_TAGS=
ANSIBLE_DOCTOR_ROLE__NAME=
ANSIBLE_DOCTOR_ROLE__AUTODETECT=True
ANSIBLE_DOCTOR_LOGGING__LEVEL="warning"
ANSIBLE_DOCTOR_LOGGING__JSON=False
ANSIBLE_DOCTOR_TEMPLATE__NAME=readme
ANSIBLE_DOCTOR_TEMPLATE__SRC=
ANSIBLE_DOCTOR_TEMPLATE__OPTIONS__TABULATE_VARS=False
ANSIBLE_DOCTOR_RENDERER__AUTOTRIM=True
ANSIBLE_DOCTOR_RENDERER__INCLUDE_HEADER=
ANSIBLE_DOCTOR_RENDERER__DEST=
ANSIBLE_DOCTOR_RENDERER__FORCE_OVERWRITE=False
```
## Pre-Commit setup
@ -119,8 +133,8 @@ To use _ansible-doctor_ with the [pre-commit](https://pre-commit.com/) framework
{{< highlight yaml "linenos=table" >}}
- repo: https://github.com/thegeeklab/ansible-doctor
# change ref to the latest release from https://github.com/thegeeklab/ansible-doctor/releases
rev: v1.4.8
# update version with `pre-commit autoupdate`
rev: v4.0.4
hooks:
- id: ansible-doctor
{{< /highlight >}}

View File

@ -1,5 +1,9 @@
---
custom_header: HEADER.md
logging:
level: debug
template: readme
template:
name: readme
renderer:
include_header: HEADER.md

View File

@ -204,7 +204,6 @@ demo_role_unset: some_value
## Dependencies
- role1
- role2
## License

View File

@ -18,6 +18,5 @@ galaxy_info:
- documentation
dependencies:
- role1
- role: role2
- name: namespace.role3

View File

@ -1,5 +1,9 @@
---
custom_header: HEADER.md
logging:
level: debug
template: readme
template:
name: readme
renderer:
include_header: HEADER.md

855
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -21,31 +21,28 @@ classifiers = [
description = "Generate documentation from annotated Ansible roles using templates."
documentation = "https://ansible-doctor.geekdocs.de/"
homepage = "https://ansible-doctor.geekdocs.de/"
include = [
"LICENSE",
]
include = ["LICENSE"]
keywords = ["ansible", "role", "documentation"]
license = "GPL-3.0-only"
name = "ansible-doctor"
packages = [
{include = "ansibledoctor"},
]
packages = [{ include = "ansibledoctor" }]
readme = "README.md"
repository = "https://github.com/thegeeklab/ansible-doctor/"
version = "0.0.0"
[tool.poetry.dependencies]
Jinja2 = "3.1.2"
anyconfig = "0.13.0"
Jinja2 = "3.1.4"
anyconfig = "0.14.0"
appdirs = "1.4.4"
colorama = "0.4.6"
environs = "10.2.0"
jsonschema = "4.20.0"
environs = "11.0.0"
jsonschema = "4.22.0"
pathspec = "0.12.1"
python = "^3.9.0"
python-json-logger = "2.0.7"
"ruamel.yaml" = "0.18.5"
ansible-core = {version = "2.14.12", optional = true}
python-json-logger = { git = "https://github.com/nhairs/python-json-logger.git", tag = "v3.1.0" }
"ruamel.yaml" = "0.18.6"
dynaconf = "3.2.5"
ansible-core = { version = "2.14.17", optional = true }
[tool.poetry.extras]
ansible-core = ["ansible-core"]
@ -54,11 +51,13 @@ ansible-core = ["ansible-core"]
ansible-doctor = "ansibledoctor.cli:main"
[tool.poetry.group.dev.dependencies]
ruff = "0.1.11"
pytest = "7.4.4"
pytest-mock = "3.12.0"
pytest-cov = "4.1.0"
ruff = "0.4.7"
pytest = "8.2.1"
pytest-mock = "3.14.0"
pytest-cov = "5.0.0"
toml = "0.10.2"
j2lint = "1.1.0"
[tool.poetry-dynamic-versioning]
enable = true
@ -97,6 +96,7 @@ exclude = [
line-length = 99
indent-width = 4
[tool.ruff.lint]
# Explanation of errors
#
# D102: Missing docstring in public method

View File

@ -2,6 +2,11 @@
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>thegeeklab/renovate-presets"],
"packageRules": [
{
"description": "Ansible base dependencies",
"matchPackageNames": ["ansible-core"],
"separateMinorPatch": true
},
{
"matchManagers": ["woodpecker"],
"matchFileNames": [".woodpecker/test.yml"],