From 5c11203a8b281f6ab34f7e85073fadcfc395503c Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 23 Jan 2025 11:24:13 +0100 Subject: [PATCH 1/9] feat(unit): add pull mirror tests --- tests/unit/objects/test_pull_mirror.py | 67 ++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 tests/unit/objects/test_pull_mirror.py diff --git a/tests/unit/objects/test_pull_mirror.py b/tests/unit/objects/test_pull_mirror.py new file mode 100644 index 000000000..3fa671bc2 --- /dev/null +++ b/tests/unit/objects/test_pull_mirror.py @@ -0,0 +1,67 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/pull_mirror.html +""" + +import pytest +import responses + +from gitlab.v4.objects import ProjectPullMirror + + +@pytest.fixture +def resp_pull_mirror(): + content = { + "update_status": "none", + "url": "https://gitlab.example.com/root/mirror.git", + "last_error": None, + "last_update_at": "2024-12-03T08:01:05.466Z", + "last_update_started_at": "2024-12-03T08:01:05.342Z", + "last_successful_update_at": None, + "enabled": True, + "mirror_trigger_builds": False, + "only_mirror_protected_branches": None, + "mirror_overwrites_diverged_branches": None, + "mirror_branch_regex": None, + } + + with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: + rsps.add( + method=responses.PUT, + url="http://localhost/api/v4/projects/1/mirror/pull", + json=content, + content_type="application/json", + status=200, + ) + + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/projects/1/mirror/pull", + status=200, + ) + + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/mirror/pull", + json=content, + content_type="application/json", + status=200, + ) + + yield rsps + + +def test_create_project_pull_mirror(project, resp_pull_mirror): + mirror = project.pull_mirror.create( + {"url": "https://gitlab.example.com/root/mirror.git"} + ) + assert mirror.enabled + + +def test_start_project_pull_mirror(project, resp_pull_mirror): + project.pull_mirror.start() + + +def test_get_project_pull_mirror(project, resp_pull_mirror): + mirror = project.pull_mirror.get() + assert isinstance(mirror, ProjectPullMirror) + assert mirror.enabled From 2411bff4fd1dab6a1dd70070441b52e9a2927a63 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 23 Jan 2025 11:34:24 +0100 Subject: [PATCH 2/9] feat(projects): add pull mirror class --- gitlab/v4/objects/projects.py | 62 +++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py index b2e86a65d..09887eddd 100644 --- a/gitlab/v4/objects/projects.py +++ b/gitlab/v4/objects/projects.py @@ -128,6 +128,8 @@ "ProjectForkManager", "ProjectRemoteMirror", "ProjectRemoteMirrorManager", + "ProjectPullMirror", + "ProjectPullMirrorManager", "ProjectStorage", "ProjectStorageManager", "SharedProject", @@ -249,6 +251,7 @@ class Project( releases: ProjectReleaseManager resource_groups: ProjectResourceGroupManager remote_mirrors: "ProjectRemoteMirrorManager" + pull_mirror: "ProjectPullMirrorManager" repositories: ProjectRegistryRepositoryManager runners: ProjectRunnerManager secure_files: ProjectSecureFileManager @@ -1240,6 +1243,65 @@ class ProjectRemoteMirrorManager( _update_attrs = RequiredOptional(optional=("enabled", "only_protected_branches")) +class ProjectPullMirror(SaveMixin, RESTObject): + _id_attr = None + + +class ProjectPullMirrorManager(GetWithoutIdMixin, UpdateMixin, RESTManager): + _path = "/projects/{project_id}/mirror/pull" + _obj_cls = ProjectPullMirror + _from_parent_attrs = {"project_id": "id"} + _update_attrs = RequiredOptional(optional=("url",)) + + def get(self, **kwargs: Any) -> ProjectPullMirror: + return cast(ProjectPullMirror, super().get(**kwargs)) + + @exc.on_http_error(exc.GitlabCreateError) + def create(self, data: Dict[str, Any], **kwargs: Any) -> ProjectPullMirror: + """Create a new object. + + Args: + data: parameters to send to the server to create the + resource + **kwargs: Extra options to send to the server (e.g. sudo) + + Returns: + A new instance of the managed object class built with + the data sent by the server + + Raises: + GitlabAuthenticationError: If authentication is not correct + GitlabCreateError: If the server cannot perform the request + """ + if TYPE_CHECKING: + assert data is not None + self._create_attrs.validate_attrs(data=data) + + if TYPE_CHECKING: + assert self.path is not None + server_data = self.gitlab.http_put(self.path, post_data=data, **kwargs) + + if TYPE_CHECKING: + assert not isinstance(server_data, requests.Response) + return self._obj_cls(self, server_data) + + @cli.register_custom_action(cls_names="ProjectPullMirrorManager") + @exc.on_http_error(exc.GitlabCreateError) + def start(self, **kwargs: Any) -> None: + """Start the pull mirroring process for the project. + + Args: + **kwargs: Extra options to send to the server (e.g. sudo) + + Raises: + GitlabAuthenticationError: If authentication is not correct + GitlabCreateError: If the server failed to perform the request + """ + if TYPE_CHECKING: + assert self.path is not None + self.gitlab.http_post(self.path, **kwargs) + + class ProjectStorage(RefreshMixin, RESTObject): pass From 3b31ade152eb61363a68cf0509867ff8738ccdaf Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 23 Jan 2025 11:35:28 +0100 Subject: [PATCH 3/9] feat(functional): add pull mirror test --- tests/functional/api/test_projects.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/functional/api/test_projects.py b/tests/functional/api/test_projects.py index 18c850680..edb7e31df 100644 --- a/tests/functional/api/test_projects.py +++ b/tests/functional/api/test_projects.py @@ -310,6 +310,24 @@ def test_project_remote_mirrors(project): mirror.delete() +def test_project_pull_mirrors(project): + mirror_url = "https://gitlab.example.com/root/mirror.git" + + mirror = project.pull_mirror.create({"url": mirror_url}) + assert mirror.url == mirror_url + + mirror.enabled = True + mirror.save() + + mirror = project.pull_mirror.get() + assert isinstance(mirror, gitlab.v4.objects.ProjectPullMirror) + assert mirror.url == mirror_url + assert mirror.enabled is True + + mirror.enabled = False + mirror.save() + + def test_project_services(project): # Use 'update' to create a service as we don't have a 'create' method and # to add one is somewhat complicated so it hasn't been done yet. From 9b374b2c051f71b8ef10e22209b8e90730af9d9b Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 23 Jan 2025 11:36:34 +0100 Subject: [PATCH 4/9] docs: add usage of pull mirror --- docs/api-objects.rst | 1 + docs/gl_objects/pull_mirror.rst | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 docs/gl_objects/pull_mirror.rst diff --git a/docs/api-objects.rst b/docs/api-objects.rst index c8d4b7891..d8e038ff5 100644 --- a/docs/api-objects.rst +++ b/docs/api-objects.rst @@ -52,6 +52,7 @@ API examples gl_objects/protected_container_repositories gl_objects/protected_environments gl_objects/protected_packages + gl_objects/pull_mirror gl_objects/releases gl_objects/runners gl_objects/remote_mirrors diff --git a/docs/gl_objects/pull_mirror.rst b/docs/gl_objects/pull_mirror.rst new file mode 100644 index 000000000..e62cd6a4e --- /dev/null +++ b/docs/gl_objects/pull_mirror.rst @@ -0,0 +1,38 @@ +###################### +Project Pull Mirror +###################### + +Pull Mirror allow you to set up pull mirroring for a project. + +References +========== + +* v4 API: + + + :class:`gitlab.v4.objects.ProjectPullMirror` + + :class:`gitlab.v4.objects.ProjectPullMirrorManager` + + :attr:`gitlab.v4.objects.Project.pull_mirror` + +* GitLab API: https://docs.gitlab.com/ce/api/pull_mirror.html + +Examples +-------- + +Get the current pull mirror of a project:: + + mirrors = project.pull_mirror.get() + +Create (and enable) a remote mirror for a project:: + + mirror = project.pull_mirror.create({'url': 'https://gitlab.com/example.git', + 'enabled': True}) + +Update an existing remote mirror's attributes:: + + mirror.enabled = False + mirror.only_protected_branches = True + mirror.save() + +Start an sync of the pull mirror:: + + mirror.start() From 9e186726c8a5ae70ca49c56b2be09b34dbf5b642 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 23 Jan 2025 11:37:24 +0100 Subject: [PATCH 5/9] docs: remove old pull mirror implementation --- docs/gl_objects/projects.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/gl_objects/projects.rst b/docs/gl_objects/projects.rst index 5697fd206..6e6c00ad4 100644 --- a/docs/gl_objects/projects.rst +++ b/docs/gl_objects/projects.rst @@ -246,14 +246,6 @@ Get a list of users for the repository:: # search for users users = p.users.list(search='pattern') -Start the pull mirroring process (EE edition):: - - project.mirror_pull() - -Get a project’s pull mirror details (EE edition):: - - mirror_pull_details = project.mirror_pull_details() - Import / Export =============== From 7f6fd5c3aac5e2f18adf212adbce0ac04c7150e1 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 28 Jan 2025 09:20:53 +0100 Subject: [PATCH 6/9] chore: add deprecation warning for mirror_pull functions --- gitlab/v4/objects/projects.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py index 09887eddd..e9990afeb 100644 --- a/gitlab/v4/objects/projects.py +++ b/gitlab/v4/objects/projects.py @@ -608,6 +608,13 @@ def mirror_pull(self, **kwargs: Any) -> None: GitlabAuthenticationError: If authentication is not correct GitlabCreateError: If the server failed to perform the request """ + utils.warn( + message=( + "project.mirror_pull() is deprecated and will be removed in a " + "future major version. Use project.pull_mirror.start() instead." + ), + category=DeprecationWarning, + ) path = f"/projects/{self.encoded_id}/mirror/pull" self.manager.gitlab.http_post(path, **kwargs) @@ -628,6 +635,13 @@ def mirror_pull_details(self, **kwargs: Any) -> Dict[str, Any]: Returns: dict of the parsed json returned by the server """ + utils.warn( + message=( + "project.mirror_pull_details() is deprecated and will be removed in a " + "future major version. Use project.pull_mirror.get() instead." + ), + category=DeprecationWarning, + ) path = f"/projects/{self.encoded_id}/mirror/pull" result = self.manager.gitlab.http_get(path, **kwargs) if TYPE_CHECKING: From f4300782485ee6c38578fa3481061bd621656b0e Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Tue, 28 Jan 2025 13:34:29 +0100 Subject: [PATCH 7/9] chore: relax typing constraints for response action --- gitlab/mixins.py | 4 ++-- gitlab/utils.py | 2 +- gitlab/v4/objects/artifacts.py | 8 ++++---- gitlab/v4/objects/files.py | 2 +- gitlab/v4/objects/jobs.py | 6 +++--- gitlab/v4/objects/packages.py | 4 ++-- gitlab/v4/objects/projects.py | 4 ++-- gitlab/v4/objects/repositories.py | 4 ++-- gitlab/v4/objects/secure_files.py | 4 ++-- gitlab/v4/objects/snippets.py | 4 ++-- tests/functional/api/test_import_export.py | 2 +- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/gitlab/mixins.py b/gitlab/mixins.py index d2e1e0d5e..e738a5c0b 100644 --- a/gitlab/mixins.py +++ b/gitlab/mixins.py @@ -640,7 +640,7 @@ def download( def download( self, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, @@ -652,7 +652,7 @@ def download( def download( self, streamed: bool = False, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: bool = False, diff --git a/gitlab/utils.py b/gitlab/utils.py index b5ca73b09..d26518b3e 100644 --- a/gitlab/utils.py +++ b/gitlab/utils.py @@ -77,7 +77,7 @@ def format(self, record: logging.LogRecord) -> str: def response_content( response: requests.Response, streamed: bool, - action: Optional[Callable[[bytes], None]], + action: Optional[Callable[[bytes], Any]], chunk_size: int, *, iterator: bool, diff --git a/gitlab/v4/objects/artifacts.py b/gitlab/v4/objects/artifacts.py index ce6f90b99..99a231e0f 100644 --- a/gitlab/v4/objects/artifacts.py +++ b/gitlab/v4/objects/artifacts.py @@ -84,7 +84,7 @@ def download( ref_name: str, job: str, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, @@ -102,7 +102,7 @@ def download( ref_name: str, job: str, streamed: bool = False, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: bool = False, @@ -177,7 +177,7 @@ def raw( artifact_path: str, job: str, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, @@ -195,7 +195,7 @@ def raw( artifact_path: str, job: str, streamed: bool = False, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: bool = False, diff --git a/gitlab/v4/objects/files.py b/gitlab/v4/objects/files.py index ce2193c2c..e1f7b2290 100644 --- a/gitlab/v4/objects/files.py +++ b/gitlab/v4/objects/files.py @@ -308,7 +308,7 @@ def raw( file_path: str, ref: Optional[str] = None, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, diff --git a/gitlab/v4/objects/jobs.py b/gitlab/v4/objects/jobs.py index 0c77d76a7..b98255acc 100644 --- a/gitlab/v4/objects/jobs.py +++ b/gitlab/v4/objects/jobs.py @@ -152,7 +152,7 @@ def artifacts( def artifacts( self, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, @@ -229,7 +229,7 @@ def artifact( self, path: str, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, @@ -305,7 +305,7 @@ def trace( def trace( self, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, diff --git a/gitlab/v4/objects/packages.py b/gitlab/v4/objects/packages.py index c31809d80..24c1c6868 100644 --- a/gitlab/v4/objects/packages.py +++ b/gitlab/v4/objects/packages.py @@ -159,7 +159,7 @@ def download( package_version: str, file_name: str, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, @@ -177,7 +177,7 @@ def download( package_version: str, file_name: str, streamed: bool = False, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: bool = False, diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py index e9990afeb..60587fa13 100644 --- a/gitlab/v4/objects/projects.py +++ b/gitlab/v4/objects/projects.py @@ -523,7 +523,7 @@ def snapshot( self, wiki: bool = False, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, @@ -536,7 +536,7 @@ def snapshot( self, wiki: bool = False, streamed: bool = False, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: bool = False, diff --git a/gitlab/v4/objects/repositories.py b/gitlab/v4/objects/repositories.py index 85dba4b4d..aece75d74 100644 --- a/gitlab/v4/objects/repositories.py +++ b/gitlab/v4/objects/repositories.py @@ -146,7 +146,7 @@ def repository_raw_blob( self, sha: str, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, @@ -273,7 +273,7 @@ def repository_archive( self, sha: Optional[str] = None, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, diff --git a/gitlab/v4/objects/secure_files.py b/gitlab/v4/objects/secure_files.py index b329756d3..9a71a6302 100644 --- a/gitlab/v4/objects/secure_files.py +++ b/gitlab/v4/objects/secure_files.py @@ -54,7 +54,7 @@ def download( def download( self, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, @@ -66,7 +66,7 @@ def download( def download( self, streamed: bool = False, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: bool = False, diff --git a/gitlab/v4/objects/snippets.py b/gitlab/v4/objects/snippets.py index 46c618e33..ebb304a2d 100644 --- a/gitlab/v4/objects/snippets.py +++ b/gitlab/v4/objects/snippets.py @@ -61,7 +61,7 @@ def content( def content( self, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, @@ -237,7 +237,7 @@ def content( def content( self, streamed: Literal[True] = True, - action: Optional[Callable[[bytes], None]] = None, + action: Optional[Callable[[bytes], Any]] = None, chunk_size: int = 1024, *, iterator: Literal[False] = False, diff --git a/tests/functional/api/test_import_export.py b/tests/functional/api/test_import_export.py index e8d9c9abc..f7444c92c 100644 --- a/tests/functional/api/test_import_export.py +++ b/tests/functional/api/test_import_export.py @@ -50,7 +50,7 @@ def test_project_import_export(gl, project, temp_dir): raise Exception("Project export taking too much time") with open(temp_dir / "gitlab-export.tgz", "wb") as f: - export.download(streamed=True, action=f.write) # type: ignore[call-overload] + export.download(streamed=True, action=f.write) output = gl.projects.import_project( open(temp_dir / "gitlab-export.tgz", "rb"), From 0c1af08bc73611d288f1f67248cff9c32c685808 Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Tue, 28 Jan 2025 13:53:16 +0100 Subject: [PATCH 8/9] chore(tests): catch deprecation warnings --- tests/unit/objects/test_projects.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/unit/objects/test_projects.py b/tests/unit/objects/test_projects.py index 84682dea3..65e19459c 100644 --- a/tests/unit/objects/test_projects.py +++ b/tests/unit/objects/test_projects.py @@ -768,11 +768,13 @@ def test_transfer_project(project, resp_transfer_project): def test_project_pull_mirror(project, resp_start_pull_mirroring_project): - project.mirror_pull() + with pytest.warns(DeprecationWarning, match="is deprecated"): + project.mirror_pull() def test_project_pull_mirror_details(project, resp_pull_mirror_details_project): - details = project.mirror_pull_details() + with pytest.warns(DeprecationWarning, match="is deprecated"): + details = project.mirror_pull_details() assert details["last_error"] is None assert details["update_status"] == "finished" From cfa6358b4c9c44d267d8c98a53ac840fbb27d3e8 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Tue, 28 Jan 2025 14:00:38 +0000 Subject: [PATCH 9/9] chore: release v5.5.0 --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ gitlab/_version.py | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b0ac9040..12d2ff4a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,39 @@ # CHANGELOG +## v5.5.0 (2025-01-28) + +### Chores + +- Add deprecation warning for mirror_pull functions + ([`7f6fd5c`](https://github.com/python-gitlab/python-gitlab/commit/7f6fd5c3aac5e2f18adf212adbce0ac04c7150e1)) + +- Relax typing constraints for response action + ([`f430078`](https://github.com/python-gitlab/python-gitlab/commit/f4300782485ee6c38578fa3481061bd621656b0e)) + +- **tests**: Catch deprecation warnings + ([`0c1af08`](https://github.com/python-gitlab/python-gitlab/commit/0c1af08bc73611d288f1f67248cff9c32c685808)) + +### Documentation + +- Add usage of pull mirror + ([`9b374b2`](https://github.com/python-gitlab/python-gitlab/commit/9b374b2c051f71b8ef10e22209b8e90730af9d9b)) + +- Remove old pull mirror implementation + ([`9e18672`](https://github.com/python-gitlab/python-gitlab/commit/9e186726c8a5ae70ca49c56b2be09b34dbf5b642)) + +### Features + +- **functional**: Add pull mirror test + ([`3b31ade`](https://github.com/python-gitlab/python-gitlab/commit/3b31ade152eb61363a68cf0509867ff8738ccdaf)) + +- **projects**: Add pull mirror class + ([`2411bff`](https://github.com/python-gitlab/python-gitlab/commit/2411bff4fd1dab6a1dd70070441b52e9a2927a63)) + +- **unit**: Add pull mirror tests + ([`5c11203`](https://github.com/python-gitlab/python-gitlab/commit/5c11203a8b281f6ab34f7e85073fadcfc395503c)) + + ## v5.4.0 (2025-01-28) ### Bug Fixes diff --git a/gitlab/_version.py b/gitlab/_version.py index f4415c059..94ca6cfdc 100644 --- a/gitlab/_version.py +++ b/gitlab/_version.py @@ -3,4 +3,4 @@ __email__ = "gauvainpocentek@gmail.com" __license__ = "LGPL3" __title__ = "python-gitlab" -__version__ = "5.4.0" +__version__ = "5.5.0"