prefect-gitlab 0.3.1__tar.gz → 0.3.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {prefect_gitlab-0.3.1/prefect_gitlab.egg-info → prefect_gitlab-0.3.3}/PKG-INFO +5 -19
  2. prefect_gitlab-0.3.3/prefect_gitlab/_version.py +34 -0
  3. prefect_gitlab-0.3.3/prefect_gitlab/credentials.py +91 -0
  4. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/prefect_gitlab/repositories.py +50 -4
  5. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3/prefect_gitlab.egg-info}/PKG-INFO +5 -19
  6. prefect_gitlab-0.3.3/prefect_gitlab.egg-info/requires.txt +3 -0
  7. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/pyproject.toml +6 -3
  8. prefect_gitlab-0.3.3/tests/test_credentials.py +46 -0
  9. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/tests/test_repositories.py +44 -3
  10. prefect_gitlab-0.3.1/prefect_gitlab/_version.py +0 -16
  11. prefect_gitlab-0.3.1/prefect_gitlab/credentials.py +0 -50
  12. prefect_gitlab-0.3.1/prefect_gitlab.egg-info/requires.txt +0 -19
  13. prefect_gitlab-0.3.1/tests/test_credentials.py +0 -17
  14. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/LICENSE +0 -0
  15. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/MANIFEST.in +0 -0
  16. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/README.md +0 -0
  17. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/prefect_gitlab/__init__.py +0 -0
  18. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/SOURCES.txt +0 -0
  19. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/dependency_links.txt +0 -0
  20. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/entry_points.txt +0 -0
  21. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/top_level.txt +0 -0
  22. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/setup.cfg +0 -0
  23. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/tests/conftest.py +0 -0
  24. {prefect_gitlab-0.3.1 → prefect_gitlab-0.3.3}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: prefect-gitlab
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: A Prefect collection for working with GitLab repositories.
5
5
  Author-email: "Prefect Technologies, Inc." <help@prefect.io>
6
6
  License: Apache License 2.0
@@ -11,32 +11,18 @@ Classifier: Intended Audience :: Developers
11
11
  Classifier: Intended Audience :: System Administrators
12
12
  Classifier: License :: OSI Approved :: Apache Software License
13
13
  Classifier: Programming Language :: Python :: 3 :: Only
14
- Classifier: Programming Language :: Python :: 3.9
15
14
  Classifier: Programming Language :: Python :: 3.10
16
15
  Classifier: Programming Language :: Python :: 3.11
17
16
  Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Topic :: Software Development :: Libraries
19
- Requires-Python: >=3.9
19
+ Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Requires-Dist: prefect>=3.0.0
23
23
  Requires-Dist: python-gitlab>=3.12.0
24
24
  Requires-Dist: tenacity>=8.2.3
25
- Provides-Extra: dev
26
- Requires-Dist: aiohttp; extra == "dev"
27
- Requires-Dist: coverage; extra == "dev"
28
- Requires-Dist: interrogate; extra == "dev"
29
- Requires-Dist: mkdocs-gen-files; extra == "dev"
30
- Requires-Dist: mkdocs-material; extra == "dev"
31
- Requires-Dist: mkdocs; extra == "dev"
32
- Requires-Dist: mkdocstrings[python]; extra == "dev"
33
- Requires-Dist: mypy; extra == "dev"
34
- Requires-Dist: pillow; extra == "dev"
35
- Requires-Dist: pre-commit; extra == "dev"
36
- Requires-Dist: pytest-asyncio; extra == "dev"
37
- Requires-Dist: pytest>=8.3; extra == "dev"
38
- Requires-Dist: pytest-env; extra == "dev"
39
- Requires-Dist: pytest-xdist; extra == "dev"
25
+ Dynamic: license-file
40
26
 
41
27
  # prefect-gitlab
