utils_devops 0.1.172__py3-none-any.whl → 0.1.173__py3-none-any.whl

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.
@@ -4002,6 +4002,80 @@ def get_compose_status(compose_file: str, env_file: Optional[str] = None) -> Dic
4002
4002
 
4003
4003
  return status
4004
4004
 
4005
+
4006
+ def cleanup_old_images_by_compose(keep_count: int = 3, dry_run: bool = False, compose_file: Optional[str] = None, env_file: Optional[str] = None) -> List[str]:
4007
+ """Clean up old Docker images, optionally limited to those in a compose file from compose"""
4008
+ removed = []
4009
+
4010
+ try:
4011
+ # Get all images
4012
+ result = run_docker_command(["docker", "images", "--format", "{{.ID}}|{{.Repository}}|{{.Tag}}|{{.CreatedAt}}"])
4013
+
4014
+ images = []
4015
+ for line in result.stdout.strip().splitlines():
4016
+ if line.strip():
4017
+ parts = line.split('|', 3)
4018
+ if len(parts) == 4:
4019
+ images.append({
4020
+ 'id': parts[0],
4021
+ 'repository': parts[1],
4022
+ 'tag': parts[2],
4023
+ 'created': parts[3]
4024
+ })
4025
+
4026
+ # Group by repository
4027
+ all_repos = {}
4028
+ for img in images:
4029
+ repo = img['repository']
4030
+ if repo not in all_repos:
4031
+ all_repos[repo] = []
4032
+ all_repos[repo].append(img)
4033
+
4034
+ # Determine which repos to clean
4035
+ repos_to_clean = all_repos # Default: all
4036
+
4037
+ if compose_file:
4038
+ try:
4039
+ compose_data = read_compose_file(compose_file, env_file)
4040
+ services = list(compose_data.get('services', {}).keys())
4041
+
4042
+ compose_repos = set()
4043
+ for service in services:
4044
+ image = get_service_image(compose_file, service, env_file)
4045
+ if image:
4046
+ repo_tag = image.split(':')
4047
+ repo = repo_tag[0]
4048
+ compose_repos.add(repo)
4049
+
4050
+ # Filter to only compose repos
4051
+ repos_to_clean = {repo: imgs for repo, imgs in all_repos.items() if repo in compose_repos}
4052
+
4053
+ if not compose_repos:
4054
+ logger.warning("No services/images found in compose file, falling back to all images")
4055
+ repos_to_clean = all_repos
4056
+ except Exception as e:
4057
+ logger.warning(f"Error reading compose file: {e}, falling back to all images")
4058
+ repos_to_clean = all_repos
4059
+
4060
+ # Clean up
4061
+ for repo, repo_images in repos_to_clean.items():
4062
+ # Sort by creation date (newest first)
4063
+ repo_images.sort(key=lambda x: x['created'], reverse=True)
4064
+
4065
+ # Remove old images
4066
+ for old_img in repo_images[keep_count:]:
4067
+ if dry_run:
4068
+ logger.info(f"Would remove: {old_img['repository']}:{old_img['tag']}")
4069
+ else:
4070
+ remove_result = run_docker_command(["docker", "rmi", old_img['id']])
4071
+ if remove_result.rc == 0:
4072
+ removed.append(f"{old_img['repository']}:{old_img['tag']}")
4073
+
4074
+ except Exception as e:
4075
+ logger.error(f"Image cleanup failed: {e}")
4076
+
4077
+ return removed
4078
+
4005
4079
  def cleanup_old_images(keep_count: int = 3, dry_run: bool = False) -> List[str]:
4006
4080
  """Clean up old Docker images"""
4007
4081
  removed = []
@@ -4923,7 +4997,7 @@ __all__ = [
4923
4997
  # Backup & restore
4924
4998
  "backup_compose", "restore_compose",
4925
4999
  # Utilities
4926
- "get_compose_status", "cleanup_old_images",
5000
+ "get_compose_status", "cleanup_old_images", "cleanup_old_images_by_compose",
4927
5001
  # Data classes
4928
5002
  "ExecResult", "LogLine", "ContainerInfo",
4929
5003
  # Exceptions
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: utils_devops
3
- Version: 0.1.172
3
+ Version: 0.1.173
4
4
  Summary: Lightweight DevOps utilities for automation scripts: config editing (YAML/JSON/INI/.env), templating, diffing, and CLI tools
5
5
  License: MIT
6
6
  Keywords: devops,automation,nginx,cli,jinja2,yaml,config,diff,templating,logging,docker,compose,file-ops
@@ -9,7 +9,7 @@ utils_devops/core/strings.py,sha256=8s0GSjcyTKwLjJjsJ_XfOJxPtyb549icDlU9SUxSvHI,
9
9
  utils_devops/core/systems.py,sha256=wNbEFUAvbMPdqWN-iXvTzvj5iE9xaWfjZYYvD0EZAH0,47577
10
10
  utils_devops/extras/__init__.py,sha256=ZXHeVLHO3_qiW9AY-UQ_YA9cQzmkLGv54a2UbyvtlM0,3571
11
11
  utils_devops/extras/aws_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- utils_devops/extras/docker_ops.py,sha256=QNa3oSXFB4LrXRprEvdJ32KkmXM7ch0IA2MKvTII0pY,198747
12
+ utils_devops/extras/docker_ops.py,sha256=JZD6hkOqbp_OqiPnZ9cYZhDQi8lqo7-YEOJ7Fup12e0,201853
13
13
  utils_devops/extras/git_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  utils_devops/extras/interaction_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  utils_devops/extras/metrics_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -19,7 +19,7 @@ utils_devops/extras/notification_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
19
19
  utils_devops/extras/performance_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  utils_devops/extras/ssh_ops.py,sha256=hTOYzyWmnZWzOLeZbCoZRLxSJiBmr0QgS_87qks-CYk,76305
21
21
  utils_devops/extras/vault_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- utils_devops-0.1.172.dist-info/METADATA,sha256=CWFA-5L6IZJSyJX07zHd_v5X7OAhbg-evhM8bwZuXgg,1903
23
- utils_devops-0.1.172.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
24
- utils_devops-0.1.172.dist-info/entry_points.txt,sha256=ei3B6ZL5yu6dOq-U1r8wsBdkXeg63RAyV7m8_ADaE6k,53
25
- utils_devops-0.1.172.dist-info/RECORD,,
22
+ utils_devops-0.1.173.dist-info/METADATA,sha256=_bmoX3spB08xrmRbSFUkP0IeJwB7-Z4r24jDvPAVSlU,1903
23
+ utils_devops-0.1.173.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
24
+ utils_devops-0.1.173.dist-info/entry_points.txt,sha256=ei3B6ZL5yu6dOq-U1r8wsBdkXeg63RAyV7m8_ADaE6k,53
25
+ utils_devops-0.1.173.dist-info/RECORD,,