imbi-plugin-github 2.16.0__tar.gz → 2.17.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.
Files changed (36) hide show
  1. imbi_plugin_github-2.17.0/.gitignore +22 -0
  2. {imbi_plugin_github-2.16.0 → imbi_plugin_github-2.17.0}/PKG-INFO +3 -4
  3. imbi_plugin_github-2.17.0/moon.yml +22 -0
  4. imbi_plugin_github-2.17.0/pyproject.toml +46 -0
  5. imbi_plugin_github-2.17.0/src/imbi/plugins/github/README.md +107 -0
  6. {imbi_plugin_github-2.16.0/src/imbi_plugin_github → imbi_plugin_github-2.17.0/src/imbi/plugins/github}/__init__.py +2 -2
  7. {imbi_plugin_github-2.16.0/src/imbi_plugin_github → imbi_plugin_github-2.17.0/src/imbi/plugins/github}/_app_auth.py +2 -2
  8. {imbi_plugin_github-2.16.0/src/imbi_plugin_github → imbi_plugin_github-2.17.0/src/imbi/plugins/github}/_repos.py +1 -1
  9. {imbi_plugin_github-2.16.0/src/imbi_plugin_github → imbi_plugin_github-2.17.0/src/imbi/plugins/github}/commits.py +20 -21
  10. {imbi_plugin_github-2.16.0/src/imbi_plugin_github → imbi_plugin_github-2.17.0/src/imbi/plugins/github}/deployment.py +8 -8
  11. {imbi_plugin_github-2.16.0/src/imbi_plugin_github → imbi_plugin_github-2.17.0/src/imbi/plugins/github}/doctor.py +6 -6
  12. {imbi_plugin_github-2.16.0/src/imbi_plugin_github → imbi_plugin_github-2.17.0/src/imbi/plugins/github}/identity.py +3 -3
  13. {imbi_plugin_github-2.16.0/src/imbi_plugin_github → imbi_plugin_github-2.17.0/src/imbi/plugins/github}/lifecycle.py +6 -6
  14. {imbi_plugin_github-2.16.0/src/imbi_plugin_github → imbi_plugin_github-2.17.0/src/imbi/plugins/github}/plugin.py +7 -8
  15. {imbi_plugin_github-2.16.0/src/imbi_plugin_github → imbi_plugin_github-2.17.0/src/imbi/plugins/github}/pull_requests.py +14 -14
  16. imbi_plugin_github-2.17.0/tests/__init__.py +0 -0
  17. {imbi_plugin_github-2.16.0 → imbi_plugin_github-2.17.0}/tests/test_commits.py +8 -8
  18. {imbi_plugin_github-2.16.0 → imbi_plugin_github-2.17.0}/tests/test_deployment.py +10 -10
  19. {imbi_plugin_github-2.16.0 → imbi_plugin_github-2.17.0}/tests/test_doctor.py +6 -6
  20. {imbi_plugin_github-2.16.0 → imbi_plugin_github-2.17.0}/tests/test_hosts.py +1 -1
  21. {imbi_plugin_github-2.16.0 → imbi_plugin_github-2.17.0}/tests/test_identity.py +4 -4
  22. {imbi_plugin_github-2.16.0 → imbi_plugin_github-2.17.0}/tests/test_lifecycle.py +8 -8
  23. {imbi_plugin_github-2.16.0 → imbi_plugin_github-2.17.0}/tests/test_pull_requests.py +5 -5
  24. {imbi_plugin_github-2.16.0 → imbi_plugin_github-2.17.0}/tests/test_repos.py +2 -2
  25. imbi_plugin_github-2.16.0/.github/workflows/publish.yml +0 -45
  26. imbi_plugin_github-2.16.0/.github/workflows/test.yml +0 -54
  27. imbi_plugin_github-2.16.0/.gitignore +0 -10
  28. imbi_plugin_github-2.16.0/.pre-commit-config.yaml +0 -39
  29. imbi_plugin_github-2.16.0/CLAUDE.md +0 -138
  30. imbi_plugin_github-2.16.0/LICENSE +0 -28
  31. imbi_plugin_github-2.16.0/justfile +0 -59
  32. imbi_plugin_github-2.16.0/pyproject.toml +0 -119
  33. imbi_plugin_github-2.16.0/uv.lock +0 -2024
  34. {imbi_plugin_github-2.16.0 → imbi_plugin_github-2.17.0}/README.md +0 -0
  35. {imbi_plugin_github-2.16.0/src/imbi_plugin_github → imbi_plugin_github-2.17.0/src/imbi/plugins/github}/_hosts.py +0 -0
  36. /imbi_plugin_github-2.16.0/tests/__init__.py → /imbi_plugin_github-2.17.0/src/imbi/plugins/github/py.typed +0 -0
