Compare commits

..

No commits in common. "main" and "v2.0.0" have entirely different histories.
main ... v2.0.0

24 changed files with 956 additions and 881 deletions

23
.chglog/CHANGELOG.tpl.md Executable file
View File

@ -0,0 +1,23 @@
# 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 -}}

25
.chglog/config.yml Executable file
View File

@ -0,0 +1,25 @@
style: github
template: CHANGELOG.tpl.md
info:
title: CHANGELOG
repository_url: https://github.com/thegeeklab/docker-tidy
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

View File

@ -1,47 +0,0 @@
---
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]+"

View File

@ -1 +0,0 @@
https://hub.docker.com/r/thegeeklab/*

View File

@ -6,39 +6,29 @@ when:
- ${CI_REPO_DEFAULT_BRANCH}
steps:
- name: build
image: docker.io/library/python:3.13
build:
image: docker.io/library/python:3.11
commands:
- git fetch -tq
- pip install poetry poetry-dynamic-versioning -qq
- poetry build
- name: security-build
image: quay.io/thegeeklab/wp-docker-buildx:6
depends_on: [build]
dryrun:
image: quay.io/thegeeklab/wp-docker-buildx:1
settings:
containerfile: Containerfile.multiarch
output: type=oci,dest=oci/${CI_REPO_NAME},tar=false
dry_run: true
platforms:
- linux/amd64
- linux/arm64
provenance: false
repo: ${CI_REPO}
registry_config:
from_secret: DOCKER_REGISTRY_CONFIG_PULL
when:
- event: [pull_request]
- name: security-scan
image: docker.io/aquasec/trivy
depends_on: [security-build]
commands:
- trivy -v
- trivy image --input oci/${CI_REPO_NAME}
environment:
TRIVY_EXIT_CODE: "1"
TRIVY_IGNORE_UNFIXED: "true"
TRIVY_NO_PROGRESS: "true"
TRIVY_SEVERITY: HIGH,CRITICAL
TRIVY_TIMEOUT: 1m
TRIVY_DB_REPOSITORY: docker.io/aquasec/trivy-db:2
- name: publish-dockerhub
image: quay.io/thegeeklab/wp-docker-buildx:6
depends_on: [security-scan]
publish-dockerhub:
group: container
image: quay.io/thegeeklab/wp-docker-buildx:1
settings:
auto_tag: true
containerfile: Containerfile.multiarch
@ -57,9 +47,9 @@ steps:
branch:
- ${CI_REPO_DEFAULT_BRANCH}
- name: publish-quay
image: quay.io/thegeeklab/wp-docker-buildx:6
depends_on: security-scan
publish-quay:
group: container
image: quay.io/thegeeklab/wp-docker-buildx:1
settings:
auto_tag: true
containerfile: Containerfile.multiarch

View File

@ -6,25 +6,31 @@ when:
- ${CI_REPO_DEFAULT_BRANCH}
steps:
- name: build
image: docker.io/library/python:3.13
build:
image: docker.io/library/python:3.11
commands:
- git fetch -tq
- pip install poetry poetry-dynamic-versioning -qq
- poetry build
- name: checksum
checksum:
image: quay.io/thegeeklab/alpine-tools
commands:
- cd dist/ && sha256sum * > ../sha256sum.txt
- name: changelog
image: quay.io/thegeeklab/git-sv
changelog-generate:
image: quay.io/thegeeklab/git-chglog
commands:
- git sv current-version
- git sv release-notes -t ${CI_COMMIT_TAG:-next} -o CHANGELOG.md
- cat CHANGELOG.md
- git fetch -tq
- git-chglog --no-color --no-emoji -o CHANGELOG.md ${CI_COMMIT_TAG:---next-tag unreleased unreleased}
- name: publish-github
changelog-format:
image: quay.io/thegeeklab/alpine-tools
commands:
- prettier CHANGELOG.md
- prettier -w CHANGELOG.md
publish-github:
image: docker.io/plugins/github-release
settings:
api_key:
@ -38,14 +44,15 @@ steps:
when:
- event: [tag]
- name: publish-pypi
image: docker.io/library/python:3.13
environment:
POETRY_HTTP_BASIC_PYPI_PASSWORD:
from_secret: pypi_password
POETRY_HTTP_BASIC_PYPI_USERNAME:
from_secret: pypi_username
publish-pypi:
image: docker.io/library/python:3.11
secrets:
- source: pypi_password
target: POETRY_HTTP_BASIC_PYPI_PASSWORD
- source: pypi_username
target: POETRY_HTTP_BASIC_PYPI_USERNAME
commands:
- git fetch -tq
- pip install poetry poetry-dynamic-versioning -qq
- poetry publish -n
when:

