From ccf0c2ad35d4dd1af4f36e411027286a0be0f49f Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Tue, 4 Sep 2018 16:36:34 +0200 Subject: [PATCH 01/33] [docs] Fix the owned/starred usage documentation Closes #579 --- docs/api-usage.rst | 2 +- docs/gl_objects/projects.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api-usage.rst b/docs/api-usage.rst index fa6e0b0da..44df38563 100644 --- a/docs/api-usage.rst +++ b/docs/api-usage.rst @@ -187,7 +187,7 @@ parameter to get all the items when using listing methods: .. code-block:: python all_groups = gl.groups.list(all=True) - all_owned_projects = gl.projects.owned(all=True) + all_owned_projects = gl.projects.list(owned=True, all=True) You can define the ``per_page`` value globally to avoid passing it to every ``list()`` method call: diff --git a/docs/gl_objects/projects.rst b/docs/gl_objects/projects.rst index 7092fe66f..8c2fc3f31 100644 --- a/docs/gl_objects/projects.rst +++ b/docs/gl_objects/projects.rst @@ -45,10 +45,10 @@ Results can also be sorted using the following parameters: projects = gl.projects.list(visibility='public') # List owned projects - projects = gl.projects.owned() + projects = gl.projects.list(owned=True) # List starred projects - projects = gl.projects.starred() + projects = gl.projects.list(starred=True) # Search projects projects = gl.projects.list(search='keyword') From 256518cc1fab21c3dbfa7b67d5edcc81119090c5 Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Tue, 4 Sep 2018 16:56:01 +0200 Subject: [PATCH 02/33] Use https:// for gitlab URL --- docs/switching-to-v4.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/switching-to-v4.rst b/docs/switching-to-v4.rst index ef2106088..e6490e3f8 100644 --- a/docs/switching-to-v4.rst +++ b/docs/switching-to-v4.rst @@ -10,7 +10,7 @@ solve some problems with the existing one. GitLab will stop supporting the v3 API soon, and you should consider switching to v4 if you use a recent version of GitLab (>= 9.0), or if you use -http://gitlab.com. +https://gitlab.com. Using the v4 API From b02c30f8b1829e87e2cc28ae7fdf8bb458a4b1c7 Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Wed, 5 Sep 2018 18:01:07 +0200 Subject: [PATCH 03/33] [docs] fix cut and paste leftover --- docs/api-usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-usage.rst b/docs/api-usage.rst index 44df38563..c2d50c4ee 100644 --- a/docs/api-usage.rst +++ b/docs/api-usage.rst @@ -2,7 +2,7 @@ Getting started with the API ############################ -python-gitlab supports both GitLab v3 and v4 APIs. To use the v3 make sure to +python-gitlab supports both GitLab v3 and v4 APIs. .. note:: From 042b706238810fa3b4fde92d298a709ebdb3a925 Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Wed, 5 Sep 2018 18:04:15 +0200 Subject: [PATCH 04/33] [docs] add a warning about https:// http to https redirection cause problems. Make notes of this in the docs. --- docs/api-usage.rst | 5 +++++ docs/cli.rst | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/docs/api-usage.rst b/docs/api-usage.rst index c2d50c4ee..73d137732 100644 --- a/docs/api-usage.rst +++ b/docs/api-usage.rst @@ -43,6 +43,11 @@ You can also use configuration files to create ``gitlab.Gitlab`` objects: See the :ref:`cli_configuration` section for more information about configuration files. +.. warning:: + + If the GitLab server you are using redirects requests from http to https, + make sure to use the ``https://`` protocol in the URL definition. + Note on password authentication ------------------------------- diff --git a/docs/cli.rst b/docs/cli.rst index 95fa6f448..220f0798a 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -78,6 +78,11 @@ parameters. You can override the values in each GitLab server section. You must define the ``url`` in each GitLab server section. +.. warning:: + + If the GitLab server you are using redirects requests from http to https, + make sure to use the ``https://`` protocol in the ``url`` definition. + Only one of ``private_token`` or ``oauth_token`` should be defined. If neither are defined an anonymous request will be sent to the Gitlab server, with very limited permissions. From 6f80380ed1de49dcc035d06408263d4961e7d18b Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Wed, 5 Sep 2018 18:13:45 +0200 Subject: [PATCH 05/33] Fix the https redirection test --- gitlab/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitlab/__init__.py b/gitlab/__init__.py index 6afccf2dc..99ff5c53f 100644 --- a/gitlab/__init__.py +++ b/gitlab/__init__.py @@ -411,7 +411,7 @@ def _check_redirects(self, result): if item.status_code not in (301, 302): continue # GET methods can be redirected without issue - if result.request.method == 'GET': + if item.request.method == 'GET': continue # Did we end-up with an https:// URL? location = item.headers.get('Location', None) From 9e60364306a894855c8e0744ed4b93cec8ea9ad0 Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Wed, 5 Sep 2018 18:43:16 +0200 Subject: [PATCH 06/33] [docs] Add a note about GroupProject limited API --- docs/gl_objects/groups.rst | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/gl_objects/groups.rst b/docs/gl_objects/groups.rst index 5ef54690a..ff45c9b0e 100644 --- a/docs/gl_objects/groups.rst +++ b/docs/gl_objects/groups.rst @@ -31,6 +31,15 @@ List a group's projects:: projects = group.projects.list() +.. note:: + + ``GroupProject`` objects returned by this API call are very limited, and do + not provide all the features of ``Project`` objects. If you need to + manipulate projects, create a new ``Project`` object:: + + first_group_project = group.projects.list()[0] + manageable_project = gl.projects.get(first_group_project.id, lazy=True) + You can filter and sort the result using the following parameters: * ``archived``: limit by archived status @@ -76,11 +85,14 @@ List the subgroups for a group:: subgroups = group.subgroups.list() - # The GroupSubgroup objects don't expose the same API as the Group - # objects. If you need to manipulate a subgroup as a group, create a new - # Group object: - real_group = gl.groups.get(subgroup_id, lazy=True) - real_group.issues.list() +.. note:: + + The ``GroupSubgroup`` objects don't expose the same API as the ``Group`` + objects. If you need to manipulate a subgroup as a group, create a new + ``Group`` object:: + + real_group = gl.groups.get(subgroup_id, lazy=True) + real_group.issues.list() Group custom attributes ======================= From 83fb4f9ec5f60a122fe9db26c426be74c335e5d5 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 10 Sep 2018 13:49:18 -0500 Subject: [PATCH 07/33] add missing comma in ProjectIssueManager _create_attrs This fixes the argument handling for assignee/milestone ID when for `project-issue create` --- gitlab/v4/objects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py index bd7635fd1..1d771ae9a 100644 --- a/gitlab/v4/objects.py +++ b/gitlab/v4/objects.py @@ -1862,8 +1862,8 @@ class ProjectIssueManager(CRUDMixin, RESTManager): 'order_by', 'sort', 'search', 'created_after', 'created_before', 'updated_after', 'updated_before') _create_attrs = (('title', ), - ('description', 'confidential', 'assignee_id', - 'assignee_idss' 'milestone_id', 'labels', 'created_at', + ('description', 'confidential', 'assignee_ids', + 'assignee_id', 'milestone_id', 'labels', 'created_at', 'due_date', 'merge_request_to_resolve_discussions_of', 'discussion_to_resolve')) _update_attrs = (tuple(), ('title', 'description', 'confidential', From 77f4d3af9c1e5f08b8f4e3aa32c7944c9814dab0 Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Wed, 26 Sep 2018 13:42:33 +0200 Subject: [PATCH 08/33] README: add a note about maintainers --- README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.rst b/README.rst index 56856b6c6..ff71fcb73 100644 --- a/README.rst +++ b/README.rst @@ -17,6 +17,12 @@ Python GitLab It supports the v4 API of GitLab, and provides a CLI tool (``gitlab``). +Maintainer(s) wanted +==================== + +We are looking for neww maintainer(s) for this project. See +https://github.com/python-gitlab/python-gitlab/issues/596. + Installation ============ From 21d257782bb1aea9d154e797986ed0f6cdd36fad Mon Sep 17 00:00:00 2001 From: Hans Donner Date: Sat, 29 Sep 2018 15:54:25 +0200 Subject: [PATCH 09/33] more flexible docker --- contrib/docker/Dockerfile | 12 ++++++------ contrib/docker/README.rst | 15 ++++++++------- contrib/docker/entrypoint-python-gitlab.sh | 21 +++++++++++++++++++++ contrib/docker/python-gitlab.cfg | 15 --------------- 4 files changed, 35 insertions(+), 28 deletions(-) create mode 100755 contrib/docker/entrypoint-python-gitlab.sh delete mode 100644 contrib/docker/python-gitlab.cfg diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index 6663cac5d..6812c328e 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -1,10 +1,10 @@ -FROM python:slim +FROM python:alpine -# Install python-gitlab RUN pip install --upgrade python-gitlab -# Copy sample configuration file -COPY python-gitlab.cfg / +COPY entrypoint-python-gitlab.sh /usr/local/bin/. -# Define the entrypoint that enable a configuration file -ENTRYPOINT ["gitlab", "--config-file", "/python-gitlab.cfg"] +WORKDIR /src + +ENTRYPOINT ["entrypoint-python-gitlab.sh"] +CMD ["--version"] diff --git a/contrib/docker/README.rst b/contrib/docker/README.rst index 90a576cf4..16276614a 100644 --- a/contrib/docker/README.rst +++ b/contrib/docker/README.rst @@ -1,19 +1,20 @@ python-gitlab docker image ========================== -Dockerfile contributed by *oupala*: -https://github.com/python-gitlab/python-gitlab/issues/295 - How to build ------------ -``docker build -t me/python-gitlab:VERSION .`` +``docker build -t python-gitlab:VERSION .`` How to use ---------- -``docker run -it -v /path/to/python-gitlab.cfg:/python-gitlab.cfg python-gitlab ...`` +``docker run -it --rm -e GITLAB_PRIVATE_TOKEN= =/python-gitlab.cfg python-gitlab ...`` + +To change the endpoint, add `-e GITLAB_URL=` + + +Bring your own config file: +``docker run -it --rm -v /path/to/python-gitlab.cfg:/python-gitlab.cfg -e GITLAB_CFG=/python-gitlab.cfg python-gitlab ...`` -To make things easier you can create a shell alias: -``alias gitlab='docker run --rm -it -v /path/to/python-gitlab.cfg:/python-gitlab.cfg python-gitlab`` diff --git a/contrib/docker/entrypoint-python-gitlab.sh b/contrib/docker/entrypoint-python-gitlab.sh new file mode 100755 index 000000000..6422ad095 --- /dev/null +++ b/contrib/docker/entrypoint-python-gitlab.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +GITLAB_CFG=${GITLAB_CFG:-"/etc/python-gitlab-default.cfg"} + +cat << EOF > /etc/python-gitlab-default.cfg +[global] +default = gitlab +ssl_verify = ${GITLAB_SSL_VERIFY:-true} +timeout = ${GITLAB_TIMEOUT:-5} +api_version = ${GITLAB_API_VERSION:-4} +per_page = ${GITLAB_PER_PAGE:-10} + +[gitlab] +url = ${GITLAB_URL:-https://gitlab.com} +private_token = ${GITLAB_PRIVATE_TOKEN} +oauth_token = ${GITLAB_OAUTH_TOKEN} +http_username = ${GITLAB_HTTP_USERNAME} +http_password = ${GITLAB_HTTP_PASSWORD} +EOF + +exec gitlab --config-file "${GITLAB_CFG}" $@ diff --git a/contrib/docker/python-gitlab.cfg b/contrib/docker/python-gitlab.cfg deleted file mode 100644 index 0e519545f..000000000 --- a/contrib/docker/python-gitlab.cfg +++ /dev/null @@ -1,15 +0,0 @@ -[global] -default = somewhere -ssl_verify = true -timeout = 5 -api_version = 3 - -[somewhere] -url = https://some.whe.re -private_token = vTbFeqJYCY3sibBP7BZM -api_version = 4 - -[elsewhere] -url = http://else.whe.re:8080 -private_token = CkqsjqcQSFH5FQKDccu4 -timeout = 1 From ea71f1d121b723140671e2090182174234f0e2a1 Mon Sep 17 00:00:00 2001 From: Eric Sabouraud Date: Wed, 3 Oct 2018 21:36:56 +0200 Subject: [PATCH 10/33] Add project protected tags management (#581) --- docs/gl_objects/projects.rst | 33 +++++++++++++++++++++++++++++++++ gitlab/v4/objects.py | 13 +++++++++++++ 2 files changed, 46 insertions(+) diff --git a/docs/gl_objects/projects.rst b/docs/gl_objects/projects.rst index 8c2fc3f31..dd444bff1 100644 --- a/docs/gl_objects/projects.rst +++ b/docs/gl_objects/projects.rst @@ -657,3 +657,36 @@ Edit project push rules:: Delete project push rules:: pr.delete() + +Project protected tags +================== + +Reference +--------- + +* v4 API: + + + :class:`gitlab.v4.objects.ProjectProtectedTag` + + :class:`gitlab.v4.objects.ProjectProtectedTagManager` + + :attr:`gitlab.v4.objects.Project.protectedtags` + +* GitLab API: https://docs.gitlab.com/ce/api/protected_tags.html + +Examples +--------- + +Get a list of protected tags from a project:: + + protected_tags = project.protectedtags.list() + +Get a single protected tag or wildcard protected tag:: + + protected_tag = project.protectedtags.get('v*') + +Protect a single repository tag or several project repository tags using a wildcard protected tag:: + + project.protectedtags.create({'name': 'v*', 'create_access_level': '40'}) + +Unprotect the given protected tag or wildcard protected tag.:: + + protected_tag.delete() diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py index 1d771ae9a..281301e0c 100644 --- a/gitlab/v4/objects.py +++ b/gitlab/v4/objects.py @@ -1965,6 +1965,18 @@ class ProjectTagManager(NoUpdateMixin, RESTManager): _create_attrs = (('tag_name', 'ref'), ('message',)) +class ProjectProtectedTag(ObjectDeleteMixin, RESTObject): + _id_attr = 'name' + _short_print_attr = 'name' + + +class ProjectProtectedTagManager(NoUpdateMixin, RESTManager): + _path = '/projects/%(project_id)s/protected_tags' + _obj_cls = ProjectProtectedTag + _from_parent_attrs = {'project_id': 'id'} + _create_attrs = (('name',), ('create_access_level',)) + + class ProjectMergeRequestApproval(SaveMixin, RESTObject): _id_attr = None @@ -3124,6 +3136,7 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject): ('pagesdomains', 'ProjectPagesDomainManager'), ('pipelines', 'ProjectPipelineManager'), ('protectedbranches', 'ProjectProtectedBranchManager'), + ('protectedtags', 'ProjectProtectedTagManager'), ('pipelineschedules', 'ProjectPipelineScheduleManager'), ('pushrules', 'ProjectPushRulesManager'), ('runners', 'ProjectRunnerManager'), From 6bb4d17a92832701b9f064a6577488cc42d20645 Mon Sep 17 00:00:00 2001 From: Max Wittig Date: Tue, 2 Oct 2018 19:56:53 +0200 Subject: [PATCH 11/33] fix(cli): print help and usage without config file Fixes #560 --- gitlab/cli.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gitlab/cli.py b/gitlab/cli.py index 48701922c..e79ac6d5d 100644 --- a/gitlab/cli.py +++ b/gitlab/cli.py @@ -98,7 +98,7 @@ def _get_base_parser(add_help=True): "will be used."), required=False) parser.add_argument("-o", "--output", - help=("Output format (v4 only): json|legacy|yaml"), + help="Output format (v4 only): json|legacy|yaml", required=False, choices=['json', 'legacy', 'yaml'], default="legacy") @@ -135,6 +135,10 @@ def main(): exit(0) parser = _get_base_parser(add_help=False) + if "--help" in sys.argv or "-h" in sys.argv: + parser.print_help() + exit(0) + # This first parsing step is used to find the gitlab config to use, and # load the propermodule (v3 or v4) accordingly. At that point we don't have # any subparser setup From c38775a5d52620a9c2d506d7b0952ea7ef0a11fc Mon Sep 17 00:00:00 2001 From: Max Wittig Date: Sat, 6 Oct 2018 16:50:27 +0200 Subject: [PATCH 12/33] refactor: rename MASTER_ACCESS to MAINTAINER_ACCESS to follow GitLab 11.0 docs See: https://docs.gitlab.com/ce/user/permissions.html#project-members-permissions --- docs/gl_objects/access_requests.rst | 4 ++-- docs/gl_objects/groups.rst | 2 +- docs/gl_objects/projects.rst | 2 +- docs/gl_objects/protected_branches.rst | 2 +- gitlab/const.py | 3 ++- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/gl_objects/access_requests.rst b/docs/gl_objects/access_requests.rst index 9a147c140..e890ce07f 100644 --- a/docs/gl_objects/access_requests.rst +++ b/docs/gl_objects/access_requests.rst @@ -10,7 +10,7 @@ following constants are provided to represent the access levels: * ``gitlab.GUEST_ACCESS``: ``10`` * ``gitlab.REPORTER_ACCESS``: ``20`` * ``gitlab.DEVELOPER_ACCESS``: ``30`` -* ``gitlab.MASTER_ACCESS``: ``40`` +* ``gitlab.MAINTAINER_ACCESS``: ``40`` * ``gitlab.OWNER_ACCESS``: ``50`` References @@ -43,7 +43,7 @@ Create an access request:: Approve an access request:: ar.approve() # defaults to DEVELOPER level - ar.approve(access_level=gitlab.MASTER_ACCESS) # explicitly set access level + ar.approve(access_level=gitlab.MAINTAINER_ACCESS) # explicitly set access level Deny (delete) an access request:: diff --git a/docs/gl_objects/groups.rst b/docs/gl_objects/groups.rst index ff45c9b0e..059367248 100644 --- a/docs/gl_objects/groups.rst +++ b/docs/gl_objects/groups.rst @@ -142,7 +142,7 @@ The following constants define the supported access levels: * ``gitlab.GUEST_ACCESS = 10`` * ``gitlab.REPORTER_ACCESS = 20`` * ``gitlab.DEVELOPER_ACCESS = 30`` -* ``gitlab.MASTER_ACCESS = 40`` +* ``gitlab.MAINTAINER_ACCESS = 40`` * ``gitlab.OWNER_ACCESS = 50`` Reference diff --git a/docs/gl_objects/projects.rst b/docs/gl_objects/projects.rst index dd444bff1..276686cbb 100644 --- a/docs/gl_objects/projects.rst +++ b/docs/gl_objects/projects.rst @@ -493,7 +493,7 @@ Add a project member:: Modify a project member (change the access level):: - member.access_level = gitlab.MASTER_ACCESS + member.access_level = gitlab.MAINTAINER_ACCESS member.save() Remove a member from the project team:: diff --git a/docs/gl_objects/protected_branches.rst b/docs/gl_objects/protected_branches.rst index bd2b22b87..f0479e0c4 100644 --- a/docs/gl_objects/protected_branches.rst +++ b/docs/gl_objects/protected_branches.rst @@ -32,7 +32,7 @@ Create a protected branch:: p_branch = project.protectedbranches.create({ 'name': '*-stable', 'merge_access_level': gitlab.DEVELOPER_ACCESS, - 'push_access_level': gitlab.MASTER_ACCESS + 'push_access_level': gitlab.MAINTAINER_ACCESS }) Delete a protected branch:: diff --git a/gitlab/const.py b/gitlab/const.py index e4766d596..62f240391 100644 --- a/gitlab/const.py +++ b/gitlab/const.py @@ -18,7 +18,8 @@ GUEST_ACCESS = 10 REPORTER_ACCESS = 20 DEVELOPER_ACCESS = 30 -MASTER_ACCESS = 40 +MAINTAINER_ACCESS = 40 +MASTER_ACCESS = MAINTAINER_ACCESS OWNER_ACCESS = 50 VISIBILITY_PRIVATE = 0 From 6585c967732fe2a53c6ad6d4d2ab39aaa68258b0 Mon Sep 17 00:00:00 2001 From: Max Wittig Date: Sun, 7 Oct 2018 17:34:01 +0200 Subject: [PATCH 13/33] docs(readme): add docs build information --- README.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.rst b/README.rst index ff71fcb73..e8f2d76dc 100644 --- a/README.rst +++ b/README.rst @@ -54,6 +54,13 @@ Documentation The full documentation for CLI and API is available on `readthedocs `_. +Build the docs +-------------- +You can build the documentation using ``sphinx``:: + + pip install sphinx + python setup.py build_sphinx + Contributing ============ From 06e8ca8747256632c8a159f760860b1ae8f2b7b5 Mon Sep 17 00:00:00 2001 From: Max Wittig Date: Fri, 5 Oct 2018 19:00:41 +0200 Subject: [PATCH 14/33] fix(docker): use docker image with current sources --- .dockerignore | 5 ++++ Dockerfile | 16 +++++++++++++ README.rst | 23 ++++++++++++++++++- contrib/docker/Dockerfile | 10 -------- contrib/docker/README.rst | 20 ---------------- ...t-python-gitlab.sh => docker-entrypoint.sh | 0 6 files changed, 43 insertions(+), 31 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile delete mode 100644 contrib/docker/Dockerfile delete mode 100644 contrib/docker/README.rst rename contrib/docker/entrypoint-python-gitlab.sh => docker-entrypoint.sh (100%) diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..204be7425 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +venv/ +dist/ +build/ +*.egg-info +.github/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..8c811b057 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.7-alpine AS build + +WORKDIR /opt/python-gitlab +COPY . . +RUN python setup.py bdist_wheel + +FROM python:3.7-alpine + +WORKDIR /opt/python-gitlab +COPY --from=build /opt/python-gitlab/dist dist/ +RUN pip install $(find dist -name *.whl) && \ + rm -rf dist/ +COPY docker-entrypoint.sh /usr/local/bin/ + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["--version"] diff --git a/README.rst b/README.rst index ff71fcb73..f4a935771 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,7 @@ It supports the v4 API of GitLab, and provides a CLI tool (``gitlab``). Maintainer(s) wanted ==================== -We are looking for neww maintainer(s) for this project. See +We are looking for new maintainer(s) for this project. See https://github.com/python-gitlab/python-gitlab/issues/596. Installation @@ -41,6 +41,27 @@ Install with pip pip install python-gitlab + +Using the python-gitlab docker image +==================================== + +How to build +------------ + +``docker build -t python-gitlab:TAG .`` + +How to use +---------- + +``docker run -it --rm -e GITLAB_PRIVATE_TOKEN= -v /path/to/python-gitlab.cfg:/python-gitlab.cfg python-gitlab ...`` + +To change the GitLab URL, use `-e GITLAB_URL=` + + +Bring your own config file: +``docker run -it --rm -v /path/to/python-gitlab.cfg:/python-gitlab.cfg -e GITLAB_CFG=/python-gitlab.cfg python-gitlab ...`` + + Bug reports =========== diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile deleted file mode 100644 index 6812c328e..000000000 --- a/contrib/docker/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM python:alpine - -RUN pip install --upgrade python-gitlab - -COPY entrypoint-python-gitlab.sh /usr/local/bin/. - -WORKDIR /src - -ENTRYPOINT ["entrypoint-python-gitlab.sh"] -CMD ["--version"] diff --git a/contrib/docker/README.rst b/contrib/docker/README.rst deleted file mode 100644 index 16276614a..000000000 --- a/contrib/docker/README.rst +++ /dev/null @@ -1,20 +0,0 @@ -python-gitlab docker image -========================== - -How to build ------------- - -``docker build -t python-gitlab:VERSION .`` - -How to use ----------- - -``docker run -it --rm -e GITLAB_PRIVATE_TOKEN= =/python-gitlab.cfg python-gitlab ...`` - -To change the endpoint, add `-e GITLAB_URL=` - - -Bring your own config file: -``docker run -it --rm -v /path/to/python-gitlab.cfg:/python-gitlab.cfg -e GITLAB_CFG=/python-gitlab.cfg python-gitlab ...`` - - diff --git a/contrib/docker/entrypoint-python-gitlab.sh b/docker-entrypoint.sh similarity index 100% rename from contrib/docker/entrypoint-python-gitlab.sh rename to docker-entrypoint.sh From d29a48981b521bf31d6f0304b88f39a63185328a Mon Sep 17 00:00:00 2001 From: Max Wittig Date: Sun, 7 Oct 2018 17:34:44 +0200 Subject: [PATCH 15/33] docs(cli): add PyYAML requirement notice Fixes #606 --- docs/cli.rst | 5 +++++ gitlab/v4/cli.py | 22 ++++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index 220f0798a..2051d0373 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -157,6 +157,11 @@ These options must be defined before the mandatory arguments. ``--output``, ``-o`` Output format. Defaults to a custom format. Can also be ``yaml`` or ``json``. + **Notice:** + + The `PyYAML package `_ is required to use the yaml output option. + You need to install it separately using ``pip install PyYAML`` + ``--fields``, ``-f`` Comma-separated list of fields to display (``yaml`` and ``json`` output formats only). If not used, all the object fields are displayed. diff --git a/gitlab/v4/cli.py b/gitlab/v4/cli.py index a876f9ee6..242874d1a 100644 --- a/gitlab/v4/cli.py +++ b/gitlab/v4/cli.py @@ -302,14 +302,24 @@ def display_list(self, data, fields, **kwargs): class YAMLPrinter(object): def display(self, d, **kwargs): - import yaml # noqa - print(yaml.safe_dump(d, default_flow_style=False)) + try: + import yaml # noqa + print(yaml.safe_dump(d, default_flow_style=False)) + except ImportError: + exit("PyYaml is not installed.\n" + "Install it with `pip install PyYaml` " + "to use the yaml output feature") def display_list(self, data, fields, **kwargs): - import yaml # noqa - print(yaml.safe_dump( - [get_dict(obj, fields) for obj in data], - default_flow_style=False)) + try: + import yaml # noqa + print(yaml.safe_dump( + [get_dict(obj, fields) for obj in data], + default_flow_style=False)) + except ImportError: + exit("PyYaml is not installed.\n" + "Install it with `pip install PyYaml` " + "to use the yaml output feature") class LegacyPrinter(object): From 54b6a545399b51a34fb11819cc24f288bc191651 Mon Sep 17 00:00:00 2001 From: mkosiarc Date: Mon, 15 Oct 2018 22:17:06 +0200 Subject: [PATCH 16/33] [docs] fix discussions typo --- docs/gl_objects/discussions.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/gl_objects/discussions.rst b/docs/gl_objects/discussions.rst index 7673b7c2d..444d883a8 100644 --- a/docs/gl_objects/discussions.rst +++ b/docs/gl_objects/discussions.rst @@ -48,7 +48,7 @@ List the discussions for a resource (issue, merge request, snippet or commit):: Get a single discussion:: - discussion = resource.discussion.get(discussion_id) + discussion = resource.discussions.get(discussion_id) You can access the individual notes in the discussion through the ``notes`` attribute. It holds a list of notes in chronological order:: @@ -68,7 +68,7 @@ You can add notes to existing discussions:: You can get and update a single note using the ``*DiscussionNote`` resources:: - discussion = resource.discussion.get(discussion_id) + discussion = resource.discussions.get(discussion_id) # Get the latest note's id note_id = discussion.attributes['note'][-1]['id'] last_note = discussion.notes.get(note_id) @@ -77,7 +77,7 @@ You can get and update a single note using the ``*DiscussionNote`` resources:: Create a new discussion:: - discussion = resource.discussion.create({'body': 'First comment of discussion'}) + discussion = resource.discussions.create({'body': 'First comment of discussion'}) You can comment on merge requests and commit diffs. Provide the ``position`` dict to define where the comment should appear in the diff:: From 31d1c5dadb5f816d23e7882aa112042db019b681 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Wed, 31 Oct 2018 10:58:48 +0100 Subject: [PATCH 17/33] Add Gitter badge to README --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index a0b182551..77b123c7e 100644 --- a/README.rst +++ b/README.rst @@ -10,6 +10,9 @@ .. image:: https://img.shields.io/pypi/pyversions/python-gitlab.svg :target: https://pypi.python.org/pypi/python-gitlab +.. image:: https://img.shields.io/gitter/room/python-gitlab/Lobby.svg + :target: https://gitter.im/python-gitlab/Lobby + Python GitLab ============= From 2c6c929f78dfda92d5ae93235bb9065d289a68cc Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Sat, 3 Nov 2018 09:52:21 +0100 Subject: [PATCH 18/33] Use the pythongitlab/test-python-gitlab docker image for tests This images is updated to the latest GitLab CE. Fix the diff() test to match the change in the API output. --- tools/build_test_env.sh | 2 +- tools/python_test_v4.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/build_test_env.sh b/tools/build_test_env.sh index ebfb80a07..3185f72ce 100755 --- a/tools/build_test_env.sh +++ b/tools/build_test_env.sh @@ -77,7 +77,7 @@ cleanup() { } try docker run --name gitlab-test --detach --publish 8080:80 \ - --publish 2222:22 gpocentek/test-python-gitlab:latest >/dev/null + --publish 2222:22 pythongitlab/test-python-gitlab:latest >/dev/null LOGIN='root' PASSWORD='5iveL!fe' diff --git a/tools/python_test_v4.py b/tools/python_test_v4.py index 79a78bc32..133aeb3be 100644 --- a/tools/python_test_v4.py +++ b/tools/python_test_v4.py @@ -390,7 +390,7 @@ ] } admin_project.commits.create(data) -assert('---' in admin_project.commits.list()[0].diff()[0]['diff']) +assert('@@' in admin_project.commits.list()[0].diff()[0]['diff']) # commit status commit = admin_project.commits.list()[0] From f7fbfca7e6a32a31dbf7ca8e1d4f83b34b7ac9db Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Sun, 28 Oct 2018 10:54:06 +0100 Subject: [PATCH 19/33] [docs] Add an example of pipeline schedule vars listing Closes #595 --- docs/gl_objects/builds.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/gl_objects/builds.rst b/docs/gl_objects/builds.rst index 51e7496c1..ee450905a 100644 --- a/docs/gl_objects/builds.rst +++ b/docs/gl_objects/builds.rst @@ -141,6 +141,13 @@ Delete a schedule:: sched.delete() +List schedule variables:: + + # note: you need to use get() to retrieve the schedule variables. The + # attribute is not present in the response of a list() call + sched = projects.pipelineschedules.get(schedule_id) + vars = sched.attributes['variables'] + Create a schedule variable:: var = sched.variables.create({'key': 'foo', 'value': 'bar'}) From f51fa19dc4f78d036f18217436add00b7d94c39d Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Sat, 3 Nov 2018 10:02:03 +0100 Subject: [PATCH 20/33] [README] Remove the "maintainer(s) wanted" notice Closes #596 --- README.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.rst b/README.rst index 77b123c7e..bed7f0eee 100644 --- a/README.rst +++ b/README.rst @@ -20,12 +20,6 @@ Python GitLab It supports the v4 API of GitLab, and provides a CLI tool (``gitlab``). -Maintainer(s) wanted -==================== - -We are looking for new maintainer(s) for this project. See -https://github.com/python-gitlab/python-gitlab/issues/596. - Installation ============ From 6ad9da04496f040ae7d95701422434bc935a5a80 Mon Sep 17 00:00:00 2001 From: Max Wittig Date: Sun, 4 Nov 2018 16:52:32 +0100 Subject: [PATCH 21/33] fix(cli): exit on config parse error, instead of crashing * Exit and hint user about possible errors * test: adjust test cases to config missing error --- gitlab/cli.py | 11 ++++++++--- gitlab/config.py | 17 +++++++++++++++++ gitlab/tests/test_config.py | 20 +++++++++++++++++--- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/gitlab/cli.py b/gitlab/cli.py index e79ac6d5d..17917f564 100644 --- a/gitlab/cli.py +++ b/gitlab/cli.py @@ -17,6 +17,7 @@ # along with this program. If not, see . from __future__ import print_function + import argparse import functools import importlib @@ -143,9 +144,13 @@ def main(): # load the propermodule (v3 or v4) accordingly. At that point we don't have # any subparser setup (options, args) = parser.parse_known_args(sys.argv) - - config = gitlab.config.GitlabConfigParser(options.gitlab, - options.config_file) + try: + config = gitlab.config.GitlabConfigParser( + options.gitlab, + options.config_file + ) + except gitlab.config.ConfigError as e: + sys.exit(e) cli_module = importlib.import_module('gitlab.v%s.cli' % config.api_version) # Now we build the entire set of subcommands and do the complete parsing diff --git a/gitlab/config.py b/gitlab/config.py index 9f4c11d7b..1c7659498 100644 --- a/gitlab/config.py +++ b/gitlab/config.py @@ -37,10 +37,27 @@ class GitlabDataError(ConfigError): pass +class GitlabConfigMissingError(ConfigError): + pass + + class GitlabConfigParser(object): def __init__(self, gitlab_id=None, config_files=None): self.gitlab_id = gitlab_id _files = config_files or _DEFAULT_FILES + file_exist = False + for file in _files: + if os.path.exists(file): + file_exist = True + if not file_exist: + raise GitlabConfigMissingError( + "Config file not found. \nPlease create one in " + "one of the following locations: {} \nor " + "specify a config file using the '-c' parameter.".format( + ", ".join(_DEFAULT_FILES) + ) + ) + self._config = configparser.ConfigParser() self._config.read(_files) diff --git a/gitlab/tests/test_config.py b/gitlab/tests/test_config.py index 0b585e801..d1e668efc 100644 --- a/gitlab/tests/test_config.py +++ b/gitlab/tests/test_config.py @@ -76,11 +76,20 @@ class TestConfigParser(unittest.TestCase): + @mock.patch('os.path.exists') + def test_missing_config(self, path_exists): + path_exists.return_value = False + with self.assertRaises(config.GitlabConfigMissingError): + config.GitlabConfigParser('test') + + @mock.patch('os.path.exists') @mock.patch('six.moves.builtins.open') - def test_invalid_id(self, m_open): + def test_invalid_id(self, m_open, path_exists): fd = six.StringIO(no_default_config) fd.close = mock.Mock(return_value=None) m_open.return_value = fd + path_exists.return_value = True + config.GitlabConfigParser('there') self.assertRaises(config.GitlabIDError, config.GitlabConfigParser) fd = six.StringIO(valid_config) @@ -90,12 +99,15 @@ def test_invalid_id(self, m_open): config.GitlabConfigParser, gitlab_id='not_there') + @mock.patch('os.path.exists') @mock.patch('six.moves.builtins.open') - def test_invalid_data(self, m_open): + def test_invalid_data(self, m_open, path_exists): fd = six.StringIO(missing_attr_config) fd.close = mock.Mock(return_value=None, side_effect=lambda: fd.seek(0)) m_open.return_value = fd + path_exists.return_value = True + config.GitlabConfigParser('one') config.GitlabConfigParser('one') self.assertRaises(config.GitlabDataError, config.GitlabConfigParser, @@ -107,11 +119,13 @@ def test_invalid_data(self, m_open): self.assertEqual('Unsupported per_page number: 200', emgr.exception.args[0]) + @mock.patch('os.path.exists') @mock.patch('six.moves.builtins.open') - def test_valid_data(self, m_open): + def test_valid_data(self, m_open, path_exists): fd = six.StringIO(valid_config) fd.close = mock.Mock(return_value=None) m_open.return_value = fd + path_exists.return_value = True cp = config.GitlabConfigParser() self.assertEqual("one", cp.gitlab_id) From a5ab2bb6272acd0285ce84ba6f01fe417c1c5124 Mon Sep 17 00:00:00 2001 From: Nic Grayson Date: Fri, 9 Nov 2018 11:45:41 -0600 Subject: [PATCH 22/33] Fix 3 typos --- docs/gl_objects/users.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/gl_objects/users.rst b/docs/gl_objects/users.rst index 3b9c040fa..9a0bbf50e 100644 --- a/docs/gl_objects/users.rst +++ b/docs/gl_objects/users.rst @@ -190,7 +190,7 @@ are admin. * GitLab API: https://docs.gitlab.com/ce/api/users.html#list-all-gpg-keys -Exemples +Examples -------- List GPG keys for a user:: @@ -232,7 +232,7 @@ are admin. * GitLab API: https://docs.gitlab.com/ce/api/users.html#list-ssh-keys -Exemples +Examples -------- List SSH keys for a user:: @@ -270,7 +270,7 @@ are admin. * GitLab API: https://docs.gitlab.com/ce/api/users.html#list-emails -Exemples +Examples -------- List emails for a user:: From b93f2a9ea9661521878ac45d70c7bd9a5a470548 Mon Sep 17 00:00:00 2001 From: Max Wittig Date: Mon, 19 Nov 2018 08:45:37 +0100 Subject: [PATCH 23/33] docs(projects): fix typo in code sample Fixes #630 --- docs/gl_objects/projects.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gl_objects/projects.rst b/docs/gl_objects/projects.rst index 276686cbb..5cc223ff5 100644 --- a/docs/gl_objects/projects.rst +++ b/docs/gl_objects/projects.rst @@ -247,7 +247,7 @@ generated by GitLab you need to: Import the project:: - gl.projects.import_project(open('/tmp/export.tgz', 'rb'), 'my_new_project') + ouput = gl.projects.import_project(open('/tmp/export.tgz', 'rb'), 'my_new_project') # Get a ProjectImport object to track the import status project_import = gl.projects.get(output['id'], lazy=True).imports.get() while project_import.import_status != 'finished': From ac2d65aacba5c19eca857290c5b47ead6bb4356d Mon Sep 17 00:00:00 2001 From: Max Wittig Date: Tue, 20 Nov 2018 11:08:33 +0100 Subject: [PATCH 24/33] docs(groups): fix typo Fixes #635 --- docs/gl_objects/groups.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gl_objects/groups.rst b/docs/gl_objects/groups.rst index 059367248..c100f87cb 100644 --- a/docs/gl_objects/groups.rst +++ b/docs/gl_objects/groups.rst @@ -62,7 +62,7 @@ Update a group:: Remove a group:: - gl.group.delete(group_id) + gl.groups.delete(group_id) # or group.delete() From 95d0d745d4bafe702c89c972f644b049d6c810ab Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Sat, 3 Nov 2018 09:50:37 +0100 Subject: [PATCH 25/33] Add support to resource label events Closes #611 --- docs/gl_objects/labels.rst | 39 +++++++++++++++++++++++++++++++++++ docs/gl_objects/projects.rst | 2 +- gitlab/v4/objects.py | 40 +++++++++++++++++++++++++++++++++++- tools/python_test_v4.py | 17 +++++++++++++++ 4 files changed, 96 insertions(+), 2 deletions(-) diff --git a/docs/gl_objects/labels.rst b/docs/gl_objects/labels.rst index 1c98971c2..a4667aac0 100644 --- a/docs/gl_objects/labels.rst +++ b/docs/gl_objects/labels.rst @@ -2,6 +2,9 @@ Labels ###### +Project labels +============== + Reference --------- @@ -48,3 +51,39 @@ Manage labels in issues and merge requests:: 'labels': ['foo']}) issue.labels.append('bar') issue.save() + +Label events +============ + +Resource label events keep track about who, when, and which label was added or +removed to an issuable. + +Group epic label events are only available in the EE edition. + +Reference +--------- + +* v4 API: + + + :class:`gitlab.v4.objects.ProjectIssueResourceLabelEvent` + + :class:`gitlab.v4.objects.ProjectIssueResourceLabelEventManager` + + :attr:`gitlab.v4.objects.ProjectIssue.resourcelabelevents` + + :class:`gitlab.v4.objects.ProjectMergeRequestResourceLabelEvent` + + :class:`gitlab.v4.objects.ProjectMergeRequestResourceLabelEventManager` + + :attr:`gitlab.v4.objects.ProjectMergeRequest.resourcelabelevents` + + :class:`gitlab.v4.objects.GroupEpicResourceLabelEvent` + + :class:`gitlab.v4.objects.GroupEpicResourceLabelEventManager` + + :attr:`gitlab.v4.objects.GroupEpic.resourcelabelevents` + +* GitLab API: https://docs.gitlab.com/ee/api/resource_label_events.html + +Examples +-------- + +Get the events for a resource (issue, merge request or epic):: + + events = resource.resourcelabelevents.list() + +Get a specific event for a resource:: + + event = resource.resourcelabelevents.get(event_id) diff --git a/docs/gl_objects/projects.rst b/docs/gl_objects/projects.rst index 5cc223ff5..c8bd3eb2a 100644 --- a/docs/gl_objects/projects.rst +++ b/docs/gl_objects/projects.rst @@ -659,7 +659,7 @@ Delete project push rules:: pr.delete() Project protected tags -================== +====================== Reference --------- diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py index 281301e0c..84b3a86cd 100644 --- a/gitlab/v4/objects.py +++ b/gitlab/v4/objects.py @@ -662,9 +662,22 @@ def create(self, data, **kwargs): return self._obj_cls(self, server_data) +class GroupEpicResourceLabelEvent(RESTObject): + pass + + +class GroupEpicResourceLabelEventManager(RetrieveMixin, RESTManager): + _path = ('/groups/%(group_id)s/epics/%(epic_id)s/resource_label_events') + _obj_cls = GroupEpicResourceLabelEvent + _from_parent_attrs = {'group_id': 'group_id', 'epic_id': 'id'} + + class GroupEpic(ObjectDeleteMixin, SaveMixin, RESTObject): _id_attr = 'iid' - _managers = (('issues', 'GroupEpicIssueManager'),) + _managers = ( + ('issues', 'GroupEpicIssueManager'), + ('resourcelabelevents', 'GroupEpicResourceLabelEventManager'), + ) class GroupEpicManager(CRUDMixin, RESTManager): @@ -1803,6 +1816,17 @@ def create(self, data, **kwargs): return source_issue, target_issue +class ProjectIssueResourceLabelEvent(RESTObject): + pass + + +class ProjectIssueResourceLabelEventManager(RetrieveMixin, RESTManager): + _path = ('/projects/%(project_id)s/issues/%(issue_iid)s' + '/resource_label_events') + _obj_cls = ProjectIssueResourceLabelEvent + _from_parent_attrs = {'project_id': 'project_id', 'issue_iid': 'iid'} + + class ProjectIssue(UserAgentDetailMixin, SubscribableMixin, TodoMixin, TimeTrackingMixin, ParticipantsMixin, SaveMixin, ObjectDeleteMixin, RESTObject): @@ -1813,6 +1837,7 @@ class ProjectIssue(UserAgentDetailMixin, SubscribableMixin, TodoMixin, ('discussions', 'ProjectIssueDiscussionManager'), ('links', 'ProjectIssueLinkManager'), ('notes', 'ProjectIssueNoteManager'), + ('resourcelabelevents', 'ProjectIssueResourceLabelEventManager'), ) @cli.register_custom_action('ProjectIssue', ('to_project_id',)) @@ -2086,6 +2111,17 @@ class ProjectMergeRequestDiscussionManager(RetrieveMixin, CreateMixin, _update_attrs = (('resolved',), tuple()) +class ProjectMergeRequestResourceLabelEvent(RESTObject): + pass + + +class ProjectMergeRequestResourceLabelEventManager(RetrieveMixin, RESTManager): + _path = ('/projects/%(project_id)s/merge_requests/%(mr_iid)s' + '/resource_label_events') + _obj_cls = ProjectMergeRequestResourceLabelEvent + _from_parent_attrs = {'project_id': 'project_id', 'mr_iid': 'iid'} + + class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin, ParticipantsMixin, SaveMixin, ObjectDeleteMixin, RESTObject): @@ -2097,6 +2133,8 @@ class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin, ('diffs', 'ProjectMergeRequestDiffManager'), ('discussions', 'ProjectMergeRequestDiscussionManager'), ('notes', 'ProjectMergeRequestNoteManager'), + ('resourcelabelevents', + 'ProjectMergeRequestResourceLabelEventManager'), ) @cli.register_custom_action('ProjectMergeRequest') diff --git a/tools/python_test_v4.py b/tools/python_test_v4.py index 133aeb3be..1a111b339 100644 --- a/tools/python_test_v4.py +++ b/tools/python_test_v4.py @@ -539,6 +539,15 @@ assert(issue1.user_agent_detail()['user_agent']) assert(issue1.participants()) +# issues labels and events +label2 = admin_project.labels.create({'name': 'label2', 'color': '#aabbcc'}) +issue1.labels = ['label2'] +issue1.save() +events = issue1.resourcelabelevents.list() +assert(events) +event = issue1.resourcelabelevents.get(events[0].id) +assert(event) + discussion = issue1.discussions.create({'body': 'Discussion body'}) assert(len(issue1.discussions.list()) == 1) d_note = discussion.notes.create({'body': 'first note'}) @@ -628,6 +637,14 @@ discussion = mr.discussions.get(discussion.id) assert(len(discussion.attributes['notes']) == 1) +# mr labels and events +mr.labels = ['label2'] +mr.save() +events = mr.resourcelabelevents.list() +assert(events) +event = mr.resourcelabelevents.get(events[0].id) +assert(event) + # basic testing: only make sure that the methods exist mr.commits() mr.changes() From 0c9a00bb154007a0a9f665ca38e6fec50d378eaf Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Thu, 22 Nov 2018 18:17:37 +0100 Subject: [PATCH 26/33] [docs] Fix the milestone filetring doc (iid -> iids) Fixes #633 --- docs/gl_objects/milestones.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gl_objects/milestones.rst b/docs/gl_objects/milestones.rst index 0d3f576d5..f24e13fc7 100644 --- a/docs/gl_objects/milestones.rst +++ b/docs/gl_objects/milestones.rst @@ -30,7 +30,7 @@ List the milestones for a project or a group:: You can filter the list using the following parameters: -* ``iid``: unique ID of the milestone for the project +* ``iids``: unique IDs of milestones for the project * ``state``: either ``active`` or ``closed`` * ``search``: to search using a string From bb251b8ef780216de03dde67912ad5fffbb30390 Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Thu, 22 Nov 2018 18:36:15 +0100 Subject: [PATCH 27/33] [docs] Fix typo in custom attributes example Closes #628 --- docs/gl_objects/users.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gl_objects/users.rst b/docs/gl_objects/users.rst index 9a0bbf50e..d86d2ed30 100644 --- a/docs/gl_objects/users.rst +++ b/docs/gl_objects/users.rst @@ -112,7 +112,7 @@ Delete a custom attribute for a user:: Search users by custom attribute:: - user.customattributes.set('role': 'QA') + user.customattributes.set('role', 'QA') gl.users.list(custom_attributes={'role': 'QA'}) User impersonation tokens From 1fb1296c9191e57e109c4e5eb9504bce191a6ff1 Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Sat, 24 Nov 2018 17:37:10 +0100 Subject: [PATCH 28/33] Improve error message handling in exceptions * Depending on the request Gitlab has a 'message' or 'error' attribute in the json data, handle both * Add some consistency by converting messages to unicode or str for exceptions (depending on the python version) Closes #616 --- gitlab/__init__.py | 8 ++++++-- gitlab/exceptions.py | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/gitlab/__init__.py b/gitlab/__init__.py index 99ff5c53f..477d56428 100644 --- a/gitlab/__init__.py +++ b/gitlab/__init__.py @@ -490,10 +490,14 @@ def http_request(self, verb, path, query_data={}, post_data=None, time.sleep(wait_time) continue + error_message = result.content try: - error_message = result.json()['message'] + error_json = result.json() + for k in ('message', 'error'): + if k in error_json: + error_message = error_json[k] except (KeyError, ValueError, TypeError): - error_message = result.content + pass if result.status_code == 401: raise GitlabAuthenticationError( diff --git a/gitlab/exceptions.py b/gitlab/exceptions.py index 650328a15..0822d3e58 100644 --- a/gitlab/exceptions.py +++ b/gitlab/exceptions.py @@ -28,7 +28,12 @@ def __init__(self, error_message="", response_code=None, # Full http response self.response_body = response_body # Parsed error message from gitlab - self.error_message = error_message + try: + # if we receive str/bytes we try to convert to unicode/str to have + # consistent message types (see #616) + self.error_message = error_message.decode() + except Exception: + self.error_message = error_message def __str__(self): if self.response_code is not None: From ef1523a23737db45d0f439badcd8be564bcb67fb Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Sat, 24 Nov 2018 18:05:34 +0100 Subject: [PATCH 29/33] [feature] Add support for members all() method Closes #589 --- docs/gl_objects/groups.rst | 5 ++++ docs/gl_objects/projects.rst | 5 ++++ gitlab/v4/objects.py | 48 ++++++++++++++++++++++++++++++++++++ tools/python_test_v4.py | 1 + 4 files changed, 59 insertions(+) diff --git a/docs/gl_objects/groups.rst b/docs/gl_objects/groups.rst index c100f87cb..7fcf980b6 100644 --- a/docs/gl_objects/groups.rst +++ b/docs/gl_objects/groups.rst @@ -164,6 +164,11 @@ List group members:: members = group.members.list() +List the group members recursively (including inherited members through +ancestor groups):: + + members = group.members.all(all=True) + Get a group member:: members = group.members.get(member_id) diff --git a/docs/gl_objects/projects.rst b/docs/gl_objects/projects.rst index 5cc223ff5..dd43294d9 100644 --- a/docs/gl_objects/projects.rst +++ b/docs/gl_objects/projects.rst @@ -478,6 +478,11 @@ List the project members:: members = project.members.list() +List the project members recursively (including inherited members through +ancestor groups):: + + members = project.members.all(all=True) + Search project members matching a query string:: members = project.members.list(query='bar') diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py index 281301e0c..3f019559f 100644 --- a/gitlab/v4/objects.py +++ b/gitlab/v4/objects.py @@ -705,6 +705,30 @@ class GroupMemberManager(CRUDMixin, RESTManager): _create_attrs = (('access_level', 'user_id'), ('expires_at', )) _update_attrs = (('access_level', ), ('expires_at', )) + @cli.register_custom_action('GroupMemberManager') + @exc.on_http_error(exc.GitlabListError) + def all(self, **kwargs): + """List all the members, included inherited ones. + + Args: + all (bool): If True, return all the items, without pagination + per_page (int): Number of items to retrieve per request + page (int): ID of the page to return (starts with page 1) + as_list (bool): If set to False and no pagination option is + defined, return a generator instead of a list + **kwargs: Extra options to send to the server (e.g. sudo) + + Raises: + GitlabAuthenticationError: If authentication is not correct + GitlabListError: If the list could not be retrieved + + Returns: + RESTObjectList: The list of members + """ + + path = '%s/all' % self.path + return self.gitlab.http_list(path, **kwargs) + class GroupMergeRequest(RESTObject): pass @@ -1884,6 +1908,30 @@ class ProjectMemberManager(CRUDMixin, RESTManager): _create_attrs = (('access_level', 'user_id'), ('expires_at', )) _update_attrs = (('access_level', ), ('expires_at', )) + @cli.register_custom_action('ProjectMemberManager') + @exc.on_http_error(exc.GitlabListError) + def all(self, **kwargs): + """List all the members, included inherited ones. + + Args: + all (bool): If True, return all the items, without pagination + per_page (int): Number of items to retrieve per request + page (int): ID of the page to return (starts with page 1) + as_list (bool): If set to False and no pagination option is + defined, return a generator instead of a list + **kwargs: Extra options to send to the server (e.g. sudo) + + Raises: + GitlabAuthenticationError: If authentication is not correct + GitlabListError: If the list could not be retrieved + + Returns: + RESTObjectList: The list of members + """ + + path = '%s/all' % self.path + return self.gitlab.http_list(path, **kwargs) + class ProjectNote(RESTObject): pass diff --git a/tools/python_test_v4.py b/tools/python_test_v4.py index 133aeb3be..8ff099b6e 100644 --- a/tools/python_test_v4.py +++ b/tools/python_test_v4.py @@ -244,6 +244,7 @@ group1.members.delete(user1.id) assert(len(group1.members.list()) == 2) +assert(len(group1.members.all())) member = group1.members.get(user2.id) member.access_level = gitlab.const.OWNER_ACCESS member.save() From 67ab6371e69fbf137b95fd03105902206faabdac Mon Sep 17 00:00:00 2001 From: Roozbeh Farahbod Date: Tue, 4 Dec 2018 13:07:19 +0100 Subject: [PATCH 30/33] fix: docker entry point argument passing Fixes the problem of passing spaces in the arguments to the docker entrypoint. Before this fix, there was virtually no way to pass spaces in arguments such as task description. --- docker-entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 6422ad095..bda814171 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -18,4 +18,4 @@ http_username = ${GITLAB_HTTP_USERNAME} http_password = ${GITLAB_HTTP_PASSWORD} EOF -exec gitlab --config-file "${GITLAB_CFG}" $@ +exec gitlab --config-file "${GITLAB_CFG}" "$@" From ad0b47667f98760d6a802a9d08b2da8f40d13e87 Mon Sep 17 00:00:00 2001 From: Roozbeh Farahbod Date: Tue, 4 Dec 2018 18:03:19 +0100 Subject: [PATCH 31/33] fix: enable use of YAML in the CLI In order to use the YAML output, PyYaml needs to be installed on the docker image. This commit adds the installation to the dockerfile as a separate layer. --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 8c811b057..489a4207a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,7 @@ FROM python:3.7-alpine WORKDIR /opt/python-gitlab COPY --from=build /opt/python-gitlab/dist dist/ +RUN pip install PyYaml RUN pip install $(find dist -name *.whl) && \ rm -rf dist/ COPY docker-entrypoint.sh /usr/local/bin/ From cebbbf67f2529bd9380276ac28abe726d3a57a81 Mon Sep 17 00:00:00 2001 From: Eric Sabouraud Date: Fri, 7 Dec 2018 18:10:41 +0100 Subject: [PATCH 32/33] Add access control options to protected branch creation --- docs/gl_objects/protected_branches.rst | 9 +++++++++ gitlab/v4/objects.py | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/gl_objects/protected_branches.rst b/docs/gl_objects/protected_branches.rst index f0479e0c4..3498aa578 100644 --- a/docs/gl_objects/protected_branches.rst +++ b/docs/gl_objects/protected_branches.rst @@ -35,6 +35,15 @@ Create a protected branch:: 'push_access_level': gitlab.MAINTAINER_ACCESS }) +Create a protected branch with more granular access control:: + + p_branch = project.protectedbranches.create({ + 'name': '*-stable', + 'allowed_to_push': [{"user_id": 99}, {"user_id": 98}], + 'allowed_to_merge': [{"group_id": 653}], + 'allowed_to_unprotect': [{"access_level": gitlab.MAINTAINER_ACCESS}] + }) + Delete a protected branch:: project.protectedbranches.delete('*-stable') diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py index 04444f762..fd673b522 100644 --- a/gitlab/v4/objects.py +++ b/gitlab/v4/objects.py @@ -3117,7 +3117,10 @@ class ProjectProtectedBranchManager(NoUpdateMixin, RESTManager): _path = '/projects/%(project_id)s/protected_branches' _obj_cls = ProjectProtectedBranch _from_parent_attrs = {'project_id': 'id'} - _create_attrs = (('name', ), ('push_access_level', 'merge_access_level')) + _create_attrs = (('name', ), + ('push_access_level', 'merge_access_level', + 'unprotect_access_level', 'allowed_to_push', + 'allowed_to_merge', 'allowed_to_unprotect')) class ProjectRunner(ObjectDeleteMixin, RESTObject): From 456f3c48e48dcff59e063c2572b6028f1abfba82 Mon Sep 17 00:00:00 2001 From: Gauvain Pocentek Date: Sun, 9 Dec 2018 09:37:38 +0100 Subject: [PATCH 33/33] Prepare the 1.7.0 release --- AUTHORS | 107 +++------------------------------------------ ChangeLog.rst | 26 +++++++++++ gitlab/__init__.py | 2 +- 3 files changed, 33 insertions(+), 102 deletions(-) diff --git a/AUTHORS b/AUTHORS index 11ae684ba..f255ad788 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,105 +1,10 @@ -Authors -------- +Authors / Maintainers +--------------------- -Gauvain Pocentek -Mika Mäenpää +Gauvain Pocentek +Max Wittig Contributors ------------ -Adam Reid -Alexander Skiba -Alex Widener -Amar Sood (tekacs) -Andjelko Horvat -Andreas Nüßlein -Andrew Austin -Armin Weihbold -Aron Pammer -Asher256 -Bancarel Valentin -Ben Brown -btmanm -Carlo Mion -Carlos Soriano -Christian -Christian Wenk -Colin D Bennett -Cosimo Lupo -Crestez Dan Leonard -Cyril Jouve -Daniel Kimsey -David Guest -derek-austin -Diego Giovane Pasqualin -Dmytro Litvinov -Eli Sarver -Eric L Frederich -Eric Sabouraud -Erik Weatherwax -fgouteroux -Greg Allen -Guillaume Delacour -Guyzmo -hakkeroid -Ian Sparks -itxaka -Ivica Arsov -Jakub Wilk -James (d0c_s4vage) Johnson -James E. Flemer -James Johnson -Jamie Bliss -Jason Antman -Jerome Robert -Johan Brandhorst -Jonathon Reinhart -Jon Banafato -Keith Wansbrough -Koen Smets -Kris Gambirazzi -leon -Lyudmil Nenov -Mart Sõmermaa -massimone88 -Matej Zerovnik -Matt Odden -Matthias Schmitz -Matus Ferech -Maura Hausman -Maxime Guyot -Max Wittig -Michael Overmeyer -Michal Galet -Mike Kobit -Mikhail Lopotkov -Miouge1 -Missionrulz -Mond WAN -Moritz Lipp -Nathan Giesbrecht -Nathan Schmidt -pa4373 -Patrick Miller -Pavel Savchenko -Peng Xiao -Pete Browne -Peter Mosmans -P. F. Chimento -Philipp Busch -Pierre Tardy -Rafael Eyng -Richard Hansen -Robert Lu -samcday -savenger -Stefan Crain -Stefan K. Dunkler -Stefan Klug -Stefano Mandruzzato -THEBAULT Julien -Tim Neumann -Tom Downes -Twan -Will Rouesnel -Will Starms -Yosi Zelensky + +See ``git log`` for a full list of contributors. diff --git a/ChangeLog.rst b/ChangeLog.rst index beac7ff94..3e96318fd 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -1,6 +1,31 @@ ChangeLog ========= +Version 1.7.0_ - 2018-12-09 +--------------------------- + +* [docs] Fix the owned/starred usage documentation +* [docs] Add a warning about http to https redirects +* Fix the https redirection test +* [docs] Add a note about GroupProject limited API +* Add missing comma in ProjectIssueManager _create_attrs +* More flexible docker image +* Add project protected tags management +* [cli] Print help and usage without config file +* Rename MASTER_ACCESS to MAINTAINER_ACCESS +* [docs] Add docs build information +* Use docker image with current sources +* [docs] Add PyYAML requirement notice +* Add Gitter badge to README +* [docs] Add an example of pipeline schedule vars listing +* [cli] Exit on config parse error, instead of crashing +* Add support for resource label events +* [docs] Fix the milestone filetring doc (iid -> iids) +* [docs] Fix typo in custom attributes example +* Improve error message handling in exceptions +* Add support for members all() method +* Add access control options to protected branch creation + Version 1.6.0_ - 2018-08-25 --------------------------- @@ -660,6 +685,7 @@ Version 0.1 - 2013-07-08 * Initial release +.. _1.7.0: https://github.com/python-gitlab/python-gitlab/compare/1.6.0...1.7.0 .. _1.6.0: https://github.com/python-gitlab/python-gitlab/compare/1.5.1...1.6.0 .. _1.5.1: https://github.com/python-gitlab/python-gitlab/compare/1.5.0...1.5.1 .. _1.5.0: https://github.com/python-gitlab/python-gitlab/compare/1.4.0...1.5.0 diff --git a/gitlab/__init__.py b/gitlab/__init__.py index 477d56428..01f9426d7 100644 --- a/gitlab/__init__.py +++ b/gitlab/__init__.py @@ -31,7 +31,7 @@ from gitlab import utils # noqa __title__ = 'python-gitlab' -__version__ = '1.6.0' +__version__ = '1.7.0' __author__ = 'Gauvain Pocentek' __email__ = 'gauvain@pocentek.net' __license__ = 'LGPL3'