alissa-tools-github-revloop 0.19.0__tar.gz → 0.20.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 (32) hide show
  1. {alissa_tools_github_revloop-0.19.0/src/main/alissa_tools_github_revloop.egg-info → alissa_tools_github_revloop-0.20.0}/PKG-INFO +1 -1
  2. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/ghclient.py +43 -15
  3. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/loop.py +41 -3
  4. alissa_tools_github_revloop-0.20.0/src/main/alissa/tools/github/revloop/version +1 -0
  5. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0/src/main/alissa_tools_github_revloop.egg-info}/PKG-INFO +1 -1
  6. alissa_tools_github_revloop-0.19.0/src/main/alissa/tools/github/revloop/version +0 -1
  7. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/LICENSE +0 -0
  8. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/MANIFEST.in +0 -0
  9. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/NOTICE +0 -0
  10. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/README.md +0 -0
  11. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/requirements.txt +0 -0
  12. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/setup.cfg +0 -0
  13. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/setup.py +0 -0
  14. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/__init__.py +0 -0
  15. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/__main__.py +0 -0
  16. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/alissa.py +0 -0
  17. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/config.py +0 -0
  18. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/proc.py +0 -0
  19. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/prreview.py +0 -0
  20. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/state.py +0 -0
  21. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/version.py +0 -0
  22. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/webui/__init__.py +0 -0
  23. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/webui/__main__.py +0 -0
  24. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/webui/auth.py +0 -0
  25. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/webui/page.py +0 -0
  26. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/webui/server.py +0 -0
  27. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/webui/sources.py +0 -0
  28. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa/tools/github/revloop/webui/sysinfo.py +0 -0
  29. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa_tools_github_revloop.egg-info/SOURCES.txt +0 -0
  30. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa_tools_github_revloop.egg-info/dependency_links.txt +0 -0
  31. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa_tools_github_revloop.egg-info/entry_points.txt +0 -0
  32. {alissa_tools_github_revloop-0.19.0 → alissa_tools_github_revloop-0.20.0}/src/main/alissa_tools_github_revloop.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alissa-tools-github-revloop
3
- Version: 0.19.0
3
+ Version: 0.20.0
4
4
  Summary: ALISSA-TOOLS-GITHUB-REVLOOP
5
5
  Home-page: https://alissa.app
6
6
  Author: Fahera
@@ -899,12 +899,28 @@ class GitHub:
899
899
  app; see ACTIONS_FALLBACK_NOTE for why that is accepted here and how the
900
900
  answer says so.
901
901
 