@@ -0,0 +1,22 @@
1
+ __pycache__
2
+ *.egg-info
3
+ *.pyc
4
+ *.env
5
+ .env
6
+ .env.test
7
+ build/
8
+ dist/
9
+ docs/site/
10
+ helm/imbi/charts/
11
+ runtime/state/
12
+ .venv/
13
+ .coverage
14
+ .mypy_cache/
15
+ .pytest_cache/
16
+ .ruff_cache/
17
+ coverage.xml
18
+ site/
19
+
20
+ # moon
21
+ .moon/cache
22
+ .moon/docker
@@ -1,10 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: imbi-plugin-github
3
- Version: 2.16.0
3
+ Version: 2.17.0
4
4
  Summary: GitHub identity plugin for Imbi (github.com / GHEC / GHES)
5
5
  Author-email: "Gavin M. Roy" <gavinr@aweber.com>
6
- License: BSD-3-Clause
7
- License-File: LICENSE
6
+ License-Expression: BSD-3-Clause
8
7
  Classifier: Development Status :: 3 - Alpha
9
8
  Classifier: Intended Audience :: Developers
10
9
  Classifier: Natural Language :: English
@@ -13,7 +12,7 @@ Classifier: Programming Language :: Python :: 3
13
12
  Classifier: Programming Language :: Python :: 3.14
14
13
  Requires-Python: >=3.14
15
14
  Requires-Dist: httpx>=0.27
16
- Requires-Dist: imbi-common[databases]==2.16.0
15
+ Requires-Dist: imbi-common[databases]==2.17.0
17
16
  Requires-Dist: pydantic>=2
18
17
  Requires-Dist: pyjwt[crypto]>=2.8
19
18
  Description-Content-Type: text/markdown
