Skip to content

Commit 0f42e32

Browse files
authored
Merge pull request #1160 from python-gitlab/feat/packages-api
Feat: Add support for packages API
2 parents a038e95 + a47dfcd commit 0f42e32

File tree

14 files changed

+322
-11
lines changed

14 files changed

+322
-11
lines changed

docs/api-objects.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ API examples
3232
gl_objects/milestones
3333
gl_objects/namespaces
3434
gl_objects/notes
35+
gl_objects/packages
3536
gl_objects/pagesdomains
3637
gl_objects/pipelines_and_jobs
3738
gl_objects/projects

docs/cli.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,30 @@ List deploy tokens for a group:
235235
236236
$ gitlab -v group-deploy-token list --group-id 3
237237
238+
List packages for a project:
239+
240+
.. code-block:: console
241+
242+
$ gitlab -v project-package list --project-id 3
243+
244+
List packages for a group:
245+
246+
.. code-block:: console
247+
248+
$ gitlab -v group-package list --group-id 3
249+
250+
Get a specific project package by id:
251+
252+
.. code-block:: console
253+
254+
$ gitlab -v project-package get --id 1 --project-id 3
255+
256+
Delete a specific project package by id:
257+
258+
.. code-block:: console
259+
260+
$ gitlab -v project-package delete --id 1 --project-id 3
261+
238262
Get a list of snippets for this project:
239263

240264
.. code-block:: console

docs/gl_objects/packages.rst

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#######
2+
Packages
3+
#######
4+
5+
Packages allow you to utilize GitLab as a private repository for a variety
6+
of common package managers.
7+
8+
Project Packages
9+
=====================
10+
11+
Reference
12+
---------
13+
14+
* v4 API:
15+
16+
+ :class:`gitlab.v4.objects.ProjectPackage`
17+
+ :class:`gitlab.v4.objects.ProjectPackageManager`
18+
+ :attr:`gitlab.v4.objects.Project.packages`
19+
20+
* GitLab API: https://docs.gitlab.com/ee/api/packages.html#within-a-project
21+
22+
Examples
23+
--------
24+
25+
List the packages in a project::
26+
27+
packages = project.packages.list()
28+
29+
Filter the results by ``package_type`` or ``package_name`` ::
30+
31+
packages = project.packages.list(package_type='pypi')
32+
33+
Get a specific package of a project by id::
34+
35+
package = project.packages.get(1)
36+
37+
Delete a package from a project::
38+
39+
package.delete()
40+
# or
41+
project.packages.delete(package.id)
42+
43+
44+
Group Packages
45+
===================
46+
47+
Reference
48+
---------
49+
50+
* v4 API:
51+
52+
+ :class:`gitlab.v4.objects.GroupPackage`
53+
+ :class:`gitlab.v4.objects.GroupPackageManager`
54+
+ :attr:`gitlab.v4.objects.Group.packages`
55+
56+
* GitLab API: https://docs.gitlab.com/ee/api/packages.html#within-a-group
57+
58+
Examples
59+
--------
60+
61+
List the packages in a group::
62+
63+
packages = group.packages.list()
64+
65+
Filter the results by ``package_type`` or ``package_name`` ::
66+
67+
packages = group.packages.list(package_type='pypi')
68+

gitlab/tests/objects/conftest.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ def created_content():
2121
return {"message": "201 Created"}
2222

2323

24+
@pytest.fixture
25+
def no_content():
26+
return {"message": "204 No Content"}
27+
28+
2429
@pytest.fixture
2530
def resp_export(accepted_content, binary_content):
2631
"""Common fixture for group and project exports."""

gitlab/tests/objects/test_commits.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,13 @@ def test_create_commit(project, resp_create_commit):
8888
data = {
8989
"branch": "master",
9090
"commit_message": "Commit message",
91-
"actions": [{"action": "create", "file_path": "README", "content": "",}],
91+
"actions": [
92+
{
93+
"action": "create",
94+
"file_path": "README",
95+
"content": "",
96+
}
97+
],
9298
}
9399
commit = project.commits.create(data)
94100
assert commit.short_id == "ed899a2f"