902
- COST: this path costs one call for the run listing plus one per kept
903
- workflow, where the read it replaces cost one. It is taken only where
904
- the alternative is a permanent CHECKS_UNKNOWN, and only on the rollup
905
- reads the caller already decided to make -- the gate's existing
906
- per-round caching is what bounds how often that is. On this fleet's
907
- repos (one or two workflows per commit) it is two or three calls.
902
+ COST, stated exactly, because it is the sentence an operator would size
903
+ rate-limit headroom from: this path costs one call for the run listing
904
+ plus one per KEPT workflow, where the read it replaces cost one.
905
+
906
+ Nothing caches that on the production path. `loop._dry_run_rollups` is
907
+ the only rollup memo and only the dry-run branch reads it, so what
908
+ bounds how often the cost is paid is the POLL INTERVAL -- both while
909
+ `_checks_at_spawn` holds a round on `checks_spawn_wait_seconds` and
910
+ while `_gate_on_checks` holds a finished verdict on
911
+ `checks_wait_seconds`, each of which re-reads the rollup every pass.
912
+
913
+ Nor is the fan-out bounded. CHECK_RUN_PAGE_LIMIT bounds the run
914
+ LISTING; the number of kept workflows is whatever the sha carries, and
915
+ each one costs a `jobs` call with `_api`'s 60s timeout, so a
916
+ many-workflow sha turns one rollup read into a long synchronous stall
917
+ inside a poll pass that has other PRs to decide. On this fleet's repos
918
+ (one or two workflows per commit) it is two or three calls; that is a
919
+ fact about these repos, not a bound this code enforces.
920
+
921
+ The trade is still worth taking where it applies: the alternative there
922
+ is a permanent CHECKS_UNKNOWN, which costs one call and can never
923
+ approve.
908
924
  """
909
925
  runs = self._rollup_listing(
910
926
  f"repos/{owner}/{repo}/actions/runs",
@@ -919,14 +935,26 @@ class GitHub:
919
935
 
920
936
  @staticmethod
921
937
  def _latest_run_per_workflow(runs: list[dict]) -> list[dict]:
922
- """One run per workflow: the most recent.
923
-
924
- A sha carries more than one run of the same workflow whenever it was
925
- triggered more than once -- a re-run shares its run id, but a distinct
926
- trigger event (a `push` and a `pull_request` on the same commit, a
927
- workflow re-dispatched by hand) creates a distinct run. Reading all of
928
- them would judge the commit on a superseded attempt: an earlier failed
929
- run whose re-trigger passed would hold the head red forever.
938
+ """One run per workflow AND trigger event: the most recent.
939
+
940
+ Two different facts are being separated here, and collapsing them was a
941
+ fail-open bug.
942
+
943
+ SUPERSEDED ATTEMPTS collapse. A sha carries more than one run of the
944
+ same workflow for the same event whenever it was triggered again -- a
945
+ re-run shares its run id, a re-dispatch does not. Reading all of them
946
+ would judge the commit on a superseded attempt: an earlier failed run
947
+ whose re-trigger passed would hold the head red forever.
948
+
949
+ DISTINCT EVENTS DO NOT. A workflow declaring `on: [push, pull_request]`
950
+ produces a `push` run and a `pull_request` run for the same sha, and
951
+ GitHub publishes a check run per job for BOTH -- so the read this
952
+ stands in for reports both. Keying on `workflow_id` alone dropped the
953
+ lower `run_number` of the pair unread, and when the dropped one was the
954
+ red one the fallback answered GREEN for a commit whose real rollup is
955
+ red. That is the fail-open direction this whole component exists to
956
+ avoid, so the key carries `event` as well. A re-run preserves its run's
957
+ `event`, so the superseded-attempt collapse above is unaffected.
930
958
 
931
959
  Recency is `run_number` then `id`, both monotonic per workflow and both
932
960
  integers -- deliberately not a timestamp string, which is the field a
@@ -938,7 +966,7 @@ class GitHub:
938
966
  for index, run_ in enumerate(runs):
939
967
  workflow_id = run_.get("workflow_id")
940
968
  key: object = (
941
- ("workflow", workflow_id)
969
+ ("workflow", workflow_id, str(run_.get("event") or ""))
942
970
  if workflow_id is not None
943
971
  else ("unidentified", index)
944
972
  )
@@ -407,6 +407,22 @@ CHECKS_FAILING_LINE = "- `{name}` — {conclusion}{url}"
407
407
 
408
408
  # Prepended when the gate held the approve for the whole wait bound and the
409
409
  # rollup still had not settled, so the verdict lands as a COMMENT.
410
+ # Prepended to an APPROVE whose green came from the Actions-API fallback rather
411
+ # than the check-runs rollup. Not a caveat on the review -- the code verdict is
412
+ # unchanged -- but on the EVIDENCE behind the approve: the fallback cannot see a
413
+ # check run posted by a third-party check app, so "green" here means "every
414
+ # Actions-produced context passed", which is the same thing only on an
415
+ # Actions-only repo.
416
+ CHECKS_FALLBACK_LEAD = (
417
+ "**CI rollup at `{sha}` read through the Actions API.** This deployment's "
418
+ "credential cannot read check runs (GitHub does not offer the `Checks` "
419
+ "permission on fine-grained PATs), so the head was confirmed green from its "
420
+ "workflow runs and jobs instead. That read sees every GitHub-Actions "
421
+ "context and no third-party check app's, so on an Actions-only repo it is "
422
+ "the same answer — flagging the path because an approve is the operator's "
423
+ "merge cue.\n\n"
424
+ )
425
+
410
426
  CHECKS_UNSETTLED_LEAD = (
411
427
  "**Recorded as a comment, not an approve — the CI rollup at `{sha}` never "
412
428
  "concluded.**\n\n"
@@ -466,10 +482,18 @@ CHECKS_STILL_RUNNING = "Still running at the bound: {names}."
466
482
  # which waited on nothing and so cannot describe anything as being "at the
467
483
  # bound" (see CHECKS_AT_SPAWN_GATE_OFF).
468
484
  CHECKS_GATE_OFF_DETAIL = "Still running when the round was queued: {names}."
485
+ # Deliberately does NOT tell the operator to grant `checks: read`, which is what
486
+ # it said before the Actions fallback existed. GitHub does not offer the `Checks`
487
+ # permission on fine-grained PATs at all, and a rollup that reaches UNKNOWN on
488
+ # such a deployment has ALREADY tried the Actions read and failed -- so that
489
+ # advice pointed at the one action that provably cannot work, at the moment the
490
+ # operator is already blocked. The remaining causes are the ones named here.
469
491
  CHECKS_UNREADABLE = (
470
492
  "The rollup could not be read: `{why}`. An unreadable rollup is not a green "
471
- "one — check that the reviewer credential carries `checks: read` on this "
472
- "repo."
493
+ "one. On a credential that cannot read check runs the loop already fell "
494
+ "back to the Actions API, so reaching this point means that read failed "
495
+ "too — check the credential's `Actions: Read`, or whether the listing was "
496
+ "truncated (more contexts than the page bound)."
473
497
  )
474
498
 
475
499
  # The operator page for a native verdict post that keeps failing. Loud on
@@ -2438,10 +2462,24 @@ class ReviewWatcher:
2438
2462
  rollup = self.github.check_rollup(pr.owner, pr.repo, judged)
2439
2463
 
2440
2464
  if rollup.state == CHECKS_GREEN:
2441
- log.debug(
2465
+ # `info`, not `debug`: this is the line an operator reads to find
2466
+ # out WHICH read path called the head green, and on the deployment
2467
+ # that needs the answer the fallback is every read.
2468
+ log.info(
2442
2469
  "%s round %d: CI rollup at %s is %s — approving as usual",
2443
2470
  pr.slug, round_, judged[:8], rollup.summary,
2444
2471
  )
2472
+ if rollup.via_actions_fallback:
2473
+ # The one state where the marker had nowhere PR-visible to go.
2474
+ # Every other path that surfaces `summary` (the hold activity
2475
+ # comment, the degraded-verdict lead) is reachable only from
2476
+ # PENDING or UNKNOWN, so "approved on green" -- the claim an
2477
+ # operator acts on -- was the single verdict that did not say
2478
+ # it came from the narrower read.
2479
+ return ChecksGate(
2480
+ lead=CHECKS_FALLBACK_LEAD.format(sha=judged[:8]),
2481
+ state=CHECKS_GREEN,
2482
+ )
2445
2483
  return ChecksGate()
2446
2484
 
2447
2485
  if rollup.state == CHECKS_RED:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alissa-tools-github-revloop
3
- Version: 0.19.0
3
+ Version: 0.20.0
4
4
  Summary: ALISSA-TOOLS-GITHUB-REVLOOP
5
5
  Home-page: https://alissa.app
6
6
  Author: Fahera