42
28
 
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '0.3.3'
32
+ __version_tuple__ = version_tuple = (0, 3, 3)
33
+
34
+ __commit_id__ = commit_id = 'gd77914141'
@@ -0,0 +1,91 @@
1
+ """Module used to enable authenticated interactions with GitLab"""
2
+
3
+ from typing import Optional
4
+ from urllib.parse import urlparse, urlunparse
5
+
6
+ from gitlab import Gitlab
7
+ from pydantic import Field, SecretStr
8
+
9
+ from prefect.blocks.core import Block
10
+
11
+
12
+ class GitLabCredentials(Block):
13
+ """
14
+ Store a GitLab personal access token to interact with private GitLab
15
+ repositories.
16
+
17
+ Attributes:
18
+ token: The personal access token to authenticate with GitLab.
19
+ url: URL to self-hosted GitLab instances.
20
+
21
+ Examples:
22
+ Load stored GitLab credentials:
23
+ ```python
24
+ from prefect_gitlab import GitLabCredentials
25
+ gitlab_credentials_block = GitLabCredentials.load("BLOCK_NAME")
26
+ ```
27
+ """
28
+
29
+ _block_type_name = "GitLab Credentials"
30
+ _logo_url = "https://images.ctfassets.net/gm98wzqotmnx/55edIimT4g9gbjhkh5a3Sp/dfdb9391d8f45c2e93e72e3a4d350771/gitlab-logo-500.png?h=250"
31
+
32
+ token: Optional[SecretStr] = Field(
33
+ title="Personal Access Token",
34
+ default=None,
35
+ description="A GitLab Personal Access Token with read_repository scope.",
36
+ )
37
+ url: Optional[str] = Field(
38
+ default=None, title="URL", description="URL to self-hosted GitLab instances."
39
+ )
40
+
41
+ def format_git_credentials(self, url: str) -> str:
42
+ """
43
+ Format and return the full git URL with GitLab credentials embedded.
44
+
45
+ Handles both personal access tokens and deploy tokens correctly:
46
+ - Personal access tokens: prefixed with "oauth2:"
47
+ - Deploy tokens (username:token format): used as-is
48
+ - Already prefixed tokens: not double-prefixed
49
+
50
+ Args:
51
+ url: Repository URL (e.g., "https://gitlab.com/org/repo.git")
52
+
53
+ Returns:
54
+ Complete URL with credentials embedded
55
+
56
+ Raises:
57
+ ValueError: If token is not configured
58
+ """
59
+ if not self.token:
60
+ raise ValueError("Token is required for GitLab authentication")
61
+
62
+ token_value = self.token.get_secret_value()
63
+
64
+ # Deploy token detection: contains ":" but not "oauth2:" prefix
65
+ # Deploy tokens should not have oauth2: prefix (GitLab 16.3.4+ rejects them)
66
+ # See: https://github.com/PrefectHQ/prefect/issues/10832
67
+ if ":" in token_value and not token_value.startswith("oauth2:"):
68
+ credentials = token_value
69
+ # Personal access token: add oauth2: prefix
70
+ # See: https://github.com/PrefectHQ/prefect/issues/16836
71
+ elif not token_value.startswith("oauth2:"):
72
+ credentials = f"oauth2:{token_value}"
73
+ else:
74
+ # Already prefixed
75
+ credentials = token_value
76
+
77
+ # Insert credentials into URL
78
+ parsed = urlparse(url)
79
+ return urlunparse(parsed._replace(netloc=f"{credentials}@{parsed.netloc}"))
80
+
81
+ def get_client(self) -> Gitlab:
82
+ """
83
+ Gets an authenticated GitLab client.
84
+
85
+ Returns:
86
+ An authenticated GitLab client.
87
+ """
88
+ # ref: https://python-gitlab.readthedocs.io/en/stable/
89
+ gitlab = Gitlab(url=self.url, oauth_token=self.token.get_secret_value())
90
+ gitlab.auth()
91
+ return gitlab
@@ -43,6 +43,7 @@ Examples:
43
43
 
44
44
  import io
45
45
  import shutil
46
+ import subprocess
46
47
  import urllib.parse
47
48
  from pathlib import Path
48
49
  from tempfile import TemporaryDirectory
@@ -51,8 +52,8 @@ from typing import Optional, Tuple, Union
51
52
  from pydantic import Field
52
53
  from tenacity import retry, stop_after_attempt, wait_fixed, wait_random
53
54
 
