Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include SHA1 hash when deleting a file #144

Merged
merged 4 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ artifactory-cleanup --help
configuration for multiple repositories and some of them are not found.
- Use `--worker-count=<WORKER_NUM>` to increase the number of workers. By default, it's 1. It's useful when you have a lot of
artifacts and you want to speed up the process.
- Use `--display-format` to customize the printed information when deleting a file. The default format is "'{path}' - {size}". Supported options are "{path}", "{size}" and "{hash}".

## Commands ##

Expand Down
4 changes: 3 additions & 1 deletion artifactory_cleanup/artifactorycleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ def __init__(
today: date,
ignore_not_found: bool,
worker_count: int,
display_format: str,
):
self.session = session
self.policies = policies
self.destroy = destroy
self.ignore_not_found = ignore_not_found
self.worker_count = worker_count
self.display_format = display_format

self._init_policies(today)

Expand Down Expand Up @@ -61,7 +63,7 @@ def cleanup(self, block_ctx_mgr, test_ctx_mgr) -> Iterator[CleanupSummary]:
# Delete artifacts
def _delete(artifact):
with test_ctx_mgr(get_name_for_ci(artifact)):
policy.delete(artifact, destroy=self.destroy, ignore_not_found=self.ignore_not_found)
policy.delete(artifact, destroy=self.destroy, display_format=self.display_format, ignore_not_found=self.ignore_not_found)

with ThreadPoolExecutor(max_workers=int(self.worker_count)) as executor:
for artifact in artifacts_to_remove:
Expand Down
9 changes: 9 additions & 0 deletions artifactory_cleanup/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ class ArtifactoryCleanupCLI(cli.Application):
mandatory=False,
)

_display_format = cli.SwitchAttr(
"--display-format",
help="Set format of the printed message when deleting a file",
default="'{path}' - {size}",
mandatory=False,
envname="ARTIFACTORY_CLEANUP_DISPLAY_FORMAT",
)

@property
def VERSION(self):
# To prevent circular imports
Expand Down Expand Up @@ -172,6 +180,7 @@ def main(self):
today=today,
ignore_not_found=self._ignore_not_found,
worker_count=self._worker_count,
display_format=self._display_format,
)

# Filter policies by name
Expand Down
11 changes: 8 additions & 3 deletions artifactory_cleanup/rules/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class ArtifactDict(TypedDict):
properties: dict
stats: dict
size: int
actual_sha1: str


class ArtifactsList(List[ArtifactDict]):
Expand Down Expand Up @@ -294,11 +295,12 @@ def filter(self, artifacts: ArtifactsList) -> ArtifactsList:
print()
return artifacts

def delete(self, artifact: ArtifactDict, destroy: bool, ignore_not_found: bool = False) -> None:
def delete(self, artifact: ArtifactDict, destroy: bool, display_format: str, ignore_not_found: bool = False) -> None:
"""
Delete the artifact
:param artifact: artifact to remove
:param destroy: if False - just log the action, do not actually remove the artifact
:param display_format: specify the format string for the file to delete, for example "'{path}' - {size}"
:param ignore_not_found: if True - do not raise an error if the artifact is not found
"""

Expand All @@ -310,11 +312,14 @@ def delete(self, artifact: ArtifactDict, destroy: bool, ignore_not_found: bool =
artifact_path = path.format(**artifact)
artifact_path = quote(artifact_path)
artifact_size = artifact.get("size", 0) or 0
artifact_hash = artifact.get("actual_sha1", "")

file_string = display_format.replace("{path}", artifact_path).replace("{size}", size(artifact_size)).replace("{hash}", artifact_hash)

if not destroy:
print(f"DEBUG - we would delete '{artifact_path}' - {size(artifact_size)}")
print(f"DEBUG - we would delete {file_string}")
return
print(f"DESTROY MODE - delete '{artifact_path} - {size(artifact_size)}'")
print(f"DESTROY MODE - delete {file_string}")
r = self.session.delete(artifact_path)
try:
r.raise_for_status()
Expand Down
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def attach_requests_mock_to(mock, server):
"modified": "2021-03-21T13:54:32.000+02:00",
"modified_by": "admin",
"updated": "2021-03-21T13:54:52.384+02:00",
"actual_sha1": "11827853eed40e8b60f5d7e45f2a730915d7704d",
"properties": [
{"key": "property-1", "value": "property-value-1"},
{"key": "property-2", "value": "property-value-2"},
Expand Down
44 changes: 44 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,47 @@ def test_require_output_json(capsys, shared_datadir, requests_mock):
assert (
"Error: Given --output-format, the following are missing ['output']" in stdout
)


@pytest.mark.usefixtures("requests_repo_name_here")
def test_display_format_default(capsys, shared_datadir, requests_mock):
_, code = ArtifactoryCleanupCLI.run(
[
"ArtifactoryCleanupCLI",
"--config",
str(shared_datadir / "cleanup.yaml"),
"--load-rules",
str(shared_datadir / "myrule.py"),
],
exit=False,
)
stdout, stderr = capsys.readouterr()
print(stdout)
assert code == 0, stdout
assert (
"DEBUG - we would delete 'repo-name-here/path/to/file/filename1.json' - 528B"
in stdout
)


@pytest.mark.usefixtures("requests_repo_name_here")
def test_display_format(capsys, shared_datadir, requests_mock):
_, code = ArtifactoryCleanupCLI.run(
[
"ArtifactoryCleanupCLI",
"--config",
str(shared_datadir / "cleanup.yaml"),
"--load-rules",
str(shared_datadir / "myrule.py"),
"--display-format",
"'{path}' ({hash}) - {size}",
],
exit=False,
)
stdout, stderr = capsys.readouterr()
print(stdout)
assert code == 0, stdout
assert (
"DEBUG - we would delete 'repo-name-here/path/to/file/filename1.json' (11827853eed40e8b60f5d7e45f2a730915d7704d) - 528B"
in stdout
)