imbi-plugin-github 2.8.0__tar.gz → 2.9.1__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 (25) hide show
  1. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/PKG-INFO +2 -2
  2. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/pyproject.toml +4 -3
  3. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/src/imbi_plugin_github/_hosts.py +17 -0
  4. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/src/imbi_plugin_github/_repos.py +25 -12
  5. imbi_plugin_github-2.9.1/src/imbi_plugin_github/commits.py +593 -0
  6. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/src/imbi_plugin_github/deployment.py +6 -7
  7. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/src/imbi_plugin_github/lifecycle.py +78 -42
  8. imbi_plugin_github-2.9.1/tests/test_commits.py +549 -0
  9. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/tests/test_lifecycle.py +78 -0
  10. imbi_plugin_github-2.9.1/tests/test_repos.py +73 -0
  11. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/uv.lock +7 -7
  12. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/.github/workflows/publish.yml +0 -0
  13. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/.github/workflows/test.yml +0 -0
  14. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/.gitignore +0 -0
  15. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/.pre-commit-config.yaml +0 -0
  16. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/CLAUDE.md +0 -0
  17. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/LICENSE +0 -0
  18. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/README.md +0 -0
  19. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/justfile +0 -0
  20. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/src/imbi_plugin_github/__init__.py +0 -0
  21. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/src/imbi_plugin_github/identity.py +0 -0
  22. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/tests/__init__.py +0 -0
  23. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/tests/test_deployment.py +0 -0
  24. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/tests/test_hosts.py +0 -0
  25. {imbi_plugin_github-2.8.0 → imbi_plugin_github-2.9.1}/tests/test_identity.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: imbi-plugin-github
3
- Version: 2.8.0
3
+ Version: 2.9.1
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==2.8.0
16
+ Requires-Dist: imbi-common[databases]==2.9.1
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 = "2.8.0"
3
+ version = "2.9.1"
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==2.8.0",
19
+ "imbi-common[databases]==2.9.1",
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]==2.8.0",
39
+ "imbi-common[server,databases]==2.9.1",
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], host: 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 an explicit ``github-repository`` link key when one is
74
- present and points at ``host``; otherwise scans the remaining
75
- same-host links and returns the first usable one. Returns
76
- ``None`` when nothing matches.
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
- preferred = links.get('github-repository')
80
- if preferred is not None:
81
- owner_repo = parse_owner_repo(preferred, target)
82
- if owner_repo is not None:
83
- return owner_repo
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 == 'github-repository':
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:
@@ -110,7 +119,11 @@ def resolve_owner_repo(
110
119
  so callers reacting to a slug rename (e.g. ``on_project_updated``)
111
120
  can still locate the pre-rename repo on GitHub.
112
121
  """
113
- derived = derive_owner_repo_from_links(ctx.project_links, host)
122
+ derived = derive_owner_repo_from_links(
123
+ ctx.project_links,
124
+ host,
125
+ preferred_key=ctx.third_party_service_slug,
126
+ )
114
127
  if derived is not None:
115
128
  return derived
116
129
  if ctx.project_type_slugs: