summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Wittig <max.wittig@siemens.com>2020-08-29 12:07:18 +0200
committerGitHub <noreply@github.com>2020-08-29 12:07:18 +0200
commit0f42e32cb756766735c7e277f099030f6b3d8fc7 (patch)
treea11d8c7f96b9dd5689520f1c8d25aa78bbfc54ef
parenta038e9567fd16259e3ed360ab0defd779e9c3901 (diff)
parenta47dfcd9ded3a0467e83396f21e6dcfa232dfdd7 (diff)
downloadgitlab-0f42e32cb756766735c7e277f099030f6b3d8fc7.tar.gz
Merge pull request #1160 from python-gitlab/feat/packages-api
Feat: Add support for packages API
-rw-r--r--docs/api-objects.rst1
-rw-r--r--docs/cli.rst24
-rw-r--r--docs/gl_objects/packages.rst68
-rw-r--r--gitlab/tests/objects/conftest.py5
-rw-r--r--gitlab/tests/objects/test_commits.py8
-rw-r--r--gitlab/tests/objects/test_packages.py119
-rw-r--r--gitlab/tests/objects/test_runners.py12
-rw-r--r--gitlab/v4/objects.py61
-rw-r--r--tools/functional/api/test_packages.py13
-rw-r--r--tools/functional/cli/test_cli_packages.py12
-rw-r--r--tools/functional/cli/test_cli_v4.py (renamed from tools/functional/test_cli_v4.py)0
-rwxr-xr-xtools/functional_tests.sh2
-rwxr-xr-xtools/py_functional_tests.sh1
-rw-r--r--tools/python_test_v4.py7
14 files changed, 322 insertions, 11 deletions
diff --git a/docs/api-objects.rst b/docs/api-objects.rst
index 32852f8..5d59497 100644
--- a/docs/api-objects.rst
+++ b/docs/api-objects.rst
@@ -32,6 +32,7 @@ API examples
gl_objects/milestones
gl_objects/namespaces
gl_objects/notes
+ gl_objects/packages
gl_objects/pagesdomains
gl_objects/pipelines_and_jobs
gl_objects/projects
diff --git a/docs/cli.rst b/docs/cli.rst
index da5a89e..95f7062 100644
--- a/docs/cli.rst
+++ b/docs/cli.rst
@@ -235,6 +235,30 @@ List deploy tokens for a group:
$ gitlab -v group-deploy-token list --group-id 3
+List packages for a project:
+
+.. code-block:: console
+
+ $ gitlab -v project-package list --project-id 3
+
+List packages for a group:
+
+.. code-block:: console
+
+ $ gitlab -v group-package list --group-id 3
+
+Get a specific project package by id:
+
+.. code-block:: console
+
+ $ gitlab -v project-package get --id 1 --project-id 3
+
+Delete a specific project package by id:
+
+.. code-block:: console
+
+ $ gitlab -v project-package delete --id 1 --project-id 3
+
Get a list of snippets for this project:
.. code-block:: console
diff --git a/docs/gl_objects/packages.rst b/docs/gl_objects/packages.rst
new file mode 100644
index 0000000..3c1782b
--- /dev/null
+++ b/docs/gl_objects/packages.rst
@@ -0,0 +1,68 @@
+#######
+Packages
+#######
+
+Packages allow you to utilize GitLab as a private repository for a variety
+of common package managers.
+
+Project Packages
+=====================
+
+Reference
+---------
+
+* v4 API:
+
+ + :class:`gitlab.v4.objects.ProjectPackage`
+ + :class:`gitlab.v4.objects.ProjectPackageManager`
+ + :attr:`gitlab.v4.objects.Project.packages`
+
+* GitLab API: https://docs.gitlab.com/ee/api/packages.html#within-a-project
+
+Examples
+--------
+
+List the packages in a project::
+
+ packages = project.packages.list()
+
+Filter the results by ``package_type`` or ``package_name`` ::
+
+ packages = project.packages.list(package_type='pypi')
+
+Get a specific package of a project by id::
+
+ package = project.packages.get(1)
+
+Delete a package from a project::
+
+ package.delete()
+ # or
+ project.packages.delete(package.id)
+
+
+Group Packages
+===================
+
+Reference
+---------
+
+* v4 API:
+
+ + :class:`gitlab.v4.objects.GroupPackage`
+ + :class:`gitlab.v4.objects.GroupPackageManager`
+ + :attr:`gitlab.v4.objects.Group.packages`
+
+* GitLab API: https://docs.gitlab.com/ee/api/packages.html#within-a-group
+
+Examples
+--------
+
+List the packages in a group::
+
+ packages = group.packages.list()
+
+Filter the results by ``package_type`` or ``package_name`` ::
+
+ packages = group.packages.list(package_type='pypi')
+
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_commits.py b/gitlab/tests/objects/test_commits.py
index 9d11508..6b98117 100644
--- a/gitlab/tests/objects/test_commits.py
+++ b/gitlab/tests/objects/test_commits.py
@@ -88,7 +88,13 @@ def test_create_commit(project, resp_create_commit):
data = {
"branch": "master",
"commit_message": "Commit message",
- "actions": [{"action": "create", "file_path": "README", "content": "",}],
+ "actions": [
+ {
+ "action": "create",
+ "file_path": "README",
+ "content": "",
+ }
+ ],
}
commit = project.commits.create(data)
assert commit.short_id == "ed899a2f"
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/tests/objects/test_runners.py b/gitlab/tests/objects/test_runners.py
index 490ba36..30fdb41 100644
--- a/gitlab/tests/objects/test_runners.py
+++ b/gitlab/tests/objects/test_runners.py
@@ -167,7 +167,9 @@ def resp_runner_delete():
status=200,
)
rsps.add(
- method=responses.DELETE, url=pattern, status=204,
+ method=responses.DELETE,
+ url=pattern,
+ status=204,
)
yield rsps
@@ -177,7 +179,9 @@ def resp_runner_disable():
with responses.RequestsMock() as rsps:
pattern = re.compile(r".*?/(groups|projects)/1/runners/6")
rsps.add(
- method=responses.DELETE, url=pattern, status=204,
+ method=responses.DELETE,
+ url=pattern,
+ status=204,
)
yield rsps
@@ -187,7 +191,9 @@ def resp_runner_verify():
with responses.RequestsMock() as rsps:
pattern = re.compile(r".*?/runners/verify")
rsps.add(
- method=responses.POST, url=pattern, status=200,
+ method=responses.POST,
+ url=pattern,
+ status=200,
)
yield rsps
diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py
index 2a3615f..84fb5c3 100644
--- a/gitlab/v4/objects.py
+++ b/gitlab/v4/objects.py
@@ -711,8 +711,14 @@ class ProjectDeployTokenManager(ListMixin, CreateMixin, DeleteMixin, RESTManager
_from_parent_attrs = {"project_id": "id"}
_obj_cls = ProjectDeployToken
_create_attrs = (
- ("name", "scopes",),
- ("expires_at", "username",),
+ (
+ "name",
+ "scopes",
+ ),
+ (
+ "expires_at",
+ "username",
+ ),
)
@@ -725,8 +731,14 @@ class GroupDeployTokenManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
_from_parent_attrs = {"group_id": "id"}
_obj_cls = GroupDeployToken
_create_attrs = (
- ("name", "scopes",),
- ("expires_at", "username",),
+ (
+ "name",
+ "scopes",
+ ),
+ (
+ "expires_at",
+ "username",
+ ),
)
@@ -1279,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
@@ -1365,6 +1394,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
("mergerequests", "GroupMergeRequestManager"),
("milestones", "GroupMilestoneManager"),
("notificationsettings", "GroupNotificationSettingsManager"),
+ ("packages", "GroupPackageManager"),
("projects", "GroupProjectManager"),
("runners", "GroupRunnerManager"),
("subgroups", "GroupSubgroupManager"),
@@ -2840,6 +2870,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"
@@ -4181,7 +4227,11 @@ class ProjectServiceManager(GetMixin, UpdateMixin, DeleteMixin, ListMixin, RESTM
),
),
"jira": (
- ("url", "username", "password",),
+ (
+ "url",
+ "username",
+ "password",
+ ),
(
"api_url",
"active",
@@ -4532,6 +4582,7 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
("milestones", "ProjectMilestoneManager"),
("notes", "ProjectNoteManager"),
("notificationsettings", "ProjectNotificationSettingsManager"),
+ ("packages", "ProjectPackageManager"),
("pagesdomains", "ProjectPagesDomainManager"),
("pipelines", "ProjectPipelineManager"),
("protectedbranches", "ProjectProtectedBranchManager"),
diff --git a/tools/functional/api/test_packages.py b/tools/functional/api/test_packages.py
new file mode 100644
index 0000000..9160a68
--- /dev/null
+++ b/tools/functional/api/test_packages.py
@@ -0,0 +1,13 @@
+"""
+GitLab API: https://docs.gitlab.com/ce/api/packages.html
+"""
+
+
+def test_list_project_packages(project):
+ packages = project.packages.list()
+ assert isinstance(packages, list)
+
+
+def test_list_group_packages(group):
+ packages = group.packages.list()
+ assert isinstance(packages, list)
diff --git a/tools/functional/cli/test_cli_packages.py b/tools/functional/cli/test_cli_packages.py
new file mode 100644
index 0000000..a3734a2
--- /dev/null
+++ b/tools/functional/cli/test_cli_packages.py
@@ -0,0 +1,12 @@
+def test_list_project_packages(gitlab_cli, project):
+ cmd = ["project-package", "list", "--project-id", project.id]
+ ret = gitlab_cli(cmd)
+
+ assert ret.success
+
+
+def test_list_group_packages(gitlab_cli, group):
+ cmd = ["group-package", "list", "--group-id", group.id]
+ ret = gitlab_cli(cmd)
+
+ assert ret.success
diff --git a/tools/functional/test_cli_v4.py b/tools/functional/cli/test_cli_v4.py
index 4f78c0c..4f78c0c 100644
--- a/tools/functional/test_cli_v4.py
+++ b/tools/functional/cli/test_cli_v4.py
diff --git a/tools/functional_tests.sh b/tools/functional_tests.sh
index b86be3a..87907c5 100755
--- a/tools/functional_tests.sh
+++ b/tools/functional_tests.sh
@@ -18,4 +18,4 @@ setenv_script=$(dirname "$0")/build_test_env.sh || exit 1
BUILD_TEST_ENV_AUTO_CLEANUP=true
. "$setenv_script" "$@" || exit 1
-pytest "$(dirname "$0")/functional/test_cli_v4.py"
+pytest "$(dirname "$0")/functional/cli"
diff --git a/tools/py_functional_tests.sh b/tools/py_functional_tests.sh
index 75bb761..1009cb9 100755
--- a/tools/py_functional_tests.sh
+++ b/tools/py_functional_tests.sh
@@ -19,3 +19,4 @@ BUILD_TEST_ENV_AUTO_CLEANUP=true
. "$setenv_script" "$@" || exit 1
try python "$(dirname "$0")"/python_test_v${API_VER}.py
+pytest "$(dirname "$0")/functional/api"
diff --git a/tools/python_test_v4.py b/tools/python_test_v4.py
index 6ecaf24..8c548be 100644
--- a/tools/python_test_v4.py
+++ b/tools/python_test_v4.py
@@ -701,7 +701,12 @@ deploy_token_group = gl.groups.create(
# Remove once https://gitlab.com/gitlab-org/gitlab/-/issues/211878 is fixed
deploy_token = deploy_token_group.deploytokens.create(
- {"name": "foo", "username": "", "expires_at": "", "scopes": ["read_repository"],}
+ {
+ "name": "foo",
+ "username": "",
+ "expires_at": "",
+ "scopes": ["read_repository"],
+ }
)
assert len(deploy_token_group.deploytokens.list()) == 1