haraka 0.2.31__tar.gz → 0.2.33__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.
- {haraka-0.2.31 → haraka-0.2.33}/PKG-INFO +1 -1
- haraka-0.2.33/haraka/post_gen/service/fileOps/purge.py +170 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka.egg-info/PKG-INFO +1 -1
- {haraka-0.2.31 → haraka-0.2.33}/pyproject.toml +1 -1
- haraka-0.2.31/haraka/post_gen/service/fileOps/purge.py +0 -130
- {haraka-0.2.31 → haraka-0.2.33}/MANIFEST.in +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/__init__.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/art/__init__.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/art/ascii/__init__.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/art/ascii/assets.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/art/ascii/frame/__init__.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/art/ascii/frame/border.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/art/ascii/frame/framer.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/art/ascii/frame/width_utils.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/art/create.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/post_gen/__init__.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/post_gen/config/__init__.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/post_gen/config/config.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/post_gen/resources/__init__.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/post_gen/resources/assets.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/post_gen/runner.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/post_gen/service/__init__.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/post_gen/service/command.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/post_gen/service/fileOps/__init__.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/post_gen/service/fileOps/files.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/post_gen/service/gitOps/__init__.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/post_gen/service/gitOps/gitops.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/utils/__init__.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/utils/common/__init__.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/utils/common/utils.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/utils/logging/__init__.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/utils/logging/log_util.py +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/utils/manifests/go-grpc-gateway-buf.yml +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/utils/manifests/go-grpc-protoc.yml +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/utils/manifests/java-springboot.yml +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka/utils/manifests/python-fastapi.yml +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka.egg-info/SOURCES.txt +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka.egg-info/dependency_links.txt +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/haraka.egg-info/top_level.txt +0 -0
- {haraka-0.2.31 → haraka-0.2.33}/setup.cfg +0 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
haraka.post_gen.utils.purge
|
4
|
+
|
5
|
+
Delete all template artefacts that are **not** required for the chosen
|
6
|
+
variant, as defined by a YAML manifest in `haraka/manifests/<variant>.yml`.
|
7
|
+
|
8
|
+
Manifest format (git-wildmatch globs):
|
9
|
+
|
10
|
+
variant: python-fastapi
|
11
|
+
keep:
|
12
|
+
- src/app/**
|
13
|
+
- tests/**
|
14
|
+
- Dockerfile
|
15
|
+
- chart/**
|
16
|
+
|
17
|
+
Anything matching a `keep:` pattern survives. Everything else is removed.
|
18
|
+
|
19
|
+
Requires: pip install pathspec PyYAML
|
20
|
+
"""
|
21
|
+
from __future__ import annotations
|
22
|
+
|
23
|
+
from pathlib import Path
|
24
|
+
|
25
|
+
from pathspec import PathSpec
|
26
|
+
from typing import Tuple, List
|
27
|
+
|
28
|
+
from haraka.post_gen.service.fileOps.files import FileOps
|
29
|
+
from haraka.utils import Logger, divider
|
30
|
+
from haraka.post_gen.config import config
|
31
|
+
|
32
|
+
_MANIFEST_DIR = Path(__file__).resolve().parent.parent.parent / "manifests"
|
33
|
+
|
34
|
+
|
35
|
+
# --------------------------------------------------------------------------- #
|
36
|
+
# main purger #
|
37
|
+
# --------------------------------------------------------------------------- #
|
38
|
+
def is_dir_protected(rel: str, spec: PathSpec) -> bool:
|
39
|
+
"""True if *rel* (or an ancestor) is matched by keep spec."""
|
40
|
+
# `spec.match_file` already walks ancestors for dirs
|
41
|
+
return spec.match_file(rel)
|
42
|
+
|
43
|
+
|
44
|
+
class ResourcePurger:
|
45
|
+
"""Filesystem cleaner driven by variant manifest files."""
|
46
|
+
|
47
|
+
def __init__(self, fops: FileOps, logger: Logger | None = None) -> None:
|
48
|
+
self._f = fops
|
49
|
+
self._log = logger or Logger("ResourcePurger")
|
50
|
+
self._log.debug("ResourcePurger initialized with FileOps instance and Logger.")
|
51
|
+
|
52
|
+
# ------------------------------ public API ----------------------------- #
|
53
|
+
def purge(self, variant: str, project_dir: Path) -> None:
|
54
|
+
"""
|
55
|
+
Remove everything outside the manifest’s `keep:` patterns.
|
56
|
+
|
57
|
+
Parameters
|
58
|
+
----------
|
59
|
+
variant
|
60
|
+
Variant key, e.g. ``python-fastapi`` or ``go-grpc-protoc``.
|
61
|
+
project_dir
|
62
|
+
Root of the freshly generated Cookiecutter project.
|
63
|
+
"""
|
64
|
+
variant = variant.lower()
|
65
|
+
self._log.info(f"Starting purge for variant: {variant}")
|
66
|
+
self._log.debug(f"Loaded variant for purge: {variant}")
|
67
|
+
|
68
|
+
raw_patterns = config.load_manifest(variant)
|
69
|
+
keep_patterns = [p.rstrip("/") for p in raw_patterns]
|
70
|
+
|
71
|
+
self._log.debug(f"Loaded manifest for variant '{variant}': {keep_patterns}")
|
72
|
+
|
73
|
+
spec = config.build_spec(keep_patterns)
|
74
|
+
self._log.debug(f"Built PathSpec for keep patterns. Total patterns: {len(keep_patterns)}")
|
75
|
+
|
76
|
+
self._log.info(f"Keeping {len(keep_patterns)} pattern(s)")
|
77
|
+
for pattern in keep_patterns:
|
78
|
+
self._log.debug(f"Keep pattern: {pattern}")
|
79
|
+
|
80
|
+
all_paths = self._walk_tree(project_dir)
|
81
|
+
|
82
|
+
matched, non_dirs, non_files, _ = self.classify_paths(all_paths, project_dir, spec)
|
83
|
+
|
84
|
+
self.print_summary(matched, non_dirs, non_files)
|
85
|
+
|
86
|
+
self._log.debug(f"Finished purging unrelated paths in project directory: {project_dir}")
|
87
|
+
|
88
|
+
divider("Project tree after purge…")
|
89
|
+
self._f.print_tree(project_dir)
|
90
|
+
|
91
|
+
|
92
|
+
# ----------------------------- internals ------------------------------ #
|
93
|
+
|
94
|
+
def _walk_tree(self, root: Path) -> List[Path]:
|
95
|
+
"""Return every file/dir under *root*, logging the walk."""
|
96
|
+
paths = list(root.rglob("*"))
|
97
|
+
self._log.debug(f"📋 All paths under {root} (total {len(paths)}:")
|
98
|
+
for p in paths:
|
99
|
+
self._log.debug(f" {p.relative_to(root)}")
|
100
|
+
return paths
|
101
|
+
|
102
|
+
|
103
|
+
# --------------------------------------------------------------------------- #
|
104
|
+
# Classification helpers #
|
105
|
+
# --------------------------------------------------------------------------- #
|
106
|
+
|
107
|
+
def classify_paths(
|
108
|
+
self,
|
109
|
+
paths: List[Path], root: Path, spec: PathSpec
|
110
|
+
) -> Tuple[List[str], List[str], List[str], List[str]]:
|
111
|
+
"""Split paths into keep/delete buckets."""
|
112
|
+
matched: List[str] = []
|
113
|
+
non_matched_files: List[str] = []
|
114
|
+
non_matched_dirs: List[str] = []
|
115
|
+
directories_skipped: List[str] = []
|
116
|
+
|
117
|
+
for path in paths:
|
118
|
+
rel = path.relative_to(root).as_posix()
|
119
|
+
|
120
|
+
if spec.match_file(rel):
|
121
|
+
self._log.debug(f"✅ KEEP {rel}")
|
122
|
+
matched.append(rel)
|
123
|
+
continue
|
124
|
+
|
125
|
+
if path.is_dir():
|
126
|
+
if is_dir_protected(rel, spec):
|
127
|
+
self._log.debug(f"⏭️ SKIPPING DELETE: Protected ancestor found: {path}")
|
128
|
+
directories_skipped.append(rel)
|
129
|
+
else:
|
130
|
+
self._log.debug(f"❌ DELETE DIR: {rel}")
|
131
|
+
non_matched_dirs.append(rel)
|
132
|
+
else:
|
133
|
+
non_matched_files.append(rel)
|
134
|
+
|
135
|
+
return matched, non_matched_dirs, non_matched_files, directories_skipped
|
136
|
+
|
137
|
+
# --------------------------------------------------------------------------- #
|
138
|
+
# Summary printing helpers #
|
139
|
+
# --------------------------------------------------------------------------- #
|
140
|
+
def _print_section(self, title: str, items: List[str]) -> None:
|
141
|
+
self._log.info(f"{title} — {len(items)}")
|
142
|
+
if items:
|
143
|
+
for p in sorted(items):
|
144
|
+
self._log.info(" • %s", p)
|
145
|
+
else:
|
146
|
+
self._log.info(" (none)")
|
147
|
+
self._log.info("-" * 70)
|
148
|
+
|
149
|
+
def print_summary(
|
150
|
+
self,
|
151
|
+
matched: List[str],
|
152
|
+
non_matched_dirs: List[str],
|
153
|
+
non_matched_files: List[str],
|
154
|
+
) -> None:
|
155
|
+
"""Human-friendly digest of keep/delete results."""
|
156
|
+
self._log.info("\n" + "=" * 70)
|
157
|
+
self._print_section("✅ MATCHED (keep)", matched)
|
158
|
+
self._print_section("🗂️ NON-MATCHED DIRECTORIES (delete)", non_matched_dirs)
|
159
|
+
self._print_section("📄 NON-MATCHED FILES (delete)", non_matched_files)
|
160
|
+
self._log.info("=" * 70)
|
161
|
+
|
162
|
+
@staticmethod
|
163
|
+
def _is_dir_protected(relative_path: str, spec: PathSpec) -> bool:
|
164
|
+
"""
|
165
|
+
Walks from `path` up to `root`, and returns True if any ancestor
|
166
|
+
is matched by the spec.
|
167
|
+
"""
|
168
|
+
if spec.match_file(relative_path):
|
169
|
+
return True
|
170
|
+
return False
|
@@ -1,130 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
haraka.post_gen.utils.purge
|
4
|
-
|
5
|
-
Delete all template artefacts that are **not** required for the chosen
|
6
|
-
variant, as defined by a YAML manifest in `haraka/manifests/<variant>.yml`.
|
7
|
-
|
8
|
-
Manifest format (git-wildmatch globs):
|
9
|
-
|
10
|
-
variant: python-fastapi
|
11
|
-
keep:
|
12
|
-
- src/app/**
|
13
|
-
- tests/**
|
14
|
-
- Dockerfile
|
15
|
-
- chart/**
|
16
|
-
|
17
|
-
Anything matching a `keep:` pattern survives. Everything else is removed.
|
18
|
-
|
19
|
-
Requires: pip install pathspec PyYAML
|
20
|
-
"""
|
21
|
-
from __future__ import annotations
|
22
|
-
|
23
|
-
from pathlib import Path
|
24
|
-
|
25
|
-
from pathspec import PathSpec
|
26
|
-
|
27
|
-
from haraka.post_gen.service.fileOps.files import FileOps
|
28
|
-
from haraka.utils import Logger, divider
|
29
|
-
from haraka.post_gen.config import config
|
30
|
-
|
31
|
-
_MANIFEST_DIR = Path(__file__).resolve().parent.parent.parent / "manifests"
|
32
|
-
|
33
|
-
|
34
|
-
# --------------------------------------------------------------------------- #
|
35
|
-
# main purger #
|
36
|
-
# --------------------------------------------------------------------------- #
|
37
|
-
class ResourcePurger:
|
38
|
-
"""Filesystem cleaner driven by variant manifest files."""
|
39
|
-
|
40
|
-
def __init__(self, fops: FileOps, logger: Logger | None = None) -> None:
|
41
|
-
self._f = fops
|
42
|
-
self._log = logger or Logger("ResourcePurger")
|
43
|
-
self._log.debug("ResourcePurger initialized with FileOps instance and Logger.")
|
44
|
-
|
45
|
-
# ------------------------------ public API ----------------------------- #
|
46
|
-
def purge(self, variant: str, project_dir: Path) -> None:
|
47
|
-
"""
|
48
|
-
Remove everything outside the manifest’s `keep:` patterns.
|
49
|
-
|
50
|
-
Parameters
|
51
|
-
----------
|
52
|
-
variant
|
53
|
-
Variant key, e.g. ``python-fastapi`` or ``go-grpc-protoc``.
|
54
|
-
project_dir
|
55
|
-
Root of the freshly generated Cookiecutter project.
|
56
|
-
"""
|
57
|
-
variant = variant.lower()
|
58
|
-
self._log.info(f"Starting purge for variant: {variant}")
|
59
|
-
self._log.debug(f"Loaded variant for purge: {variant}")
|
60
|
-
|
61
|
-
raw_patterns = config.load_manifest(variant)
|
62
|
-
keep_patterns = [p.rstrip("/") for p in raw_patterns]
|
63
|
-
|
64
|
-
self._log.debug(f"Loaded manifest for variant '{variant}': {keep_patterns}")
|
65
|
-
|
66
|
-
spec = config.build_spec(keep_patterns)
|
67
|
-
self._log.debug(f"Built PathSpec for keep patterns. Total patterns: {len(keep_patterns)}")
|
68
|
-
|
69
|
-
self._log.info(f"Keeping {len(keep_patterns)} pattern(s)")
|
70
|
-
for pattern in keep_patterns:
|
71
|
-
self._log.debug(f"Keep pattern: {pattern}")
|
72
|
-
|
73
|
-
self._purge_unrelated(project_dir, spec)
|
74
|
-
self._log.debug(f"Finished purging unrelated paths in project directory: {project_dir}")
|
75
|
-
|
76
|
-
divider("Project tree after purge…")
|
77
|
-
self._f.print_tree(project_dir)
|
78
|
-
|
79
|
-
# --------------------------------------------------------------------------- #
|
80
|
-
# internals #
|
81
|
-
# --------------------------------------------------------------------------- #
|
82
|
-
def _purge_unrelated(self, root: Path, spec: PathSpec) -> None:
|
83
|
-
"""
|
84
|
-
Walk *root* recursively and delete every path **not** matched by *spec*.
|
85
|
-
A directory is preserved if **it or any ancestor** is matched.
|
86
|
-
"""
|
87
|
-
all_paths = list(root.rglob("*"))
|
88
|
-
self._log.debug("📋 Scanning %d paths under %s", len(all_paths), root)
|
89
|
-
|
90
|
-
keep: list[str] = []
|
91
|
-
delete_files: list[str] = []
|
92
|
-
delete_dirs: list[str] = []
|
93
|
-
|
94
|
-
for path in all_paths:
|
95
|
-
rel = path.relative_to(root).as_posix()
|
96
|
-
|
97
|
-
if spec.match_file(rel):
|
98
|
-
keep.append(rel)
|
99
|
-
continue
|
100
|
-
|
101
|
-
if path.is_dir():
|
102
|
-
if self._dir_has_kept_ancestor(rel, spec):
|
103
|
-
self._log.debug("⏭️ SKIP DIR (kept ancestor): %s", rel)
|
104
|
-
else:
|
105
|
-
self._log.debug("❌ DELETE DIR: %s", rel)
|
106
|
-
delete_dirs.append(rel)
|
107
|
-
else:
|
108
|
-
self._log.debug("❌ DELETE FILE: %s", rel)
|
109
|
-
delete_files.append(rel)
|
110
|
-
|
111
|
-
# -- perform deletions -------------------------------------------------- #
|
112
|
-
for f in delete_files:
|
113
|
-
self._f.remove_file(root / f)
|
114
|
-
|
115
|
-
# delete directories bottom-up to avoid “directory not empty” errors
|
116
|
-
for d in sorted(delete_dirs, key=lambda p: p.count("/"), reverse=True):
|
117
|
-
(root / d).rmdir()
|
118
|
-
|
119
|
-
# -- summary ------------------------------------------------------------ #
|
120
|
-
self._log.info("✅ kept : %d", len(keep))
|
121
|
-
self._log.info("🗂️ dirs : %d deleted", len(delete_dirs))
|
122
|
-
self._log.info("📄 files : %d deleted", len(delete_files))
|
123
|
-
|
124
|
-
@staticmethod
|
125
|
-
def _dir_has_kept_ancestor(rel: str, spec: PathSpec) -> bool:
|
126
|
-
"""
|
127
|
-
Return True if *rel* **or any of its ancestors** is matched by *spec*.
|
128
|
-
"""
|
129
|
-
parts = rel.split("/")
|
130
|
-
return any(spec.match_file("/".join(parts[: i + 1])) for i in range(len(parts)))
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|