imbi-plugin-github 1.5.0__tar.gz → 2.9.0__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.
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/PKG-INFO +2 -2
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/pyproject.toml +4 -3
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/src/imbi_plugin_github/_hosts.py +17 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/src/imbi_plugin_github/_repos.py +41 -15
- imbi_plugin_github-2.9.0/src/imbi_plugin_github/commits.py +593 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/src/imbi_plugin_github/deployment.py +12 -13
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/src/imbi_plugin_github/lifecycle.py +411 -26
- imbi_plugin_github-2.9.0/tests/test_commits.py +549 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/tests/test_deployment.py +4 -4
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/tests/test_lifecycle.py +507 -5
- imbi_plugin_github-2.9.0/tests/test_repos.py +73 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/uv.lock +7 -7
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/.github/workflows/publish.yml +0 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/.github/workflows/test.yml +0 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/.gitignore +0 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/.pre-commit-config.yaml +0 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/CLAUDE.md +0 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/LICENSE +0 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/README.md +0 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/justfile +0 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/src/imbi_plugin_github/__init__.py +0 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/src/imbi_plugin_github/identity.py +0 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/tests/__init__.py +0 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/tests/test_hosts.py +0 -0
- {imbi_plugin_github-1.5.0 → imbi_plugin_github-2.9.0}/tests/test_identity.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: imbi-plugin-github
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.9.0
|
|
4
4
|
Summary: GitHub identity plugin for Imbi (github.com / GHEC / GHES)
|
|
5
5
|
Author-email: "Gavin M. Roy" <gavinr@aweber.com>
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.14
|
|
14
14
|
Requires-Python: >=3.14
|
|
15
15
|
Requires-Dist: httpx>=0.27
|
|
16
|
-
Requires-Dist: imbi-common
|
|
16
|
+
Requires-Dist: imbi-common[databases]==2.9.0
|
|
17
17
|
Requires-Dist: pydantic>=2
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "imbi-plugin-github"
|
|
3
|
-
version = "
|
|
3
|
+
version = "2.9.0"
|
|
4
4
|
description = "GitHub identity plugin for Imbi (github.com / GHEC / GHES)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.14"
|
|
@@ -16,7 +16,7 @@ classifiers = [
|
|
|
16
16
|
]
|
|
17
17
|
dependencies = [
|
|
18
18
|
"httpx>=0.27",
|
|
19
|
-
"imbi-common
|
|
19
|
+
"imbi-common[databases]==2.9.0",
|
|
20
20
|
"pydantic>=2",
|
|
21
21
|
]
|
|
22
22
|
|
|
@@ -30,12 +30,13 @@ github-deployment-es = "imbi_plugin_github.deployment:GitHubEnterpriseServerDepl
|
|
|
30
30
|
github-lifecycle = "imbi_plugin_github.lifecycle:GitHubLifecyclePlugin"
|
|
31
31
|
github-lifecycle-ec = "imbi_plugin_github.lifecycle:GitHubEnterpriseCloudLifecyclePlugin"
|
|
32
32
|
github-lifecycle-es = "imbi_plugin_github.lifecycle:GitHubEnterpriseServerLifecyclePlugin"
|
|
33
|
+
github-commit-sync = "imbi_plugin_github.commits:GitHubCommitSyncPlugin"
|
|
33
34
|
|
|
34
35
|
[dependency-groups]
|
|
35
36
|
dev = [
|
|
36
37
|
"basedpyright",
|
|
37
38
|
"coverage[toml]",
|
|
38
|
-
"imbi-common[server,databases]",
|
|
39
|
+
"imbi-common[server,databases]==2.9.0",
|
|
39
40
|
"pre-commit",
|
|
40
41
|
"pytest",
|
|
41
42
|
"pytest-asyncio",
|
|
@@ -48,3 +48,20 @@ def require_ghec_tenant_host(host: str, label: str) -> str:
|
|
|
48
48
|
f'got {host!r}'
|
|
49
49
|
)
|
|
50
50
|
return host
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def host_to_api_base(host: str) -> str:
|
|
54
|
+
"""Map a resolved GitHub host to its REST API base.
|
|
55
|
+
|
|
56
|
+
The single source of truth for GitHub's flavor routing:
|
|
57
|
+
``github.com`` -> ``api.github.com``, a ``*.ghe.com`` tenant ->
|
|
58
|
+
``api.<tenant>.ghe.com``, and a GHES appliance -> ``<host>/api/v3``.
|
|
59
|
+
Shared by the deployment plugins (which resolve the host per call
|
|
60
|
+
via ``_resolve_host``) and the commit-sync webhook action (which
|
|
61
|
+
resolves it from connected-plugin options at runtime).
|
|
62
|
+
"""
|
|
63
|
+
if host == 'github.com':
|
|
64
|
+
return 'https://api.github.com'
|
|
65
|
+
if host.endswith('.ghe.com'):
|
|
66
|
+
return f'https://api.{host}'
|
|
67
|
+
return f'https://{host}/api/v3'
|
|
@@ -66,23 +66,32 @@ def parse_owner_repo(url: str, target_host: str) -> tuple[str, str] | None:
|
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
def derive_owner_repo_from_links(
|
|
69
|
-
links: dict[str, str],
|
|
69
|
+
links: dict[str, str],
|
|
70
|
+
host: str,
|
|
71
|
+
*,
|
|
72
|
+
preferred_key: str | None = None,
|
|
70
73
|
) -> tuple[str, str] | None:
|
|
71
74
|
"""Find a project link pointing at ``host`` and parse owner/repo.
|
|
72
75
|
|
|
73
|
-
Prefers
|
|
74
|
-
present and
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
Prefers the dashboard link keyed by ``preferred_key`` (the bound
|
|
77
|
+
third-party-service slug) when present and pointing at ``host``,
|
|
78
|
+
then the legacy ``github-repository`` link key, then scans the
|
|
79
|
+
remaining same-host links and returns the first usable one.
|
|
80
|
+
Returns ``None`` when nothing matches.
|
|
77
81
|
"""
|
|
78
82
|
target = host.lower()
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
tried: set[str] = set()
|
|
84
|
+
for key in (preferred_key, 'github-repository'):
|
|
85
|
+
if not key or key in tried:
|
|
86
|
+
continue
|
|
87
|
+
tried.add(key)
|
|
88
|
+
url = links.get(key)
|
|
89
|
+
if url is not None:
|
|
90
|
+
owner_repo = parse_owner_repo(url, target)
|
|
91
|
+
if owner_repo is not None:
|
|
92
|
+
return owner_repo
|
|
84
93
|
for key, url in links.items():
|
|
85
|
-
if key
|
|
94
|
+
if key in tried:
|
|
86
95
|
continue
|
|
87
96
|
owner_repo = parse_owner_repo(url, target)
|
|
88
97
|
if owner_repo is not None:
|
|
@@ -91,7 +100,11 @@ def derive_owner_repo_from_links(
|
|
|
91
100
|
|
|
92
101
|
|
|
93
102
|
def resolve_owner_repo(
|
|
94
|
-
ctx: PluginContext,
|
|
103
|
+
ctx: PluginContext,
|
|
104
|
+
host: str,
|
|
105
|
+
plugin_label: str,
|
|
106
|
+
*,
|
|
107
|
+
prefer_previous_slug: bool = False,
|
|
95
108
|
) -> tuple[str, str]:
|
|
96
109
|
"""Resolve the repo for a plugin call from ``ctx``.
|
|
97
110
|
|
|
@@ -100,12 +113,25 @@ def resolve_owner_repo(
|
|
|
100
113
|
:class:`ValueError` when neither path produces a candidate so the
|
|
101
114
|
caller can surface a clear "set the GitHub Repository link"
|
|
102
115
|
message to the operator.
|
|
116
|
+
|
|
117
|
+
``prefer_previous_slug`` makes the slug fallback prefer
|
|
118
|
+
``ctx.previous_project_slug`` (when set) over ``ctx.project_slug``
|
|
119
|
+
so callers reacting to a slug rename (e.g. ``on_project_updated``)
|
|
120
|
+
can still locate the pre-rename repo on GitHub.
|
|
103
121
|
"""
|
|
104
|
-
derived = derive_owner_repo_from_links(
|
|
122
|
+
derived = derive_owner_repo_from_links(
|
|
123
|
+
ctx.project_links,
|
|
124
|
+
host,
|
|
125
|
+
preferred_key=ctx.third_party_service_slug,
|
|
126
|
+
)
|
|
105
127
|
if derived is not None:
|
|
106
128
|
return derived
|
|
107
|
-
if ctx.project_type_slugs
|
|
108
|
-
|
|
129
|
+
if ctx.project_type_slugs:
|
|
130
|
+
slug = ctx.project_slug
|
|
131
|
+
if prefer_previous_slug and ctx.previous_project_slug:
|
|
132
|
+
slug = ctx.previous_project_slug
|
|
133
|
+
if slug:
|
|
134
|
+
return ctx.project_type_slugs[0], slug
|
|
109
135
|
raise ValueError(
|
|
110
136
|
f'{plugin_label} could not determine the target repository: '
|
|
111
137
|
"set the project's GitHub Repository link or tag the project "
|