View File

@ -6,53 +6,58 @@ when:
- ${CI_REPO_DEFAULT_BRANCH}
steps:
- name: assets
assets:
image: quay.io/thegeeklab/alpine-tools
commands:
- make doc
- name: markdownlint
markdownlint:
image: quay.io/thegeeklab/markdownlint-cli
depends_on: [assets]
commands:
- markdownlint 'README.md' 'CONTRIBUTING.md'
- name: spellcheck
spellcheck:
image: quay.io/thegeeklab/alpine-tools
depends_on: [assets]
commands:
- spellchecker --files 'docs/**/*.md' 'README.md' 'CONTRIBUTING.md' -d .dictionary -p spell indefinite-article syntax-urls
- spellchecker --files '_docs/**/*.md' 'README.md' 'CONTRIBUTING.md' -d .dictionary -p spell indefinite-article syntax-urls
environment:
FORCE_COLOR: "true"
NPM_CONFIG_LOGLEVEL: "error"
- name: link-validation
image: docker.io/lycheeverse/lychee
depends_on: [assets]
testbuild:
image: quay.io/thegeeklab/hugo:0.115.2
commands:
- lychee --no-progress --format detailed docs/content README.md
- hugo --panicOnWarning -s docs/ -b http://localhost:8000/
- name: build
image: quay.io/thegeeklab/hugo:0.136.5
depends_on: [link-validation]
link-validation:
image: quay.io/thegeeklab/link-validator
commands:
- link-validator --color=always --rate-limit 10
environment:
LINK_VALIDATOR_BASE_DIR: docs/public
LINK_VALIDATOR_RETRIES: "3"
build:
image: quay.io/thegeeklab/hugo:0.115.2
commands:
- hugo --panicOnWarning -s docs/
- name: beautify
beautify:
image: quay.io/thegeeklab/alpine-tools
depends_on: [build]
commands:
- html-beautify -r -f 'docs/public/**/*.html'
environment:
FORCE_COLOR: "true"
NPM_CONFIG_LOGLEVEL: error
- name: publish
publish:
image: quay.io/thegeeklab/wp-s3-action
depends_on: [beautify]
settings:
access_key:
from_secret: s3_access_key
bucket: geekdocs
delete: true
endpoint:
from_secret: s3_endpoint
endpoint: https://sp.rknet.org
path_style: true
secret_key:
from_secret: s3_secret_access_key
@ -63,16 +68,16 @@ steps:
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
status: [success, failure]
status: [success]
- name: pushrm-dockerhub
pushrm-dockerhub:
image: docker.io/chko/docker-pushrm:1
depends_on: [publish]
secrets:
- source: docker_password
target: DOCKER_PASS
- source: docker_username
target: DOCKER_USER
environment:
DOCKER_PASS:
from_secret: docker_password
DOCKER_USER:
from_secret: docker_username
PUSHRM_FILE: README.md
PUSHRM_SHORT: Keep docker hosts tidy
PUSHRM_TARGET: ${CI_REPO}
@ -82,12 +87,12 @@ steps:
- ${CI_REPO_DEFAULT_BRANCH}
status: [success]
- name: pushrm-quay
pushrm-quay:
image: docker.io/chko/docker-pushrm:1
depends_on: [publish]
secrets:
- source: quay_token
target: APIKEY__QUAY_IO
environment:
APIKEY__QUAY_IO:
from_secret: quay_token
PUSHRM_FILE: README.md
PUSHRM_TARGET: quay.io/${CI_REPO}
when:

