{"id":30237700,"url":"https://github.com/devopshq/artifactory-cleanup","last_synced_at":"2025-08-15T02:07:44.539Z","repository":{"id":38098866,"uuid":"226035357","full_name":"devopshq/artifactory-cleanup","owner":"devopshq","description":"Extended cleanup tool for JFrog Artifactory","archived":false,"fork":false,"pushed_at":"2025-07-08T05:57:37.000Z","size":238,"stargazers_count":142,"open_issues_count":21,"forks_count":73,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-07-08T07:03:09.924Z","etag":null,"topics":["artifactory","artifactory-api","artifactory-cleanup","cleanup","hacktoberfest","jfrog","jfrog-artifactory"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devopshq.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-12-05T06:48:30.000Z","updated_at":"2025-07-08T05:57:42.000Z","dependencies_parsed_at":"2025-01-13T10:33:41.494Z","dependency_job_id":"b1d808d8-7d41-4b84-86ac-4d0ec27a4979","html_url":"https://github.com/devopshq/artifactory-cleanup","commit_stats":{"total_commits":151,"total_committers":27,"mean_commits":5.592592592592593,"dds":"0.45033112582781454","last_synced_commit":"10118d03e1298dc91f45827ceaf1c7261cc10218"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/devopshq/artifactory-cleanup","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devopshq%2Fartifactory-cleanup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devopshq%2Fartifactory-cleanup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devopshq%2Fartifactory-cleanup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devopshq%2Fartifactory-cleanup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devopshq","download_url":"https://codeload.github.com/devopshq/artifactory-cleanup/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devopshq%2Fartifactory-cleanup/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270511293,"owners_count":24597666,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-15T02:00:12.559Z","response_time":110,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["artifactory","artifactory-api","artifactory-cleanup","cleanup","hacktoberfest","jfrog","jfrog-artifactory"],"created_at":"2025-08-15T02:07:41.963Z","updated_at":"2025-08-15T02:07:44.516Z","avatar_url":"https://github.com/devopshq.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Artifactory cleanup #\n\n`artifactory-cleanup` is an extended and flexible cleanup tool for JFrog Artifactory.\n\nThe tool has simple YAML-defined cleanup configuration and can be extended with your own rules on Python.\nEverything must be as a code, even cleanup policies!\n\n# Tables of Contents\n\n\u003c!-- toc --\u003e\n\n- [Artifactory cleanup](#artifactory-cleanup)\n- [Tables of Contents](#tables-of-contents)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Notes](#notes)\n  - [Commands](#commands)\n- [Rules](#rules)\n  - [Common](#common)\n  - [Delete](#delete)\n  - [Keep](#keep)\n  - [Docker](#docker)\n  - [Filters](#filters)\n  - [Create your own rule](#create-your-own-rule)\n- [How to](#how-to)\n  - [How to connect self-signed certificates for docker?](#how-to-connect-self-signed-certificates-for-docker)\n  - [How to clean up Conan repository?](#how-to-clean-up-conan-repository)\n  - [How to keep latest N docker images?](#how-to-keep-latest-n-docker-images)\n- [Release](#release)\n\n\u003c!-- tocstop --\u003e\n\n# Installation\n\nAs simple as one command!\n\n```bash\n# docker\ndocker pull devopshq/artifactory-cleanup\ndocker run --rm devopshq/artifactory-cleanup artifactory-cleanup --help\n\n# python (later we call it 'cli')\npython3 -mpip install artifactory-cleanup\nartifactory-cleanup --help\n```\n\n# Usage\n\nSuppose you want to remove **all artifacts older than N days** from **reponame** repository.\nYou should take the following steps:\n\n1. Install `artifactory-cleanup` (see above)\n2. Create a configuration file `artifactory-cleanup.yaml`. variables.\n\n```yaml\n# artifactory-cleanup.yaml\nartifactory-cleanup:\n  server: https://repo.example.com/artifactory\n  # $VAR is auto populated from environment variables\n  user: $ARTIFACTORY_USERNAME\n  password: $ARTIFACTORY_PASSWORD\n\n  policies:\n    - name: Remove all files from repo-name-here older than 7 days\n      rules:\n        - rule: Repo\n          name: \"reponame\"\n        - rule: DeleteOlderThan\n          days: 7\n```\n\n3. Run the command **TO SHOW** (not remove) artifacts that will be deleted. By default `artifactory-cleanup` uses \"dry\n   mode\".\n\n```bash\n# Set the credentials with delete permissions\nexport ARTIFACTORY_USERNAME=usernamehere\nexport ARTIFACTORY_PASSWORD=password\n\n# docker\ndocker run --rm -v \"$(pwd)\":/app -e ARTIFACTORY_USERNAME -e ARTIFACTORY_PASSWORD devopshq/artifactory-cleanup artifactory-cleanup\n\n# cli\nartifactory-cleanup\n```\n\n4. Verify that right artifacts will be removed and add `--destroy` flag **TO REMOVE** artifacts:\n\n```bash\n# docker\ndocker run --rm -v \"$(pwd)\":/app -e ARTIFACTORY_USERNAME -e ARTIFACTORY_PASSWORD devopshq/artifactory-cleanup artifactory-cleanup --destroy\n\n# cli\nartifactory-cleanup --destroy\n```\n\nLooking for more examples? Check [examples](./examples) folder!\n\n## Notes\n\n- **Always** specify version of `artifactory-cleanup` when using it in the production. `1.0.0` is just an example, find the latest version in pypi: https://pypi.org/project/artifactory-cleanup/\n\n```bash\n# docker\ndocker pull devopshq/artifactory-cleanup:1.0.0\ndocker run --rm devopshq/artifactory-cleanup:1.0.0 artifactory-cleanup --version\n\n# python (later we call it 'cli')\npython3 -mpip install artifactory-cleanup==1.0.0\nartifactory-cleanup --help\n```\n\n- Use CI servers or cron-like utilities to run `artifactory-cleanup` every day (or every hour). TeamCity and GitHub have\n  built-in support and show additional logs format\n- Do not save credentials in the configuration file, use environment variables.\n- Use `--ignore-not-found` flag to ignore errors when the repository is not found. It's useful when you have a\n  configuration for multiple repositories and some of them are not found.\n- Use `--worker-count=\u003cWORKER_NUM\u003e` to increase the number of workers. By default, it's 1. It's useful when you have a lot of\n  artifacts and you want to speed up the process.\n\n## Commands ##\n\n```bash\n# Debug - \"dry run\" mode by default\n# debug run - only print artifacts. it does not delete any artifacts\nartifactory-cleanup\n\n# Debug run only for policytestname.\nartifactory-cleanup --policy policytestname\n\n# REMOVE\n# For remove artifacts use --destroy\nartifactory-cleanup --destroy\n\n# For remove artifacts use environment variable\nexport ARTIFACTORY_CLEANUP_DESTROY=True\nartifactory-cleanup\n\n# Specify config filename\nartifactory-cleanup --config artifactory-cleanup.yaml\n\n# Specify config filename using environment variable\nexport ARTIFACTORY_CLEANUP_CONFIG_FILE=artifactory-cleanup.yaml\nartifactory-cleanup --config artifactory-cleanup.yaml\n\n# Look in the future - shows what the tool WILL remove after 10 days\nartifactory-cleanup --days-in-future=10\n\n# Not satisfied with built-in rules? Write your own rules in python and connect them!\nartifactory-cleanup --load-rules=myrule.py\ndocker run -v \"$(pwd)\":/app devopshq/artifactory-cleanup artifactory-cleanup --load-rules=myrule.py\n\n# Save the table summary in a file\nartifactory-cleanup --output=myfile.txt\n\n# Save the summary in a json file\nartifactory-cleanup --output=myfile.txt --output-format=json\n\n# Save the summary in a json file and append the list of all removed artifacts\nartifactory-cleanup --output=myfile.json --output-format json --output-artifacts\n```\n\n# Rules\n\n## Common\n\n- `Repo` - Apply the rule to one repository. If no name is specified, it is taken from the rule name (in `CleanupPolicy`\n  definition)\n\n```yaml\n- rule: Repo\n  name: reponame\n```\n\n```yaml\n# OR - if you have a single policy for the repo - you can name the policy as reponame\n# Both configurations are equal\npolicies:\n  - name: reponame\n    rules:\n      - rule: Repo\n```\n\n- `RepoList` - Apply the policy to list of repositories.\n\n```yaml\n- rule: RepoList\n  repos:\n    - repo1\n    - repo2\n    - repo3\n```\n\n- `RepoByMask` - Apply rule to repositories matching by mask\n\n```yaml\n- rule: RepoByMask\n  mask: \"*.banned\"\n```\n\n- `PropertyEq` - Delete repository artifacts only with a specific property value (property_key is the name of the\n  parameter, property_value is the value)\n\n```yaml\n- rule: PropertyEq\n  property_key: key-name\n  property_value: 1\n```\n\n- `PropertyNeq` - Delete repository artifacts only if the value != specified. If there is no value, delete it anyway.\n  Allows you to specify the deletion flag `do_not_delete = 1`\n\n```yaml\n- rule: PropertyNeq\n  property_key: key-name\n  property_value: 1\n```\n\n## Delete\n\n- `DeleteOlderThan` - deletes artifacts that are older than N days\n\n```yaml\n- rule: DeleteOlderThan\n  days: 1\n```\n\n- `DeleteWithoutDownloads`  - deletes artifacts that have never been downloaded (DownloadCount=0). Better to use\n  with `DeleteOlderThan` rule\n\n```yaml\n- rule: DeleteWithoutDownloads\n```\n\n- `DeleteOlderThanNDaysWithoutDownloads` - deletes artifacts that are older than N days and have not been\n  downloaded\n\n```yaml\n- rule: DeleteOlderThanNDaysWithoutDownloads\n  days: 1\n```\n\n- `DeleteNotUsedSince` - delete artifacts that were downloaded, but for a long time. N days passed. Or not\n  downloaded at all from the moment of creation and it's been N days\n\n```yaml\n- rule: DeleteNotUsedSince\n  days: 1\n```\n\n- `DeleteEmptyFolders` - Clean up empty folders in given repository list\n\n```yaml\n- rule: DeleteEmptyFolders\n```\n\n- `DeleteByRegexpName` - delete artifacts whose name matches the specified regexp\n\n```yaml\n- rule: DeleteByRegexpName\n  regex_pattern: '\\d'\n```\n\n- `DeleteLeastRecentlyUsedFiles` - delete the least recently used files and keep at most requested number of files. Creation is interpreted as a first usage\n\n```yaml\n- rule: DeleteLeastRecentlyUsedFiles\n  keep: 10\n```\n\n## Keep\n\n- `KeepLatestNFiles` - Leaves the last (by creation time) files in the amount of N pieces. WITHOUT accounting subfolders\n\n```yaml\n- rule: KeepLatestNFiles\n  count: 1\n```\n\n- `KeepLatestNFilesInFolder` - Leaves the last (by creation time) files in the number of N pieces in each folder\n\n```yaml\n- rule: KeepLatestNFilesInFolder\n  count: 1\n```\n\n- `KeepLatestVersionNFilesInFolder` - Leaves the latest N (by version) files in each folder. The definition of the version is using regexp. By default it parses [semver](https://semver.org/) using the regex - `([\\d]+\\.[\\d]+\\.[\\d]+)\")`\n\n```yaml\n- rule: KeepLatestVersionNFilesInFolder\n  count: 1\n  custom_regexp: \"[^\\\\d][\\\\._]((\\\\d+\\\\.)+\\\\d+)\"\n```\n\n- `KeepLatestNupkgNVersions` - Leaves N nupkg (adds `*.nupkg` filter) in release feature builds\n\n```yaml\n- rule: KeepLatestNupkgNVersions\n  count: 1\n```\n\n## Docker\n\n- `DeleteDockerImagesOlderThan` - Delete docker images that are older than N days\n\n```yaml\n- rule: DeleteDockerImagesOlderThan\n  days: 1\n```\n\n- `DeleteDockerImagesOlderThanNDaysWithoutDownloads` - Deletes docker images that are older than N days and have\n  not been downloaded\n\n```yaml\n- rule: DeleteDockerImagesOlderThanNDaysWithoutDownloads\n  days: 1\n```\n\n- `DeleteDockerImagesNotUsed` - Removes Docker image not downloaded since N days\n\n```yaml\n- rule: DeleteDockerImagesNotUsed\n  days: 1\n```\n\n- `IncludeDockerImages` - Apply to docker images with the specified names and tags\n\n```yaml\n- rule: IncludeDockerImages\n  masks: \"*singlemask*\"\n- rule: IncludeDockerImages\n  masks:\n    - \"*production*\"\n    - \"*release*\"\n```\n\n- `ExcludeDockerImages` - Exclude Docker images by name and tags.\n\n```yaml\n- rule: ExcludeDockerImages\n  masks:\n    - \"*production*\"\n    - \"*release*\"\n```\n\n- `KeepLatestNVersionImagesByProperty(count=N, custom_regexp='some-regexp', number_of_digits_in_version=X)` - Leaves N\n  Docker images with the same major. `(^\\d+\\.\\d+\\.\\d+$)` is the default regexp how to determine version which matches semver `1.1.1`. If you\n  need to add minor then set `number_of_digits_in_version` to 2 or if patch then set to 3 (by default we match major, which 1). Semver tags\n  prefixed with `v` are supported by updating the regexp to include (an optional) `v` in the expression (e.g., `(^v?\\d+\\.\\d+\\.\\d+$)`).\n\n```yaml\n- rule: KeepLatestNVersionImagesByProperty\n  count: 1\n  custom_regexp: \"[^\\\\d][\\\\._]((\\\\d+\\\\.)+\\\\d+)\"\n```\n\n- `KeepLatestNDockerImages(count=N)` - Leaves N\n  most recently updated Docker image digests. This ensures all tags matching the same digest is kept.\n\n```yaml\n- rule: KeepLatestNDockerImages\n  count: 1\n```\n\n- `DeleteDockerImageIfNotContainedInProperties(docker_repo='docker-local', properties_prefix='my-prop', image_prefix=None, full_docker_repo_name=None)`\n  \\- Remove Docker image, if it is not found in the properties of the artifact repository.\n\n- `DeleteDockerImageIfNotContainedInPropertiesValue(docker_repo='docker-local', properties_prefix='my-prop', image_prefix=None, full_docker_repo_name=None)`\n  \\- Remove Docker image, if it is not found in the properties of the artifact repository.\n\n## Filters\n\n- `IncludePath` - Apply to artifacts by path / mask.\n\n```yaml\n- rule: IncludePath\n  masks: \"*production*\"\n- rule: IncludePath\n  masks:\n   - \"*production*\"\n   - \"*develop*\"\n```\n\n- `IncludeFilename` - Apply to artifacts by name/mask\n\n```yaml\n- rule: IncludeFilename\n  masks:\n   - \"*production*\"\n   - \"*develop*\"\n```\n\n- `ExcludePath` - Exclude artifacts by path/mask\n\n```yaml\n- rule: ExcludePath\n  masks:\n   - \"*production*\"\n   - \"*develop*\"\n```\n\n- `ExcludeFilename` - Exclude artifacts by name/mask\n\n```yaml\n- rule: ExcludeFilename\n  masks:\n    - \"*.tag.gz\"\n    - \"*.zip\"\n```\n\n- `FilterByRegexpPath` - delete artifacts whose path matches the specified regexp\n\n```yaml\n- rule: FilterByRegexpPath\n  path: '\\d'\n```\n\n## Create your own rule\n\nIf you want to create your own rule, you can do it!\n\nThe basic flow how the tool calls Rules:\n\n1. `Rule.check(*args, **kwargs)` - verify that the Rule configured right. Call other services to get more information.\n2. `Rule.aql_add_filter(filters)` - add Artifactory Query Language expressions\n3. `Rule.aql_add_text(aql)` - add text to the result aql query\n4. `artifactory-cleanup` calls Artifactory with AQL and pass the result to the next step\n5. `Rule.filter(artifacts)` - filter out artifacts. The method returns **artifacts that will be removed!**.\n    - To keep artifacts use `artifacts.keep(artifact)` method\n\nCreate `myrule.py` file at the same folder as `artifactory-cleanup.yaml`:\n\n```python\n# myrule.py\nfrom typing import List\n\nfrom artifactory_cleanup import register\nfrom artifactory_cleanup.rules import Rule, ArtifactsList\n\n\nclass MySimpleRule(Rule):\n    \"\"\"\n    This doc string is used as rule title\n\n    For more methods look at Rule source code\n    \"\"\"\n\n    def __init__(self, my_param: str, value: int):\n        self.my_param = my_param\n        self.value = value\n\n    def aql_add_filter(self, filters: List) -\u003e List:\n        print(f\"Today is {self.today}\")\n        print(self.my_param)\n        print(self.value)\n        return filters\n\n    def filter(self, artifacts: ArtifactsList) -\u003e ArtifactsList:\n        \"\"\"I'm here just to print the list\"\"\"\n        print(self.my_param)\n        print(self.value)\n        # You can make requests to artifactory by using self.session:\n        # url = f\"/api/storage/{self.repo}\"\n        # r = self.session.get(url)\n        # r.raise_for_status()\n        return artifacts\n\n\n# Register your rule in the system\nregister(MySimpleRule)\n```\n\nUse `rule: MySimpleRule` in configuration:\n\n```yaml\n# artifactory-cleanup.yaml\n- rule: MySimpleRule\n  my_param: \"Hello, world!\"\n  value: 42\n```\n\nSpecify `--load-rules` to the command:\n\n```bash\n# docker\ndocker run -v \"$(pwd)\":/app devopshq/artifactory-cleanup artifactory-cleanup --load-rules=myrule.py\n\n# cli\nartifactory-cleanup --load-rules=myrule.py\n```\n\n# How to\n\n## How to connect self-signed certificates for docker?\n\nIn case you have set up your Artifactory self-signed certificates, place all certificates of the chain of trust into\nthe `certificates` folder and add additional argument to the command:\n\n```bash\ndocker run -v \"$(pwd)\":/app -v \"$(pwd)/certificates\":/mnt/self-signed-certs/ devopshq/artifactory-cleanup artifactory-cleanup\n```\n\n## How to clean up Conan repository?\n\nWe can handle conan's metadata by creating two policies:\n\n1. First one removes files but keep all metadata.\n2. Second one look at folders and if it contains only medata files - removes it (because there's no associated with\n   metadata files)\n\nThe idea came from https://github.com/devopshq/artifactory-cleanup/issues/47\n\n```yaml\n# artifactory-cleanup.yaml\nartifactory-cleanup:\n  server: https://repo.example.com/artifactory\n  user: $ARTIFACTORY_USERNAME\n  password: $ARTIFACTORY_PASSWORD\n\n  policies:\n    - name: Conan - delete files older than 60 days\n      rules:\n        - rule: Repo\n          name: \"conan-testing\"\n        - rule: DeleteNotUsedSince\n          days: 60\n        - rule: ExcludeFilename\n          masks:\n            - \".timestamp\"\n            - \"index.json\"\n    - name: Conan - delete empty folders (to fix the index)\n      rules:\n        - rule: Repo\n          name: \"conan-testing\"\n        - rule: DeleteEmptyFolders\n        - rule: ExcludeFilename\n          masks:\n            - \".timestamp\"\n            - \"index.json\"\n```\n\n## How to keep latest N docker images?\n\nWe can combine docker rules with usual \"files\" rules!\n\nThe idea came from https://github.com/devopshq/artifactory-cleanup/issues/61\n\n```yaml\n# artifactory-cleanup.yaml\nartifactory-cleanup:\n  server: https://repo.example.com/artifactory\n  user: $ARTIFACTORY_USERNAME\n  password: $ARTIFACTORY_PASSWORD\n\n  policies:\n    - name: Remove docker images, but keep last 3\n      rules:\n        # Select repo\n        - rule: Repo\n          name: docker-demo\n        # Delete docker images older than 30 days\n        - rule: DeleteDockerImagesOlderThan\n          days: 30\n        # Keep these tags for all images\n        - rule: ExcludeDockerImages\n          masks:\n            - \"*:latest\"\n            - \"*:release*\"\n        # Exclude these docker tags\n        - rule: ExcludePath\n          masks: \"*base-tools*\"\n        # Keep 3 docker tags for all images\n        - rule: KeepLatestNFilesInFolder\n          count: 3\n```\n\n# Release\n\nIn order to provide a new release of `artifactory-cleanup`, there are two steps involved.\n\n1. Bump the version in the [setup.py](setup.py)\n2. Bump the version in the [__init__.py](./artifactory_cleanup/__init__.py)\n3. Create a Git release tag (in format `1.0.1`) by creating a release on GitHub\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevopshq%2Fartifactory-cleanup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevopshq%2Fartifactory-cleanup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevopshq%2Fartifactory-cleanup/lists"}