Repository: devopshq/artifactory-cleanup

Issue:
Formatting Question - KeepLatestNVersionImagesByProperty
Thank you for developing this wonderful tool, it has been very useful.
Having said that, i am having some issues getting some of the docker rules to work.

For example, my yaml file looks like this:

```yaml
artifactory-cleanup:
  server: $SERVER
  user: $USER
  password: $PASS

  policies:
     - name: Test
       rules:
         - rule: RepoList
           repos:
             - docker-repo
         - rule: KeepLatestNVersionImagesByProperty
           count: 2
```

and im getting the following error after the result queries:
```yaml
********************************************************************************
Found 1457 artifacts
Filter artifacts - rule: RepoList - Apply the policy to a list of repositories.
Filter artifacts - rule: KeepLatestNVersionImagesByProperty - Leaves ``count`` Docker images with the same major.
Traceback (most recent call last):
  File "/Users/nferrovia/Library/Python/3.8/bin/artifactory-cleanup", line 8, in <module>
    sys.exit(ArtifactoryCleanupCLI())
  File "/Users/nferrovia/Library/Python/3.9/lib/python/site-packages/plumbum/cli/application.py", line 177, in __new__
    return cls.run()
  File "/Users/nferrovia/Library/Python/3.9/lib/python/site-packages/plumbum/cli/application.py", line 634, in run
    retcode = inst.main(*tailargs)
  File "/Users/nferrovia/Library/Python/3.9/lib/python/site-packages/artifactory_cleanup/cli.py", line 121, in main
    for summary in cleanup.cleanup(
  File "/Users/nferrovia/Library/Python/3.9/lib/python/site-packages/artifactory_cleanup/artifactorycleanup.py", line 53, in cleanup
    artifacts_to_remove = policy.filter(artifacts)
  File "/Users/nferrovia/Library/Python/3.9/lib/python/site-packages/artifactory_cleanup/rules/base.py", line 284, in filter
    artifacts = rule.filter(artifacts)
  File "/Users/nferrovia/Library/Python/3.9/lib/python/site-packages/artifactory_cleanup/rules/docker.py", line 235, in filter
    grouped = pydash.group_by(artifacts, iteratee=_groupby)
  File "/Users/nferrovia/Library/Python/3.9/lib/python/site-packages/pydash/collections.py", line 397, in group_by
    key = cbk(value)
  File "/Users/nferrovia/Library/Python/3.9/lib/python/site-packages/artifactory_cleanup/rules/docker.py", line 232, in _groupby
    return self.get_version(artifact)[: self.number_of_digits_in_version]
  File "/Users/nferrovia/Library/Python/3.9/lib/python/site-packages/artifactory_cleanup/rules/docker.py", line 222, in get_version
    raise ValueError(f"Can not find version in '{artifact}'")
ValueError: Can not find version in '{'repo': ............
```

the error leads me to believe it is some mistake on my part defining the versions, but im not sure what im doing wrong.
Our Artifactory directory looks like this:

```yaml
Artifactory
     docker-repo
           microservice1
                  v2.31.0 (dockerimage)
                        manifest.json
                        layer1
                        layer2
                        layer....
                  v2.32.0
                       ....
                       ....
                  v2.....
           microservice2
           microservice....
     other-repo1
     other-repo2
```

It help me a lot if i could get some guidance regarding the proper syntax! ty.

Discussion:

Patch:
diff --git a/README.md b/README.md
index 9e724dd..ffdcf02 100644
--- a/README.md
+++ b/README.md
@@ -117,10 +117,10 @@ artifactory-cleanup --help
 ```bash
 # Debug - "dry run" mode by default
 # debug run - only print artifacts. it does not delete any artifacts
-artifactory-cleanup 
+artifactory-cleanup
 
 # Debug run only for policytestname.
-artifactory-cleanup --policy-name policytestname 
+artifactory-cleanup --policy-name policytestname
 
 # REMOVE
 # For remove artifacts use --destroy
@@ -317,12 +317,8 @@ policies:
 
 - `KeepLatestNVersionImagesByProperty(count=N, custom_regexp='some-regexp', number_of_digits_in_version=X)` - Leaves 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
-  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)
-
-- `DeleteDockerImageIfNotContainedInProperties(docker_repo='docker-local', properties_prefix='my-prop', image_prefix=None, full_docker_repo_name=None)`
-    - Remove Docker image, if it is not found in the properties of the artifact repository.
-- `DeleteDockerImageIfNotContainedInPropertiesValue(docker_repo='docker-local', properties_prefix='my-prop', image_prefix=None, full_docker_repo_name=None)`
-    - Remove Docker image, if it is not found in the properties of the artifact repository.
+  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
+  prefixed with `v` are supported by updating the regexp to include (an optional) `v` in the expression (e.g., `(^v?\d+\.\d+\.\d+$)`).
 
 ```yaml
 - rule: KeepLatestNVersionImagesByProperty
