Skip to content

Commit c0f49d8

Browse files
authored
Merge pull request #215 from eaaltonen/deleteAll-buildnumbers
Add support for deleting all build numbers of a given build name
2 parents 75115e0 + cf2e6f9 commit c0f49d8

File tree

4 files changed

+68
-17
lines changed

4 files changed

+68
-17
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ This library enables you to manage Artifactory resources such as users, groups,
4949
+ [Create build](#create-build)
5050
+ [Promote a build](#promote-a-build)
5151
+ [Delete one or more builds](#delete-one-or-more-builds)
52+
+ [Delete all build numbers of a build](#delete-all-build-numbers-of-a-build)
5253
+ [Rename a build](#rename-a-build)
5354
+ [Get differences between two builds](#get-differences-between-two-builds)
5455
* [Contributing](#contributing)
@@ -553,6 +554,12 @@ build_delete_request = BuildDeleteRequest(buildName="<build_name>", buildNumbers
553554
art.builds.delete(build_delete_request)
554555
```
555556

557+
#### Delete all build numbers of a build
558+
```python
559+
build_delete_request = BuildDeleteRequest(buildName="<build_name>", deleteAll=True)
560+
art.builds.delete(build_delete_request)
561+
```
562+
556563
#### Rename a build
557564
```python
558565
art.builds.build_rename("<build_name>", "<new_build_name>")

pyartifactory/models/build.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
from typing import Dict, List, Optional
77

8-
from pydantic import BaseModel, Field
8+
from pydantic import BaseModel, Field, model_serializer, model_validator
9+
from pydantic_core import PydanticCustomError
10+
from typing_extensions import Self
911

1012

1113
class BuildProperties(BaseModel):
@@ -127,10 +129,29 @@ class BuildPromotionRequest(BaseModel):
127129
class BuildDeleteRequest(BaseModel):
128130
project: str = ""
129131
buildName: str
130-
buildNumbers: List[str]
132+
buildNumbers: List[str] = []
131133
deleteArtifacts: bool = False
132134
deleteAll: bool = False
133135

136+
@model_validator(mode="after")
137+
def check_numbers_or_delete_all(self) -> Self:
138+
if self.buildNumbers or self.deleteAll:
139+
return self
140+
raise PydanticCustomError("request_schema_error", "Either buildNumbers or deleteAll must be specified", None)
141+
142+
@model_serializer()
143+
def serialize_model(self):
144+
d = {
145+
"project": self.project,
146+
"buildName": self.buildName,
147+
"buildNumbers": self.buildNumbers,
148+
"deleteArtifacts": self.deleteArtifacts,
149+
"deleteAll": self.deleteAll,
150+
}
151+
if self.deleteAll:
152+
d.pop("buildNumbers")
153+
return d
154+
134155

135156
class BuildDiffResponseDetail(BaseModel):
136157
updated: List[str] = []

pyartifactory/objects/build.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,19 @@ def delete(self, delete_build: BuildDeleteRequest) -> None:
120120
:return: None
121121
"""
122122
try:
123-
for _build_number in delete_build.buildNumbers:
124-
self._get(
125-
f"api/{self._uri}/{delete_build.buildName}/{_build_number}",
126-
)
127-
# all build numbers exist
123+
if delete_build.buildNumbers:
124+
for _build_number in delete_build.buildNumbers:
125+
self._get(
126+
f"api/{self._uri}/{delete_build.buildName}/{_build_number}",
127+
)
128+
# all build numbers exist
129+
128130
self._post(f"api/{self._uri}/delete", json=delete_build.model_dump())
129-
logger.debug("Builds %s deleted from %s", ",".join(delete_build.buildNumbers), delete_build.buildName)
131+
132+
if delete_build.buildNumbers:
133+
logger.debug("Builds %s deleted from %s", ",".join(delete_build.buildNumbers), delete_build.buildName)
134+
else:
135+
logger.debug("Deleted all builds of %s", delete_build.buildName)
130136
except requests.exceptions.HTTPError as error:
131137
self._raise_exception(error)
132138

tests/test_build.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
BUILD_DIFF = BuildDiffResponse()
3131
BUILD_PROMOTION_REQUEST = BuildPromotionRequest(sourceRepo="repo-abc", targetRepo="repo-def")
3232
BUILD_PROMOTION_RESPONSE = BuildPromotionResult()
33-
BUILD_DELETE_REQUEST = BuildDeleteRequest(buildName="build", buildNumbers=["abc", "123"])
33+
BUILD_DELETE_BUILDNUMBERS_REQUEST = BuildDeleteRequest(buildName="build", buildNumbers=["abc", "123"])
3434
BUILD_DELETE_ERROR = BuildError(errors=[{"status": 404, "message": "Not found"}])
3535
BUILD_CREATE_REQUEST = BuildCreateRequest(name="a-build", number="build-xx", started="2014-09-30T12:00:19.893+0300")
3636

@@ -170,41 +170,58 @@ def test_list_build(mocker):
170170

171171

172172
@responses.activate
173-
def test_delete_build_success(mocker):
174-
for _build_number in BUILD_DELETE_REQUEST.buildNumbers:
173+
def test_delete_buildnumbers_success(mocker):
174+
for _build_number in BUILD_DELETE_BUILDNUMBERS_REQUEST.buildNumbers:
175175
responses.add(
176176
responses.GET,
177-
f"{URL}/api/build/{BUILD_DELETE_REQUEST.buildName}/{_build_number}",
177+
f"{URL}/api/build/{BUILD_DELETE_BUILDNUMBERS_REQUEST.buildName}/{_build_number}",
178178
json=BUILD_INFO.model_dump(),
179179
status=200,
180180
)
181181

182-
responses.add(responses.POST, f"{URL}/api/build/delete", json=BUILD_DELETE_REQUEST.model_dump(), status=200)
182+
responses.add(
183+
responses.POST,
184+
f"{URL}/api/build/delete",
185+
json=BUILD_DELETE_BUILDNUMBERS_REQUEST.model_dump(),
186+
status=200,
187+
)
188+
189+
artifactory_build = ArtifactoryBuild(AuthModel(url=URL, auth=AUTH))
190+
mocker.spy(artifactory_build, "delete")
191+
artifactory_build.delete(BUILD_DELETE_BUILDNUMBERS_REQUEST)
192+
193+
194+
@responses.activate
195+
def test_delete_all_success(mocker):
196+
delete_all_request = BuildDeleteRequest(buildName="build", deleteAll=True)
197+
assert "buildNumbers" not in delete_all_request.model_dump()
198+
199+
responses.add(responses.POST, f"{URL}/api/build/delete", json=delete_all_request.model_dump(), status=200)
183200

184201
artifactory_build = ArtifactoryBuild(AuthModel(url=URL, auth=AUTH))
185202
mocker.spy(artifactory_build, "delete")
186-
artifactory_build.delete(BUILD_DELETE_REQUEST)
203+
artifactory_build.delete(delete_all_request)
187204

188205

189206
@responses.activate
190207
def test_delete_build_error_not_exist(mocker):
191208
responses.add(
192209
responses.GET,
193-
f"{URL}/api/build/{BUILD_DELETE_REQUEST.buildName}/{BUILD_DELETE_REQUEST.buildNumbers[0]}",
210+
f"{URL}/api/build/{BUILD_DELETE_BUILDNUMBERS_REQUEST.buildName}/{BUILD_DELETE_BUILDNUMBERS_REQUEST.buildNumbers[0]}",
194211
json=BUILD_INFO.model_dump(),
195212
status=200,
196213
)
197214
responses.add(
198215
responses.GET,
199-
f"{URL}/api/build/{BUILD_DELETE_REQUEST.buildName}/{BUILD_DELETE_REQUEST.buildNumbers[-1]}",
216+
f"{URL}/api/build/{BUILD_DELETE_BUILDNUMBERS_REQUEST.buildName}/{BUILD_DELETE_BUILDNUMBERS_REQUEST.buildNumbers[-1]}",
200217
body=NOT_FOUND_EXCEPTION_BODY,
201218
status=200,
202219
)
203220

204221
artifactory_build = ArtifactoryBuild(AuthModel(url=URL, auth=AUTH))
205222
mocker.spy(artifactory_build, "delete")
206223
with pytest.raises(ArtifactoryError):
207-
artifactory_build.delete(BUILD_DELETE_REQUEST)
224+
artifactory_build.delete(BUILD_DELETE_BUILDNUMBERS_REQUEST)
208225

209226

210227
@responses.activate

0 commit comments

Comments
 (0)