gitlab/tests/objects/test_packages.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
"""
2+
GitLab API: https://docs.gitlab.com/ce/api/packages.html
3+
"""
4+
import re
5+
6+
import pytest
7+
import responses
8+
9+
from gitlab.v4.objects import GroupPackage, ProjectPackage
10+
11+
12+
package_content = {
13+
"id": 1,
14+
"name": "com/mycompany/my-app",
15+
"version": "1.0-SNAPSHOT",
16+
"package_type": "maven",
17+
"_links": {
18+
"web_path": "/namespace1/project1/-/packages/1",
19+
"delete_api_path": "/namespace1/project1/-/packages/1",
20+
},
21+
"created_at": "2019-11-27T03:37:38.711Z",
22+
"pipeline": {
23+
"id": 123,
24+
"status": "pending",
25+
"ref": "new-pipeline",
26+
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
27+
"web_url": "https://example.com/foo/bar/pipelines/47",
28+
"created_at": "2016-08-11T11:28:34.085Z",
29+
"updated_at": "2016-08-11T11:32:35.169Z",
30+
"user": {
31+
"name": "Administrator",
32+
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
33+
},
34+
},
35+
"versions": [
36+
{
37+
"id": 2,
38+
"version": "2.0-SNAPSHOT",
39+
"created_at": "2020-04-28T04:42:11.573Z",
40+
"pipeline": {
41+
"id": 234,
42+
"status": "pending",
43+
"ref": "new-pipeline",
44+
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
45+
"web_url": "https://example.com/foo/bar/pipelines/58",
46+
"created_at": "2016-08-11T11:28:34.085Z",
47+
"updated_at": "2016-08-11T11:32:35.169Z",
48+
"user": {
49+
"name": "Administrator",
50+
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
51+
},
52+
},
53+
}
54+
],
55+
}
56+
57+
58+
@pytest.fixture
59+
def resp_list_packages():
60+
with responses.RequestsMock() as rsps:
61+
rsps.add(
62+
method=responses.GET,
63+
url=re.compile(r"http://localhost/api/v4/(groups|projects)/1/packages"),
64+
json=[package_content],
65+
content_type="application/json",
66+
status=200,
67+
)
68+
yield rsps
69+
70+
71+
@pytest.fixture
72+
def resp_get_package():
73+
with responses.RequestsMock() as rsps:
74+
rsps.add(
75+
method=responses.GET,
76+
url="http://localhost/api/v4/projects/1/packages/1",
77+
json=package_content,
78+
content_type="application/json",
79+
status=200,
80+
)
81+
yield rsps
82+
83+
84+
@pytest.fixture
85+
def resp_delete_package(no_content):
86+
with responses.RequestsMock() as rsps:
87+
rsps.add(
88+
method=responses.DELETE,
89+
url="http://localhost/api/v4/projects/1/packages/1",
90+
json=no_content,
91+
content_type="application/json",
92+
status=204,
93+
)
94+
yield rsps
95+
96+
97+
def test_list_project_packages(project, resp_list_packages):
98+
packages = project.packages.list()
99+
assert isinstance(packages, list)
100+
assert isinstance(packages[0], ProjectPackage)
101+
assert packages[0].version == "1.0-SNAPSHOT"
102+
103+
104+
def test_list_group_packages(group, resp_list_packages):
105+
packages = group.packages.list()
106+
assert isinstance(packages, list)
107+
assert isinstance(packages[0], GroupPackage)
108+
assert packages[0].version == "1.0-SNAPSHOT"
109+
110+
111+
def test_get_project_package(project, resp_get_package):
112+
package = project.packages.get(1)
113+
assert isinstance(package, ProjectPackage)
114+
assert package.version == "1.0-SNAPSHOT"
115+
116+
117+
def test_delete_project_package(project, resp_delete_package):
118+
package = project.packages.get(1, lazy=True)
119+
package.delete()

gitlab/tests/objects/test_runners.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,9 @@ def resp_runner_delete():
167167
status=200,
168168
)
169169
rsps.add(
170-
method=responses.DELETE, url=pattern, status=204,
170+
method=responses.DELETE,
171+
url=pattern,
172+
status=204,
171173
)
172174
yield rsps
173175

@@ -177,7 +179,9 @@ def resp_runner_disable():
177179
with responses.RequestsMock() as rsps:
178180
pattern = re.compile(r".*?/(groups|projects)/1/runners/6")
179181
rsps.add(
180-
method=responses.DELETE, url=pattern, status=204,
182+
method=responses.DELETE,
183+
url=pattern,
184+
status=204,
181185
)
182186
yield rsps
183187