@@ -330,6 +326,12 @@ policies:
   custom_regexp: "[^\\d][\\._]((\\d+\\.)+\\d+)"
 ```
 
+- `DeleteDockerImageIfNotContainedInProperties(docker_repo='docker-local', properties_prefix='my-prop', image_prefix=None, full_docker_repo_name=None)`
+  \- Remove Docker image, if it is not found in the properties of the artifact repository.
+
+- `DeleteDockerImageIfNotContainedInPropertiesValue(docker_repo='docker-local', properties_prefix='my-prop', image_prefix=None, full_docker_repo_name=None)`
+  \- Remove Docker image, if it is not found in the properties of the artifact repository.
+
 ## Filters
 
 - `IncludePath` - Apply to artifacts by path / mask.
@@ -338,7 +340,7 @@ policies:
 - rule: IncludePath
   masks: "*production*"
 - rule: IncludePath
-  masks: 
+  masks:
    - "*production*"
    - "*develop*"
 ```
@@ -347,7 +349,7 @@ policies:
 
 ```yaml
 - rule: IncludeFilename
-  masks: 
+  masks:
    - "*production*"
    - "*develop*"
 ```
@@ -356,7 +358,7 @@ policies:
 
 ```yaml
 - rule: ExcludePath
-  masks: 
+  masks:
    - "*production*"
    - "*develop*"
 ```
diff --git a/artifactory_cleanup/rules/docker.py b/artifactory_cleanup/rules/docker.py
index e7b54e5..5556bd2 100644
--- a/artifactory_cleanup/rules/docker.py
+++ b/artifactory_cleanup/rules/docker.py
@@ -223,9 +223,13 @@ class KeepLatestNVersionImagesByProperty(RuleForDocker):
         if not match:
             raise ValueError(f"Can not find version in '{artifact}'")
         version_str = match.group()
+        if version_str.startswith("v"):
+            version_str = version_str[1:]
+            return tuple(["v"] + list(map(int, version_str.split("."))))
         version = tuple(map(int, version_str.split(".")))
         return version
 
+
     def filter(self, artifacts):
         artifacts.sort(key=lambda x: x["path"])
 


Test patch:
diff --git a/tests/test_rules_docker.py b/tests/test_rules_docker.py
index 23e53be..0bf1666 100644
--- a/tests/test_rules_docker.py
+++ b/tests/test_rules_docker.py
@@ -63,6 +63,21 @@ class TestKeepLatestNVersionImagesByProperty:
                 "path": "baz/0.1.83",
                 "name": "manifest.json",
             },
+            {
+                "properties": {"docker.manifest": "v0.1.100"},
+                "path": "qux/v0.1.100",
+                "name": "manifest.json",
+            },
+            {
+                "properties": {"docker.manifest": "v0.1.200"},
+                "path": "qux/v0.1.200",
+                "name": "manifest.json",
+            },
+            {
+                "properties": {"docker.manifest": "v0.1.99"},
+                "path": "qux/v0.1.99",
+                "name": "manifest.json",
+            },
         ]
         artifacts = ArtifactsList.from_response(data)
         policy = CleanupPolicy(
@@ -72,6 +87,7 @@ class TestKeepLatestNVersionImagesByProperty:
             KeepLatestNVersionImagesByProperty(
                 count=2,
                 number_of_digits_in_version=1,
+                custom_regexp=r"(^v?\d+\.\d+\.\d+$)",
             ),
         )
         assert policy.filter(artifacts) == [
@@ -93,4 +109,10 @@ class TestKeepLatestNVersionImagesByProperty:
                 "properties": {"docker.manifest": "1.1.1"},
                 "stats": {},
             },
+            {
+                "name" : "v0.1.99",
+                "path": "qux",
+                "properties": {"docker.manifest": "v0.1.99"},
+                "stats": {},
+            },
         ]