From d2997530bc3355048143bc29580ef32fc21dac3d Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Fri, 28 Aug 2020 21:40:04 +0200 Subject: [PATCH 1/5] chore: make latest black happy with existing code --- gitlab/tests/objects/test_commits.py | 8 +++++++- gitlab/tests/objects/test_runners.py | 12 +++++++++--- gitlab/v4/objects.py | 26 +++++++++++++++++++++----- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/gitlab/tests/objects/test_commits.py b/gitlab/tests/objects/test_commits.py index 9d11508c6..6b9811700 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_runners.py b/gitlab/tests/objects/test_runners.py index 490ba36a0..30fdb41b5 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 2a3615fa3..37c33e2ef 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", + ), ) @@ -4181,7 +4193,11 @@ class ProjectServiceManager(GetMixin, UpdateMixin, DeleteMixin, ListMixin, RESTM ), ), "jira": ( - ("url", "username", "password",), + ( + "url", + "username", + "password", + ), ( "api_url", "active", From f245ffbfad6f1d1f66d386a4b00b3a6ff3e74daa Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Fri, 28 Aug 2020 22:49:58 +0200 Subject: [PATCH 2/5] chore: update tools dir for latest black version --- tools/python_test_v4.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/python_test_v4.py b/tools/python_test_v4.py index 6ecaf24b4..8c548bef0 100644 --- a/tools/python_test_v4.py +++ b/tools/python_test_v4.py @@ -701,7 +701,12 @@ # 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 From 71495d127d30d2f4c00285485adae5454a590584 Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Sat, 29 Aug 2020 01:11:38 +0200 Subject: [PATCH 3/5] feat(api): add support for Packages API --- gitlab/v4/objects.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py index 37c33e2ef..84fb5c323 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"), @@ -2852,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" @@ -4548,6 +4582,7 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject): ("milestones", "ProjectMilestoneManager"), ("notes", "ProjectNoteManager"), ("notificationsettings", "ProjectNotificationSettingsManager"), + ("packages", "ProjectPackageManager"), ("pagesdomains", "ProjectPagesDomainManager"), ("pipelines", "ProjectPipelineManager"), ("protectedbranches", "ProjectProtectedBranchManager"), From 7ea178bad398c8c2851a4584f4dca5b8adc89d29 Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Sat, 29 Aug 2020 10:45:51 +0200 Subject: [PATCH 4/5] test(packages): add tests for Packages API --- gitlab/tests/objects/conftest.py | 5 + gitlab/tests/objects/test_packages.py | 119 ++++++++++++++++++++++ tools/functional/api/test_packages.py | 13 +++ tools/functional/cli/test_cli_packages.py | 12 +++ tools/functional/{ => cli}/test_cli_v4.py | 0 tools/functional_tests.sh | 2 +- tools/py_functional_tests.sh | 1 + 7 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 gitlab/tests/objects/test_packages.py create mode 100644 tools/functional/api/test_packages.py create mode 100644 tools/functional/cli/test_cli_packages.py rename tools/functional/{ => cli}/test_cli_v4.py (100%) diff --git a/gitlab/tests/objects/conftest.py b/gitlab/tests/objects/conftest.py index 76f76d1cf..d8a40d968 100644 --- a/gitlab/tests/objects/conftest.py +++ b/gitlab/tests/objects/conftest.py @@ -21,6 +21,11 @@ def created_content(): return {"message": "201 Created"} +@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.""" diff --git a/gitlab/tests/objects/test_packages.py b/gitlab/tests/objects/test_packages.py new file mode 100644 index 000000000..d4d97ffe5 --- /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/tools/functional/api/test_packages.py b/tools/functional/api/test_packages.py new file mode 100644 index 000000000..9160a6820 --- /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 000000000..a3734a2fd --- /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 similarity index 100% rename from tools/functional/test_cli_v4.py rename to tools/functional/cli/test_cli_v4.py diff --git a/tools/functional_tests.sh b/tools/functional_tests.sh index b86be3a93..87907c52d 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 75bb7613d..1009cb981 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" From a47dfcd9ded3a0467e83396f21e6dcfa232dfdd7 Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Sat, 29 Aug 2020 10:46:17 +0200 Subject: [PATCH 5/5] docs(packages): add examples for Packages API and cli usage --- docs/api-objects.rst | 1 + docs/cli.rst | 24 +++++++++++++ docs/gl_objects/packages.rst | 68 ++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 docs/gl_objects/packages.rst diff --git a/docs/api-objects.rst b/docs/api-objects.rst index 32852f8fd..5d5949702 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 da5a89e91..95f706250 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 000000000..3c1782b90 --- /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') +