imbi-plugin-github 2.33.0__tar.gz → 2.35.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-2.33.0 → imbi_plugin_github-2.35.0}/PKG-INFO +2 -2
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/pyproject.toml +2 -2
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/commits.py +27 -22
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/pull_requests.py +9 -8
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/tests/test_commits.py +33 -33
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/tests/test_pull_requests.py +5 -5
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/.gitignore +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/README.md +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/moon.yml +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/README.md +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/__init__.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/_app_auth.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/_hosts.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/_repos.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/deployment.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/doctor.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/identity.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/lifecycle.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/plugin.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/py.typed +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/tests/__init__.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/tests/test_app_auth_scopes.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/tests/test_deployment.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/tests/test_doctor.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/tests/test_hosts.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/tests/test_identity.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/tests/test_lifecycle.py +0 -0
- {imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/tests/test_repos.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: imbi-plugin-github
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.35.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-Expression: BSD-3-Clause
|
|
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.14
|
|
13
13
|
Requires-Python: >=3.14
|
|
14
14
|
Requires-Dist: httpx>=0.27
|
|
15
|
-
Requires-Dist: imbi-common[databases]==2.
|
|
15
|
+
Requires-Dist: imbi-common[databases]==2.35.0
|
|
16
16
|
Requires-Dist: pydantic>=2
|
|
17
17
|
Requires-Dist: pyjwt[crypto]>=2.8
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "imbi-plugin-github"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.35.0"
|
|
4
4
|
description = "GitHub identity plugin for Imbi (github.com / GHEC / GHES)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.14"
|
|
@@ -18,7 +18,7 @@ classifiers = [
|
|
|
18
18
|
]
|
|
19
19
|
dependencies = [
|
|
20
20
|
"httpx>=0.27",
|
|
21
|
-
"imbi-common[databases]==2.
|
|
21
|
+
"imbi-common[databases]==2.35.0",
|
|
22
22
|
"pydantic>=2",
|
|
23
23
|
"pyjwt[crypto]>=2.8",
|
|
24
24
|
]
|
|
@@ -19,11 +19,11 @@ backfill: there is no push payload, so the GitHub host is read from
|
|
|
19
19
|
it walks the full default-branch history and the complete tag list rather
|
|
20
20
|
than a single push delta.
|
|
21
21
|
|
|
22
|
-
Commit / tag rows are
|
|
23
|
-
``tags``
|
|
24
|
-
best-effort: a
|
|
25
|
-
hiccup never 5xxs the webhook,
|
|
26
|
-
recording behaves.
|
|
22
|
+
Commit / tag rows are published via :func:`imbi.common.iggy.publish` to
|
|
23
|
+
the ``commits`` / ``tags`` streams, which the ClickHouse sink drains into
|
|
24
|
+
the tables of the same name. Writes are best-effort: a publish failure is
|
|
25
|
+
logged and swallowed so an analytics hiccup never 5xxs the webhook,
|
|
26
|
+
exactly as the gateway's own event recording behaves.
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
29
|
from __future__ import annotations
|
|
@@ -40,7 +40,7 @@ import httpx
|
|
|
40
40
|
import jsonpointer
|
|
41
41
|
import pydantic
|
|
42
42
|
|
|
43
|
-
from imbi.common import cache, clickhouse
|
|
43
|
+
from imbi.common import cache, clickhouse, iggy
|
|
44
44
|
from imbi.common.json_pointer import JsonPointer
|
|
45
45
|
from imbi.common.models import CommitRecord, TagRecord
|
|
46
46
|
from imbi.common.plugins.base import (
|
|
@@ -67,6 +67,9 @@ from imbi.plugins.github.deployment import (
|
|
|
67
67
|
|
|
68
68
|
LOGGER = logging.getLogger(__name__)
|
|
69
69
|
|
|
70
|
+
#: The Iggy topic every row this plugin publishes goes to.
|
|
71
|
+
_TOPIC = 'github'
|
|
72
|
+
|
|
70
73
|
_HTTP_TIMEOUT_SECONDS = 10.0
|
|
71
74
|
_ZERO_SHA = '0' * 40
|
|
72
75
|
# GitHub's compare endpoint caps ``commits[]`` at 250 per page and
|
|
@@ -903,25 +906,26 @@ async def _fetch_commits_since(
|
|
|
903
906
|
return out
|
|
904
907
|
|
|
905
908
|
|
|
906
|
-
async def
|
|
907
|
-
|
|
909
|
+
async def _publish_best_effort(
|
|
910
|
+
stream: str, records: list[pydantic.BaseModel], project_id: str
|
|
908
911
|
) -> int:
|
|
909
|
-
"""
|
|
912
|
+
"""Publish ``records`` to ``stream``; return rows sent (0 on fail).
|
|
910
913
|
|
|
911
|
-
|
|
912
|
-
|
|
914
|
+
The stream is named for the ClickHouse table the sink lands it in.
|
|
915
|
+
``imbi.common.iggy.publish`` rejects an empty list, so an empty set
|
|
916
|
+
short-circuits to 0. A publish failure is logged and swallowed,
|
|
913
917
|
mirroring the webhook actions -- an analytics hiccup must not fail the
|
|
914
918
|
sync.
|
|
915
919
|
"""
|
|
916
920
|
if not records:
|
|
917
921
|
return 0
|
|
918
922
|
try:
|
|
919
|
-
await
|
|
923
|
+
await iggy.publish(stream, _TOPIC, records)
|
|
920
924
|
except Exception:
|
|
921
925
|
LOGGER.exception(
|
|
922
926
|
'github-commit-sync: failed to record %d %s rows for project %s',
|
|
923
927
|
len(records),
|
|
924
|
-
|
|
928
|
+
stream,
|
|
925
929
|
project_id,
|
|
926
930
|
)
|
|
927
931
|
return 0
|
|
@@ -1094,7 +1098,7 @@ async def sync_commits(
|
|
|
1094
1098
|
if not records:
|
|
1095
1099
|
return
|
|
1096
1100
|
try:
|
|
1097
|
-
await
|
|
1101
|
+
await iggy.publish('commits', _TOPIC, records)
|
|
1098
1102
|
except Exception:
|
|
1099
1103
|
LOGGER.exception(
|
|
1100
1104
|
'github-commit-sync: failed to record %d commits for project %s',
|
|
@@ -1464,7 +1468,7 @@ async def sync_tags(
|
|
|
1464
1468
|
if not records:
|
|
1465
1469
|
return # every tag in this delivery was unpeelable (warned above)
|
|
1466
1470
|
try:
|
|
1467
|
-
await
|
|
1471
|
+
await iggy.publish('tags', _TOPIC, records)
|
|
1468
1472
|
except Exception:
|
|
1469
1473
|
LOGGER.exception(
|
|
1470
1474
|
'github-commit-sync: failed to record %d tags for project %s',
|
|
@@ -1478,7 +1482,8 @@ sync_commits_descriptor = ActionDescriptor(
|
|
|
1478
1482
|
label='Sync Commit History',
|
|
1479
1483
|
description=(
|
|
1480
1484
|
'Fetch the full set of commits in a push (via the GitHub compare '
|
|
1481
|
-
'API) and
|
|
1485
|
+
'API) and publish them to the stream that lands in the ClickHouse '
|
|
1486
|
+
'commits table.'
|
|
1482
1487
|
),
|
|
1483
1488
|
callable=typing.cast(
|
|
1484
1489
|
'typing.Any', 'imbi.plugins.github.commits:sync_commits'
|
|
@@ -1492,8 +1497,8 @@ sync_tags_descriptor = ActionDescriptor(
|
|
|
1492
1497
|
name='sync_tags',
|
|
1493
1498
|
label='Sync Tag History',
|
|
1494
1499
|
description=(
|
|
1495
|
-
'
|
|
1496
|
-
'tag list) in the ClickHouse tags table.'
|
|
1500
|
+
'Publish the pushed tag (and, when reconcile_all is set, the full '
|
|
1501
|
+
'tag list) to the stream that lands in the ClickHouse tags table.'
|
|
1497
1502
|
),
|
|
1498
1503
|
callable=typing.cast(
|
|
1499
1504
|
'typing.Any', 'imbi.plugins.github.commits:sync_tags'
|
|
@@ -1625,10 +1630,10 @@ class GitHubCommitSync(CommitSyncCapability):
|
|
|
1625
1630
|
for item in raw_commits
|
|
1626
1631
|
if item.get('sha')
|
|
1627
1632
|
]
|
|
1628
|
-
commits_recorded = await
|
|
1633
|
+
commits_recorded = await _publish_best_effort(
|
|
1629
1634
|
'commits', commit_records, ctx.project_id
|
|
1630
1635
|
)
|
|
1631
|
-
tags_recorded = await
|
|
1636
|
+
tags_recorded = await _publish_best_effort(
|
|
1632
1637
|
'tags', list(tags), ctx.project_id
|
|
1633
1638
|
)
|
|
1634
1639
|
return commits_recorded, tags_recorded
|
|
@@ -1724,7 +1729,7 @@ class GitHubCommitSync(CommitSyncCapability):
|
|
|
1724
1729
|
)
|
|
1725
1730
|
if record is None:
|
|
1726
1731
|
return 0 # unpeelable (warned by _tag_record)
|
|
1727
|
-
return await
|
|
1732
|
+
return await _publish_best_effort('tags', [record], ctx.project_id)
|
|
1728
1733
|
|
|
1729
1734
|
async def sync_new_commits(
|
|
1730
1735
|
self,
|
|
@@ -1791,7 +1796,7 @@ class GitHubCommitSync(CommitSyncCapability):
|
|
|
1791
1796
|
for item in raw
|
|
1792
1797
|
if item.get('sha')
|
|
1793
1798
|
]
|
|
1794
|
-
return await
|
|
1799
|
+
return await _publish_best_effort('commits', records, ctx.project_id)
|
|
1795
1800
|
|
|
1796
1801
|
async def _fetch_new_commits(
|
|
1797
1802
|
self,
|
{imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/pull_requests.py
RENAMED
|
@@ -13,10 +13,11 @@ GitHub's list-PRs API omits per-file diff stats
|
|
|
13
13
|
(``additions``/``deletions``/``changed_files``), so backfill rows
|
|
14
14
|
store ``0`` for those fields; they are accurate on webhook-driven rows.
|
|
15
15
|
|
|
16
|
-
PR rows are
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
webhook, exactly as the
|
|
16
|
+
PR rows are published via :func:`imbi.common.iggy.publish` to the
|
|
17
|
+
``pull_requests`` stream, which the ClickHouse sink drains into the table
|
|
18
|
+
of the same name. Writes are best-effort: a publish failure is logged and
|
|
19
|
+
swallowed so an analytics hiccup never 5xxs the webhook, exactly as the
|
|
20
|
+
gateway's own event recording behaves.
|
|
20
21
|
"""
|
|
21
22
|
|
|
22
23
|
from __future__ import annotations
|
|
@@ -29,7 +30,7 @@ import httpx
|
|
|
29
30
|
import jsonpointer
|
|
30
31
|
import pydantic
|
|
31
32
|
|
|
32
|
-
from imbi.common import
|
|
33
|
+
from imbi.common import iggy
|
|
33
34
|
from imbi.common.json_pointer import JsonPointer
|
|
34
35
|
from imbi.common.models import PullRequestRecord
|
|
35
36
|
from imbi.common.plugins.base import (
|
|
@@ -43,7 +44,7 @@ from imbi.plugins.github._repos import resolve_owner_repo
|
|
|
43
44
|
from imbi.plugins.github.commits import (
|
|
44
45
|
_BACKFILL_MAX_WAIT_SECONDS, # pyright: ignore[reportPrivateUsage]
|
|
45
46
|
_client, # pyright: ignore[reportPrivateUsage]
|
|
46
|
-
|
|
47
|
+
_publish_best_effort, # pyright: ignore[reportPrivateUsage]
|
|
47
48
|
_request, # pyright: ignore[reportPrivateUsage]
|
|
48
49
|
_resolve, # pyright: ignore[reportPrivateUsage]
|
|
49
50
|
_resolve_bearer, # pyright: ignore[reportPrivateUsage]
|
|
@@ -237,7 +238,7 @@ async def sync_pull_requests(
|
|
|
237
238
|
)
|
|
238
239
|
return
|
|
239
240
|
try:
|
|
240
|
-
await
|
|
241
|
+
await iggy.publish('pull_requests', 'github', [record])
|
|
241
242
|
except Exception:
|
|
242
243
|
LOGGER.exception(
|
|
243
244
|
'github-pr-sync: failed to record PR #%d for project %s',
|
|
@@ -356,6 +357,6 @@ class GitHubPullRequestSync(PullRequestSyncCapability):
|
|
|
356
357
|
record = _pr_record(pr, project_id=ctx.project_id)
|
|
357
358
|
if record is not None:
|
|
358
359
|
records.append(record)
|
|
359
|
-
return await
|
|
360
|
+
return await _publish_best_effort(
|
|
360
361
|
'pull_requests', records, ctx.project_id
|
|
361
362
|
)
|
|
@@ -25,7 +25,7 @@ from imbi.plugins.github.plugin import GitHubPlugin, GitHubWebhookActions
|
|
|
25
25
|
|
|
26
26
|
_ZERO = '0' * 40
|
|
27
27
|
_CREDS = {'access_token': 'gho_test'}
|
|
28
|
-
_INSERT = 'imbi.plugins.github.commits.
|
|
28
|
+
_INSERT = 'imbi.plugins.github.commits.iggy.publish'
|
|
29
29
|
_QUERY = 'imbi.plugins.github.commits.clickhouse.query'
|
|
30
30
|
|
|
31
31
|
|
|
@@ -312,7 +312,7 @@ class SyncCommitsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
312
312
|
event=_event(_push(before=base, after=head)),
|
|
313
313
|
)
|
|
314
314
|
insert.assert_awaited_once()
|
|
315
|
-
table, records = _await_args(insert)
|
|
315
|
+
table, _topic, records = _await_args(insert)
|
|
316
316
|
self.assertEqual('commits', table)
|
|
317
317
|
self.assertEqual(2, len(records))
|
|
318
318
|
first = records[0]
|
|
@@ -368,7 +368,7 @@ class SyncCommitsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
368
368
|
event=_event(_push(before=_ZERO, after=head)),
|
|
369
369
|
)
|
|
370
370
|
insert.assert_awaited_once()
|
|
371
|
-
_, records = _await_args(insert)
|
|
371
|
+
_, _, records = _await_args(insert)
|
|
372
372
|
self.assertEqual('e' * 40, records[0].sha)
|
|
373
373
|
|
|
374
374
|
@respx.mock
|
|
@@ -414,7 +414,7 @@ class SyncCommitsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
414
414
|
)
|
|
415
415
|
self.assertTrue(route.called)
|
|
416
416
|
insert.assert_awaited_once()
|
|
417
|
-
table, records = _await_args(insert)
|
|
417
|
+
table, _topic, records = _await_args(insert)
|
|
418
418
|
self.assertEqual('commits', table)
|
|
419
419
|
self.assertEqual(1, len(records))
|
|
420
420
|
self.assertEqual(head, records[0].sha)
|
|
@@ -451,7 +451,7 @@ class SyncCommitsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
451
451
|
action_config=_WORKFLOW_RUN_CONFIG,
|
|
452
452
|
event=_event(_workflow_run(head=head)),
|
|
453
453
|
)
|
|
454
|
-
_, records = _await_args(insert)
|
|
454
|
+
_, _, records = _await_args(insert)
|
|
455
455
|
self.assertEqual(first_pushed, records[0].pushed_at)
|
|
456
456
|
self.assertEqual('pass', records[0].ci_status)
|
|
457
457
|
lookup = next(
|
|
@@ -481,7 +481,7 @@ class SyncCommitsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
481
481
|
action_config=commits.SyncCommitsConfig(),
|
|
482
482
|
event=_event(_push(before=base, after=head)),
|
|
483
483
|
)
|
|
484
|
-
_, records = _await_args(insert)
|
|
484
|
+
_, _, records = _await_args(insert)
|
|
485
485
|
by_sha = {r.sha: r for r in records}
|
|
486
486
|
self.assertEqual(first_pushed, by_sha['c' * 40].pushed_at)
|
|
487
487
|
self.assertGreater(by_sha['d' * 40].pushed_at, first_pushed)
|
|
@@ -508,7 +508,7 @@ class SyncCommitsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
508
508
|
event=_event(_workflow_run(head=head)),
|
|
509
509
|
)
|
|
510
510
|
insert.assert_awaited_once()
|
|
511
|
-
_, records = _await_args(insert)
|
|
511
|
+
_, _, records = _await_args(insert)
|
|
512
512
|
self.assertEqual(head, records[0].sha)
|
|
513
513
|
|
|
514
514
|
@respx.mock
|
|
@@ -590,7 +590,7 @@ class SyncCommitsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
590
590
|
'100', route.calls[0].request.url.params.get('per_page')
|
|
591
591
|
)
|
|
592
592
|
insert.assert_awaited_once()
|
|
593
|
-
_, records = _await_args(insert)
|
|
593
|
+
_, _, records = _await_args(insert)
|
|
594
594
|
self.assertEqual('fail', records[0].ci_status)
|
|
595
595
|
|
|
596
596
|
@respx.mock
|
|
@@ -633,7 +633,7 @@ class SyncCommitsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
633
633
|
action_config=commits.SyncCommitsConfig(),
|
|
634
634
|
event=_event(_push(before=base, after=head)),
|
|
635
635
|
)
|
|
636
|
-
_, records = _await_args(insert)
|
|
636
|
+
_, _, records = _await_args(insert)
|
|
637
637
|
self.assertEqual(2, len(records))
|
|
638
638
|
|
|
639
639
|
@respx.mock
|
|
@@ -716,7 +716,7 @@ class SyncCommitsCiStatusTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
716
716
|
action_config=commits.SyncCommitsConfig(),
|
|
717
717
|
event=_event(_push(before=base, after=head)),
|
|
718
718
|
)
|
|
719
|
-
_, records = _await_args(insert)
|
|
719
|
+
_, _, records = _await_args(insert)
|
|
720
720
|
return records
|
|
721
721
|
|
|
722
722
|
@respx.mock
|
|
@@ -848,7 +848,7 @@ class SyncTagsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
848
848
|
action_config=commits.SyncTagsConfig(),
|
|
849
849
|
event=_event(self._tag_push(after=sha)),
|
|
850
850
|
)
|
|
851
|
-
table, records = _await_args(insert)
|
|
851
|
+
table, _topic, records = _await_args(insert)
|
|
852
852
|
self.assertEqual('tags', table)
|
|
853
853
|
self.assertEqual(1, len(records))
|
|
854
854
|
self.assertIsInstance(records[0], TagRecord)
|
|
@@ -886,7 +886,7 @@ class SyncTagsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
886
886
|
action_config=commits.SyncTagsConfig(),
|
|
887
887
|
event=_event(self._tag_push(after=sha)),
|
|
888
888
|
)
|
|
889
|
-
_, records = _await_args(insert)
|
|
889
|
+
_, _, records = _await_args(insert)
|
|
890
890
|
self.assertEqual(
|
|
891
891
|
datetime.datetime.fromisoformat('2026-02-03T04:05:06Z'),
|
|
892
892
|
records[0].tagged_at,
|
|
@@ -920,7 +920,7 @@ class SyncTagsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
920
920
|
),
|
|
921
921
|
)
|
|
922
922
|
self.assertTrue(route.called)
|
|
923
|
-
_, records = _await_args(insert)
|
|
923
|
+
_, _, records = _await_args(insert)
|
|
924
924
|
self.assertEqual(
|
|
925
925
|
datetime.datetime.fromisoformat('2026-02-03T04:05:06Z'),
|
|
926
926
|
records[0].tagged_at,
|
|
@@ -946,7 +946,7 @@ class SyncTagsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
946
946
|
action_config=commits.SyncTagsConfig(),
|
|
947
947
|
event=_event(self._tag_push(after=sha)),
|
|
948
948
|
)
|
|
949
|
-
_, records = _await_args(insert)
|
|
949
|
+
_, _, records = _await_args(insert)
|
|
950
950
|
self.assertIsNone(records[0].tagged_at)
|
|
951
951
|
|
|
952
952
|
@respx.mock
|
|
@@ -983,7 +983,7 @@ class SyncTagsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
983
983
|
action_config=commits.SyncTagsConfig(),
|
|
984
984
|
event=_event(self._tag_push(after=sha)),
|
|
985
985
|
)
|
|
986
|
-
_, records = _await_args(insert)
|
|
986
|
+
_, _, records = _await_args(insert)
|
|
987
987
|
self.assertEqual('Release 1.2.3', records[0].message)
|
|
988
988
|
self.assertEqual('Rel Bot', records[0].tagger_name)
|
|
989
989
|
self.assertIsNotNone(records[0].tagged_at)
|
|
@@ -1060,7 +1060,7 @@ class SyncTagsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
1060
1060
|
action_config=commits.SyncTagsConfig(reconcile_all=True),
|
|
1061
1061
|
event=_event(self._tag_push(after=tag_sha)),
|
|
1062
1062
|
)
|
|
1063
|
-
_, records = _await_args(insert)
|
|
1063
|
+
_, _, records = _await_args(insert)
|
|
1064
1064
|
self.assertEqual(['v1.2.3'], [r.name for r in records])
|
|
1065
1065
|
self.assertEqual(commit_sha, records[0].sha)
|
|
1066
1066
|
|
|
@@ -1114,7 +1114,7 @@ class SyncTagsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
1114
1114
|
action_config=commits.SyncTagsConfig(reconcile_all=True),
|
|
1115
1115
|
event=_event(self._tag_push(after=sha)),
|
|
1116
1116
|
)
|
|
1117
|
-
_, records = _await_args(insert)
|
|
1117
|
+
_, _, records = _await_args(insert)
|
|
1118
1118
|
names = {r.name for r in records}
|
|
1119
1119
|
self.assertEqual({'v1.2.3', 'v1.0.0'}, names)
|
|
1120
1120
|
urls = {r.name: r.url for r in records}
|
|
@@ -1189,7 +1189,7 @@ class SyncTagsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
1189
1189
|
action_config=commits.SyncTagsConfig(reconcile_all=True),
|
|
1190
1190
|
event=_event(self._tag_push(after=sha)),
|
|
1191
1191
|
)
|
|
1192
|
-
_, records = _await_args(insert)
|
|
1192
|
+
_, _, records = _await_args(insert)
|
|
1193
1193
|
names = {r.name for r in records}
|
|
1194
1194
|
self.assertEqual({'v1.2.3', 'v1.0.0'}, names)
|
|
1195
1195
|
urls = {r.name: r.url for r in records}
|
|
@@ -1667,13 +1667,13 @@ class SyncAllHistoryTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
1667
1667
|
commit_call = next(
|
|
1668
1668
|
c for c in insert.await_args_list if c.args[0] == 'commits'
|
|
1669
1669
|
)
|
|
1670
|
-
records = commit_call.args[
|
|
1670
|
+
records = commit_call.args[2]
|
|
1671
1671
|
self.assertIsInstance(records[0], CommitRecord)
|
|
1672
1672
|
self.assertEqual('main', records[0].ref)
|
|
1673
1673
|
tag_call = next(
|
|
1674
1674
|
c for c in insert.await_args_list if c.args[0] == 'tags'
|
|
1675
1675
|
)
|
|
1676
|
-
urls = {r.name: r.url for r in tag_call.args[
|
|
1676
|
+
urls = {r.name: r.url for r in tag_call.args[2]}
|
|
1677
1677
|
self.assertEqual(
|
|
1678
1678
|
{
|
|
1679
1679
|
'v1.0.0': 'https://github.com/octo/demo/releases/tag/v1.0.0',
|
|
@@ -1709,7 +1709,7 @@ class SyncAllHistoryTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
1709
1709
|
commit_call = next(
|
|
1710
1710
|
c for c in insert.await_args_list if c.args[0] == 'commits'
|
|
1711
1711
|
)
|
|
1712
|
-
by_sha = {r.sha: r for r in commit_call.args[
|
|
1712
|
+
by_sha = {r.sha: r for r in commit_call.args[2]}
|
|
1713
1713
|
self.assertEqual(first_pushed, by_sha['d' * 40].pushed_at)
|
|
1714
1714
|
self.assertGreater(by_sha['c' * 40].pushed_at, first_pushed)
|
|
1715
1715
|
|
|
@@ -1738,7 +1738,7 @@ class SyncAllHistoryTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
1738
1738
|
commit_call = next(
|
|
1739
1739
|
c for c in insert.await_args_list if c.args[0] == 'commits'
|
|
1740
1740
|
)
|
|
1741
|
-
records = commit_call.args[
|
|
1741
|
+
records = commit_call.args[2]
|
|
1742
1742
|
self.assertEqual('pass', records[0].ci_status)
|
|
1743
1743
|
self.assertEqual('unknown', records[1].ci_status)
|
|
1744
1744
|
|
|
@@ -1948,7 +1948,7 @@ class SyncAllHistoryTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
1948
1948
|
tag_call = next(
|
|
1949
1949
|
c for c in insert.await_args_list if c.args[0] == 'tags'
|
|
1950
1950
|
)
|
|
1951
|
-
record = tag_call.args[
|
|
1951
|
+
record = tag_call.args[2][0]
|
|
1952
1952
|
self.assertEqual('Release 2.0.0', record.message)
|
|
1953
1953
|
self.assertEqual('Rel Bot', record.tagger_name)
|
|
1954
1954
|
self.assertIsNotNone(record.tagged_at)
|
|
@@ -2005,7 +2005,7 @@ class SyncAllHistoryTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
2005
2005
|
r
|
|
2006
2006
|
for c in insert.await_args_list
|
|
2007
2007
|
if c.args[0] == 'tags'
|
|
2008
|
-
for r in c.args[
|
|
2008
|
+
for r in c.args[2]
|
|
2009
2009
|
]
|
|
2010
2010
|
|
|
2011
2011
|
@respx.mock
|
|
@@ -2116,7 +2116,7 @@ class SyncTagTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
2116
2116
|
)
|
|
2117
2117
|
written, insert = await self._sync()
|
|
2118
2118
|
self.assertEqual(1, written)
|
|
2119
|
-
record = insert.await_args_list[0].args[
|
|
2119
|
+
record = insert.await_args_list[0].args[2][0]
|
|
2120
2120
|
self.assertEqual('2.23.0', record.name)
|
|
2121
2121
|
self.assertEqual('c' * 40, record.sha)
|
|
2122
2122
|
self.assertEqual(
|
|
@@ -2152,7 +2152,7 @@ class SyncTagTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
2152
2152
|
)
|
|
2153
2153
|
written, insert = await self._sync()
|
|
2154
2154
|
self.assertEqual(1, written)
|
|
2155
|
-
record = insert.await_args_list[0].args[
|
|
2155
|
+
record = insert.await_args_list[0].args[2][0]
|
|
2156
2156
|
self.assertEqual('c' * 40, record.sha)
|
|
2157
2157
|
self.assertEqual('Release 2.23.0', record.message)
|
|
2158
2158
|
self.assertEqual('Alice', record.tagger_name)
|
|
@@ -2163,7 +2163,7 @@ class SyncTagTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
2163
2163
|
self._mock_release('2026-08-04T13:00:00Z')
|
|
2164
2164
|
written, insert = await self._sync()
|
|
2165
2165
|
self.assertEqual(1, written)
|
|
2166
|
-
record = insert.await_args_list[0].args[
|
|
2166
|
+
record = insert.await_args_list[0].args[2][0]
|
|
2167
2167
|
self.assertEqual(
|
|
2168
2168
|
datetime.datetime(2026, 8, 4, 13, 0, tzinfo=datetime.UTC),
|
|
2169
2169
|
record.tagged_at,
|
|
@@ -2284,7 +2284,7 @@ class SyncNewCommitsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
2284
2284
|
self.assertIn(
|
|
2285
2285
|
f'{"a" * 40}...main', str(compare.calls.last.request.url)
|
|
2286
2286
|
)
|
|
2287
|
-
record = insert.await_args_list[0].args[
|
|
2287
|
+
record = insert.await_args_list[0].args[2][0]
|
|
2288
2288
|
self.assertEqual('main', record.ref)
|
|
2289
2289
|
|
|
2290
2290
|
@respx.mock
|
|
@@ -2304,7 +2304,7 @@ class SyncNewCommitsTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
2304
2304
|
):
|
|
2305
2305
|
written, insert = await self._sync()
|
|
2306
2306
|
self.assertEqual(2, written)
|
|
2307
|
-
by_sha = {r.sha: r for r in insert.await_args_list[0].args[
|
|
2307
|
+
by_sha = {r.sha: r for r in insert.await_args_list[0].args[2]}
|
|
2308
2308
|
self.assertEqual(first_pushed, by_sha['c' * 40].pushed_at)
|
|
2309
2309
|
self.assertGreater(by_sha['d' * 40].pushed_at, first_pushed)
|
|
2310
2310
|
|
|
@@ -2581,7 +2581,7 @@ class SyncCommitsThrottleTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
2581
2581
|
)
|
|
2582
2582
|
slept.assert_awaited_once_with(3.0)
|
|
2583
2583
|
insert.assert_awaited_once()
|
|
2584
|
-
_, records = _await_args(insert)
|
|
2584
|
+
_, _, records = _await_args(insert)
|
|
2585
2585
|
self.assertEqual(1, len(records))
|
|
2586
2586
|
|
|
2587
2587
|
@respx.mock
|
|
@@ -2757,7 +2757,7 @@ class AuthorAttributionTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
2757
2757
|
action_config=commits.SyncCommitsConfig(),
|
|
2758
2758
|
event=_event(_push(before=base, after=head)),
|
|
2759
2759
|
)
|
|
2760
|
-
_, records = _await_args(insert)
|
|
2760
|
+
_, _, records = _await_args(insert)
|
|
2761
2761
|
self.assertEqual('dev@example.com', records[0].author_user)
|
|
2762
2762
|
resolver.assert_awaited_once_with('42')
|
|
2763
2763
|
|
|
@@ -2777,7 +2777,7 @@ class AuthorAttributionTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
2777
2777
|
action_config=commits.SyncCommitsConfig(),
|
|
2778
2778
|
event=_event(_push(before=base, after=head)),
|
|
2779
2779
|
)
|
|
2780
|
-
_, records = _await_args(insert)
|
|
2780
|
+
_, _, records = _await_args(insert)
|
|
2781
2781
|
self.assertEqual('', records[0].author_user)
|
|
2782
2782
|
|
|
2783
2783
|
@respx.mock
|
|
@@ -2813,7 +2813,7 @@ class AuthorAttributionTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
2813
2813
|
commit_call = next(
|
|
2814
2814
|
c for c in insert.await_args_list if c.args[0] == 'commits'
|
|
2815
2815
|
)
|
|
2816
|
-
by_sha = {r.sha: r.author_user for r in commit_call.args[
|
|
2816
|
+
by_sha = {r.sha: r.author_user for r in commit_call.args[2]}
|
|
2817
2817
|
self.assertEqual('dev@example.com', by_sha['c' * 40])
|
|
2818
2818
|
self.assertEqual('dev@example.com', by_sha['d' * 40])
|
|
2819
2819
|
self.assertEqual('', by_sha['e' * 40])
|
|
@@ -28,11 +28,11 @@ def _gen_pem() -> str:
|
|
|
28
28
|
|
|
29
29
|
# Generated once for the module; signing only needs a valid RSA key.
|
|
30
30
|
_APP_KEY_PEM = _gen_pem()
|
|
31
|
-
# ``sync_all_history``
|
|
31
|
+
# ``sync_all_history`` publishes through ``_publish_best_effort`` (defined in
|
|
32
32
|
# commits.py), so its ClickHouse call resolves in the commits namespace.
|
|
33
|
-
_INSERT_BACKFILL = 'imbi.plugins.github.commits.
|
|
34
|
-
# The webhook action
|
|
35
|
-
_INSERT_WEBHOOK = 'imbi.plugins.github.pull_requests.
|
|
33
|
+
_INSERT_BACKFILL = 'imbi.plugins.github.commits.iggy.publish'
|
|
34
|
+
# The webhook action publishes directly through the pull_requests namespace.
|
|
35
|
+
_INSERT_WEBHOOK = 'imbi.plugins.github.pull_requests.iggy.publish'
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def _connection(
|
|
@@ -140,7 +140,7 @@ class SyncPullRequestsActionTestCase(unittest.IsolatedAsyncioTestCase):
|
|
|
140
140
|
)
|
|
141
141
|
insert.assert_awaited_once()
|
|
142
142
|
assert insert.await_args is not None
|
|
143
|
-
table, records = insert.await_args.args
|
|
143
|
+
table, _topic, records = insert.await_args.args
|
|
144
144
|
self.assertEqual(table, 'pull_requests')
|
|
145
145
|
self.assertEqual(records[0].pr_number, 7)
|
|
146
146
|
self.assertEqual(records[0].author, 'octocat')
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/_app_auth.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/deployment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{imbi_plugin_github-2.33.0 → imbi_plugin_github-2.35.0}/src/imbi/plugins/github/lifecycle.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|