@@ -187,7 +191,9 @@ def resp_runner_verify():
187191
with responses.RequestsMock() as rsps:
188192
pattern = re.compile(r".*?/runners/verify")
189193
rsps.add(
190-
method=responses.POST, url=pattern, status=200,
194+
method=responses.POST,
195+
url=pattern,
196+
status=200,
191197
)
192198
yield rsps
193199

gitlab/v4/objects.py

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -711,8 +711,14 @@ class ProjectDeployTokenManager(ListMixin, CreateMixin, DeleteMixin, RESTManager
711711
_from_parent_attrs = {"project_id": "id"}
712712
_obj_cls = ProjectDeployToken
713713
_create_attrs = (
714-
("name", "scopes",),
715-
("expires_at", "username",),
714+
(
715+
"name",
716+
"scopes",
717+
),
718+
(
719+
"expires_at",
720+
"username",
721+
),
716722
)
717723

718724

@@ -725,8 +731,14 @@ class GroupDeployTokenManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
725731
_from_parent_attrs = {"group_id": "id"}
726732
_obj_cls = GroupDeployToken
727733
_create_attrs = (
728-
("name", "scopes",),
729-
("expires_at", "username",),
734+
(
735+
"name",
736+
"scopes",
737+
),
738+
(
739+
"expires_at",
740+
"username",
741+
),
730742
)
731743

732744

@@ -1279,6 +1291,23 @@ class GroupNotificationSettingsManager(NotificationSettingsManager):
12791291
_from_parent_attrs = {"group_id": "id"}
12801292

12811293

1294+
class GroupPackage(RESTObject):
1295+
pass
1296+
1297+
1298+
class GroupPackageManager(ListMixin, RESTManager):
1299+
_path = "/groups/%(group_id)s/packages"
1300+
_obj_cls = GroupPackage
1301+
_from_parent_attrs = {"group_id": "id"}
1302+
_list_filters = (
1303+
"exclude_subgroups",
1304+
"order_by",
1305+
"sort",
1306+
"package_type",
1307+
"package_name",
1308+
)
1309+
1310+
12821311
class GroupProject(RESTObject):
12831312
pass
12841313

@@ -1365,6 +1394,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
13651394
("mergerequests", "GroupMergeRequestManager"),
13661395
("milestones", "GroupMilestoneManager"),
13671396
("notificationsettings", "GroupNotificationSettingsManager"),
1397+
("packages", "GroupPackageManager"),
13681398
("projects", "GroupProjectManager"),
13691399
("runners", "GroupRunnerManager"),
13701400
("subgroups", "GroupSubgroupManager"),
@@ -2840,6 +2870,22 @@ class ProjectNotificationSettingsManager(NotificationSettingsManager):
28402870
_from_parent_attrs = {"project_id": "id"}
28412871

28422872

2873+
class ProjectPackage(ObjectDeleteMixin, RESTObject):
2874+
pass
2875+
2876+
2877+
class ProjectPackageManager(ListMixin, GetMixin, DeleteMixin, RESTManager):
2878+
_path = "/projects/%(project_id)s/packages"
2879+
_obj_cls = ProjectPackage
2880+
_from_parent_attrs = {"project_id": "id"}
2881+
_list_filters = (
2882+
"order_by",
2883+
"sort",
2884+
"package_type",
2885+
"package_name",
2886+
)
2887+
2888+
28432889
class ProjectPagesDomain(SaveMixin, ObjectDeleteMixin, RESTObject):
28442890
_id_attr = "domain"
28452891

@@ -4181,7 +4227,11 @@ class ProjectServiceManager(GetMixin, UpdateMixin, DeleteMixin, ListMixin, RESTM
41814227
),
41824228
),
41834229
"jira": (
4184-
("url", "username", "password",),
4230+
(
4231+
"url",
4232+
"username",
4233+
"password",
4234+
),
41854235
(
41864236
"api_url",
41874237
"active",
@@ -4532,6 +4582,7 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
45324582
("milestones", "ProjectMilestoneManager"),
45334583
("notes", "ProjectNoteManager"),
45344584
("notificationsettings", "ProjectNotificationSettingsManager"),
4585+
("packages", "ProjectPackageManager"),
45354586
("pagesdomains", "ProjectPagesDomainManager"),
45364587
("pipelines", "ProjectPipelineManager"),
45374588
("protectedbranches", "ProjectProtectedBranchManager"),

0 commit comments

Comments
 (0)