mirror of
https://github.com/thegeeklab/ansible-doctor.git
synced 2024-06-10 04:10:53 +02:00
Compare commits
250 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
abee343056 | ||
|
29320b6b96 | ||
8e042c739e | |||
9b20c11660 | |||
|
5760ee0832 | ||
|
db94c07396 | ||
|
73bbd746d3 | ||
ab0372bef5 | |||
3df7e465db | |||
89c6a11be4 | |||
|
4051d2915d | ||
|
fe4e4e5f7a | ||
|
172e4f4380 | ||
|
fada900568 | ||
|
013f760c8a | ||
|
00adc389a2 | ||
|
84fdc06315 | ||
|
db68e80372 | ||
|
af702628eb | ||
|
81d4e97af6 | ||
|
a33f3c53bb | ||
|
ccc2d249f8 | ||
|
f7ff5fd624 | ||
|
acee6e1285 | ||
|
2375ad118d | ||
|
a2f02527d9 | ||
|
0bf59ac34f | ||
|
94ec1a632b | ||
|
075e1f91ca | ||
4cf63bf2fe | |||
|
e3f797d5e3 | ||
|
3a0c5ae35f | ||
|
9f7f943c93 | ||
|
b38c4aa2b8 | ||
|
ed167d1443 | ||
|
da6fd26c6d | ||
|
522c21f8fc | ||
|
28e39055e3 | ||
|
894965286b | ||
|
c2e0f787ce | ||
|
d524537fd3 | ||
|
a559b654ca | ||
|
aa78adf912 | ||
6e88c18375 | |||
7b9ba09f1d | |||
|
08883952c1 | ||
|
e1ef4937dd | ||
|
571222f6f5 | ||
|
6d50525021 | ||
|
3ade4698e7 | ||
|
ee81c8ee73 | ||
|
03df5bd79b | ||
|
1e32a8f87a | ||
|
dab9bc8691 | ||
e2eaa81c4f | |||
|
732f588aa9 | ||
6619351fbd | |||
|
8f6f444931 | ||
|
49b861cabc | ||
|
59b497d745 | ||
b2f4fd2bd8 | |||
|
864af95606 | ||
|
758c87ee80 | ||
|
9b0edda70a | ||
|
1d32f7548a | ||
|
704cdb9d7c | ||
|
c7f3fe57a0 | ||
|
1270d7cb7d | ||
|
461eeb2d74 | ||
|
0817646004 | ||
|
5242fd882a | ||
|
052c668d92 | ||
1e8a8beef7 | |||
|
c124460c11 | ||
|
38bd53f7bc | ||
|
dbf9c979ac | ||
|
fe12548387 | ||
ae14704b74 | |||
|
2232a12bc8 | ||
78d4c5f44b | |||
c3068a573f | |||
|
505f9b58cc | ||
|
593df92d32 | ||
|
568b91654d | ||
|
4afabc4284 | ||
|
10ff283ec2 | ||
|
1d0ff92bf3 | ||
|
7245a4149c | ||
|
df155dcf8a | ||
2ce29b2bff | |||
1dc53d1970 | |||
2f1f42318b | |||
|
69d682df79 | ||
223b1d8814 | |||
|
70539cc9a2 | ||
|
982e2db2df | ||
|
787c09a741 | ||
|
95c2a6aeaf | ||
|
855f48894a | ||
2270051d0d | |||
|
c0f100b70e | ||
|
41ed10270d | ||
|
eef09f4a42 | ||
|
0460f09627 | ||
6a7ae3011d | |||
0db500b0a8 | |||
dfa10dd209 | |||
|
82398da75d | ||
|
0257b874e9 | ||
|
40b96ae285 | ||
0f65f50e06 | |||
|
9476810896 | ||
|
2eb9aad213 | ||
cacc92f831 | |||
9536cd400d | |||
593a90ff10 | |||
|
a1e3e669d4 | ||
|
3e1eb79a5b | ||
9aba240985 | |||
|
9ba01f9b99 | ||
|
fd30e47e11 | ||
|
8f3053d739 | ||
|
4f42f5e133 | ||
|
0a23dd3539 | ||
|
a4a29c1598 | ||
|
b33a4b0f60 | ||
|
2727ecf6c2 | ||
|
a44b7789e5 | ||
|
972abb8d9e | ||
|
0456c5b870 | ||
|
4d016fcae5 | ||
|
6362c1cbcf | ||
4fc8c8d923 | |||
536c74eac1 | |||
315fc2b521 | |||
|
92fed951b0 | ||
d6ce15fc00 | |||
66f7738139 | |||
|
6e0a07f260 | ||
|
250df17565 | ||
|
9a9f6986b2 | ||
|
b4e06adc2c | ||
|
e78494d258 | ||
|
c7169539e8 | ||
|
0a166c1d39 | ||
|
e1044ecc5e | ||
9862372841 | |||
|
0668d31878 | ||
|
4c608330dc | ||
|
94bbce7bbe | ||
|
66523c1c15 | ||
|
b340dabf24 | ||
c5bbdbc764 | |||
|
9d227aa7c3 | ||
|
eb0a2f2dcd | ||
|
05884eddac | ||
|
7eb4f91a5d | ||
|
036126ee1e | ||
|
4d9366cf7a | ||
|
3d2ba7593d | ||
8e3dd49970 | |||
|
33caaf78c9 | ||
|
2b55025150 | ||
|
7f0bc4ad57 | ||
|
89639f82f9 | ||
|
4e81bceaa0 | ||
|
b979852f71 | ||
0cf7fc9ce2 | |||
|
69afdf56ed | ||
|
2817668e0a | ||
|
220a55e524 | ||
a4a497ca4e | |||
|
aa29881542 | ||
|
54709b26b7 | ||
|
4a3241d0e6 | ||
|
415aabf64a | ||
|
dbb3a89f5f | ||
|
de993d2b8c | ||
|
7e5b22ae03 | ||
|
825f749898 | ||
|
18ca26f7ea | ||
|
fff4fd7d4b | ||
301de5c654 | |||
|
114f87306f | ||
|
ad49089c3c | ||
|
8fcc5ea92b | ||
0b031bcec1 | |||
|
9b913d4032 | ||
|
45547d718b | ||
|
836f13ffa6 | ||
|
49e4a7183a | ||
|
4888d01fae | ||
1144c6f733 | |||
8280a62fb9 | |||
|
584e212f52 | ||
|
8faa599a98 | ||
|
b84e0569a4 | ||
458f9fc577 | |||
|
ea5760fa29 | ||
|
b67896d238 | ||
588f41a3ea | |||
|
b6001fe81d | ||
|
7d1478e3c1 | ||
|
98660389a5 | ||
|
eed89ca661 | ||
|
bfd8c8e549 | ||
|
b14b2b5153 | ||
|
8acb86a6da | ||
|
70f458dce6 | ||
|
6d57d2df59 | ||
34cc4ca062 | |||
|
0057baabca | ||
|
af85c0ec05 | ||
|
4206f228db | ||
|
ed7f28bcfa | ||
|
af340128ba | ||
|
d5237a7b7f | ||
|
370713a178 | ||
9ab2fd1c0a | |||
|
9667ff2773 | ||
4fbaa27668 | |||
|
f72b9573ec | ||
|
0ccec85234 | ||
|
adf9461953 | ||
596b27e696 | |||
|
e4acc05fd9 | ||
|
bc32109897 | ||
|
26d7a3cb8d | ||
d47427e128 | |||
|
3d544b3beb | ||
|
17a448d416 | ||
|
693fcc1359 | ||
|
88fc60af80 | ||
|
c0f674cbe8 | ||
|
578dde6e9a | ||
|
d77fa10686 | ||
|
bc2f11ae0b | ||
71c1679d6c | |||
|
6d293f8b2b | ||
|
7b721dcfa3 | ||
|
997959eda2 | ||
|
152e4e6adb | ||
|
a1ea9e9a6c | ||
|
f86ab8b187 | ||
|
daf94a971c | ||
a95add9fbe | |||
|
bad8c7e7a1 | ||
|
641c51a248 | ||
|
ea9c5b2dfb | ||
|
6b9690646e |
|
@ -1,23 +0,0 @@
|
|||
# Changelog
|
||||
|
||||
{{ range .Versions -}}
|
||||
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]({{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}){{ else }}{{ .Tag.Name }}{{ end }} ({{ datetime "2006-01-02" .Tag.Date }})
|
||||
|
||||
{{ range .CommitGroups -}}
|
||||
### {{ .Title }}
|
||||
|
||||
{{ range .Commits -}}
|
||||
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ (regexReplaceAll "(.*)/issues/(.*)" (regexReplaceAll "(Co-\\w*-by.*)" .Subject "") "${1}/pull/${2}") | trim }}
|
||||
{{ end }}
|
||||
{{- end -}}
|
||||
|
||||
{{- if .NoteGroups -}}
|
||||
{{ range .NoteGroups -}}
|
||||
### {{ .Title }}
|
||||
|
||||
{{ range .Notes }}
|
||||
{{ .Body }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
|
@ -1,25 +0,0 @@
|
|||
style: github
|
||||
template: CHANGELOG.tpl.md
|
||||
info:
|
||||
title: CHANGELOG
|
||||
repository_url: https://github.com/thegeeklab/ansible-doctor
|
||||
options:
|
||||
commit_groups:
|
||||
title_maps:
|
||||
feat: Features
|
||||
fix: Bug Fixes
|
||||
perf: Performance Improvements
|
||||
refactor: Code Refactoring
|
||||
chore: Others
|
||||
test: Testing
|
||||
ci: CI Pipeline
|
||||
docs: Documentation
|
||||
header:
|
||||
pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$"
|
||||
pattern_maps:
|
||||
- Type
|
||||
- Scope
|
||||
- Subject
|
||||
notes:
|
||||
keywords:
|
||||
- BREAKING CHANGE
|
425
.drone.jsonnet
425
.drone.jsonnet
|
@ -1,425 +0,0 @@
|
|||
local PythonVersion(pyversion='3.7') = {
|
||||
name: 'python' + std.strReplace(pyversion, '.', '') + '-pytest',
|
||||
image: 'python:' + pyversion,
|
||||
environment: {
|
||||
PY_COLORS: 1,
|
||||
},
|
||||
commands: [
|
||||
'pip install poetry poetry-dynamic-versioning -qq',
|
||||
'poetry config experimental.new-installer false',
|
||||
'poetry install',
|
||||
'poetry version',
|
||||
'poetry run ansible-doctor --help',
|
||||
],
|
||||
depends_on: [
|
||||
'fetch',
|
||||
],
|
||||
};
|
||||
|
||||
local PipelineLint = {
|
||||
kind: 'pipeline',
|
||||
name: 'lint',
|
||||
platform: {
|
||||
os: 'linux',
|
||||
arch: 'amd64',
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
name: 'check-format',
|
||||
image: 'python:3.11',
|
||||
environment: {
|
||||
PY_COLORS: 1,
|
||||
},
|
||||
commands: [
|
||||
'git fetch -tq',
|
||||
'pip install poetry poetry-dynamic-versioning -qq',
|
||||
'poetry config experimental.new-installer false',
|
||||
'poetry install',
|
||||
'poetry run yapf -dr ./ansibledoctor',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'check-coding',
|
||||
image: 'python:3.11',
|
||||
environment: {
|
||||
PY_COLORS: 1,
|
||||
},
|
||||
commands: [
|
||||
'git fetch -tq',
|
||||
'pip install poetry poetry-dynamic-versioning -qq',
|
||||
'poetry config experimental.new-installer false',
|
||||
'poetry install',
|
||||
'poetry run ruff ./ansibledoctor',
|
||||
],
|
||||
},
|
||||
],
|
||||
trigger: {
|
||||
ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'],
|
||||
},
|
||||
};
|
||||
|
||||
local PipelineTest = {
|
||||
kind: 'pipeline',
|
||||
name: 'test',
|
||||
platform: {
|
||||
os: 'linux',
|
||||
arch: 'amd64',
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
name: 'fetch',
|
||||
image: 'python:3.11',
|
||||
commands: [
|
||||
'git fetch -tq',
|
||||
],
|
||||
},
|
||||
PythonVersion(pyversion='3.7'),
|
||||
PythonVersion(pyversion='3.8'),
|
||||
PythonVersion(pyversion='3.9'),
|
||||
PythonVersion(pyversion='3.10'),
|
||||
PythonVersion(pyversion='3.11'),
|
||||
],
|
||||
depends_on: [
|
||||
'lint',
|
||||
],
|
||||
trigger: {
|
||||
ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'],
|
||||
},
|
||||
};
|
||||
|
||||
local PipelineBuildPackage = {
|
||||
kind: 'pipeline',
|
||||
name: 'build-package',
|
||||
platform: {
|
||||
os: 'linux',
|
||||
arch: 'amd64',
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
name: 'build',
|
||||
image: 'python:3.11',
|
||||
commands: [
|
||||
'git fetch -tq',
|
||||
'pip install poetry poetry-dynamic-versioning -qq',
|
||||
'poetry build',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'checksum',
|
||||
image: 'alpine',
|
||||
commands: [
|
||||
'cd dist/ && sha256sum * > ../sha256sum.txt',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'changelog-generate',
|
||||
image: 'thegeeklab/git-chglog',
|
||||
commands: [
|
||||
'git fetch -tq',
|
||||
'git-chglog --no-color --no-emoji -o CHANGELOG.md ${DRONE_TAG:---next-tag unreleased unreleased}',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'changelog-format',
|
||||
image: 'thegeeklab/alpine-tools',
|
||||
commands: [
|
||||
'prettier CHANGELOG.md',
|
||||
'prettier -w CHANGELOG.md',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'publish-github',
|
||||
image: 'plugins/github-release',
|
||||
settings: {
|
||||
overwrite: true,
|
||||
api_key: { from_secret: 'github_token' },
|
||||
files: ['dist/*', 'sha256sum.txt'],
|
||||
title: '${DRONE_TAG}',
|
||||
note: 'CHANGELOG.md',
|
||||
},
|
||||
when: {
|
||||
ref: ['refs/tags/**'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'publish-pypi',
|
||||
image: 'python:3.11',
|
||||
commands: [
|
||||
'git fetch -tq',
|
||||
'pip install poetry poetry-dynamic-versioning -qq',
|
||||
'poetry publish -n',
|
||||
],
|
||||
environment: {
|
||||
POETRY_HTTP_BASIC_PYPI_USERNAME: { from_secret: 'pypi_username' },
|
||||
POETRY_HTTP_BASIC_PYPI_PASSWORD: { from_secret: 'pypi_password' },
|
||||
},
|
||||
when: {
|
||||
ref: ['refs/tags/**'],
|
||||
},
|
||||
},
|
||||
],
|
||||
depends_on: [
|
||||
'test',
|
||||
],
|
||||
trigger: {
|
||||
ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'],
|
||||
},
|
||||
};
|
||||
|
||||
local PipelineBuildContainer = {
|
||||
kind: 'pipeline',
|
||||
name: 'build-container',
|
||||
platform: {
|
||||
os: 'linux',
|
||||
arch: 'amd64',
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
name: 'build',
|
||||
image: 'python:3.11',
|
||||
commands: [
|
||||
'git fetch -tq',
|
||||
'pip install poetry poetry-dynamic-versioning -qq',
|
||||
'poetry build',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'dryrun',
|
||||
image: 'thegeeklab/drone-docker-buildx:23',
|
||||
settings: {
|
||||
dry_run: true,
|
||||
dockerfile: 'Dockerfile.multiarch',
|
||||
repo: 'thegeeklab/${DRONE_REPO_NAME}',
|
||||
platforms: [
|
||||
'linux/amd64',
|
||||
'linux/arm64',
|
||||
],
|
||||
provenance: false,
|
||||
},
|
||||
depends_on: ['build'],
|
||||
when: {
|
||||
ref: ['refs/pull/**'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'publish-dockerhub',
|
||||
image: 'thegeeklab/drone-docker-buildx:23',
|
||||
settings: {
|
||||
auto_tag: true,
|
||||
dockerfile: 'Dockerfile.multiarch',
|
||||
repo: 'thegeeklab/${DRONE_REPO_NAME}',
|
||||
username: { from_secret: 'docker_username' },
|
||||
password: { from_secret: 'docker_password' },
|
||||
platforms: [
|
||||
'linux/amd64',
|
||||
'linux/arm64',
|
||||
],
|
||||
provenance: false,
|
||||
},
|
||||
when: {
|
||||
ref: ['refs/heads/main', 'refs/tags/**'],
|
||||
},
|
||||
depends_on: ['dryrun'],
|
||||
},
|
||||
{
|
||||
name: 'publish-quay',
|
||||
image: 'thegeeklab/drone-docker-buildx:23',
|
||||
settings: {
|
||||
auto_tag: true,
|
||||
dockerfile: 'Dockerfile.multiarch',
|
||||
registry: 'quay.io',
|
||||
repo: 'quay.io/thegeeklab/${DRONE_REPO_NAME}',
|
||||
username: { from_secret: 'quay_username' },
|
||||
password: { from_secret: 'quay_password' },
|
||||
platforms: [
|
||||
'linux/amd64',
|
||||
'linux/arm64',
|
||||
],
|
||||
provenance: false,
|
||||
},
|
||||
when: {
|
||||
ref: ['refs/heads/main', 'refs/tags/**'],
|
||||
},
|
||||
depends_on: ['dryrun'],
|
||||
},
|
||||
],
|
||||
depends_on: [
|
||||
'test',
|
||||
],
|
||||
trigger: {
|
||||
ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'],
|
||||
},
|
||||
};
|
||||
|
||||
local PipelineDocs = {
|
||||
kind: 'pipeline',
|
||||
name: 'docs',
|
||||
platform: {
|
||||
os: 'linux',
|
||||
arch: 'amd64',
|
||||
},
|
||||
concurrency: {
|
||||
limit: 1,
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
name: 'assets',
|
||||
image: 'thegeeklab/alpine-tools',
|
||||
commands: [
|
||||
'make doc',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'markdownlint',
|
||||
image: 'thegeeklab/markdownlint-cli',
|
||||
commands: [
|
||||
"markdownlint 'docs/content/**/*.md' 'README.md' 'CONTRIBUTING.md'",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'spellcheck',
|
||||
image: 'thegeeklab/alpine-tools',
|
||||
commands: [
|
||||
"spellchecker --files 'docs/content/**/*.md' 'README.md' -d .dictionary -p spell indefinite-article syntax-urls --no-suggestions",
|
||||
],
|
||||
environment: {
|
||||
FORCE_COLOR: true,
|
||||
NPM_CONFIG_LOGLEVEL: 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'testbuild',
|
||||
image: 'thegeeklab/hugo:0.105.0',
|
||||
commands: [
|
||||
'hugo --panicOnWarning -s docs/ -b http://localhost:8000/',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'link-validation',
|
||||
image: 'thegeeklab/link-validator',
|
||||
commands: [
|
||||
'link-validator --color=always --rate-limit 10',
|
||||
],
|
||||
environment: {
|
||||
LINK_VALIDATOR_BASE_DIR: 'docs/public',
|
||||
LINK_VALIDATOR_RETRIES: '3',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'build',
|
||||
image: 'thegeeklab/hugo:0.105.0',
|
||||
commands: [
|
||||
'hugo --panicOnWarning -s docs/',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'beautify',
|
||||
image: 'thegeeklab/alpine-tools',
|
||||
commands: [
|
||||
"html-beautify -r -f 'docs/public/**/*.html'",
|
||||
],
|
||||
environment: {
|
||||
FORCE_COLOR: true,
|
||||
NPM_CONFIG_LOGLEVEL: 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'publish',
|
||||
image: 'thegeeklab/drone-s3-sync',
|
||||
settings: {
|
||||
access_key: { from_secret: 's3_access_key' },
|
||||
bucket: 'geekdocs',
|
||||
delete: true,
|
||||
endpoint: 'https://sp.rknet.org',
|
||||
path_style: true,
|
||||
secret_key: { from_secret: 's3_secret_access_key' },
|
||||
source: 'docs/public/',
|
||||
strip_prefix: 'docs/public/',
|
||||
target: '/${DRONE_REPO_NAME}',
|
||||
},
|
||||
when: {
|
||||
ref: ['refs/heads/main', 'refs/tags/**'],
|
||||
},
|
||||
},
|
||||
],
|
||||
depends_on: [
|
||||
'build-package',
|
||||
'build-container',
|
||||
],
|
||||
trigger: {
|
||||
ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'],
|
||||
},
|
||||
};
|
||||
|
||||
local PipelineNotifications = {
|
||||
kind: 'pipeline',
|
||||
name: 'notifications',
|
||||
platform: {
|
||||
os: 'linux',
|
||||
arch: 'amd64',
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
name: 'pushrm-dockerhub',
|
||||
image: 'chko/docker-pushrm:1',
|
||||
environment: {
|
||||
DOCKER_PASS: {
|
||||
from_secret: 'docker_password',
|
||||
},
|
||||
DOCKER_USER: {
|
||||
from_secret: 'docker_username',
|
||||
},
|
||||
PUSHRM_FILE: 'README.md',
|
||||
PUSHRM_SHORT: 'Annotation based documentation for your Ansible roles',
|
||||
PUSHRM_TARGET: 'thegeeklab/${DRONE_REPO_NAME}',
|
||||
},
|
||||
when: {
|
||||
status: ['success'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pushrm-quay',
|
||||
image: 'chko/docker-pushrm:1',
|
||||
environment: {
|
||||
APIKEY__QUAY_IO: {
|
||||
from_secret: 'quay_token',
|
||||
},
|
||||
PUSHRM_FILE: 'README.md',
|
||||
PUSHRM_TARGET: 'quay.io/thegeeklab/${DRONE_REPO_NAME}',
|
||||
},
|
||||
when: {
|
||||
status: ['success'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'matrix',
|
||||
image: 'thegeeklab/drone-matrix',
|
||||
settings: {
|
||||
homeserver: { from_secret: 'matrix_homeserver' },
|
||||
roomid: { from_secret: 'matrix_roomid' },
|
||||
template: 'Status: **{{ .Build.Status }}**<br/> Build: [{{ .Repo.Owner }}/{{ .Repo.Name }}]({{ .Build.Link }}){{ if .Build.Branch }} ({{ .Build.Branch }}){{ end }} by {{ .Commit.Author }}<br/> Message: {{ .Commit.Message.Title }}',
|
||||
username: { from_secret: 'matrix_username' },
|
||||
password: { from_secret: 'matrix_password' },
|
||||
},
|
||||
when: {
|
||||
status: ['success', 'failure'],
|
||||
},
|
||||
},
|
||||
],
|
||||
depends_on: [
|
||||
'docs',
|
||||
],
|
||||
trigger: {
|
||||
ref: ['refs/heads/main', 'refs/tags/**'],
|
||||
status: ['success', 'failure'],
|
||||
},
|
||||
};
|
||||
|
||||
[
|
||||
PipelineLint,
|
||||
PipelineTest,
|
||||
PipelineBuildPackage,
|
||||
PipelineBuildContainer,
|
||||
PipelineDocs,
|
||||
PipelineNotifications,
|
||||
]
|
433
.drone.yml
433
.drone.yml
|
@ -1,433 +0,0 @@
|
|||
---
|
||||
kind: pipeline
|
||||
name: lint
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: check-format
|
||||
image: python:3.11
|
||||
commands:
|
||||
- git fetch -tq
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install
|
||||
- poetry run yapf -dr ./ansibledoctor
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
|
||||
- name: check-coding
|
||||
image: python:3.11
|
||||
commands:
|
||||
- git fetch -tq
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install
|
||||
- poetry run ruff ./ansibledoctor
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
- refs/tags/**
|
||||
- refs/pull/**
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: test
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: fetch
|
||||
image: python:3.11
|
||||
commands:
|
||||
- git fetch -tq
|
||||
|
||||
- name: python37-pytest
|
||||
image: python:3.7
|
||||
commands:
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install
|
||||
- poetry version
|
||||
- poetry run ansible-doctor --help
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
depends_on:
|
||||
- fetch
|
||||
|
||||
- name: python38-pytest
|
||||
image: python:3.8
|
||||
commands:
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install
|
||||
- poetry version
|
||||
- poetry run ansible-doctor --help
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
depends_on:
|
||||
- fetch
|
||||
|
||||
- name: python39-pytest
|
||||
image: python:3.9
|
||||
commands:
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install
|
||||
- poetry version
|
||||
- poetry run ansible-doctor --help
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
depends_on:
|
||||
- fetch
|
||||
|
||||
- name: python310-pytest
|
||||
image: python:3.10
|
||||
commands:
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install
|
||||
- poetry version
|
||||
- poetry run ansible-doctor --help
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
depends_on:
|
||||
- fetch
|
||||
|
||||
- name: python311-pytest
|
||||
image: python:3.11
|
||||
commands:
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install
|
||||
- poetry version
|
||||
- poetry run ansible-doctor --help
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
depends_on:
|
||||
- fetch
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
- refs/tags/**
|
||||
- refs/pull/**
|
||||
|
||||
depends_on:
|
||||
- lint
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: build-package
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: python:3.11
|
||||
commands:
|
||||
- git fetch -tq
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry build
|
||||
|
||||
- name: checksum
|
||||
image: alpine
|
||||
commands:
|
||||
- cd dist/ && sha256sum * > ../sha256sum.txt
|
||||
|
||||
- name: changelog-generate
|
||||
image: thegeeklab/git-chglog
|
||||
commands:
|
||||
- git fetch -tq
|
||||
- git-chglog --no-color --no-emoji -o CHANGELOG.md ${DRONE_TAG:---next-tag unreleased unreleased}
|
||||
|
||||
- name: changelog-format
|
||||
image: thegeeklab/alpine-tools
|
||||
commands:
|
||||
- prettier CHANGELOG.md
|
||||
- prettier -w CHANGELOG.md
|
||||
|
||||
- name: publish-github
|
||||
image: plugins/github-release
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: github_token
|
||||
files:
|
||||
- dist/*
|
||||
- sha256sum.txt
|
||||
note: CHANGELOG.md
|
||||
overwrite: true
|
||||
title: ${DRONE_TAG}
|
||||
when:
|
||||
ref:
|
||||
- refs/tags/**
|
||||
|
||||
- name: publish-pypi
|
||||
image: python:3.11
|
||||
commands:
|
||||
- git fetch -tq
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry publish -n
|
||||
environment:
|
||||
POETRY_HTTP_BASIC_PYPI_PASSWORD:
|
||||
from_secret: pypi_password
|
||||
POETRY_HTTP_BASIC_PYPI_USERNAME:
|
||||
from_secret: pypi_username
|
||||
when:
|
||||
ref:
|
||||
- refs/tags/**
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
- refs/tags/**
|
||||
- refs/pull/**
|
||||
|
||||
depends_on:
|
||||
- test
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: build-container
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: python:3.11
|
||||
commands:
|
||||
- git fetch -tq
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry build
|
||||
|
||||
- name: dryrun
|
||||
image: thegeeklab/drone-docker-buildx:23
|
||||
settings:
|
||||
dockerfile: Dockerfile.multiarch
|
||||
dry_run: true
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
provenance: false
|
||||
repo: thegeeklab/${DRONE_REPO_NAME}
|
||||
when:
|
||||
ref:
|
||||
- refs/pull/**
|
||||
depends_on:
|
||||
- build
|
||||
|
||||
- name: publish-dockerhub
|
||||
image: thegeeklab/drone-docker-buildx:23
|
||||
settings:
|
||||
auto_tag: true
|
||||
dockerfile: Dockerfile.multiarch
|
||||
password:
|
||||
from_secret: docker_password
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
provenance: false
|
||||
repo: thegeeklab/${DRONE_REPO_NAME}
|
||||
username:
|
||||
from_secret: docker_username
|
||||
when:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
- refs/tags/**
|
||||
depends_on:
|
||||
- dryrun
|
||||
|
||||
- name: publish-quay
|
||||
image: thegeeklab/drone-docker-buildx:23
|
||||
settings:
|
||||
auto_tag: true
|
||||
dockerfile: Dockerfile.multiarch
|
||||
password:
|
||||
from_secret: quay_password
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
provenance: false
|
||||
registry: quay.io
|
||||
repo: quay.io/thegeeklab/${DRONE_REPO_NAME}
|
||||
username:
|
||||
from_secret: quay_username
|
||||
when:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
- refs/tags/**
|
||||
depends_on:
|
||||
- dryrun
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
- refs/tags/**
|
||||
- refs/pull/**
|
||||
|
||||
depends_on:
|
||||
- test
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: docs
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
concurrency:
|
||||
limit: 1
|
||||
|
||||
steps:
|
||||
- name: assets
|
||||
image: thegeeklab/alpine-tools
|
||||
commands:
|
||||
- make doc
|
||||
|
||||
- name: markdownlint
|
||||
image: thegeeklab/markdownlint-cli
|
||||
commands:
|
||||
- markdownlint 'docs/content/**/*.md' 'README.md' 'CONTRIBUTING.md'
|
||||
|
||||
- name: spellcheck
|
||||
image: thegeeklab/alpine-tools
|
||||
commands:
|
||||
- spellchecker --files 'docs/content/**/*.md' 'README.md' -d .dictionary -p spell indefinite-article syntax-urls --no-suggestions
|
||||
environment:
|
||||
FORCE_COLOR: true
|
||||
NPM_CONFIG_LOGLEVEL: error
|
||||
|
||||
- name: testbuild
|
||||
image: thegeeklab/hugo:0.105.0
|
||||
commands:
|
||||
- hugo --panicOnWarning -s docs/ -b http://localhost:8000/
|
||||
|
||||
- name: link-validation
|
||||
image: thegeeklab/link-validator
|
||||
commands:
|
||||
- link-validator --color=always --rate-limit 10
|
||||
environment:
|
||||
LINK_VALIDATOR_BASE_DIR: docs/public
|
||||
LINK_VALIDATOR_RETRIES: 3
|
||||
|
||||
- name: build
|
||||
image: thegeeklab/hugo:0.105.0
|
||||
commands:
|
||||
- hugo --panicOnWarning -s docs/
|
||||
|
||||
- name: beautify
|
||||
image: thegeeklab/alpine-tools
|
||||
commands:
|
||||
- html-beautify -r -f 'docs/public/**/*.html'
|
||||
environment:
|
||||
FORCE_COLOR: true
|
||||
NPM_CONFIG_LOGLEVEL: error
|
||||
|
||||
- name: publish
|
||||
image: thegeeklab/drone-s3-sync
|
||||
settings:
|
||||
access_key:
|
||||
from_secret: s3_access_key
|
||||
bucket: geekdocs
|
||||
delete: true
|
||||
endpoint: https://sp.rknet.org
|
||||
path_style: true
|
||||
secret_key:
|
||||
from_secret: s3_secret_access_key
|
||||
source: docs/public/
|
||||
strip_prefix: docs/public/
|
||||
target: /${DRONE_REPO_NAME}
|
||||
when:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
- refs/tags/**
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
- refs/tags/**
|
||||
- refs/pull/**
|
||||
|
||||
depends_on:
|
||||
- build-package
|
||||
- build-container
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: notifications
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: pushrm-dockerhub
|
||||
image: chko/docker-pushrm:1
|
||||
environment:
|
||||
DOCKER_PASS:
|
||||
from_secret: docker_password
|
||||
DOCKER_USER:
|
||||
from_secret: docker_username
|
||||
PUSHRM_FILE: README.md
|
||||
PUSHRM_SHORT: Annotation based documentation for your Ansible roles
|
||||
PUSHRM_TARGET: thegeeklab/${DRONE_REPO_NAME}
|
||||
when:
|
||||
status:
|
||||
- success
|
||||
|
||||
- name: pushrm-quay
|
||||
image: chko/docker-pushrm:1
|
||||
environment:
|
||||
APIKEY__QUAY_IO:
|
||||
from_secret: quay_token
|
||||
PUSHRM_FILE: README.md
|
||||
PUSHRM_TARGET: quay.io/thegeeklab/${DRONE_REPO_NAME}
|
||||
when:
|
||||
status:
|
||||
- success
|
||||
|
||||
- name: matrix
|
||||
image: thegeeklab/drone-matrix
|
||||
settings:
|
||||
homeserver:
|
||||
from_secret: matrix_homeserver
|
||||
password:
|
||||
from_secret: matrix_password
|
||||
roomid:
|
||||
from_secret: matrix_roomid
|
||||
template: "Status: **{{ .Build.Status }}**<br/> Build: [{{ .Repo.Owner }}/{{ .Repo.Name }}]({{ .Build.Link }}){{ if .Build.Branch }} ({{ .Build.Branch }}){{ end }} by {{ .Commit.Author }}<br/> Message: {{ .Commit.Message.Title }}"
|
||||
username:
|
||||
from_secret: matrix_username
|
||||
when:
|
||||
status:
|
||||
- success
|
||||
- failure
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
- refs/tags/**
|
||||
status:
|
||||
- success
|
||||
- failure
|
||||
|
||||
depends_on:
|
||||
- docs
|
||||
|
||||
---
|
||||
kind: signature
|
||||
hmac: f8f7e3a3001f6fd9bf3654d92e2e030155054e692ac58a333e16f15f2c69f780
|
||||
|
||||
...
|
8
.github/settings.yml
vendored
8
.github/settings.yml
vendored
|
@ -52,7 +52,11 @@ branches:
|
|||
required_status_checks:
|
||||
strict: false
|
||||
contexts:
|
||||
- continuous-integration/drone/pr
|
||||
enforce_admins: true
|
||||
- ci/woodpecker/pr/lint
|
||||
- ci/woodpecker/pr/test
|
||||
- ci/woodpecker/pr/build-package
|
||||
- ci/woodpecker/pr/build-container
|
||||
- ci/woodpecker/pr/docs
|
||||
enforce_admins: false
|
||||
required_linear_history: true
|
||||
restrictions: null
|
||||
|
|
47
.gitsv/config.yml
Normal file
47
.gitsv/config.yml
Normal file
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
version: "1.1"
|
||||
|
||||
versioning:
|
||||
update-major: []
|
||||
update-minor: [feat]
|
||||
update-patch: [fix, perf, refactor, chore, test, ci, docs]
|
||||
|
||||
tag:
|
||||
pattern: "v%d.%d.%d"
|
||||
|
||||
release-notes:
|
||||
sections:
|
||||
- name: Features
|
||||
commit-types: [feat]
|
||||
section-type: commits
|
||||
- name: Bug Fixes
|
||||
commit-types: [fix]
|
||||
section-type: commits
|
||||
- name: Performance Improvements
|
||||
commit-types: [perf]
|
||||
section-type: commits
|
||||
- name: Code Refactoring
|
||||
commit-types: [refactor]
|
||||
section-type: commits
|
||||
- name: Others
|
||||
commit-types: [chore]
|
||||
section-type: commits
|
||||
- name: Testing
|
||||
commit-types: [test]
|
||||
section-type: commits
|
||||
- name: CI Pipeline
|
||||
commit-types: [ci]
|
||||
section-type: commits
|
||||
- name: Documentation
|
||||
commit-types: [docs]
|
||||
section-type: commits
|
||||
- name: Breaking Changes
|
||||
section-type: breaking-changes
|
||||
|
||||
commit-message:
|
||||
footer:
|
||||
issue:
|
||||
key: issue
|
||||
add-value-prefix: "#"
|
||||
issue:
|
||||
regex: "#?[0-9]+"
|
1
.lycheeignore
Normal file
1
.lycheeignore
Normal file
|
@ -0,0 +1 @@
|
|||
https://hub.docker.com/r/thegeeklab/*
|
|
@ -5,3 +5,6 @@ MD041: False
|
|||
MD024: False
|
||||
MD004:
|
||||
style: dash
|
||||
MD033:
|
||||
allowed_elements:
|
||||
- "br"
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
- id: ansible-doctor
|
||||
name: ansible-doctor
|
||||
description: Create annotation based documentation for your Ansible roles.
|
||||
entry: ansible-doctor -f
|
||||
entry: ansible-doctor -f -qqq
|
||||
language: python
|
||||
pass_filenames: False
|
||||
always_run: True
|
||||
additional_dependencies:
|
||||
- .[ansible-core]
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
.drone.yml
|
||||
*.tpl.md
|
||||
LICENSE
|
||||
|
|
73
.woodpecker/build-container.yml
Normal file
73
.woodpecker/build-container.yml
Normal file
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
when:
|
||||
- event: [pull_request, tag]
|
||||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: docker.io/library/python:3.12
|
||||
commands:
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry build
|
||||
|
||||
- name: dryrun
|
||||
image: quay.io/thegeeklab/wp-docker-buildx:4
|
||||
settings:
|
||||
containerfile: Containerfile.multiarch
|
||||
dry_run: true
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
provenance: false
|
||||
repo: ${CI_REPO}
|
||||
when:
|
||||
- event: [pull_request]
|
||||
|
||||
- name: publish-dockerhub
|
||||
image: quay.io/thegeeklab/wp-docker-buildx:4
|
||||
group: container
|
||||
settings:
|
||||
auto_tag: true
|
||||
containerfile: Containerfile.multiarch
|
||||
password:
|
||||
from_secret: docker_password
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
provenance: false
|
||||
repo: ${CI_REPO}
|
||||
username:
|
||||
from_secret: docker_username
|
||||
when:
|
||||
- event: [tag]
|
||||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
|
||||
- name: publish-quay
|
||||
image: quay.io/thegeeklab/wp-docker-buildx:4
|
||||
group: container
|
||||
settings:
|
||||
auto_tag: true
|
||||
containerfile: Containerfile.multiarch
|
||||
password:
|
||||
from_secret: quay_password
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
provenance: false
|
||||
registry: quay.io
|
||||
repo: quay.io/${CI_REPO}
|
||||
username:
|
||||
from_secret: quay_username
|
||||
when:
|
||||
- event: [tag]
|
||||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
|
||||
depends_on:
|
||||
- lint
|
||||
- test
|
56
.woodpecker/build-package.yml
Normal file
56
.woodpecker/build-package.yml
Normal file
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
when:
|
||||
- event: [pull_request, tag]
|
||||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: docker.io/library/python:3.12
|
||||
commands:
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry build
|
||||
|
||||
- name: checksum
|
||||
image: quay.io/thegeeklab/alpine-tools
|
||||
commands:
|
||||
- cd dist/ && sha256sum * > ../sha256sum.txt
|
||||
|
||||
- name: changelog
|
||||
image: quay.io/thegeeklab/git-sv
|
||||
commands:
|
||||
- git sv current-version
|
||||
- git sv release-notes -t ${CI_COMMIT_TAG:-next} -o CHANGELOG.md
|
||||
- cat CHANGELOG.md
|
||||
|
||||
- name: publish-github
|
||||
image: docker.io/plugins/github-release
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: github_token
|
||||
files:
|
||||
- dist/*
|
||||
- sha256sum.txt
|
||||
note: CHANGELOG.md
|
||||
overwrite: true
|
||||
title: ${CI_COMMIT_TAG}
|
||||
when:
|
||||
- event: [tag]
|
||||
|
||||
- name: publish-pypi
|
||||
image: docker.io/library/python:3.12
|
||||
secrets:
|
||||
- source: pypi_password
|
||||
target: POETRY_HTTP_BASIC_PYPI_PASSWORD
|
||||
- source: pypi_username
|
||||
target: POETRY_HTTP_BASIC_PYPI_USERNAME
|
||||
commands:
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry publish -n
|
||||
when:
|
||||
- event: [tag]
|
||||
|
||||
depends_on:
|
||||
- lint
|
||||
- test
|
100
.woodpecker/docs.yml
Normal file
100
.woodpecker/docs.yml
Normal file
|
@ -0,0 +1,100 @@
|
|||
---
|
||||
when:
|
||||
- event: [pull_request, tag]
|
||||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
|
||||
steps:
|
||||
- name: assets
|
||||
image: quay.io/thegeeklab/alpine-tools
|
||||
commands:
|
||||
- make doc
|
||||
|
||||
- name: markdownlint
|
||||
image: quay.io/thegeeklab/markdownlint-cli
|
||||
group: test
|
||||
commands:
|
||||
- markdownlint 'README.md' 'CONTRIBUTING.md'
|
||||
|
||||
- name: spellcheck
|
||||
image: quay.io/thegeeklab/alpine-tools
|
||||
group: test
|
||||
commands:
|
||||
- spellchecker --files 'docs/**/*.md' 'README.md' 'CONTRIBUTING.md' -d .dictionary -p spell indefinite-article syntax-urls
|
||||
environment:
|
||||
FORCE_COLOR: "true"
|
||||
|
||||
- name: link-validation
|
||||
image: docker.io/lycheeverse/lychee
|
||||
group: test
|
||||
commands:
|
||||
- lychee --no-progress --format detailed docs/content README.md
|
||||
|
||||
- name: build
|
||||
image: quay.io/thegeeklab/hugo:0.125.7
|
||||
commands:
|
||||
- hugo --panicOnWarning -s docs/
|
||||
|
||||
- name: beautify
|
||||
image: quay.io/thegeeklab/alpine-tools
|
||||
commands:
|
||||
- html-beautify -r -f 'docs/public/**/*.html'
|
||||
environment:
|
||||
FORCE_COLOR: "true"
|
||||
|
||||
- name: publish
|
||||
image: quay.io/thegeeklab/wp-s3-action
|
||||
settings:
|
||||
access_key:
|
||||
from_secret: s3_access_key
|
||||
bucket: geekdocs
|
||||
delete: true
|
||||
endpoint:
|
||||
from_secret: s3_endpoint
|
||||
path_style: true
|
||||
secret_key:
|
||||
from_secret: s3_secret_access_key
|
||||
source: docs/public/
|
||||
strip_prefix: docs/public/
|
||||
target: /${CI_REPO_NAME}
|
||||
when:
|
||||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
status: [success, failure]
|
||||
|
||||
- name: pushrm-dockerhub
|
||||
image: docker.io/chko/docker-pushrm:1
|
||||
secrets:
|
||||
- source: docker_password
|
||||
target: DOCKER_PASS
|
||||
- source: docker_username
|
||||
target: DOCKER_USER
|
||||
environment:
|
||||
PUSHRM_FILE: README.md
|
||||
PUSHRM_SHORT: Annotation based documentation for your Ansible roles
|
||||
PUSHRM_TARGET: ${CI_REPO}
|
||||
when:
|
||||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
status: [success]
|
||||
|
||||
- name: pushrm-quay
|
||||
image: docker.io/chko/docker-pushrm:1
|
||||
secrets:
|
||||
- source: quay_token
|
||||
target: APIKEY__QUAY_IO
|
||||
environment:
|
||||
PUSHRM_FILE: README.md
|
||||
PUSHRM_TARGET: quay.io/${CI_REPO}
|
||||
when:
|
||||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
status: [success]
|
||||
|
||||
depends_on:
|
||||
- build-package
|
||||
- build-container
|
34
.woodpecker/lint.yml
Normal file
34
.woodpecker/lint.yml
Normal file
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
when:
|
||||
- event: [pull_request, tag]
|
||||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
|
||||
steps:
|
||||
- name: check-format
|
||||
image: docker.io/library/python:3.12
|
||||
commands:
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry install -E ansible-core
|
||||
- poetry run ruff format --check --diff ./${CI_REPO_NAME//-/}
|
||||
environment:
|
||||
PY_COLORS: "1"
|
||||
|
||||
- name: check-coding
|
||||
image: docker.io/library/python:3.12
|
||||
commands:
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry install -E ansible-core
|
||||
- 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"
|
26
.woodpecker/notify.yml
Normal file
26
.woodpecker/notify.yml
Normal file
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
when:
|
||||
- event: [tag]
|
||||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
|
||||
runs_on: [success, failure]
|
||||
|
||||
steps:
|
||||
- name: matrix
|
||||
image: quay.io/thegeeklab/wp-matrix
|
||||
settings:
|
||||
homeserver:
|
||||
from_secret: matrix_homeserver
|
||||
password:
|
||||
from_secret: matrix_password
|
||||
roomid:
|
||||
from_secret: matrix_roomid
|
||||
username:
|
||||
from_secret: matrix_username
|
||||
when:
|
||||
- status: [success, failure]
|
||||
|
||||
depends_on:
|
||||
- docs
|
34
.woodpecker/test.yml
Normal file
34
.woodpecker/test.yml
Normal file
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
when:
|
||||
- event: [pull_request, tag]
|
||||
- event: [push, manual]
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
|
||||
variables:
|
||||
- &pytest_base
|
||||
group: pytest
|
||||
commands:
|
||||
- pip install poetry poetry-dynamic-versioning -qq
|
||||
- poetry install -E ansible-core
|
||||
- poetry version
|
||||
- poetry run ${CI_REPO_NAME} --help
|
||||
environment:
|
||||
PY_COLORS: "1"
|
||||
|
||||
steps:
|
||||
- name: python-312
|
||||
image: docker.io/library/python:3.12
|
||||
<<: *pytest_base
|
||||
|
||||
- name: python-311
|
||||
image: docker.io/library/python:3.11
|
||||
<<: *pytest_base
|
||||
|
||||
- name: python-310
|
||||
image: docker.io/library/python:3.10
|
||||
<<: *pytest_base
|
||||
|
||||
- name: python-39
|
||||
image: docker.io/library/python:3.9
|
||||
<<: *pytest_base
|
|
@ -3,7 +3,7 @@
|
|||
## Security
|
||||
|
||||
If you think you have found a **security issue**, please do not mention it in this repository.
|
||||
Instead, send an email to security@thegeeklab.de with as many details as possible so it can be handled confidential.
|
||||
Instead, send an email to `security@thegeeklab.de` with as many details as possible so it can be handled confidential.
|
||||
|
||||
## Bug Reports and Feature Requests
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM python:3.11-alpine@sha256:84630610c68e7c97384bc6e10f5490ab7b8398c30cdfffefa139ae20c3407cda
|
||||
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,9 +12,9 @@ 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") && \
|
||||
pip install --no-cache-dir $(find / -name "ansible_doctor-*.whl")[ansible-core] && \
|
||||
rm -f ansible_doctor-*.whl && \
|
||||
rm -rf /var/cache/apk/* && \
|
||||
rm -rf /root/.cache/
|
2
Makefile
2
Makefile
|
@ -1,5 +1,5 @@
|
|||
# renovate: datasource=github-releases depName=thegeeklab/hugo-geekdoc
|
||||
THEME_VERSION := v0.37.1
|
||||
THEME_VERSION := v0.46.0
|
||||
THEME := hugo-geekdoc
|
||||
BASEDIR := docs
|
||||
THEMEDIR := $(BASEDIR)/themes
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Annotation based documentation for your Ansible roles
|
||||
|
||||
[![Build Status](https://img.shields.io/drone/build/thegeeklab/ansible-doctor?logo=drone&server=https%3A%2F%2Fdrone.thegeeklab.de)](https://drone.thegeeklab.de/thegeeklab/ansible-doctor)
|
||||
[![Build Status](https://ci.thegeeklab.de/api/badges/thegeeklab/ansible-doctor/status.svg)](https://ci.thegeeklab.de/repos/thegeeklab/ansible-doctor)
|
||||
[![Docker Hub](https://img.shields.io/badge/dockerhub-latest-blue.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/thegeeklab/ansible-doctor)
|
||||
[![Quay.io](https://img.shields.io/badge/quay-latest-blue.svg?logo=docker&logoColor=white)](https://quay.io/repository/thegeeklab/ansible-doctor)
|
||||
[![Python Version](https://img.shields.io/pypi/pyversions/ansible-doctor.svg)](https://pypi.org/project/ansible-doctor/)
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
"""Provide version information."""
|
||||
|
||||
__version__ = "0.0.0"
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
import ansible # noqa
|
||||
except ImportError:
|
||||
sys.exit("ERROR: Python requirements are missing: 'ansible-core' not found.")
|
||||
|
|
|
@ -24,6 +24,8 @@ class AnnotationItem:
|
|||
for sub in self.data.get(key):
|
||||
return f"AnnotationItem({key}: {sub})"
|
||||
|
||||
return "None"
|
||||
|
||||
def get_obj(self):
|
||||
return self.data
|
||||
|
||||
|
@ -179,7 +181,5 @@ class Annotation:
|
|||
return {key: json.loads(string)}
|
||||
except ValueError:
|
||||
self.log.sysexit_with_message(
|
||||
"Json value error: Can't parse json in {}:{}:\n{}".format(
|
||||
rfile, str(num), line.strip()
|
||||
)
|
||||
f"Json value error: Can't parse json in {rfile}:{num!s}:\n{line.strip()}"
|
||||
)
|
||||
|
|
|
@ -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,86 +39,107 @@ 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,
|
||||
help="run recursively over the base directory subfolders"
|
||||
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,
|
||||
help="force overwrite output file"
|
||||
default=self.config.config.renderer.force_overwrite,
|
||||
help="force overwrite output file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--dry-run",
|
||||
dest="dry_run",
|
||||
action="store_true",
|
||||
default=None,
|
||||
help="dry run without writing"
|
||||
default=self.config.config.dry_run,
|
||||
help="dry run without writing",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--no-role-detection",
|
||||
dest="role_detection",
|
||||
action="store_false",
|
||||
default=None,
|
||||
help="disable automatic role detection"
|
||||
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{str(e)}")
|
||||
self.logger.info(f"Using config file: {self.config.config_file}")
|
||||
self.log.sysexit_with_message(f"Can not set log level.\n{e!s}")
|
||||
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:
|
||||
|
|
|
@ -3,329 +3,210 @@
|
|||
|
||||
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
|
||||
},
|
||||
"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
|
||||
},
|
||||
}
|
||||
class Config:
|
||||
"""Create configuration object."""
|
||||
|
||||
ANNOTATIONS = {
|
||||
"meta": {
|
||||
"name": "meta",
|
||||
"automatic": True,
|
||||
"subtypes": ["value"],
|
||||
"allow_multiple": False
|
||||
"allow_multiple": False,
|
||||
},
|
||||
"todo": {
|
||||
"name": "todo",
|
||||
"automatic": True,
|
||||
"subtypes": ["value"],
|
||||
"allow_multiple": True
|
||||
"allow_multiple": True,
|
||||
},
|
||||
"var": {
|
||||
"name": "var",
|
||||
"automatic": True,
|
||||
"subtypes": ["value", "example", "description"],
|
||||
"allow_multiple": False
|
||||
"subtypes": ["value", "example", "description", "type", "deprecated"],
|
||||
"allow_multiple": False,
|
||||
},
|
||||
"example": {
|
||||
"name": "example",
|
||||
"automatic": True,
|
||||
"subtypes": [],
|
||||
"allow_multiple": False
|
||||
"allow_multiple": False,
|
||||
},
|
||||
"tag": {
|
||||
"name": "tag",
|
||||
"automatic": True,
|
||||
"subtypes": ["value", "description"],
|
||||
"allow_multiple": False
|
||||
"allow_multiple": False,
|
||||
},
|
||||
}
|
||||
|
||||
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']}"
|
||||
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)
|
||||
)
|
||||
|
||||
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.safe_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):
|
||||
def validate(self):
|
||||
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
|
||||
)
|
||||
raise ansibledoctor.exception.ConfigError("Configuration error", schema_error) from e
|
||||
self.config.validators.validate_all()
|
||||
except ValidationError as e:
|
||||
raise ansibledoctor.exception.ConfigError("Configuration error", e.message) 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
|
||||
def is_role(self):
|
||||
self.config.role_name = self.config.get(
|
||||
"role_name", os.path.basename(self.config.base_dir)
|
||||
)
|
||||
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
|
||||
|
||||
|
@ -333,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
|
||||
|
||||
|
@ -343,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):
|
||||
|
|
|
@ -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,20 +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):
|
||||
|
@ -78,17 +77,20 @@ class Generator:
|
|||
with open(header_file) as a:
|
||||
header_content = a.read()
|
||||
except FileNotFoundError as e:
|
||||
self.log.sysexit_with_message(f"Can not open custom header file\n{str(e)}")
|
||||
self.log.sysexit_with_message(f"Can not open custom header file\n{e!s}")
|
||||
|
||||
if (
|
||||
len(files_to_overwite) > 0 and self.config.config.get("force_overwrite") is False
|
||||
len(files_to_overwite) > 0
|
||||
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)
|
||||
self.logger.warning(f"This files will be overwritten:\n{files_to_overwite_string}")
|
||||
prompt = f"These files will be overwritten:\n{files_to_overwite_string}".replace(
|
||||
"\n", "\n... "
|
||||
)
|
||||
|
||||
try:
|
||||
if not FileUtils.query_yes_no("Do you want to continue?"):
|
||||
if not FileUtils.query_yes_no(f"{prompt}\nDo you want to continue?"):
|
||||
self.log.sysexit_with_message("Aborted...")
|
||||
except ansibledoctor.exception.InputError as e:
|
||||
self.logger.debug(str(e))
|
||||
|
@ -96,12 +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))
|
||||
|
@ -115,14 +119,17 @@ class Generator:
|
|||
loader=FileSystemLoader(self.config.get_template()),
|
||||
lstrip_blocks=True,
|
||||
trim_blocks=True,
|
||||
autoescape=jinja2.select_autoescape()
|
||||
autoescape=jinja2.select_autoescape(),
|
||||
)
|
||||
jenv.filters["to_nice_yaml"] = self._to_nice_yaml
|
||||
jenv.filters["deep_get"] = self._deep_get
|
||||
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"))
|
||||
|
@ -133,16 +140,14 @@ class Generator:
|
|||
except (
|
||||
jinja2.exceptions.UndefinedError,
|
||||
jinja2.exceptions.TemplateSyntaxError,
|
||||
jinja2.exceptions.TemplateRuntimeError
|
||||
jinja2.exceptions.TemplateRuntimeError,
|
||||
) as e:
|
||||
self.log.sysexit_with_message(
|
||||
"Jinja2 templating error while loading file: '{}'\n{}".format(
|
||||
file, str(e)
|
||||
)
|
||||
f"Jinja2 templating error while loading file: '{file}'\n{e!s}"
|
||||
)
|
||||
except UnicodeEncodeError as e:
|
||||
self.log.sysexit_with_message(
|
||||
f"Unable to print special characters\n{str(e)}"
|
||||
f"Unable to print special characters\n{e!s}"
|
||||
)
|
||||
|
||||
def _to_nice_yaml(self, a, indent=4, **kw):
|
||||
|
@ -156,8 +161,9 @@ class Generator:
|
|||
def _deep_get(self, _, dictionary, keys):
|
||||
default = None
|
||||
return reduce(
|
||||
lambda d, key: d.get(key, default)
|
||||
if isinstance(d, dict) else default, keys.split("."), dictionary
|
||||
lambda d, key: d.get(key, default) if isinstance(d, dict) else default,
|
||||
keys.split("."),
|
||||
dictionary,
|
||||
)
|
||||
|
||||
@pass_eval_context
|
||||
|
@ -166,11 +172,13 @@ class Generator:
|
|||
value = [value]
|
||||
|
||||
normalized = jinja2.filters.do_join(eval_ctx, value, d, attribute=None)
|
||||
for s in [r" +(\n|\t| )", r"(\n|\t) +"]:
|
||||
normalized = re.sub(s, "\\1", normalized)
|
||||
|
||||
return normalized
|
||||
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()
|
||||
|
|
|
@ -3,17 +3,16 @@
|
|||
|
||||
import fnmatch
|
||||
from collections import defaultdict
|
||||
from contextlib import suppress
|
||||
|
||||
import anyconfig
|
||||
import ruamel.yaml
|
||||
from nested_lookup import nested_lookup
|
||||
|
||||
from ansibledoctor.annotation import Annotation
|
||||
from ansibledoctor.config import SingleConfig
|
||||
from ansibledoctor.contstants import YAML_EXTENSIONS
|
||||
from ansibledoctor.exception import YAMLError
|
||||
from ansibledoctor.file_registry import Registry
|
||||
from ansibledoctor.utils import SingleLog, UnsafeTag, flatten
|
||||
from ansibledoctor.utils import SingleLog, flatten
|
||||
from ansibledoctor.utils.yamlhelper import parse_yaml, parse_yaml_ansible
|
||||
|
||||
|
||||
class Parser:
|
||||
|
@ -31,96 +30,60 @@ class Parser:
|
|||
self._parse_task_tags()
|
||||
self._populate_doc_data()
|
||||
|
||||
def _yaml_remove_comments(self, d):
|
||||
if isinstance(d, dict):
|
||||
for k, v in d.items():
|
||||
self._yaml_remove_comments(k)
|
||||
self._yaml_remove_comments(v)
|
||||
elif isinstance(d, list):
|
||||
for elem in d:
|
||||
self._yaml_remove_comments(elem)
|
||||
|
||||
with suppress(AttributeError):
|
||||
attr = "comment" if isinstance(
|
||||
d, ruamel.yaml.scalarstring.ScalarString
|
||||
) else ruamel.yaml.comments.Comment.attrib
|
||||
delattr(d, attr)
|
||||
|
||||
def _parse_var_files(self):
|
||||
for rfile in self._files_registry.get_files():
|
||||
if any(fnmatch.fnmatch(rfile, "*/defaults/*." + ext) for ext in YAML_EXTENSIONS):
|
||||
with open(rfile, encoding="utf8") as yaml_file:
|
||||
with open(rfile, encoding="utf8") as yamlfile:
|
||||
try:
|
||||
ruamel.yaml.add_constructor(
|
||||
UnsafeTag.yaml_tag,
|
||||
UnsafeTag.yaml_constructor,
|
||||
constructor=ruamel.yaml.SafeConstructor
|
||||
)
|
||||
raw = parse_yaml(yamlfile)
|
||||
except YAMLError as e:
|
||||
self.log.sysexit_with_message(f"Unable to read yaml file {rfile}\n{e}")
|
||||
|
||||
raw = ruamel.yaml.YAML(typ="rt").load(yaml_file)
|
||||
self._yaml_remove_comments(raw)
|
||||
data = defaultdict(dict, raw or {})
|
||||
|
||||
data = defaultdict(dict, raw or {})
|
||||
for key, value in data.items():
|
||||
self._data["var"][key] = {"value": {key: value}}
|
||||
except (
|
||||
ruamel.yaml.composer.ComposerError,
|
||||
ruamel.yaml.scanner.ScannerError,
|
||||
ruamel.yaml.constructor.ConstructorError,
|
||||
ruamel.yaml.constructor.DuplicateKeyError,
|
||||
) as e:
|
||||
message = f"{e.context} {e.problem}"
|
||||
self.log.sysexit_with_message(
|
||||
f"Unable to read yaml file {rfile}\n{message}"
|
||||
)
|
||||
for key, value in data.items():
|
||||
self._data["var"][key] = {"value": {key: value}}
|
||||
|
||||
def _parse_meta_file(self):
|
||||
self._data["meta"]["name"] = {"value": self.config.config["role_name"]}
|
||||
|
||||
for rfile in self._files_registry.get_files():
|
||||
if any("meta/main." + ext in rfile for ext in YAML_EXTENSIONS):
|
||||
with open(rfile, encoding="utf8") as yaml_file:
|
||||
with open(rfile, encoding="utf8") as yamlfile:
|
||||
try:
|
||||
raw = ruamel.yaml.YAML(typ="rt").load(yaml_file)
|
||||
self._yaml_remove_comments(raw)
|
||||
raw = parse_yaml(yamlfile)
|
||||
except YAMLError as e:
|
||||
self.log.sysexit_with_message(f"Unable to read yaml file {rfile}\n{e}")
|
||||
|
||||
data = defaultdict(dict, raw)
|
||||
if data.get("galaxy_info"):
|
||||
for key, value in data.get("galaxy_info").items():
|
||||
self._data["meta"][key] = {"value": value}
|
||||
data = defaultdict(dict, raw)
|
||||
if data.get("galaxy_info"):
|
||||
for key, value in data.get("galaxy_info").items():
|
||||
self._data["meta"][key] = {"value": value}
|
||||
|
||||
if data.get("dependencies") is not None:
|
||||
self._data["meta"]["dependencies"] = {
|
||||
"value": data.get("dependencies")
|
||||
}
|
||||
|
||||
self._data["meta"]["name"] = {"value": self.config.config["role_name"]}
|
||||
except (
|
||||
ruamel.yaml.composer.ComposerError, ruamel.yaml.scanner.ScannerError
|
||||
) as e:
|
||||
message = f"{e.context} {e.problem}"
|
||||
self.log.sysexit_with_message(
|
||||
f"Unable to read yaml file {rfile}\n{message}"
|
||||
)
|
||||
if data.get("dependencies") is not None:
|
||||
self._data["meta"]["dependencies"] = {"value": data.get("dependencies")}
|
||||
|
||||
def _parse_task_tags(self):
|
||||
for rfile in self._files_registry.get_files():
|
||||
if any(fnmatch.fnmatch(rfile, "*/tasks/*." + ext) for ext in YAML_EXTENSIONS):
|
||||
with open(rfile, encoding="utf8") as yaml_file:
|
||||
with open(rfile, encoding="utf8") as yamlfile:
|
||||
try:
|
||||
raw = ruamel.yaml.YAML(typ="rt").load(yaml_file)
|
||||
self._yaml_remove_comments(raw)
|
||||
raw = parse_yaml_ansible(yamlfile)
|
||||
except YAMLError as e:
|
||||
self.log.sysexit_with_message(f"Unable to read yaml file {rfile}\n{e}")
|
||||
|
||||
tags = list(set(flatten(nested_lookup("tags", raw))))
|
||||
for tag in [
|
||||
x for x in tags if x not in self.config.config["exclude_tags"]
|
||||
]:
|
||||
self._data["tag"][tag] = {"value": tag}
|
||||
except (
|
||||
ruamel.yaml.composer.ComposerError, ruamel.yaml.scanner.ScannerError
|
||||
) as e:
|
||||
message = f"{e.context} {e.problem}"
|
||||
self.log.sysexit_with_message(
|
||||
f"Unable to read yaml file {rfile}\n{message}"
|
||||
)
|
||||
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}
|
||||
|
||||
def _populate_doc_data(self):
|
||||
"""Generate the documentation data object."""
|
||||
|
|
|
@ -10,6 +10,12 @@ class DoctorError(Exception):
|
|||
self.original_exception = original_exception
|
||||
|
||||
|
||||
class YAMLError(DoctorError):
|
||||
"""Errors while reading a yaml file."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ConfigError(DoctorError):
|
||||
"""Errors related to config file handling."""
|
||||
|
||||
|
|
|
@ -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,21 +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(
|
||||
"Adding file to '{}': {}".format(
|
||||
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:
|
||||
|
|
7
ansibledoctor/templates/hugo-book/_requirements.j2
Normal file
7
ansibledoctor/templates/hugo-book/_requirements.j2
Normal file
|
@ -0,0 +1,7 @@
|
|||
## Requirements
|
||||
|
||||
{% if meta | deep_get(meta, "min_ansible_version.value") %}
|
||||
- Minimum Ansible version: `{{ meta.min_ansible_version.value }}`
|
||||
{% else %}
|
||||
None.
|
||||
{% endif %}
|
|
@ -1,10 +1,13 @@
|
|||
- [Requirements](#requirements)
|
||||
{% 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 %}
|
||||
|
|
|
@ -1,13 +1,29 @@
|
|||
{% set var = role.var | default({}) %}
|
||||
{% if var %}
|
||||
|
||||
## Default Variables
|
||||
{% for key, item in var | dictsort %}
|
||||
|
||||
### {{ key }}
|
||||
{% if item.description is defined and item.description %}
|
||||
{% set description = [item.description] if item.description is string else item.description %}
|
||||
|
||||
{{ item.description | map("replace", "\n\n", "\n") | safe_join("\n") }}
|
||||
{{ description | map("replace", "\n\n", "\n") | safe_join("\n") }}
|
||||
{% endif %}
|
||||
{% if item.deprecated is defined or item.type is defined %}
|
||||
|
||||
{% if item.deprecated is defined %}
|
||||
{% set deprecated = [item.deprecated] if item.deprecated is string else item.deprecated %}
|
||||
{% set deprecated_string = deprecated | map("replace", "\n\n", "\n") | safe_join("\n") %}
|
||||
{% if deprecated_string %}
|
||||
**_Deprecated:_** {{ deprecated_string }}<br />
|
||||
{% else %}
|
||||
**_Deprecated_**<br />
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if item.type is defined and item.type %}
|
||||
{% set type = [item.type] if item.type is string else item.type %}
|
||||
**_Type:_** {{ type | map("replace", "\n\n", "\n") | safe_join("\n") }}<br />
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if item.value is defined and item.value %}
|
||||
|
||||
|
|
49
ansibledoctor/templates/hugo-book/_vars_tabulated.j2
Normal file
49
ansibledoctor/templates/hugo-book/_vars_tabulated.j2
Normal 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 %}
|
|
@ -9,19 +9,27 @@ summary: {{ meta.summary.value | safe_join(" ") }}
|
|||
---
|
||||
{% endif %}
|
||||
{% if description | deep_get(meta, "description.value") %}
|
||||
{% set description = [meta.description.value] if meta.description.value is string else meta.description.value %}
|
||||
|
||||
{{ meta.description.value | map("replace", "\n\n", "\n") | safe_join("\n") }}
|
||||
{{ description | map("replace", "\n\n", "\n") | safe_join("\n") }}
|
||||
{% endif %}
|
||||
|
||||
<!--more-->
|
||||
|
||||
{# TOC #}
|
||||
{% include '_toc.j2' %}
|
||||
{% include '_toc.j2' +%}
|
||||
|
||||
{# Requirements #}
|
||||
{% include '_requirements.j2' %}
|
||||
|
||||
{# Vars #}
|
||||
{% if options.tabulate_vars %}
|
||||
{% include '_vars_tabulated.j2' %}
|
||||
{% else %}
|
||||
{% include '_vars.j2' %}
|
||||
{% endif %}
|
||||
|
||||
{# Todo #}
|
||||
{# Tag #}
|
||||
{% include '_tag.j2' %}
|
||||
|
||||
{# Todo #}
|
||||
|
|
|
@ -3,17 +3,25 @@
|
|||
# {{ meta.name.value | safe_join(" ") }}
|
||||
{% endif %}
|
||||
{% if description | deep_get(meta, "description.value") %}
|
||||
{% set description = [meta.description.value] if meta.description.value is string else meta.description.value %}
|
||||
|
||||
{{ meta.description.value | map("replace", "\n\n", "\n") | safe_join("\n") }}
|
||||
{{ description | map("replace", "\n\n", "\n") | safe_join("\n") }}
|
||||
{% endif %}
|
||||
|
||||
{# TOC #}
|
||||
{% include '_toc.j2' %}
|
||||
{% include '_toc.j2' +%}
|
||||
|
||||
{# Requirements #}
|
||||
{% include '_requirements.j2' %}
|
||||
|
||||
{# Vars #}
|
||||
{% if options.tabulate_vars %}
|
||||
{% include '_vars_tabulated.j2' %}
|
||||
{% else %}
|
||||
{% include '_vars.j2' %}
|
||||
{% endif %}
|
||||
|
||||
{# Todo #}
|
||||
{# Tag #}
|
||||
{% include '_tag.j2' %}
|
||||
|
||||
{# Todo #}
|
||||
|
|
|
@ -20,7 +20,7 @@ None.
|
|||
|
||||
## License
|
||||
|
||||
{{ meta.license.value }}
|
||||
{{ meta.license.value | safe_join(" ") }}
|
||||
{% endif %}
|
||||
{% if author | deep_get(meta, "author.value") %}
|
||||
|
||||
|
|
7
ansibledoctor/templates/readme/_requirements.j2
Normal file
7
ansibledoctor/templates/readme/_requirements.j2
Normal file
|
@ -0,0 +1,7 @@
|
|||
## Requirements
|
||||
|
||||
{% if meta | deep_get(meta, "min_ansible_version.value") %}
|
||||
- Minimum Ansible version: `{{ meta.min_ansible_version.value }}`
|
||||
{% else %}
|
||||
None.
|
||||
{% endif %}
|
|
@ -1,12 +1,15 @@
|
|||
## Table of content
|
||||
|
||||
- [Requirements](#requirements)
|
||||
{% 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 %}
|
||||
|
|
|
@ -1,13 +1,29 @@
|
|||
{% set var = role.var | default({}) %}
|
||||
{% if var %}
|
||||
|
||||
## Default Variables
|
||||
{% for key, item in var | dictsort %}
|
||||
|
||||
### {{ key }}
|
||||
{% if item.description is defined and item.description %}
|
||||
{% set description = [item.description] if item.description is string else item.description %}
|
||||
|
||||
{{ item.description | map("replace", "\n\n", "\n") | safe_join("\n") }}
|
||||
{{ description | map("replace", "\n\n", "\n") | safe_join("\n") }}
|
||||
{% endif %}
|
||||
{% if item.deprecated is defined or item.type is defined %}
|
||||
|
||||
{% if item.deprecated is defined %}
|
||||
{% set deprecated = [item.deprecated] if item.deprecated is string else item.deprecated %}
|
||||
{% set deprecated_string = deprecated | map("replace", "\n\n", "\n") | safe_join("\n") %}
|
||||
{% if deprecated_string %}
|
||||
**_Deprecated:_** {{ deprecated_string }}<br />
|
||||
{% else %}
|
||||
**_Deprecated_**<br />
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if item.type is defined and item.type %}
|
||||
{% set type = [item.type] if item.type is string else item.type %}
|
||||
**_Type:_** {{ type | map("replace", "\n\n", "\n") | safe_join("\n") }}<br />
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if item.value is defined and item.value %}
|
||||
|
||||
|
|
49
ansibledoctor/templates/readme/_vars_tabulated.j2
Normal file
49
ansibledoctor/templates/readme/_vars_tabulated.j2
Normal 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 %}
|
|
@ -5,7 +5,6 @@ import logging
|
|||
import os
|
||||
import sys
|
||||
from collections.abc import Iterable
|
||||
from distutils.util import strtobool
|
||||
|
||||
import colorama
|
||||
from pythonjsonlogger import jsonlogger
|
||||
|
@ -16,6 +15,30 @@ CONSOLE_FORMAT = "{}{}[%(levelname)s]{} %(message)s"
|
|||
JSON_FORMAT = "%(asctime)s %(levelname)s %(message)s"
|
||||
|
||||
|
||||
def strtobool(value):
|
||||
"""Convert a string representation of truth to true or false."""
|
||||
|
||||
_map = {
|
||||
"y": True,
|
||||
"yes": True,
|
||||
"t": True,
|
||||
"true": True,
|
||||
"on": True,
|
||||
"1": True,
|
||||
"n": False,
|
||||
"no": False,
|
||||
"f": False,
|
||||
"false": False,
|
||||
"off": False,
|
||||
"0": False,
|
||||
}
|
||||
|
||||
try:
|
||||
return _map[str(value).lower()]
|
||||
except KeyError as err:
|
||||
raise ValueError(f'"{value}" is not a valid bool value') from err
|
||||
|
||||
|
||||
def to_bool(string):
|
||||
return bool(strtobool(str(string)))
|
||||
|
||||
|
@ -23,8 +46,7 @@ def to_bool(string):
|
|||
def flatten(items):
|
||||
for x in items:
|
||||
if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
|
||||
for sub_x in flatten(x):
|
||||
yield sub_x
|
||||
yield from flatten(x)
|
||||
else:
|
||||
yield x
|
||||
|
||||
|
@ -106,15 +128,15 @@ class LogFilter:
|
|||
class MultilineFormatter(logging.Formatter):
|
||||
"""Reset color after newline characters."""
|
||||
|
||||
def format(self, record): # noqa
|
||||
record.msg = record.msg.replace("\n", f"\n{colorama.Style.RESET_ALL}... ")
|
||||
def format(self, record):
|
||||
record.msg = record.msg.strip().replace("\n", f"\n{colorama.Style.RESET_ALL}... ")
|
||||
return logging.Formatter.format(self, record)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
@ -123,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):
|
||||
|
@ -214,7 +233,7 @@ class Log:
|
|||
handler.addFilter(LogFilter(logging.DEBUG))
|
||||
handler.setFormatter(
|
||||
MultilineFormatter(
|
||||
self.critical(
|
||||
self.debug(
|
||||
CONSOLE_FORMAT.format(
|
||||
colorama.Fore.BLUE, colorama.Style.BRIGHT, colorama.Style.RESET_ALL
|
||||
)
|
||||
|
@ -227,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."""
|
||||
|
@ -265,7 +300,7 @@ class Log:
|
|||
sys.exit(code)
|
||||
|
||||
def sysexit_with_message(self, msg, code=1):
|
||||
self.logger.critical(str(msg))
|
||||
self.logger.critical(str(msg).strip())
|
||||
self.sysexit(code)
|
||||
|
||||
|
||||
|
@ -275,19 +310,6 @@ class SingleLog(Log, metaclass=Singleton):
|
|||
pass
|
||||
|
||||
|
||||
class UnsafeTag:
|
||||
"""Handle custom yaml unsafe tag."""
|
||||
|
||||
yaml_tag = "!unsafe"
|
||||
|
||||
def __init__(self, value):
|
||||
self.unsafe = value
|
||||
|
||||
@staticmethod
|
||||
def yaml_constructor(loader, node):
|
||||
return loader.construct_scalar(node)
|
||||
|
||||
|
||||
class FileUtils:
|
||||
"""Mics static methods for file handling."""
|
||||
|
85
ansibledoctor/utils/yamlhelper.py
Normal file
85
ansibledoctor/utils/yamlhelper.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
"""Utils for YAML file operations."""
|
||||
|
||||
from collections import defaultdict
|
||||
from contextlib import suppress
|
||||
|
||||
import ruamel.yaml
|
||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||
|
||||
import ansibledoctor.exception
|
||||
|
||||
|
||||
class UnsafeTag:
|
||||
"""Handle custom yaml unsafe tag."""
|
||||
|
||||
yaml_tag = "!unsafe"
|
||||
|
||||
def __init__(self, value):
|
||||
self.unsafe = value
|
||||
|
||||
@staticmethod
|
||||
def yaml_constructor(loader, node):
|
||||
return loader.construct_scalar(node)
|
||||
|
||||
|
||||
def parse_yaml_ansible(yamlfile):
|
||||
try:
|
||||
loader = AnsibleLoader(yamlfile)
|
||||
data = loader.get_single_data() or []
|
||||
except (
|
||||
ruamel.yaml.parser.ParserError,
|
||||
ruamel.yaml.scanner.ScannerError,
|
||||
ruamel.yaml.constructor.ConstructorError,
|
||||
ruamel.yaml.composer.ComposerError,
|
||||
) as e:
|
||||
message = (
|
||||
f"{e.context} in line {e.context_mark.line}, column {e.context_mark.line}\n"
|
||||
f"{e.problem} in line {e.problem_mark.line}, column {e.problem_mark.column}"
|
||||
)
|
||||
raise ansibledoctor.exception.YAMLError(message) from e
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def parse_yaml(yamlfile):
|
||||
try:
|
||||
ruamel.yaml.add_constructor(
|
||||
UnsafeTag.yaml_tag,
|
||||
UnsafeTag.yaml_constructor,
|
||||
constructor=ruamel.yaml.SafeConstructor,
|
||||
)
|
||||
|
||||
data = ruamel.yaml.YAML(typ="rt").load(yamlfile)
|
||||
_yaml_remove_comments(data)
|
||||
data = defaultdict(dict, data or {})
|
||||
except (
|
||||
ruamel.yaml.parser.ParserError,
|
||||
ruamel.yaml.scanner.ScannerError,
|
||||
ruamel.yaml.constructor.ConstructorError,
|
||||
ruamel.yaml.composer.ComposerError,
|
||||
) as e:
|
||||
message = (
|
||||
f"{e.context} in line {e.context_mark.line}, column {e.context_mark.line}\n"
|
||||
f"{e.problem} in line {e.problem_mark.line}, column {e.problem_mark.column}"
|
||||
)
|
||||
raise ansibledoctor.exception.YAMLError(message) from e
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def _yaml_remove_comments(d):
|
||||
if isinstance(d, dict):
|
||||
for k, v in d.items():
|
||||
_yaml_remove_comments(k)
|
||||
_yaml_remove_comments(v)
|
||||
elif isinstance(d, list):
|
||||
for elem in d:
|
||||
_yaml_remove_comments(elem)
|
||||
|
||||
with suppress(AttributeError):
|
||||
attr = (
|
||||
"comment"
|
||||
if isinstance(d, ruamel.yaml.scalarstring.ScalarString)
|
||||
else ruamel.yaml.comments.Comment.attrib
|
||||
)
|
||||
delattr(d, attr)
|
|
@ -2,7 +2,7 @@
|
|||
title: Documentation
|
||||
---
|
||||
|
||||
[![Build Status](https://img.shields.io/drone/build/thegeeklab/ansible-doctor?logo=drone&server=https%3A%2F%2Fdrone.thegeeklab.de)](https://drone.thegeeklab.de/thegeeklab/ansible-doctor)
|
||||
[![Build Status](https://ci.thegeeklab.de/api/badges/thegeeklab/ansible-doctor/status.svg)](https://ci.thegeeklab.de/repos/thegeeklab/ansible-doctor)
|
||||
[![Docker Hub](https://img.shields.io/badge/dockerhub-latest-blue.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/thegeeklab/ansible-doctor)
|
||||
[![Quay.io](https://img.shields.io/badge/quay-latest-blue.svg?logo=docker&logoColor=white)](https://quay.io/repository/thegeeklab/ansible-doctor)
|
||||
[![Python Version](https://img.shields.io/pypi/pyversions/ansible-doctor.svg)](https://pypi.org/project/ansible-doctor/)
|
||||
|
|
|
@ -4,11 +4,12 @@ title: Using pip
|
|||
|
||||
```Shell
|
||||
# From PyPI as unprivileged user
|
||||
$ pip install ansible-doctor --user
|
||||
$ pip install ansible-doctor[ansible-core] --user
|
||||
|
||||
# .. or as root
|
||||
$ sudo pip install ansible-doctor
|
||||
$ sudo pip install ansible-doctor[ansible-core]
|
||||
|
||||
# From Wheel file
|
||||
$ pip install https://github.com/thegeeklab/ansible-doctor/releases/download/v0.1.1/ansible_doctor-0.1.1-py2.py3-none-any.whl
|
||||
# Please check first whether a newer version is available.
|
||||
$ pip install https://github.com/thegeeklab/ansible-doctor/releases/download/v3.1.4/ansible_doctor-3.1.4-py2.py3-none-any.whl[ansible-core]
|
||||
```
|
||||
|
|
|
@ -20,31 +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
|
||||
|
||||
# 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:
|
||||
|
@ -54,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
|
||||
|
@ -86,21 +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_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
|
||||
|
@ -113,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 >}}
|
||||
|
|
|
@ -12,7 +12,7 @@ ansible-doctor FOLDER
|
|||
|
||||
If no folder is passed to _ansible-doctor_, the current working directory is used. The first step is to determine if the specified folder is an Ansible role. This check is very simple and only verifies if there is a sub-directory named `tasks` in the specified folder. After a successful check, _ansible-doctor_ registers all files of the role to search them for annotations.
|
||||
|
||||
Without any further work _ansible-doctor_ can already create a documentation of the available variables and some meta information if the role contains [meta information](https://galaxy.ansible.com/docs/contributing/creating_role.html#role-metadata). This basic information can be extended with a set of available annotations. If you want to see it in action you can find a [demo role](https://github.com/thegeeklab/ansible-doctor/tree/main/example) with a lot of examples in the repository.
|
||||
Without any further work _ansible-doctor_ can already create a documentation of the available variables and some meta information if the role contains. This basic information can be extended with a set of available annotations. If you want to see it in action you can find a [demo role](https://github.com/thegeeklab/ansible-doctor/tree/main/example) with a lot of examples in the repository.
|
||||
|
||||
## Annotations
|
||||
|
||||
|
@ -44,23 +44,28 @@ option1
|
|||
: the name of the variable to which additional information should be added
|
||||
|
||||
option2
|
||||
: supports `["value", "example", "description"]` as information scopes
|
||||
: supports `["value", "example", "description", "type", "deprecated"]` as information scopes
|
||||
|
||||
**Example:**
|
||||
#### `value`
|
||||
|
||||
```YAML
|
||||
# The `$` operator is required for the `value` option. It's an indicator for the parster to signalize that the `<value>`
|
||||
# need to be parsed as JSON. The JSON string is then converted to YAML for the documentation.
|
||||
# @var docker_registry_password:value: $ "secure_overwrite"
|
||||
# @var docker_registry_password: $ "secure_overwrite"
|
||||
```yaml
|
||||
# @var docker_registry_password:value: $ "secret"
|
||||
docker_registry_password: "secret"
|
||||
```
|
||||
|
||||
# It's also possible to define more complex values. Keep in mind the `<value>` need to be a valid JSON string.
|
||||
# @var docker_registry_insecure: $ ["myregistrydomain.com:5000", "localhost:5000"]
|
||||
#### `example`
|
||||
|
||||
# For the example option, the `$` operator is optional. If it is set, the `<value>` need to be a valid JSON
|
||||
# string as described above. If not, the value is passed to the documentation unformatted.
|
||||
# @var docker_registry_password:example: $ "%8gv_5GA?"
|
||||
```yaml
|
||||
# @var docker_registry_password:example: $ "randomPassw0rd"
|
||||
# @var docker_registry_password:example: >
|
||||
# docker_registry_password: "randomPassw0rd"
|
||||
# @end
|
||||
docker_registry_password: "secret"
|
||||
```
|
||||
|
||||
#### `description`
|
||||
|
||||
```yaml
|
||||
# @var docker_registry_password:description: Very secure password to login to the docker registry.
|
||||
# @var docker_registry_password:description: >
|
||||
# Multi line description are possible as well.
|
||||
|
@ -69,6 +74,21 @@ option2
|
|||
docker_registry_password: "secret"
|
||||
```
|
||||
|
||||
#### `type`
|
||||
|
||||
```yaml
|
||||
# @var docker_registry_password:type: string
|
||||
docker_registry_password: "secret"
|
||||
```
|
||||
|
||||
#### `deprecated`
|
||||
|
||||
```yaml
|
||||
# @var docker_registry_password:deprecated: true
|
||||
# @var docker_registry_password:deprecated: since v1.0.0
|
||||
docker_registry_password: "secret"
|
||||
```
|
||||
|
||||
### `@tag`
|
||||
|
||||
Used tags within the Ansible task files will be auto-discovered. This identifier can be used to define tags manually or add extended information to discovered tags.
|
||||
|
|
162
docs/static/socialmedia.svg
vendored
Normal file
162
docs/static/socialmedia.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 20 KiB |
BIN
docs/static/socialmedia2.png
vendored
BIN
docs/static/socialmedia2.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 30 KiB |
|
@ -1,5 +1,9 @@
|
|||
---
|
||||
custom_header: HEADER.md
|
||||
logging:
|
||||
level: debug
|
||||
template: readme
|
||||
|
||||
template:
|
||||
name: readme
|
||||
|
||||
renderer:
|
||||
include_header: HEADER.md
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# demo-role-custom-header
|
||||
|
||||
[![Build Status](https://img.shields.io/drone/build/thegeeklab/ansible-doctor?logo=drone&server=https%3A%2F%2Fdrone.thegeeklab.de)](https://drone.thegeeklab.de/thegeeklab/ansible-doctor)
|
||||
[![Build Status](https://ci.thegeeklab.de/api/badges/thegeeklab/ansible-doctor/status.svg)](https://ci.thegeeklab.de/repos/thegeeklab/ansible-doctor)
|
||||
[![License: GPL-3.0](https://img.shields.io/github/license/thegeeklab/ansible-doctor)](https://github.com/thegeeklab/ansible-doctor/blob/main/LICENSE)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# demo-role-custom-header
|
||||
|
||||
[![Build Status](https://img.shields.io/drone/build/thegeeklab/ansible-doctor?logo=drone&server=https%3A%2F%2Fdrone.thegeeklab.de)](https://drone.thegeeklab.de/thegeeklab/ansible-doctor)
|
||||
[![Build Status](https://ci.thegeeklab.de/api/badges/thegeeklab/ansible-doctor/status.svg)](https://ci.thegeeklab.de/repos/thegeeklab/ansible-doctor)
|
||||
[![License: GPL-3.0](https://img.shields.io/github/license/thegeeklab/ansible-doctor)](https://github.com/thegeeklab/ansible-doctor/blob/main/LICENSE)
|
||||
|
||||
Role to demonstrate ansible-doctor. It is also possible to overwrite
|
||||
|
@ -8,7 +8,10 @@ the default description with an annotation.
|
|||
|
||||
## Table of content
|
||||
|
||||
- [Requirements](#requirements)
|
||||
- [Default Variables](#default-variables)
|
||||
- [demo_role_deprecated](#demo_role_deprecated)
|
||||
- [demo_role_deprecated_info](#demo_role_deprecated_info)
|
||||
- [demo_role_dict](#demo_role_dict)
|
||||
- [demo_role_empty](#demo_role_empty)
|
||||
- [demo_role_empty_dict](#demo_role_empty_dict)
|
||||
|
@ -26,8 +29,33 @@ the default description with an annotation.
|
|||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
- Minimum Ansible version: `2.10`
|
||||
|
||||
## Default Variables
|
||||
|
||||
### demo_role_deprecated
|
||||
|
||||
**_Deprecated_**<br />
|
||||
|
||||
#### Default value
|
||||
|
||||
```YAML
|
||||
demo_role_deprecated: b
|
||||
```
|
||||
|
||||
### demo_role_deprecated_info
|
||||
|
||||
**_Deprecated:_** This variable is deprected since `v2.0.0` and will be removed in a future release.<br />
|
||||
**_Type:_** string<br />
|
||||
|
||||
#### Default value
|
||||
|
||||
```YAML
|
||||
demo_role_deprecated_info: a
|
||||
```
|
||||
|
||||
### demo_role_dict
|
||||
|
||||
#### Default value
|
||||
|
@ -176,7 +204,6 @@ demo_role_unset: some_value
|
|||
|
||||
## Dependencies
|
||||
|
||||
- role1
|
||||
- role2
|
||||
|
||||
## License
|
||||
|
|
|
@ -57,3 +57,11 @@ demo_role_override: original
|
|||
## Complex value
|
||||
# @var demo_role_override_complex:value: $ {"foo":"bar", "second":"value"}
|
||||
demo_role_override_complex: {}
|
||||
|
||||
# @var demo_role_deprecated:deprecated:
|
||||
demo_role_deprecated: "b"
|
||||
|
||||
# @var demo_role_deprecated_info:deprecated: >
|
||||
# This variable is deprected since `v2.0.0` and will be removed in a future release.
|
||||
# @var demo_role_deprecated_info:type: string
|
||||
demo_role_deprecated_info: "a"
|
||||
|
|
|
@ -8,14 +8,15 @@ galaxy_info:
|
|||
description: Role to demonstrate ansible-doctor.
|
||||
author: John Doe
|
||||
license: MIT
|
||||
min_ansible_version: 2.4
|
||||
min_ansible_version: "2.10"
|
||||
platforms:
|
||||
- name: EL
|
||||
versions:
|
||||
- 7
|
||||
- "9"
|
||||
galaxy_tags:
|
||||
- demo
|
||||
- documentation
|
||||
|
||||
dependencies:
|
||||
- role1
|
||||
- role: role2
|
||||
- name: namespace.role3
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
- name: Demo task with a tag list
|
||||
debug:
|
||||
msg: "Demo message"
|
||||
tags:
|
||||
- module-tag
|
||||
tags:
|
||||
- role-tag1
|
||||
- role-tag2
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
---
|
||||
custom_header: HEADER.md
|
||||
logging:
|
||||
level: debug
|
||||
template: readme
|
||||
|
||||
template:
|
||||
name: readme
|
||||
|
||||
renderer:
|
||||
include_header: HEADER.md
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# other-role-custom-header
|
||||
|
||||
[![Build Status](https://img.shields.io/drone/build/thegeeklab/ansible-doctor?logo=drone&server=https%3A%2F%2Fdrone.thegeeklab.de)](https://drone.thegeeklab.de/thegeeklab/ansible-doctor)
|
||||
[![Build Status](https://ci.thegeeklab.de/api/badges/thegeeklab/ansible-doctor/status.svg)](https://ci.thegeeklab.de/repos/thegeeklab/ansible-doctor)
|
||||
[![License: GPL-3.0](https://img.shields.io/github/license/thegeeklab/ansible-doctor)](https://github.com/thegeeklab/ansible-doctor/blob/main/LICENSE)
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
# other-role-custom-header
|
||||
|
||||
[![Build Status](https://img.shields.io/drone/build/thegeeklab/ansible-doctor?logo=drone&server=https%3A%2F%2Fdrone.thegeeklab.de)](https://drone.thegeeklab.de/thegeeklab/ansible-doctor)
|
||||
[![Build Status](https://ci.thegeeklab.de/api/badges/thegeeklab/ansible-doctor/status.svg)](https://ci.thegeeklab.de/repos/thegeeklab/ansible-doctor)
|
||||
[![License: GPL-3.0](https://img.shields.io/github/license/thegeeklab/ansible-doctor)](https://github.com/thegeeklab/ansible-doctor/blob/main/LICENSE)
|
||||
|
||||
Role to demonstrate ansible-doctor. It is also possible to overwrite
|
||||
the default description with an annotation.
|
||||
Role to demonstrate ansible-doctor.
|
||||
|
||||
## Table of content
|
||||
|
||||
- [Requirements](#requirements)
|
||||
- [Default Variables](#default-variables)
|
||||
- [demo_role_unset](#demo_role_unset)
|
||||
- [Discovered Tags](#discovered-tags)
|
||||
|
@ -18,6 +18,10 @@ the default description with an annotation.
|
|||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
- Minimum Ansible version: `2.10`
|
||||
|
||||
## Default Variables
|
||||
|
||||
### demo_role_unset
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
---
|
||||
# @meta description: >
|
||||
# Role to demonstrate ansible-doctor. It is also possible to overwrite
|
||||
# the default description with an annotation.
|
||||
# @end
|
||||
# @meta author: [John Doe](https\://blog.example.com)
|
||||
galaxy_info:
|
||||
description: Role to demonstrate ansible-doctor.
|
||||
author: John Doe
|
||||
license: MIT
|
||||
min_ansible_version: 2.4
|
||||
min_ansible_version: "2.10"
|
||||
platforms:
|
||||
- name: EL
|
||||
versions:
|
||||
- 7
|
||||
- "9"
|
||||
galaxy_tags:
|
||||
- demo
|
||||
- documentation
|
||||
|
||||
dependencies:
|
||||
- role1
|
||||
- role: role2
|
||||
- name: namespace.role3
|
||||
|
|
1186
poetry.lock
generated
1186
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
143
pyproject.toml
143
pyproject.toml
|
@ -10,11 +10,10 @@ classifiers = [
|
|||
"Natural Language :: English",
|
||||
"Operating System :: POSIX",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Topic :: Utilities",
|
||||
"Topic :: Software Development",
|
||||
"Topic :: Software Development :: Documentation",
|
||||
|
@ -22,42 +21,43 @@ 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 = "9.5.0"
|
||||
jsonschema = "4.17.3"
|
||||
nested-lookup = "0.2.25"
|
||||
pathspec = "0.11.0"
|
||||
python = "^3.7.0"
|
||||
python-json-logger = "2.0.4"
|
||||
"ruamel.yaml" = "0.17.21"
|
||||
environs = "11.0.0"
|
||||
jsonschema = "4.22.0"
|
||||
pathspec = "0.12.1"
|
||||
python = "^3.9.0"
|
||||
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"]
|
||||
|
||||
[tool.poetry.scripts]
|
||||
ansible-doctor = "ansibledoctor.cli:main"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "0.0.243"
|
||||
pytest = "7.2.1"
|
||||
pytest-mock = "3.10.0"
|
||||
pytest-cov = "4.0.0"
|
||||
ruff = "0.4.7"
|
||||
pytest = "8.2.1"
|
||||
pytest-mock = "3.14.0"
|
||||
pytest-cov = "5.0.0"
|
||||
toml = "0.10.2"
|
||||
yapf = "0.32.0"
|
||||
j2lint = "1.1.0"
|
||||
|
||||
|
||||
[tool.poetry-dynamic-versioning]
|
||||
enable = true
|
||||
|
@ -65,7 +65,7 @@ style = "semver"
|
|||
vcs = "git"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = "ansibledoctor --cov=ansibledoctor --cov-report=xml:coverage.xml --cov-report=term --cov-append --no-cov-on-fail"
|
||||
addopts = "ansibledoctor --cov=ansibledoctor --cov-report=xml:coverage.xml --cov-report=term --no-cov-on-fail"
|
||||
filterwarnings = [
|
||||
"ignore::FutureWarning",
|
||||
"ignore::DeprecationWarning",
|
||||
|
@ -76,22 +76,27 @@ filterwarnings = [
|
|||
omit = ["**/test/*"]
|
||||
|
||||
[build-system]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
build-backend = "poetry_dynamic_versioning.backend"
|
||||
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"]
|
||||
|
||||
[tool.ruff]
|
||||
exclude = [
|
||||
".git",
|
||||
"__pycache__",
|
||||
"build",
|
||||
"dist",
|
||||
"test",
|
||||
"*.pyc",
|
||||
"*.egg-info",
|
||||
".cache",
|
||||
".eggs",
|
||||
"env*",
|
||||
".git",
|
||||
"__pycache__",
|
||||
"build",
|
||||
"dist",
|
||||
"test",
|
||||
"*.pyc",
|
||||
"*.egg-info",
|
||||
".cache",
|
||||
".eggs",
|
||||
"env*",
|
||||
]
|
||||
|
||||
line-length = 99
|
||||
indent-width = 4
|
||||
|
||||
[tool.ruff.lint]
|
||||
# Explanation of errors
|
||||
#
|
||||
# D102: Missing docstring in public method
|
||||
|
@ -102,45 +107,39 @@ exclude = [
|
|||
# D203: One blank line required before class docstring
|
||||
# D212: Multi-line docstring summary should start at the first line
|
||||
ignore = [
|
||||
"D102",
|
||||
"D103",
|
||||
"D105",
|
||||
"D107",
|
||||
"D202",
|
||||
"D203",
|
||||
"D212",
|
||||
"D102",
|
||||
"D103",
|
||||
"D105",
|
||||
"D107",
|
||||
"D202",
|
||||
"D203",
|
||||
"D212",
|
||||
"UP038",
|
||||
"RUF012",
|
||||
]
|
||||
line-length = 99
|
||||
select = [
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"Q",
|
||||
"W",
|
||||
"I",
|
||||
"S",
|
||||
"BLE",
|
||||
"N",
|
||||
"UP",
|
||||
"B",
|
||||
"A",
|
||||
"C4",
|
||||
"T20",
|
||||
"SIM",
|
||||
"RET",
|
||||
"ARG",
|
||||
"ERA",
|
||||
"RUF",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"Q",
|
||||
"W",
|
||||
"I",
|
||||
"S",
|
||||
"BLE",
|
||||
"N",
|
||||
"UP",
|
||||
"B",
|
||||
"A",
|
||||
"C4",
|
||||
"T20",
|
||||
"SIM",
|
||||
"RET",
|
||||
"ARG",
|
||||
"ERA",
|
||||
"RUF",
|
||||
]
|
||||
|
||||
[tool.ruff.flake8-quotes]
|
||||
inline-quotes = "double"
|
||||
|
||||
[tool.yapf]
|
||||
based_on_style = "google"
|
||||
column_limit = 99
|
||||
dedent_closing_brackets = true
|
||||
coalesce_brackets = true
|
||||
split_before_logical_operator = true
|
||||
indent_dictionary_value = true
|
||||
allow_split_before_dict_value = false
|
||||
[tool.ruff.format]
|
||||
quote-style = "double"
|
||||
indent-style = "space"
|
||||
line-ending = "lf"
|
||||
|
|
|
@ -1,4 +1,17 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["github>thegeeklab/renovate-presets"]
|
||||
"extends": ["github>thegeeklab/renovate-presets"],
|
||||
"packageRules": [
|
||||
{
|
||||
"description": "Ansible base dependencies",
|
||||
"matchPackageNames": ["ansible-core"],
|
||||
"separateMinorPatch": true
|
||||
},
|
||||
{
|
||||
"matchManagers": ["woodpecker"],
|
||||
"matchFileNames": [".woodpecker/test.yml"],
|
||||
"matchPackageNames": ["docker.io/library/python"],
|
||||
"enabled": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user