View File

@ -6,22 +6,22 @@ when:
- ${CI_REPO_DEFAULT_BRANCH}
steps:
- name: check-format
image: docker.io/library/python:3.13
depends_on: []
check-format:
image: docker.io/library/python:3.11
commands:
- git fetch -tq
- pip install poetry poetry-dynamic-versioning -qq
- poetry install
- poetry run ruff format --check --diff ./${CI_REPO_NAME//-/}
- poetry run yapf -dr ./${CI_REPO_NAME//-/}
environment:
PY_COLORS: "1"
- name: check-coding
image: docker.io/library/python:3.13
depends_on: []
check-coding:
image: docker.io/library/python:3.11
commands:
- git fetch -tq
- pip install poetry poetry-dynamic-versioning -qq
- poetry install
- poetry run ruff check ./${CI_REPO_NAME//-/}
- poetry run ruff ./${CI_REPO_NAME//-/}
environment:
PY_COLORS: "1"

View File

@ -8,17 +8,17 @@ when:
runs_on: [success, failure]
steps:
- name: matrix
matrix:
image: quay.io/thegeeklab/wp-matrix
settings:
homeserver:
from_secret: matrix_homeserver
room_id:
from_secret: matrix_room_id
user_id:
from_secret: matrix_user_id
access_token:
from_secret: matrix_access_token
password:
from_secret: matrix_password
roomid:
from_secret: matrix_roomid
username:
from_secret: matrix_username
when:
- status: [success, failure]

View File

@ -5,9 +5,21 @@ when:
branch:
- ${CI_REPO_DEFAULT_BRANCH}
variables:
- &pytest_base
depends_on: []
matrix:
PYTHON_VERSION:
- docker.io/library/python:3.8
- docker.io/library/python:3.9
- docker.io/library/python:3.10
- docker.io/library/python:3.11
steps:
fetch:
image: docker.io/library/python:3.11
commands:
- git fetch -tq
pytest:
image: ${PYTHON_VERSION}
commands:
- pip install poetry poetry-dynamic-versioning -qq
- poetry install
@ -16,20 +28,3 @@ variables:
- poetry run ${CI_REPO_NAME} --help
environment:
PY_COLORS: "1"
steps:
- name: python-313
image: docker.io/library/python:3.13
<<: *pytest_base
- 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

View File

@ -1,4 +1,4 @@
FROM python:3.13-alpine@sha256:fcbcbbecdeae71d3b77445d9144d1914df55110f825ab62b04a66c7c33c09373
FROM python:3.11-alpine@sha256:5d769f990397afbb2aca24b0655e404c0f2806d268f454b052e81e39d87abf42
LABEL maintainer="Robert Kaussow <mail@thegeeklab.de>"
LABEL org.opencontainers.image.authors="Robert Kaussow <mail@thegeeklab.de>"

View File

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

View File

@ -7,6 +7,7 @@ Keep docker hosts tidy
[![Python Version](https://img.shields.io/pypi/pyversions/docker-tidy.svg)](https://pypi.org/project/docker-tidy/)
[![PyPI Status](https://img.shields.io/pypi/status/docker-tidy.svg)](https://pypi.org/project/docker-tidy/)
[![PyPI Release](https://img.shields.io/pypi/v/docker-tidy.svg)](https://pypi.org/project/docker-tidy/)
[![Codecov](https://img.shields.io/codecov/c/github/thegeeklab/docker-tidy)](https://codecov.io/gh/thegeeklab/docker-tidy)
[![GitHub contributors](https://img.shields.io/github/contributors/thegeeklab/docker-tidy)](https://github.com/thegeeklab/docker-tidy/graphs/contributors)
[![Source: GitHub](https://img.shields.io/badge/source-github-blue.svg?logo=github&logoColor=white)](https://github.com/thegeeklab/docker-tidy)
[![License: Apache-2.0](https://img.shields.io/github/license/thegeeklab/docker-tidy)](https://github.com/thegeeklab/docker-tidy/blob/main/LICENSE)

View File

@ -45,7 +45,9 @@ class AutoStop:
) or (not prefix and self._has_been_running_since(container, max_run_time)):
self.logger.info(
"Stopping container {id} {name}: running since {started}".format(
id=container["Id"][:16], name=name, started=container["State"]["StartedAt"]
id=container["Id"][:16],
name=name,
started=container["State"]["StartedAt"]
)
)
@ -61,6 +63,7 @@ class AutoStop:
self.logger.warning(f"Error stopping {cid}: {e!s}")
def _build_container_matcher(self, prefixes):
def matcher(name):
return any(name.startswith(prefix) for prefix in prefixes)

View File

@ -36,7 +36,7 @@ class DockerTidy:
action="store_true",
default=None,
dest="dry_run",
help="only log actions, don't stop anything",
help="only log actions, don't stop anything"
)
parser.add_argument(
"-t",
@ -44,7 +44,7 @@ class DockerTidy:
type=int,
dest="http_timeout",
metavar="HTTP_TIMEOUT",
help="HTTP timeout in seconds for making docker API calls",
help="HTTP timeout in seconds for making docker API calls"
)
parser.add_argument(
"-v", dest="logging.level", action="append_const", const=-1, help="increase log level"
@ -64,7 +64,7 @@ class DockerTidy:
dest="gc.max_container_age",
metavar="MAX_CONTAINER_AGE",
help="maximum age for a container, containers older than this age "
"will be removed (dateparser value)",
"will be removed (dateparser value)"
)
parser_gc.add_argument(
"--max-image-age",
@ -72,13 +72,13 @@ class DockerTidy:
dest="gc.max_image_age",
metavar="MAX_IMAGE_AGE",
help="maxium age for an image, images older than this age will be "
"removed (dateparser value)",
"removed (dateparser value)"
)
parser_gc.add_argument(
"--dangling-volumes",
action="store_true",
dest="gc.dangling_volumes",
help="dangling volumes will be removed",
help="dangling volumes will be removed"
)
parser_gc.add_argument(
"--exclude-image",
@ -86,7 +86,7 @@ class DockerTidy:
type=str,
dest="gc.exclude_images",
metavar="EXCLUDE_IMAGE",
help="never remove images with this tag",
help="never remove images with this tag"
)
parser_gc.add_argument(
"--exclude-container-label",
@ -94,7 +94,8 @@ class DockerTidy:
type=str,
dest="gc.exclude_container_labels",
metavar="EXCLUDE_CONTAINER_LABEL",
help="never remove containers with this label key or label key=value",
help="never remove containers with this label key "
"or label key=value"
)
parser_stop = subparsers.add_parser(
@ -105,7 +106,7 @@ class DockerTidy:
type=timedelta_validator,
dest="stop.max_run_time",
metavar="MAX_RUN_TIME",
help="maximum time a container is allows to run (dateparser value)",
help="maximum time a container is allows to run (dateparser value)"
)
parser_stop.add_argument(
"--prefix",
@ -113,7 +114,7 @@ class DockerTidy:
type=str,
dest="stop.prefix",
metavar="PREFIX",
help="only stop containers which match one of the prefix",
help="only stop containers which match one of the prefix"
)
return parser.parse_args().__dict__

View File

@ -36,73 +36,73 @@ class Config:
"config_file": {
"default": "",
"env": "CONFIG_FILE",
"type": environs.Env().str,
"type": environs.Env().str
},
"dry_run": {
"default": False,
"env": "DRY_RUN",
"file": True,
"type": environs.Env().bool,
"type": environs.Env().bool
},
"http_timeout": {
"default": 60,
"env": "HTTP_TIMEOUT",
"file": True,
"type": environs.Env().int,
"type": environs.Env().int
},
"logging.level": {
"default": "WARNING",
"env": "LOG_LEVEL",
"file": True,
"type": environs.Env().str,
"type": environs.Env().str
},
"logging.json": {
"default": False,
"env": "LOG_JSON",
"file": True,
"type": environs.Env().bool,
"type": environs.Env().bool
},
"gc.max_container_age": {
"default": "",
"env": "GC_MAX_CONTAINER_AGE",
"file": True,
"type": env.timedelta_validator,
"type": env.timedelta_validator
},
"gc.max_image_age": {
"default": "",
"env": "GC_MAX_IMAGE_AGE",
"file": True,
"type": env.timedelta_validator,
"type": env.timedelta_validator
},
"gc.dangling_volumes": {
"default": False,
"env": "GC_DANGLING_VOLUMES",
"file": True,
"type": environs.Env().bool,
"type": environs.Env().bool
},
"gc.exclude_images": {
"default": [],
"env": "GC_EXCLUDE_IMAGES",
"file": True,
"type": environs.Env().list,
"type": environs.Env().list
},
"gc.exclude_container_labels": {
"default": [],
"env": "GC_EXCLUDE_CONTAINER_LABELS",
"file": True,
"type": environs.Env().list,
"type": environs.Env().list
},
"stop.max_run_time": {
"default": "",
"env": "STOP_MAX_RUN_TIME",
"file": True,
"type": env.timedelta_validator,
"type": env.timedelta_validator
},
"stop.prefix": {
"default": [],
"env": "STOP_PREFIX",
"file": True,
"type": environs.Env().list,
"type": environs.Env().list
},
}
@ -194,7 +194,7 @@ class Config:
with open(config, encoding="utf8") as stream:
s = stream.read()
try:
normalized = ruamel.yaml.YAML(typ="safe", pure=True).load(s)
normalized = ruamel.yaml.safe_load(s)
except (ruamel.yaml.composer.ComposerError, ruamel.yaml.scanner.ScannerError) as e:
message = f"{e.context} {e.problem}"
raise dockertidy.exception.ConfigError(
@ -234,19 +234,20 @@ class Config:
try:
anyconfig.validate(config, self.schema, ac_schema_safe=False)
except jsonschema.exceptions.ValidationError as e:
schema = format_as_index(list(e.relative_schema_path)[:-1])
schema_error = f"Failed validating '{e.validator}' in schema {schema}\n{e.message}"
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 dockertidy.exception.ConfigError("Configuration error", schema_error) from e
return True
def _add_dict_branch(self, tree, vector, value):
key = vector[0]
tree[key] = (
value
if len(vector) == 1
else self._add_dict_branch(tree.get(key, {}), vector[1:], value)
)
tree[key] = value \
if len(vector) == 1 \
else self._add_dict_branch(tree[key] if key in tree else {}, vector[1:], value)
return tree

View File

@ -55,8 +55,7 @@ class GarbageCollector:
self.logger.info(
"Removing container {} {} {}".format(
container["Id"][:16],
container.get("Name", "").lstrip("/"),
container["State"]["FinishedAt"],
container.get("Name", "").lstrip("/"), container["State"]["FinishedAt"]
)
)
@ -74,7 +73,9 @@ class GarbageCollector:
return containers
def include_container(container):
return not self._should_exclude_container_with_labels(container)
if self._should_exclude_container_with_labels(container):
return False
return True
return filter(include_container, containers)
@ -162,6 +163,7 @@ class GarbageCollector:
self._remove_image(image_summary, timedelta(config["gc"]["max_image_age"]))
def _filter_excluded_images(self, images, exclude_set):
def include_image(image_summary):
image_tags = image_summary.get("RepoTags")
if self._no_image_tags(image_tags):
@ -174,6 +176,7 @@ class GarbageCollector:
return filter(include_image, images)
def _filter_images_in_use(self, images, image_tags_in_use):
def get_tag_set(image_summary):
image_tags = image_summary.get("RepoTags")
if self._no_image_tags(image_tags):
@ -187,6 +190,7 @@ class GarbageCollector:
return filter(image_not_in_use, images)
def _filter_images_in_use_by_id(self, images, image_ids_in_use):
def image_not_in_use(image_summary):
return image_summary["Id"] not in image_ids_in_use
@ -252,6 +256,7 @@ class GarbageCollector:
self.logger.warning(f"Error calling {func.__name__} {params} {e!s}")
def _format_image(self, image, image_summary):
def get_tags():
tags = image_summary.get("RepoTags")
if not tags or tags == ["<none>:<none>"]:
@ -308,8 +313,7 @@ class GarbageCollector:
self.cleanup_volumes()
if (
not config["gc"]["max_container_age"]
and not config["gc"]["max_image_age"]
not config["gc"]["max_container_age"] and not config["gc"]["max_image_age"]
and not config["gc"]["dangling_volumes"]
):
self.logger.ing("Skipped, no arguments given")

View File

@ -46,7 +46,7 @@ class LogFilter:
class MultilineFormatter(logging.Formatter):
"""Logging Formatter to reset color after newline characters."""
def format(self, record):
def format(self, record): # noqa
record.msg = record.msg.replace("\n", f"\n{colorama.Style.RESET_ALL}... ")
return logging.Formatter.format(self, record)
@ -54,7 +54,7 @@ class MultilineFormatter(logging.Formatter):
class MultilineJsonFormatter(jsonlogger.JsonFormatter):
"""Logging Formatter to remove newline characters."""
def format(self, record):
def format(self, record): # noqa
record.msg = record.msg.replace("\n", " ")
return jsonlogger.JsonFormatter.format(self, record)
@ -93,7 +93,9 @@ class Log:
handler.addFilter(LogFilter(logging.WARNING))
handler.setFormatter(
MultilineFormatter(
self.warning(CONSOLE_FORMAT.format(colorama.Fore.YELLOW, colorama.Style.RESET_ALL))
self.warning(
CONSOLE_FORMAT.format(colorama.Fore.YELLOW, colorama.Style.RESET_ALL)
)
)
)

View File

@ -36,7 +36,10 @@ def timedelta(value, dt_format=None):
return None
timedelta = dateparser.parse(
value, settings={"TO_TIMEZONE": "UTC", "RETURN_AS_TIMEZONE_AWARE": True}
value, settings={
"TO_TIMEZONE": "UTC",
"RETURN_AS_TIMEZONE_AWARE": True
}
)
if dt_format:

View File

@ -1,29 +1,7 @@
#!/usr/bin/env python3
"""Global utility methods and classes."""
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
from distutils.util import strtobool
def to_bool(string):
@ -43,6 +21,7 @@ class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]

View File

@ -7,6 +7,7 @@ title: Documentation
[![Python Version](https://img.shields.io/pypi/pyversions/docker-tidy.svg)](https://pypi.org/project/docker-tidy/)
[![PyPI Status](https://img.shields.io/pypi/status/docker-tidy.svg)](https://pypi.org/project/docker-tidy/)
[![PyPI Release](https://img.shields.io/pypi/v/docker-tidy.svg)](https://pypi.org/project/docker-tidy/)
[![Codecov](https://img.shields.io/codecov/c/github/thegeeklab/docker-tidy)](https://codecov.io/gh/thegeeklab/docker-tidy)
[![GitHub contributors](https://img.shields.io/github/contributors/thegeeklab/docker-tidy)](https://github.com/thegeeklab/docker-tidy/graphs/contributors)
[![Source: GitHub](https://img.shields.io/badge/source-github-blue.svg?logo=github&logoColor=white)](https://github.com/thegeeklab/docker-tidy)
[![License: Apache-2.0](https://img.shields.io/github/license/thegeeklab/docker-tidy)](https://github.com/thegeeklab/docker-tidy/blob/main/LICENSE)

1240
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,10 +10,10 @@ classifiers = [
"Natural Language :: English",
"Operating System :: POSIX",
"Programming Language :: Python :: 3",
"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",
"Programming Language :: Python :: 3.13",
"Topic :: System :: Systems Administration",
"Topic :: Utilities",
"Topic :: Software Development",
@ -21,52 +21,64 @@ classifiers = [
description = "Keep docker hosts tidy."
documentation = "https://docker-tidy.geekdocs.de/"
homepage = "https://docker-tidy.geekdocs.de/"
include = ["LICENSE"]
include = [
"LICENSE",
]
keywords = ["docker", "gc", "prune", "garbage"]
license = "Apache-2.0"
name = "docker-tidy"
packages = [{ include = "dockertidy" }]
packages = [
{include = "dockertidy"},
]
readme = "README.md"
repository = "https://github.com/thegeeklab/docker-tidy/"
version = "0.0.0"
[tool.poetry.dependencies]
anyconfig = "0.14.0"
anyconfig = "0.13.0"
appdirs = "1.4.4"
certifi = "2024.8.30"
certifi = "2023.7.22"
colorama = "0.4.6"
dateparser = "1.2.0"
docker = "7.1.0"
dateparser = "1.1.8"
docker = "6.1.3"
docker-pycreds = "0.4.0"
environs = "11.2.0"
idna = "3.10"
environs = "9.5.0"
idna = "3.4"
ipaddress = "1.0.23"
jsonschema = "4.23.0"
jsonschema = "4.19.0"
nested-lookup = "0.2.25"
pathspec = "0.12.1"
python = "^3.10.0"
python-dateutil = "2.9.0.post0"
pathspec = "0.11.2"
python = "^3.8.0"
python-dateutil = "2.8.2"
python-json-logger = "2.0.7"
requests = "2.32.3"
"ruamel.yaml" = "0.18.6"
websocket_client = "1.8.0"
zipp = "3.21.0"
requests = "2.31.0"
"ruamel.yaml" = "0.17.32"
websocket_client = "1.6.2"
zipp = "3.16.2"
[tool.poetry.scripts]
docker-tidy = "dockertidy.cli:main"
[tool.poetry.group.dev.dependencies]
ruff = "0.7.3"
pytest = "8.3.3"
pytest-mock = "3.14.0"
pytest-cov = "6.0.0"
ruff = "0.0.286"
pytest = "7.4.0"
pytest-mock = "3.11.1"
pytest-cov = "4.1.0"
toml = "0.10.2"
yapf = "0.40.1"
[tool.poetry-dynamic-versioning]
enable = true
style = "semver"
vcs = "git"
[tool.isort]
default_section = "THIRDPARTY"
force_single_line = true
line_length = 99
sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
skip_glob = ["**/.env*", "**/env/*", "**/.venv/*", "**/docs/*"]
[tool.pytest.ini_options]
addopts = "dockertidy --cov=dockertidy --cov-report=xml:coverage.xml --cov-report=term --no-cov-on-fail"
filterwarnings = [
@ -84,22 +96,17 @@ 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
@ -110,39 +117,47 @@ indent-width = 4
# 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",
"UP038",
"RUF012",
"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.format]
quote-style = "double"
indent-style = "space"
line-ending = "lf"
[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

View File

@ -1,12 +1,4 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>thegeeklab/renovate-presets"],
"packageRules": [
{
"matchManagers": ["woodpecker"],
"matchFileNames": [".woodpecker/test.yml"],
"matchPackageNames": ["docker.io/library/python"],
"enabled": false
}
]
"extends": ["github>thegeeklab/renovate-presets"]
}