55
+ from prefect._internal.compatibility.async_dispatch import async_dispatch
54
56
  from prefect.filesystems import ReadableDeploymentStorage
55
- from prefect.utilities.asyncutils import sync_compatible
56
57
  from prefect.utilities.processutils import run_process
57
58
  from prefect_gitlab.credentials import GitLabCredentials
58
59
 
@@ -135,7 +136,6 @@ class GitLabRepository(ReadableDeploymentStorage):
135
136
 
136
137
  return str(content_source), str(content_destination)
137
138
 
138
- @sync_compatible
139
139
  @retry(
140
140
  stop=stop_after_attempt(MAX_CLONE_ATTEMPTS),
141
141
  wait=wait_fixed(CLONE_RETRY_MIN_DELAY_SECONDS)
@@ -145,13 +145,14 @@ class GitLabRepository(ReadableDeploymentStorage):
145
145
  ),
146
146
  reraise=True,
147
147
  )
148
- async def get_directory(
148
+ async def aget_directory(
149
149
  self, from_path: Optional[str] = None, local_path: Optional[str] = None
150
150
  ) -> None:
151
151
  """
152
152
  Clones a GitLab project specified in `from_path` to the provided `local_path`;
153
153
  defaults to cloning the repository reference configured on the Block to the
154
- present working directory.
154
+ present working directory. Async version.
155
+
155
156
  Args:
156
157
  from_path: If provided, interpreted as a subdirectory of the underlying
157
158
  repository that will be copied to the provided local path.
@@ -184,3 +185,48 @@ class GitLabRepository(ReadableDeploymentStorage):
184
185
  shutil.copytree(
185
186
  src=content_source, dst=content_destination, dirs_exist_ok=True
186
187
  )
188
+
189
+ @retry(
190
+ stop=stop_after_attempt(MAX_CLONE_ATTEMPTS),
191
+ wait=wait_fixed(CLONE_RETRY_MIN_DELAY_SECONDS)
192
+ + wait_random(
193
+ CLONE_RETRY_MIN_DELAY_JITTER_SECONDS,
194
+ CLONE_RETRY_MAX_DELAY_JITTER_SECONDS,
195
+ ),
196
+ reraise=True,
197
+ )
198
+ @async_dispatch(aget_directory)
199
+ def get_directory(
200
+ self, from_path: Optional[str] = None, local_path: Optional[str] = None
201
+ ) -> None:
202
+ """
203
+ Clones a GitLab project specified in `from_path` to the provided `local_path`;
204
+ defaults to cloning the repository reference configured on the Block to the
205
+ present working directory.
206
+
207
+ Args:
208
+ from_path: If provided, interpreted as a subdirectory of the underlying
209
+ repository that will be copied to the provided local path.
210
+ local_path: A local path to clone to; defaults to present working directory.
211
+ """
212
+ cmd = ["git", "clone", self._create_repo_url()]
213
+ if self.reference:
214
+ cmd += ["-b", self.reference]
215
+
216
+ if self.git_depth is not None:
217
+ cmd += ["--depth", str(self.git_depth)]
218
+
219
+ with TemporaryDirectory(suffix="prefect") as tmp_dir:
220
+ cmd.append(tmp_dir)
221
+
222
+ result = subprocess.run(cmd, capture_output=True, text=True)
223
+ if result.returncode != 0:
224
+ raise OSError(f"Failed to pull from remote:\n {result.stderr}")
225
+
226
+ content_source, content_destination = self._get_paths(
227
+ dst_dir=local_path, src_dir=tmp_dir, sub_directory=from_path
228
+ )
229
+
230
+ shutil.copytree(
231
+ src=content_source, dst=content_destination, dirs_exist_ok=True
232
+ )
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: prefect-gitlab
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: A Prefect collection for working with GitLab repositories.
5
5
  Author-email: "Prefect Technologies, Inc." <help@prefect.io>
6
6
  License: Apache License 2.0
@@ -11,32 +11,18 @@ Classifier: Intended Audience :: Developers
11
11
  Classifier: Intended Audience :: System Administrators
12
12
  Classifier: License :: OSI Approved :: Apache Software License
13
13
  Classifier: Programming Language :: Python :: 3 :: Only
14
- Classifier: Programming Language :: Python :: 3.9
15
14
  Classifier: Programming Language :: Python :: 3.10
16
15
  Classifier: Programming Language :: Python :: 3.11
17
16
  Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Topic :: Software Development :: Libraries
19
- Requires-Python: >=3.9
19
+ Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Requires-Dist: prefect>=3.0.0
23
23
  Requires-Dist: python-gitlab>=3.12.0
24
24
  Requires-Dist: tenacity>=8.2.3
25
- Provides-Extra: dev
26
- Requires-Dist: aiohttp; extra == "dev"
27
- Requires-Dist: coverage; extra == "dev"
28
- Requires-Dist: interrogate; extra == "dev"
29
- Requires-Dist: mkdocs-gen-files; extra == "dev"
30
- Requires-Dist: mkdocs-material; extra == "dev"
31
- Requires-Dist: mkdocs; extra == "dev"
32
- Requires-Dist: mkdocstrings[python]; extra == "dev"
33
- Requires-Dist: mypy; extra == "dev"
34
- Requires-Dist: pillow; extra == "dev"
35
- Requires-Dist: pre-commit; extra == "dev"
36
- Requires-Dist: pytest-asyncio; extra == "dev"
37
- Requires-Dist: pytest>=8.3; extra == "dev"
38
- Requires-Dist: pytest-env; extra == "dev"
39
- Requires-Dist: pytest-xdist; extra == "dev"
25
+ Dynamic: license-file
40
26
 
41
27
  # prefect-gitlab
42
28
 
@@ -0,0 +1,3 @@
1
+ prefect>=3.0.0
2
+ python-gitlab>=3.12.0
3
+ tenacity>=8.2.3
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
  name = "prefect-gitlab"
7
7
  description = "A Prefect collection for working with GitLab repositories."
8
8
  readme = "README.md"
9
- requires-python = ">=3.9"
9
+ requires-python = ">=3.10"
10
10
  license = { text = "Apache License 2.0" }
11
11
  keywords = ["prefect"]
12
12
  authors = [{ name = "Prefect Technologies, Inc.", email = "help@prefect.io" }]
@@ -16,16 +16,16 @@ classifiers = [
16
16
  "Intended Audience :: System Administrators",
17
17
  "License :: OSI Approved :: Apache Software License",
18
18
  "Programming Language :: Python :: 3 :: Only",
19
- "Programming Language :: Python :: 3.9",
20
19
  "Programming Language :: Python :: 3.10",
21
20
  "Programming Language :: Python :: 3.11",
22
21
  "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
23
  "Topic :: Software Development :: Libraries",
24
24
  ]
25
25
  dependencies = ["prefect>=3.0.0", "python-gitlab>=3.12.0", "tenacity>=8.2.3"]
26
26
  dynamic = ["version"]
27
27
 
28
- [project.optional-dependencies]
28
+ [dependency-groups]
29
29
  dev = [
30
30
  "aiohttp",
31
31
  "coverage",
@@ -74,3 +74,6 @@ show_missing = true
74
74
  asyncio_default_fixture_loop_scope = "session"
75
75
  asyncio_mode = "auto"
76
76
  env = ["PREFECT_TEST_MODE=1"]
77
+
78
+ [tool.uv.sources]
79
+ prefect = { path = "../../../" }
@@ -0,0 +1,46 @@
1
+ from unittest.mock import MagicMock
2
+
3
+ import pytest
4
+ from prefect_gitlab.credentials import GitLabCredentials
5
+
6
+
7
+ def test_gitlab_credentials_get_client(monkeypatch):
8
+ mock_gitlab = MagicMock()
9
+ monkeypatch.setattr("prefect_gitlab.credentials.Gitlab", mock_gitlab)
10
+ gitlab_credentials = GitLabCredentials(
11
+ url="https://gitlab.example.com", token="my-token"
12
+ )
13
+ gitlab_credentials.get_client()
14
+ mock_gitlab.assert_called_once_with(
15
+ url=gitlab_credentials.url,
16
+ oauth_token=gitlab_credentials.token.get_secret_value(),
17
+ )
18
+ mock_gitlab.assert_called_once()
19
+
20
+
21
+ def test_format_git_credentials_personal_access_token():
22
+ """Test that personal access tokens get oauth2: prefix and are embedded in URL."""
23
+ credentials = GitLabCredentials(token="my-personal-token")
24
+ result = credentials.format_git_credentials("https://gitlab.com/org/repo.git")
25
+ assert result == "https://oauth2:my-personal-token@gitlab.com/org/repo.git"
26
+
27
+
28
+ def test_format_git_credentials_deploy_token():
29
+ """Test that deploy tokens (username:token format) are used as-is in URL."""
30
+ credentials = GitLabCredentials(token="deploy-user:deploy-token-value")
31
+ result = credentials.format_git_credentials("https://gitlab.com/org/repo.git")
32
+ assert result == "https://deploy-user:deploy-token-value@gitlab.com/org/repo.git"
33
+
34
+
35
+ def test_format_git_credentials_already_prefixed():
36
+ """Test that already-prefixed tokens don't get double-prefixed in URL."""
37
+ credentials = GitLabCredentials(token="oauth2:my-token")
38
+ result = credentials.format_git_credentials("https://gitlab.com/org/repo.git")
39
+ assert result == "https://oauth2:my-token@gitlab.com/org/repo.git"
40
+
41
+
42
+ def test_format_git_credentials_no_token_raises():
43
+ """Test that missing token raises ValueError."""
44
+ credentials = GitLabCredentials()
45
+ with pytest.raises(ValueError, match="Token is required for GitLab authentication"):
46
+ credentials.format_git_credentials("https://gitlab.com/org/repo.git")
@@ -1,7 +1,8 @@
1
1
  import os
2
2
  from pathlib import Path
3
3
  from tempfile import TemporaryDirectory
4
- from typing import Set, Tuple
4
+ from typing import Coroutine, Set, Tuple
5
+ from unittest.mock import AsyncMock
5
6
 
6
7
  import prefect_gitlab
7
8
  import pytest
@@ -9,8 +10,6 @@ from prefect_gitlab.credentials import GitLabCredentials
9
10
  from prefect_gitlab.repositories import GitLabRepository # noqa: E402
10
11
  from pydantic import SecretStr
11
12
 
12
- from prefect.testing.utilities import AsyncMock
13
-
14
13
 
15
14
  class TestGitLab:
16
15
  def setup_test_directory(
@@ -253,3 +252,45 @@ class TestGitLab:
253
252
  print(mock.call_count)
254
253
  # Verify that the function retried the expected number of times
255
254
  assert mock.call_count == MAX_CLONE_ATTEMPTS
255
+
256
+
257
+ class TestGitLabRepositoryAsyncDispatch:
258
+ """Tests for GitLabRepository.get_directory migrated from @sync_compatible to @async_dispatch.
259
+
260
+ These tests verify the critical behavior from issue #15008 where
261
+ @sync_compatible would incorrectly return coroutines in sync context.
262
+ """
263
+
264
+ def test_get_directory_sync_context_returns_none_not_coroutine(self, monkeypatch):
265
+ """get_directory must return None (not coroutine) in sync context.
266
+
267
+ This is the critical regression test for issues #14712 and #14625.
268
+ """
269
+ import subprocess
270
+ from unittest.mock import MagicMock
271
+
272
+ mock_result = MagicMock()
273
+ mock_result.returncode = 0
274
+ mock_result.stderr = ""
275
+ monkeypatch.setattr(subprocess, "run", MagicMock(return_value=mock_result))
276
+
277
+ g = GitLabRepository(repository="prefect")
278
+ result = g.get_directory()
279
+
280
+ assert not isinstance(result, Coroutine), "sync context returned coroutine"
281
+ assert result is None
282
+
283
+ async def test_get_directory_async_context_returns_coroutine(self, monkeypatch):
284
+ """get_directory should dispatch to async and return coroutine in async context."""
285
+
286
+ class p:
287
+ returncode = 0
288
+
289
+ mock = AsyncMock(return_value=p())
290
+ monkeypatch.setattr(prefect_gitlab.repositories, "run_process", mock)
291
+
292
+ g = GitLabRepository(repository="prefect")
293
+ result = g.get_directory()
294
+
295
+ assert isinstance(result, Coroutine)
296
+ await result # should complete without error
@@ -1,16 +0,0 @@
1
- # file generated by setuptools_scm
2
- # don't change, don't track in version control
3
- TYPE_CHECKING = False
4
- if TYPE_CHECKING:
5
- from typing import Tuple, Union
6
- VERSION_TUPLE = Tuple[Union[int, str], ...]
7
- else:
8
- VERSION_TUPLE = object
9
-
10
- version: str
11
- __version__: str
12
- __version_tuple__: VERSION_TUPLE
13
- version_tuple: VERSION_TUPLE
14
-
15
- __version__ = version = '0.3.1'
16
- __version_tuple__ = version_tuple = (0, 3, 1)
@@ -1,50 +0,0 @@
1
- """Module used to enable authenticated interactions with GitLab"""
2
-
3
- from typing import Optional
4
-
5
- from gitlab import Gitlab
6
- from pydantic import Field, SecretStr
7
-
8
- from prefect.blocks.core import Block
9
-
10
-
11
- class GitLabCredentials(Block):
12
- """
13
- Store a GitLab personal access token to interact with private GitLab
14
- repositories.
15
-
16
- Attributes:
17
- token: The personal access token to authenticate with GitLab.
18
- url: URL to self-hosted GitLab instances.
19
-
20
- Examples:
21
- Load stored GitLab credentials:
22
- ```python
23
- from prefect_gitlab import GitLabCredentials
24
- gitlab_credentials_block = GitLabCredentials.load("BLOCK_NAME")
25
- ```
26
- """
27
-
28
- _block_type_name = "GitLab Credentials"
29
- _logo_url = "https://images.ctfassets.net/gm98wzqotmnx/55edIimT4g9gbjhkh5a3Sp/dfdb9391d8f45c2e93e72e3a4d350771/gitlab-logo-500.png?h=250"
30
-
31
- token: Optional[SecretStr] = Field(
32
- title="Personal Access Token",
33
- default=None,
34
- description="A GitLab Personal Access Token with read_repository scope.",
35
- )
36
- url: Optional[str] = Field(
37
- default=None, title="URL", description="URL to self-hosted GitLab instances."
38
- )
39
-
40
- def get_client(self) -> Gitlab:
41
- """
42
- Gets an authenticated GitLab client.
43
-
44
- Returns:
45
- An authenticated GitLab client.
46
- """
47
- # ref: https://python-gitlab.readthedocs.io/en/stable/
48
- gitlab = Gitlab(url=self.url, oauth_token=self.token.get_secret_value())
49
- gitlab.auth()
50
- return gitlab
@@ -1,19 +0,0 @@
1
- prefect>=3.0.0
2
- python-gitlab>=3.12.0
3
- tenacity>=8.2.3
4
-
5
- [dev]
6
- aiohttp
7
- coverage
8
- interrogate
9
- mkdocs-gen-files
10
- mkdocs-material
11
- mkdocs
12
- mkdocstrings[python]
13
- mypy
14
- pillow
15
- pre-commit
16
- pytest-asyncio
17
- pytest>=8.3
18
- pytest-env
19
- pytest-xdist
@@ -1,17 +0,0 @@
1
- from unittest.mock import MagicMock
2
-
3
- from prefect_gitlab.credentials import GitLabCredentials
4
-
5
-
6
- def test_gitlab_credentials_get_client(monkeypatch):
7
- mock_gitlab = MagicMock()
8
- monkeypatch.setattr("prefect_gitlab.credentials.Gitlab", mock_gitlab)
9
- gitlab_credentials = GitLabCredentials(
10
- url="https://gitlab.example.com", token="my-token"
11
- )
12
- gitlab_credentials.get_client()
13
- mock_gitlab.assert_called_once_with(
14
- url=gitlab_credentials.url,
15
- oauth_token=gitlab_credentials.token.get_secret_value(),
16
- )
17
- mock_gitlab.assert_called_once()
File without changes
File without changes
File without changes