summaryrefslogtreecommitdiff
path: root/gitlab
diff options
context:
space:
mode:
Diffstat (limited to 'gitlab')
-rw-r--r--gitlab/tests/objects/conftest.py5
-rw-r--r--gitlab/tests/objects/test_job_artifacts.py31
-rw-r--r--gitlab/tests/objects/test_packages.py119
-rw-r--r--gitlab/v4/objects.py69
4 files changed, 224 insertions, 0 deletions
diff --git a/gitlab/tests/objects/conftest.py b/gitlab/tests/objects/conftest.py
index 76f76d1..d8a40d9 100644
--- a/gitlab/tests/objects/conftest.py
+++ b/gitlab/tests/objects/conftest.py
@@ -22,6 +22,11 @@ def created_content():
@pytest.fixture
+def no_content():
+ return {"message": "204 No Content"}
+
+
+@pytest.fixture
def resp_export(accepted_content, binary_content):
"""Common fixture for group and project exports."""
export_status_content = {
diff --git a/gitlab/tests/objects/test_job_artifacts.py b/gitlab/tests/objects/test_job_artifacts.py
new file mode 100644
index 0000000..c441b4b
--- /dev/null
+++ b/gitlab/tests/objects/test_job_artifacts.py
@@ -0,0 +1,31 @@
+"""
+GitLab API: https://docs.gitlab.com/ee/api/job_artifacts.html
+"""
+
+import pytest
+import responses
+
+
+ref_name = "master"
+job = "build"
+
+
+@pytest.fixture
+def resp_artifacts_by_ref_name(binary_content):
+ url = f"http://localhost/api/v4/projects/1/jobs/artifacts/{ref_name}/download?job={job}"
+
+ with responses.RequestsMock() as rsps:
+ rsps.add(
+ method=responses.GET,
+ url=url,
+ body=binary_content,
+ content_type="application/octet-stream",
+ status=200,
+ )
+ yield rsps
+
+
+def test_download_artifacts_by_ref_name(gl, binary_content, resp_artifacts_by_ref_name):
+ project = gl.projects.get(1, lazy=True)
+ artifacts = project.artifacts(ref_name=ref_name, job=job)
+ assert artifacts == binary_content
diff --git a/gitlab/tests/objects/test_packages.py b/gitlab/tests/objects/test_packages.py
new file mode 100644
index 0000000..d4d97ff
--- /dev/null
+++ b/gitlab/tests/objects/test_packages.py
@@ -0,0 +1,119 @@
+"""
+GitLab API: https://docs.gitlab.com/ce/api/packages.html
+"""
+import re
+
+import pytest
+import responses
+
+from gitlab.v4.objects import GroupPackage, ProjectPackage
+
+
+package_content = {
+ "id": 1,
+ "name": "com/mycompany/my-app",
+ "version": "1.0-SNAPSHOT",
+ "package_type": "maven",
+ "_links": {
+ "web_path": "/namespace1/project1/-/packages/1",
+ "delete_api_path": "/namespace1/project1/-/packages/1",
+ },
+ "created_at": "2019-11-27T03:37:38.711Z",
+ "pipeline": {
+ "id": 123,
+ "status": "pending",
+ "ref": "new-pipeline",
+ "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
+ "web_url": "https://example.com/foo/bar/pipelines/47",
+ "created_at": "2016-08-11T11:28:34.085Z",
+ "updated_at": "2016-08-11T11:32:35.169Z",
+ "user": {
+ "name": "Administrator",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ },
+ },
+ "versions": [
+ {
+ "id": 2,
+ "version": "2.0-SNAPSHOT",
+ "created_at": "2020-04-28T04:42:11.573Z",
+ "pipeline": {
+ "id": 234,
+ "status": "pending",
+ "ref": "new-pipeline",
+ "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
+ "web_url": "https://example.com/foo/bar/pipelines/58",
+ "created_at": "2016-08-11T11:28:34.085Z",
+ "updated_at": "2016-08-11T11:32:35.169Z",
+ "user": {
+ "name": "Administrator",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ },
+ },
+ }
+ ],
+}
+
+
+@pytest.fixture
+def resp_list_packages():
+ with responses.RequestsMock() as rsps:
+ rsps.add(
+ method=responses.GET,
+ url=re.compile(r"http://localhost/api/v4/(groups|projects)/1/packages"),
+ json=[package_content],
+ content_type="application/json",
+ status=200,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_get_package():
+ with responses.RequestsMock() as rsps:
+ rsps.add(
+ method=responses.GET,
+ url="http://localhost/api/v4/projects/1/packages/1",
+ json=package_content,
+ content_type="application/json",
+ status=200,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_delete_package(no_content):
+ with responses.RequestsMock() as rsps:
+ rsps.add(
+ method=responses.DELETE,
+ url="http://localhost/api/v4/projects/1/packages/1",
+ json=no_content,
+ content_type="application/json",
+ status=204,
+ )
+ yield rsps
+
+
+def test_list_project_packages(project, resp_list_packages):
+ packages = project.packages.list()
+ assert isinstance(packages, list)
+ assert isinstance(packages[0], ProjectPackage)
+ assert packages[0].version == "1.0-SNAPSHOT"
+
+
+def test_list_group_packages(group, resp_list_packages):
+ packages = group.packages.list()
+ assert isinstance(packages, list)
+ assert isinstance(packages[0], GroupPackage)
+ assert packages[0].version == "1.0-SNAPSHOT"
+
+
+def test_get_project_package(project, resp_get_package):
+ package = project.packages.get(1)
+ assert isinstance(package, ProjectPackage)
+ assert package.version == "1.0-SNAPSHOT"
+
+
+def test_delete_project_package(project, resp_delete_package):
+ package = project.packages.get(1, lazy=True)
+ package.delete()
diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py
index e515ea1..e7d7d23 100644
--- a/gitlab/v4/objects.py
+++ b/gitlab/v4/objects.py
@@ -1291,6 +1291,23 @@ class GroupNotificationSettingsManager(NotificationSettingsManager):
_from_parent_attrs = {"group_id": "id"}
+class GroupPackage(RESTObject):
+ pass
+
+
+class GroupPackageManager(ListMixin, RESTManager):
+ _path = "/groups/%(group_id)s/packages"
+ _obj_cls = GroupPackage
+ _from_parent_attrs = {"group_id": "id"}
+ _list_filters = (
+ "exclude_subgroups",
+ "order_by",
+ "sort",
+ "package_type",
+ "package_name",
+ )
+
+
class GroupProject(RESTObject):
pass
@@ -1377,6 +1394,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
("mergerequests", "GroupMergeRequestManager"),
("milestones", "GroupMilestoneManager"),
("notificationsettings", "GroupNotificationSettingsManager"),
+ ("packages", "GroupPackageManager"),
("projects", "GroupProjectManager"),
("runners", "GroupRunnerManager"),
("subgroups", "GroupSubgroupManager"),
@@ -2863,6 +2881,22 @@ class ProjectNotificationSettingsManager(NotificationSettingsManager):
_from_parent_attrs = {"project_id": "id"}
+class ProjectPackage(ObjectDeleteMixin, RESTObject):
+ pass
+
+
+class ProjectPackageManager(ListMixin, GetMixin, DeleteMixin, RESTManager):
+ _path = "/projects/%(project_id)s/packages"
+ _obj_cls = ProjectPackage
+ _from_parent_attrs = {"project_id": "id"}
+ _list_filters = (
+ "order_by",
+ "sort",
+ "package_type",
+ "package_name",
+ )
+
+
class ProjectPagesDomain(SaveMixin, ObjectDeleteMixin, RESTObject):
_id_attr = "domain"
@@ -4572,6 +4606,7 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
("milestones", "ProjectMilestoneManager"),
("notes", "ProjectNoteManager"),
("notificationsettings", "ProjectNotificationSettingsManager"),
+ ("packages", "ProjectPackageManager"),
("pagesdomains", "ProjectPagesDomainManager"),
("pipelines", "ProjectPipelineManager"),
("protectedbranches", "ProjectProtectedBranchManager"),
@@ -5111,6 +5146,40 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
path, post_data={"namespace": to_namespace}, **kwargs
)
+ @cli.register_custom_action("Project", ("ref_name", "job"), ("job_token",))
+ @exc.on_http_error(exc.GitlabGetError)
+ def artifacts(
+ self, ref_name, job, streamed=False, action=None, chunk_size=1024, **kwargs
+ ):
+ """Get the job artifacts archive from a specific tag or branch.
+
+ Args:
+ ref_name (str): Branch or tag name in repository. HEAD or SHA references
+ are not supported.
+ artifact_path (str): Path to a file inside the artifacts archive.
+ job (str): The name of the job.
+ job_token (str): Job token for multi-project pipeline triggers.
+ streamed (bool): If True the data will be processed by chunks of
+ `chunk_size` and each chunk is passed to `action` for
+ treatment
+ action (callable): Callable responsible of dealing with chunk of
+ data
+ chunk_size (int): Size of each chunk
+ **kwargs: Extra options to send to the server (e.g. sudo)
+
+ Raises:
+ GitlabAuthenticationError: If authentication is not correct
+ GitlabGetError: If the artifacts could not be retrieved
+
+ Returns:
+ str: The artifacts if `streamed` is False, None otherwise.
+ """
+ path = "/projects/%s/jobs/artifacts/%s/download" % (self.get_id(), ref_name)
+ result = self.manager.gitlab.http_get(
+ path, job=job, streamed=streamed, raw=True, **kwargs
+ )
+ return utils.response_content(result, streamed, action, chunk_size)
+
@cli.register_custom_action("Project", ("ref_name", "artifact_path", "job"))
@exc.on_http_error(exc.GitlabGetError)
def artifact(