@@ -0,0 +1,22 @@
1
+ # yaml-language-server: $schema=https://moonrepo.dev/schemas/project.json
2
+ layer: 'library'
3
+ stack: 'backend'
4
+ language: 'python'
5
+ tags:
6
+ - 'python'
7
+
8
+ project:
9
+ name: 'imbi-plugin-github'
10
+ description: 'GitHub identity plugin for Imbi'
11
+
12
+ dependsOn:
13
+ - 'common'
14
+
15
+ # Inherits lint / format / typecheck / test from .moon/tasks/python.yml. The
16
+ # shared test task points --cov-config at this member's pyproject, where
17
+ # [tool.coverage] scopes source + threshold to the member.
18
+ tasks:
19
+ test:
20
+ args:
21
+ - '--cov'
22
+ - '--cov-report=term-missing'
@@ -0,0 +1,46 @@
1
+ [project]
2
+ name = "imbi-plugin-github"
3
+ version = "2.17.0"
4
+ description = "GitHub identity plugin for Imbi (github.com / GHEC / GHES)"
5
+ readme = "README.md"
6
+ requires-python = ">=3.14"
7
+ license = "BSD-3-Clause"
8
+ authors = [
9
+ { name = "Gavin M. Roy", email = "gavinr@aweber.com" },
10
+ ]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Intended Audience :: Developers",
14
+ "Natural Language :: English",
15
+ "Operating System :: OS Independent",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.14",
18
+ ]
19
+ dependencies = [
20
+ "httpx>=0.27",
21
+ "imbi-common[databases]==2.17.0",
22
+ "pydantic>=2",
23
+ "pyjwt[crypto]>=2.8",
24
+ ]
25
+
26
+ [build-system]
27
+ requires = ["hatchling"]
28
+ build-backend = "hatchling.build"
29
+
30
+ # Coverage for the per-member `moon run github:test` run. Scoped to this
31
+ # member so the report/threshold reflect only imbi.plugins.github; the aggregate
32
+ # `root:coverage` run uses the root pyproject config instead.
33
+ [tool.coverage.run]
34
+ branch = true
35
+ source_pkgs = ["imbi.plugins.github"]
36
+
37
+ [tool.coverage.report]
38
+ exclude_also = ["typing.TYPE_CHECKING"]
39
+ fail_under = 85
40
+ show_missing = true
41
+
42
+ [tool.hatch.build.targets.wheel]
43
+ packages = ["src/imbi"]
44
+ include = [
45
+ "src/imbi/plugins/github/py.typed",
46
+ ]
@@ -0,0 +1,107 @@
1
+ # imbi-plugin-github
2
+
3
+ GitHub plugins for Imbi. Three flavors (github.com, GitHub Enterprise Cloud,
4
+ GitHub Enterprise Server) of each plugin type so the admin UI can wire
5
+ projects to the right backend.
6
+
7
+ ## Plugin types
8
+
9
+ | Type | Slugs |
10
+ | ---------- | -------------------------------------------------------------------- |
11
+ | Identity | `github`, `github-enterprise-cloud`, `github-enterprise-server` |
12
+ | Deployment | `github-deployment`, `github-deployment-ec`, `github-deployment-es` |
13
+ | Lifecycle | `github-lifecycle`, `github-lifecycle-ec`, `github-lifecycle-es` |
14
+ | Webhook | `github-commit-sync` |
15
+
16
+ ### Identity
17
+
18
+ Implements the OAuth App flow. The access token returned by the OAuth
19
+ grant is passed straight to GitHub APIs as a `Bearer` token, so
20
+ `materialize()` is a no-op.
21
+
22
+ ### Deployment
23
+
24
+ Drives the GitHub Deployments API (`POST /repos/{owner}/{repo}/deployments`)
25
+ plus tag and release creation. Promote behaviour is inferred from the
26
+ ref shape by the host (semver → trigger Deployment, raw SHA → cut tag
27
+ + Release). Per-env workflow inputs ride on the `USES_PLUGIN` edge as
28
+ `env_payloads` and arrive on `PluginContext.environment_config`.
29
+
30
+ ### Lifecycle
31
+
32
+ Reacts to project archive / unarchive by archiving the matching repo via
33
+ `PATCH /repos/{owner}/{repo}` with `{"archived": true|false}`. When the
34
+ `archive_target_org` option is set, archive **also** transfers the repo to
35
+ that org first via `POST /repos/{owner}/{repo}/transfer` — useful for
36
+ moving sunset projects into a dedicated "archive" org so they no longer
37
+ crowd primary-org searches.
38
+
39
+ GitHub refuses to transfer archived repos, so an already-archived source
40
+ is briefly unarchived, transferred, and re-archived at the destination.
41
+ On unarchive the plugin only flips `archived` back to `false` at the
42
+ repo's current location — it does **not** transfer back to the original
43
+ org.
44
+
45
+ Archiving requires admin scope on the repo; transferring additionally
46
+ requires admin permission on the target organization.
47
+
48
+ ### Webhook (commit / tag sync)
49
+
50
+ A single `github-commit-sync` webhook-action plugin exposes two actions
51
+ the gateway dispatches on `push` deliveries:
52
+
53
+ | Action | Handler | Records into ClickHouse |
54
+ | -------------- | -------------------------------- | ----------------------- |
55
+ | `sync_commits` | `github-commit-sync#sync_commits`| `commits` |
56
+ | `sync_tags` | `github-commit-sync#sync_tags` | `tags` |
57
+
58
+ `sync_commits` fetches the full set of commits in a push via the compare
59
+ API (paginated, so it isn't capped by the 20-commit inline payload limit);
60
+ `sync_tags` records the pushed tag and, with `reconcile_all`, the repo's
61
+ full tag list. Branch/tag gating is the rule's CEL `filter_expression`
62
+ (e.g. `ref == "refs/heads/main"`, `ref.startsWith("refs/tags/")`). The API
63
+ flavor (github.com / GHEC / GHES) is resolved at runtime — explicit
64
+ `api_base_url`, else a connected GitHub plugin on the same service, else
65
+ the service endpoint, else the payload's `repository.url`.
66
+
67
+ Unlike identity/deployment/lifecycle (which act as the OAuth user),
68
+ commit-sync runs without an actor and authenticates with a **service**
69
+ credential in one of two modes, resolved per call:
70
+
71
+ - **PAT** — a static `access_token`.
72
+ - **GitHub App** — `app_id` + `private_key`; the plugin signs an App JWT
73
+ and mints a short-lived **installation token** (cached process-wide
74
+ until shortly before it expires), so no static, expiring token is
75
+ stored. `installation_id` is optional — when unset it is discovered
76
+ from the pushed repository (`GET /repos/{owner}/{repo}/installation`).
77
+ The App needs **Contents: Read-only**.
78
+
79
+ ## Manifest options (identity)
80
+
81
+ | Option | Required | Description |
82
+ | ---------------- | --------- | -------------------------------------------------------------------------- |
83
+ | `host` | GHEC/GHES | Tenant or appliance host (e.g. `tenant.ghe.com`, `github.example.com`). |
84
+ | `default_scopes` | no | Space-separated default OAuth scopes (default: `read:user user:email repo workflow`). |
85
+
86
+ ## Credentials (identity)
87
+
88
+ | Field | Required |
89
+ | ---------------- | -------- |
90
+ | `client_id` | yes |
91
+ | `client_secret` | yes |
92
+
93
+ ## Credentials (commit-sync)
94
+
95
+ Provide **either** the PAT field **or** the GitHub App fields (all
96
+ individually optional; validated per call):
97
+
98
+ | Field | Mode | Description |
99
+ | ----------------- | ---- | ------------------------------------------------------ |
100
+ | `access_token` | PAT | Static personal/service token. |
101
+ | `app_id` | App | GitHub App identifier. |
102
+ | `private_key` | App | App private key — raw PEM or base64-encoded PEM. |
103
+ | `installation_id` | App | Optional; discovered from the repo when unset. |
104
+
105
+ ## License
106
+
107
+ BSD-3-Clause.
@@ -1,11 +1,11 @@
1
1
  """Imbi GitHub plugin (Architecture v3).
2
2
 
3
- One :class:`~imbi_plugin_github.plugin.GitHubPlugin` backs every GitHub
3
+ One :class:`~imbi.plugins.github.plugin.GitHubPlugin` backs every GitHub
4
4
  Integration. The registry discovers this package by its ``imbi_plugin_*``
5
5
  name and reads the module-level :data:`PLUGIN` attribute.
6
6
  """
7
7
 
8
- from imbi_plugin_github.plugin import GitHubPlugin
8
+ from imbi.plugins.github.plugin import GitHubPlugin
9
9
 
10
10
  #: The package's single plugin, discovered by the registry convention scan.
11
11
  PLUGIN = GitHubPlugin
@@ -9,7 +9,7 @@ per hour per ``(app, installation, host)`` rather than one per webhook
9
9
  delivery.
10
10
 
11
11
  All three GitHub flavors work unchanged: the caller resolves the API
12
- base via :func:`imbi_plugin_github._hosts.host_to_api_base` and passes
12
+ base via :func:`imbi.plugins.github._hosts.host_to_api_base` and passes
13
13
  it in, so the JWT exchange hits ``api.github.com``,
14
14
  ``api.<tenant>.ghe.com``, or ``<ghes>/api/v3`` as appropriate.
15
15
  """
@@ -26,7 +26,7 @@ import typing
26
26
  import httpx
27
27
  import jwt
28
28
 
29
- from imbi_plugin_github.deployment import (
29
+ from imbi.plugins.github.deployment import (
30
30
  _auth_headers, # pyright: ignore[reportPrivateUsage]
31
31
  _raise_on_401, # pyright: ignore[reportPrivateUsage]
32
32
  )
@@ -12,7 +12,7 @@ from __future__ import annotations
12
12
 
13
13
  import urllib.parse
14
14
 
15
- from imbi_common.plugins.base import PluginContext
15
+ from imbi.common.plugins.base import PluginContext
16
16
 
17
17
  # Reserved GitHub URL prefixes that share the host with repository URLs
18
18
  # but never point at a real ``<owner>/<repo>`` pair. Any link whose
@@ -20,7 +20,7 @@ it walks the full default-branch history and the complete tag list rather
20
20
  than a single push delta.
21
21
 
22
22
  Commit / tag rows are written to the shared ClickHouse ``commits`` /
23
- ``tags`` tables via :func:`imbi_common.clickhouse.insert`. Writes are
23
+ ``tags`` tables via :func:`imbi.common.clickhouse.insert`. Writes are
24
24
  best-effort: a storage failure is logged and swallowed so an analytics
25
25
  hiccup never 5xxs the webhook, exactly as the gateway's own event
26
26
  recording behaves.
@@ -40,21 +40,21 @@ import urllib.parse
40
40
  import httpx
41
41
  import jsonpointer
42
42
  import pydantic
43
- from imbi_common import clickhouse
44
- from imbi_common.json_pointer import JsonPointer
45
- from imbi_common.models import CommitRecord, TagRecord
46
- from imbi_common.plugins.base import (
43
+
44
+ from imbi.common import clickhouse
45
+ from imbi.common.json_pointer import JsonPointer
46
+ from imbi.common.models import CommitRecord, TagRecord
47
+ from imbi.common.plugins.base import (
47
48
  ActionDescriptor,
48
49
  CheckStatus,
49
50
  CommitSyncCapability,
50
51
  PluginContext,
51
52
  )
52
- from imbi_common.plugins.errors import PluginRateLimited
53
-
54
- from imbi_plugin_github import _app_auth
55
- from imbi_plugin_github._hosts import host_to_api_base, resolve_host
56
- from imbi_plugin_github._repos import resolve_owner_repo
57
- from imbi_plugin_github.deployment import (
53
+ from imbi.common.plugins.errors import PluginRateLimited
54
+ from imbi.plugins.github import _app_auth
55
+ from imbi.plugins.github._hosts import host_to_api_base, resolve_host
56
+ from imbi.plugins.github._repos import resolve_owner_repo
57
+ from imbi.plugins.github.deployment import (
58
58
  _auth_headers, # pyright: ignore[reportPrivateUsage]
59
59
  _check_runs_to_status, # pyright: ignore[reportPrivateUsage]
60
60
  _checks_disabled, # pyright: ignore[reportPrivateUsage]
@@ -254,7 +254,7 @@ async def _resolve_bearer(
254
254
  ) -> str:
255
255
  """Resolve the Bearer token used for the repo's GitHub API calls.
256
256
 
257
- Thin wrapper over :func:`imbi_plugin_github._app_auth.resolve_bearer`
257
+ Thin wrapper over :func:`imbi.plugins.github._app_auth.resolve_bearer`
258
258
  (the single source of truth), kept as a module-local name for the
259
259
  commit-sync call sites and tests.
260
260
  """
@@ -263,10 +263,7 @@ async def _resolve_bearer(
263
263
 
264
264
  def _resolve(pointer: jsonpointer.JsonPointer, event: object) -> object:
265
265
  """Resolve a JSON Pointer against the event, ``None`` if absent."""
266
- return typing.cast(
267
- 'object',
268
- pointer.resolve(event, None), # pyright: ignore[reportUnknownMemberType]
269
- )
266
+ return pointer.resolve(event, None)
270
267
 
271
268
 
272
269
  def _branch_short_name(ref: str) -> str:
@@ -721,7 +718,7 @@ async def _insert_best_effort(
721
718
  ) -> int:
722
719
  """Insert ``records`` into ``table``; return rows written (0 on fail).
723
720
 
724
- ``imbi_common.clickhouse.insert`` rejects an empty list, so an empty
721
+ ``imbi.common.clickhouse.insert`` rejects an empty list, so an empty
725
722
  set short-circuits to 0. A storage failure is logged and swallowed,
726
723
  mirroring the webhook actions -- an analytics hiccup must not fail the
727
724
  sync.
@@ -1178,10 +1175,10 @@ sync_commits_descriptor = ActionDescriptor(
1178
1175
  'API) and record them in the ClickHouse commits table.'
1179
1176
  ),
1180
1177
  callable=typing.cast(
1181
- 'typing.Any', 'imbi_plugin_github.commits:sync_commits'
1178
+ 'typing.Any', 'imbi.plugins.github.commits:sync_commits'
1182
1179
  ),
1183
1180
  config_model=typing.cast(
1184
- 'typing.Any', 'imbi_plugin_github.commits:SyncCommitsConfig'
1181
+ 'typing.Any', 'imbi.plugins.github.commits:SyncCommitsConfig'
1185
1182
  ),
1186
1183
  )
