docker-image-cleanup 0.1.2__tar.gz → 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: docker-image-cleanup
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Clean up old Docker images from local storage with retention controls
5
5
  Keywords: docker,cleanup,images,disk-space
6
6
  Author: Michael Bianco
@@ -14,6 +14,11 @@ Requires-Python: >=3.11
14
14
  Project-URL: Repository, https://github.com/iloveitaly/docker-image-cleanup
15
15
  Description-Content-Type: text/markdown
16
16
 
17
+ [![Release Notes](https://img.shields.io/github/release/iloveitaly/docker-image-cleanup)](https://github.com/iloveitaly/docker-image-cleanup/releases)
18
+ [![Downloads](https://static.pepy.tech/badge/docker-image-cleanup/month)](https://pepy.tech/project/docker-image-cleanup)
19
+ ![GitHub CI Status](https://github.com/iloveitaly/docker-image-cleanup/actions/workflows/build_and_publish.yml/badge.svg)
20
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
21
+
17
22
  # Clean Up Images from Self-Hosted Docker Registries
18
23
 
19
24
  If you're running a self-hosted Docker registry (using tools like [unregistry](https://github.com/psviderski/unregistry)), you'll want to clean up old images to reclaim disk space. This tool connects to your local Docker daemon and removes old images from specified repositories while keeping the ones you actually need.
@@ -123,3 +128,8 @@ The cleanup process is straightforward:
123
128
  Images with multiple tags are handled intelligently. If all tags on an image would be removed, the entire image is deleted. If only some tags would be removed, those tags are untagged but the image remains.
124
129
 
125
130
  # [MIT License](LICENSE.md)
131
+
132
+ ---
133
+
134
+ *This project was created from [iloveitaly/python-package-template](https://github.com/iloveitaly/python-package-template)*
135
+ > theirs
@@ -1,3 +1,8 @@
1
+ [![Release Notes](https://img.shields.io/github/release/iloveitaly/docker-image-cleanup)](https://github.com/iloveitaly/docker-image-cleanup/releases)
2
+ [![Downloads](https://static.pepy.tech/badge/docker-image-cleanup/month)](https://pepy.tech/project/docker-image-cleanup)
3
+ ![GitHub CI Status](https://github.com/iloveitaly/docker-image-cleanup/actions/workflows/build_and_publish.yml/badge.svg)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
1
6
  # Clean Up Images from Self-Hosted Docker Registries
2
7
 
3
8
  If you're running a self-hosted Docker registry (using tools like [unregistry](https://github.com/psviderski/unregistry)), you'll want to clean up old images to reclaim disk space. This tool connects to your local Docker daemon and removes old images from specified repositories while keeping the ones you actually need.
@@ -107,3 +112,8 @@ The cleanup process is straightforward:
107
112
  Images with multiple tags are handled intelligently. If all tags on an image would be removed, the entire image is deleted. If only some tags would be removed, those tags are untagged but the image remains.
108
113
 
109
114
  # [MIT License](LICENSE.md)
115
+
116
+ ---
117
+
118
+ *This project was created from [iloveitaly/python-package-template](https://github.com/iloveitaly/python-package-template)*
119
+ > theirs
@@ -12,6 +12,8 @@ from pydantic import BaseModel, Field
12
12
  from structlog_config import configure_logger
13
13
  from whenever import Instant
14
14
 
15
+ from .version import __version__ as __version__
16
+
15
17
  log = configure_logger()
16
18
 
17
19
 
@@ -48,9 +50,25 @@ def parse_docker_image(
48
50
  used_image_ids: set[
49
51
  str
50
52
  ], # Use Any due to pyright resolution issues with docker library types
51
- ) -> ImageInfo:
52
- """Parses docker.models.images.Image into our ImageInfo model."""
53
- created_instant = Instant.parse_iso(image_data.attrs["Created"])
53
+ ) -> ImageInfo | None:
54
+ """Parses docker.models.images.Image into our ImageInfo model.
55
+
56
+ Returns None if the creation date cannot be determined.
57
+ """
58
+ created_str = image_data.attrs.get("Created")
59
+ if not created_str:
60
+ metadata = image_data.attrs.get("Metadata") or {}
61
+ created_str = metadata.get("LastTagTime")
62
+
63
+ if not created_str or created_str == "0001-01-01T00:00:00Z":
64
+ log.warning(
65
+ "could not determine image age, skipping",
66
+ image_id=image_data.id,
67
+ tags=image_data.tags,
68
+ )
69
+ return None
70
+
71
+ created_instant = Instant.parse_iso(created_str)
54
72
  image_id = image_data.id
55
73
 
56
74
  return ImageInfo(
@@ -71,7 +89,11 @@ def get_images_to_process(
71
89
  log.info("no images found for repository", repo=repo_name)
72
90
  return []
73
91
 
74
- parsed_images = [parse_docker_image(img, used_image_ids) for img in images]
92
+ parsed_images = [
93
+ parsed
94
+ for img in images
95
+ if (parsed := parse_docker_image(img, used_image_ids)) is not None
96
+ ]
75
97
  return sorted(parsed_images, key=lambda img: img.created, reverse=True)
76
98
 
77
99
 
@@ -0,0 +1,34 @@
1
+ """Version handling for docker-image-cleanup."""
2
+
3
+ import importlib.metadata
4
+ from pathlib import Path
5
+
6
+
7
+ def is_local_source_checkout() -> bool:
8
+ """Check if the code is running from a local source checkout."""
9
+ package_dir = Path(__file__).resolve().parent
10
+ # Since this is a flat layout (module-root = ""), repo root is the parent of the package dir
11
+ repo_root = package_dir.parent
12
+
13
+ return (repo_root / ".git").exists() and (repo_root / "pyproject.toml").exists()
14
+
15
+
16
+ def get_version() -> str:
17
+ """Get the version string, appending .dev if running from source."""
18
+ try:
19
+ # Try to get the version of the installed package
20
+ version = importlib.metadata.version("docker-image-cleanup")
21
+ except importlib.metadata.PackageNotFoundError:
22
+ # Fallback for local development if not installed
23
+ version = "0.1.0"
24
+
25
+ if not is_local_source_checkout():
26
+ return version
27
+
28
+ if version.endswith(".dev"):
29
+ return version
30
+
31
+ return f"{version}.dev"
32
+
33
+
34
+ __version__ = get_version()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "docker-image-cleanup"
3
- version = "0.1.2"
3
+ version = "0.1.3"
4
4
  description = "Clean up old Docker images from local storage with retention controls"
5
5
  keywords = ["docker", "cleanup", "images", "disk-space"]
6
6
  readme = "README.md"
@@ -15,12 +15,14 @@ dependencies = [
15
15
  authors = [{ name = "Michael Bianco", email = "mike@mikebian.co" }]
16
16
  urls = { "Repository" = "https://github.com/iloveitaly/docker-image-cleanup" }
17
17
 
18
+
18
19
  # additional packaging information: https://packaging.python.org/en/latest/specifications/core-metadata/#license
19
20
  [project.scripts]
20
21
  docker-image-cleanup = "docker_image_cleanup:main"
21
22
 
23
+
22
24
  [build-system]
23
- requires = ["uv_build>=0.9.0,<0.10.0"]
25
+ requires = ["uv_build>=0.11.0,<0.12"]
24
26
  build-backend = "uv_build"
25
27
 
26
28
  [tool.uv.build-backend]
@@ -28,10 +30,24 @@ build-backend = "uv_build"
28
30
  module-root = ""
29
31
 
30
32
  [dependency-groups]
31
- dev = ["pytest>=8.3.3", "pyright[nodejs]>=1.1.380", "ruff>=0.8.0"]
33
+ dev = [
34
+ "pytest>=8.3.4",
35
+ "pyright[nodejs]>=1.1.408",
36
+ "ruff>=0.8.0",
37
+ "coverage>=7.13.4",
38
+ "pytest-cov>=7.0.0",
39
+ "covdefaults>=2.3.0",
40
+ ]
32
41
 
33
42
  [tool.pyright]
34
43
  exclude = ["examples/", "playground/", "tmp/", ".venv/", "tests/"]
35
44
 
36
- [tool.ruff]
37
- extend-exclude = ["playground.py", "playground/"]
45
+ [tool.pytest.ini_options]
46
+ addopts = "--cov --cov-report=term-missing --cov-report=html:tmp/htmlcov"
47
+
48
+ [tool.coverage.run]
49
+ plugins = ["covdefaults"]
50
+ source = ["docker_image_cleanup"]
51
+
52
+ [tool.coverage.report]
53
+ fail_under = 50