1187
1184
 
@@ -1192,9 +1189,11 @@ sync_tags_descriptor = ActionDescriptor(
1192
1189
  'Record the pushed tag (and, when reconcile_all is set, the full '
1193
1190
  'tag list) in the ClickHouse tags table.'
1194
1191
  ),
1195
- callable=typing.cast('typing.Any', 'imbi_plugin_github.commits:sync_tags'),
1192
+ callable=typing.cast(
1193
+ 'typing.Any', 'imbi.plugins.github.commits:sync_tags'
1194
+ ),
1196
1195
  config_model=typing.cast(
1197
- 'typing.Any', 'imbi_plugin_github.commits:SyncTagsConfig'
1196
+ 'typing.Any', 'imbi.plugins.github.commits:SyncTagsConfig'
1198
1197
  ),
1199
1198
  )
1200
1199
 
@@ -31,7 +31,8 @@ import typing
31
31
  import urllib.parse
32
32
 
33
33
  import httpx
34
- from imbi_common.plugins.base import (
34
+
35
+ from imbi.common.plugins.base import (
35
36
  CheckStatus,
36
37
  Commit,
37
38
  CompareResult,
@@ -46,10 +47,9 @@ from imbi_common.plugins.base import (
46
47
  RemoteDeployment,
47
48
  WorkflowFile,
48
49
  )
49
- from imbi_common.plugins.errors import PluginAuthenticationFailed
50
-
51
- from imbi_plugin_github._hosts import flavor_host, host_to_api_base
52
- from imbi_plugin_github._repos import (
50
+ from imbi.common.plugins.errors import PluginAuthenticationFailed
51
+ from imbi.plugins.github._hosts import flavor_host, host_to_api_base
52
+ from imbi.plugins.github._repos import (
53
53
  derive_owner_repo_from_links,
54
54
  parse_owner_repo,
55
55
  resolve_owner_repo,
@@ -345,7 +345,7 @@ class GitHubDeployment(DeploymentCapability):
345
345
  )
346
346
 
347
347
  # Backwards-compatible aliases for the previously private helpers.
348
- # The actual logic lives in :mod:`imbi_plugin_github._repos`; these
348
+ # The actual logic lives in :mod:`imbi.plugins.github._repos`; these
349
349
  # remain so existing tests that reach into the class continue to
350
350
  # work and so subclasses overriding the resolution path still have
351
351
  # a stable surface to hook into.
@@ -378,7 +378,7 @@ class GitHubDeployment(DeploymentCapability):
378
378
  """
379
379
  # Local import: ``_app_auth`` imports this module for its shared
380
380
  # HTTP helpers, so a top-level import here would be circular.
381
- from imbi_plugin_github import _app_auth
381
+ from imbi.plugins.github import _app_auth
382
382
 
383
383
  owner, repo = self._owner_repo(ctx)
384
384
  return await _app_auth.resolve_bearer(
@@ -397,7 +397,7 @@ class GitHubDeployment(DeploymentCapability):
397
397
  ``raise_for_status``. A response hook records that redirect; once
398
398
  the caller's request succeeds we resolve the repo's new
399
399
  ``full_name``/``html_url`` and stash a
400
- :class:`~imbi_common.plugins.base.LinkWriteback` on ``ctx`` so the
400
+ :class:`~imbi.common.plugins.base.LinkWriteback` on ``ctx`` so the
401
401
  host can persist the project's updated stored link. This is the
402
402
  single chokepoint for every deployment call.
403
403
  """
@@ -20,7 +20,8 @@ import re
20
20
  import typing
21
21
 
22
22
  import httpx
23
- from imbi_common.plugins.base import (
23
+
24
+ from imbi.common.plugins.base import (
24
25
  AnalysisCapability,
25
26
  AnalysisResultItem,
26
27
  AnalysisResultStatus,
@@ -31,14 +32,13 @@ from imbi_common.plugins.base import (
31
32
  ServiceConnection,
32
33
  ServiceWriteback,
33
34
  )
34
- from imbi_common.plugins.errors import PluginAuthenticationFailed
35
-
36
- from imbi_plugin_github._hosts import (
35
+ from imbi.common.plugins.errors import PluginAuthenticationFailed
36
+ from imbi.plugins.github._hosts import (
37
37
  flavor_host,
38
38
  host_to_api_base,
39
39
  )
40
- from imbi_plugin_github._repos import derive_owner_repo_from_links
41
- from imbi_plugin_github.commits import (
40
+ from imbi.plugins.github._repos import derive_owner_repo_from_links
41
+ from imbi.plugins.github.commits import (
42
42
  _resolve_bearer, # pyright: ignore[reportPrivateUsage]
43
43
  )
44
44
 
@@ -18,15 +18,15 @@ import typing
18
18
  import urllib.parse
19
19
 
20
20
  import httpx
21
- from imbi_common.plugins.base import (
21
+
22
+ from imbi.common.plugins.base import (
22
23
  AuthorizationRequest,
23
24
  IdentityCapability,
24
25
  IdentityCredentials,
25
26
  IdentityProfile,
26
27
  PluginContext,
27
28
  )
28
-
29
- from imbi_plugin_github._hosts import flavor_host, host_to_api_base
29
+ from imbi.plugins.github._hosts import flavor_host, host_to_api_base
30
30
 
31
31
  LOGGER = logging.getLogger(__name__)
32
32
 
@@ -47,7 +47,8 @@ import logging
47
47
  import typing
48
48
 
49
49
  import httpx
50
- from imbi_common.plugins.base import (
50
+
51
+ from imbi.common.plugins.base import (
51
52
  LifecycleCapability,
52
53
  LifecycleResult,
53
54
  LinkWriteback,
@@ -55,11 +56,10 @@ from imbi_common.plugins.base import (
55
56
  RelocationTarget,
56
57
  ServiceWriteback,
57
58
  )
58
- from imbi_common.plugins.errors import PluginAuthenticationFailed
59
- from imbi_common.plugins.templates import expand_template
60
-
61
- from imbi_plugin_github._hosts import flavor_host, host_to_api_base
62
- from imbi_plugin_github._repos import (
59
+ from imbi.common.plugins.errors import PluginAuthenticationFailed
60
+ from imbi.common.plugins.templates import expand_template
61
+ from imbi.plugins.github._hosts import flavor_host, host_to_api_base
62
+ from imbi.plugins.github._repos import (
63
63
  derive_owner_repo_from_links,
64
64
  resolve_owner_repo,
65
65
  )
@@ -16,7 +16,7 @@ handler for each capability the platform hosts:
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- from imbi_common.plugins.base import (
19
+ from imbi.common.plugins.base import (
20
20
  ActionDescriptor,
21
21
  Capability,
22
22
  CredentialField,
@@ -26,17 +26,16 @@ from imbi_common.plugins.base import (
26
26
  PluginOption,
27
27
  WebhookActionsCapability,
28
28
  )
29
-
30
- from imbi_plugin_github.commits import (
29
+ from imbi.plugins.github.commits import (
31
30
  GitHubCommitSync,
32
31
  sync_commits_descriptor,
33
32
  sync_tags_descriptor,
34
33
  )
35
- from imbi_plugin_github.deployment import GitHubDeployment
36
- from imbi_plugin_github.doctor import GitHubDoctor
37
- from imbi_plugin_github.identity import DEFAULT_SCOPES, GitHubIdentity
38
- from imbi_plugin_github.lifecycle import GitHubLifecycle
39
- from imbi_plugin_github.pull_requests import (
34
+ from imbi.plugins.github.deployment import GitHubDeployment
35
+ from imbi.plugins.github.doctor import GitHubDoctor
36
+ from imbi.plugins.github.identity import DEFAULT_SCOPES, GitHubIdentity
37
+ from imbi.plugins.github.lifecycle import GitHubLifecycle
38
+ from imbi.plugins.github.pull_requests import (
40
39
  GitHubPullRequestSync,
41
40
  sync_pull_requests_descriptor,
42
41
  )
@@ -14,7 +14,7 @@ GitHub's list-PRs API omits per-file diff stats
14
14
  store ``0`` for those fields; they are accurate on webhook-driven rows.
15
15
 
16
16
  PR rows are written to the shared ClickHouse ``pull_requests`` table via
17
- :func:`imbi_common.clickhouse.insert`. Writes are best-effort: a storage
17
+ :func:`imbi.common.clickhouse.insert`. Writes are best-effort: a storage
18
18
  failure is logged and swallowed so an analytics hiccup never 5xxs the
19
19
  webhook, exactly as the gateway's own event recording behaves.
20
20
  """
@@ -28,19 +28,19 @@ import typing
28
28
  import httpx
29
29
  import jsonpointer
30
30
  import pydantic
31
- from imbi_common import clickhouse
32
- from imbi_common.json_pointer import JsonPointer
33
- from imbi_common.models import PullRequestRecord
34
- from imbi_common.plugins.base import (
31
+
32
+ from imbi.common import clickhouse
33
+ from imbi.common.json_pointer import JsonPointer
34
+ from imbi.common.models import PullRequestRecord
35
+ from imbi.common.plugins.base import (
35
36
  ActionDescriptor,
36
37
  PluginContext,
37
38
  PullRequestSyncCapability,
38
39
  )
39
-
40
- from imbi_plugin_github._app_auth import AppNotInstalledError
41
- from imbi_plugin_github._hosts import host_to_api_base
42
- from imbi_plugin_github._repos import resolve_owner_repo
43
- from imbi_plugin_github.commits import (
40
+ from imbi.plugins.github._app_auth import AppNotInstalledError
41
+ from imbi.plugins.github._hosts import host_to_api_base
42
+ from imbi.plugins.github._repos import resolve_owner_repo
43
+ from imbi.plugins.github.commits import (
44
44
  _BACKFILL_MAX_WAIT_SECONDS, # pyright: ignore[reportPrivateUsage]
45
45
  _client, # pyright: ignore[reportPrivateUsage]
46
46
  _insert_best_effort, # pyright: ignore[reportPrivateUsage]
@@ -49,7 +49,7 @@ from imbi_plugin_github.commits import (
49
49
  _resolve_bearer, # pyright: ignore[reportPrivateUsage]
50
50
  _resolve_host_for_context, # pyright: ignore[reportPrivateUsage]
51
51
  )
52
- from imbi_plugin_github.deployment import (
52
+ from imbi.plugins.github.deployment import (
53
53
  _next_page_url, # pyright: ignore[reportPrivateUsage]
54
54
  _parse_iso, # pyright: ignore[reportPrivateUsage]
55
55
  _query_param, # pyright: ignore[reportPrivateUsage]
@@ -252,11 +252,11 @@ sync_pull_requests_descriptor = ActionDescriptor(
252
252
  ),
253
253
  callable=typing.cast(
254
254
  'typing.Any',
255
- 'imbi_plugin_github.pull_requests:sync_pull_requests',
255
+ 'imbi.plugins.github.pull_requests:sync_pull_requests',
256
256
  ),
257
257
  config_model=typing.cast(
258
258
  'typing.Any',
259
- 'imbi_plugin_github.pull_requests:SyncPRsConfig',
259
+ 'imbi.plugins.github.pull_requests:SyncPRsConfig',
260
260
  ),
261
261
  )
262
262
 
@@ -266,7 +266,7 @@ class GitHubPullRequestSync(PullRequestSyncCapability):
266
266
 
267
267
  Uses the Integration's shared credential blob (PAT or GitHub App),
268
268
  resolved identically to
269
- :class:`~imbi_plugin_github.commits.GitHubCommitSync`. The
269
+ :class:`~imbi.plugins.github.commits.GitHubCommitSync`. The
270
270
  gateway-side incremental sync flows through the
271
271
  ``sync_pull_requests`` webhook action catalogued by the plugin's
272
272
  ``webhook-actions`` capability.
File without changes
@@ -11,20 +11,20 @@ import httpx
11
11
  import respx
12
12
  from cryptography.hazmat.primitives import serialization
13
13
  from cryptography.hazmat.primitives.asymmetric import rsa
14
- from imbi_common.models import CommitRecord, TagRecord
15
- from imbi_common.plugins.base import PluginContext
16
- from imbi_common.plugins.errors import (
14
+
15
+ from imbi.common.models import CommitRecord, TagRecord
16
+ from imbi.common.plugins.base import PluginContext
17
+ from imbi.common.plugins.errors import (
17
18
  PluginAuthenticationFailed,
18
19
  PluginRateLimited,
19
20
  )
20
-
21
- from imbi_plugin_github import _app_auth, commits, deployment
22
- from imbi_plugin_github.plugin import GitHubPlugin, GitHubWebhookActions
21
+ from imbi.plugins.github import _app_auth, commits, deployment
22
+ from imbi.plugins.github.plugin import GitHubPlugin, GitHubWebhookActions
23
23
 
24
24
  _ZERO = '0' * 40
25
25
  _CREDS = {'access_token': 'gho_test'}
26
- _INSERT = 'imbi_plugin_github.commits.clickhouse.insert'
27
- _QUERY = 'imbi_plugin_github.commits.clickhouse.query'
26
+ _INSERT = 'imbi.plugins.github.commits.clickhouse.insert'
27
+ _QUERY = 'imbi.plugins.github.commits.clickhouse.query'
28
28
 
29
29
 
30
30
  def _gen_pem() -> str: