alissa-tools-github-genloop 0.1.3__tar.gz → 0.1.4__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 (28) hide show
  1. {alissa_tools_github_genloop-0.1.3/src/main/alissa_tools_github_genloop.egg-info → alissa_tools_github_genloop-0.1.4}/PKG-INFO +1 -1
  2. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa/tools/github/genloop/github.py +35 -4
  3. alissa_tools_github_genloop-0.1.4/src/main/alissa/tools/github/genloop/version +1 -0
  4. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4/src/main/alissa_tools_github_genloop.egg-info}/PKG-INFO +1 -1
  5. alissa_tools_github_genloop-0.1.3/src/main/alissa/tools/github/genloop/version +0 -1
  6. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/LICENSE +0 -0
  7. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/MANIFEST.in +0 -0
  8. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/NOTICE +0 -0
  9. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/README.md +0 -0
  10. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/requirements.txt +0 -0
  11. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/setup.cfg +0 -0
  12. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/setup.py +0 -0
  13. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa/tools/github/genloop/__init__.py +0 -0
  14. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa/tools/github/genloop/__main__.py +0 -0
  15. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa/tools/github/genloop/alissa_cli.py +0 -0
  16. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa/tools/github/genloop/alissa_client.py +0 -0
  17. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa/tools/github/genloop/config.py +0 -0
  18. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa/tools/github/genloop/loop.py +0 -0
  19. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa/tools/github/genloop/loop_events.py +0 -0
  20. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa/tools/github/genloop/pass_directive.py +0 -0
  21. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa/tools/github/genloop/plan.py +0 -0
  22. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa/tools/github/genloop/proc.py +0 -0
  23. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa/tools/github/genloop/state.py +0 -0
  24. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa/tools/github/genloop/version.py +0 -0
  25. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa_tools_github_genloop.egg-info/SOURCES.txt +0 -0
  26. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa_tools_github_genloop.egg-info/dependency_links.txt +0 -0
  27. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa_tools_github_genloop.egg-info/entry_points.txt +0 -0
  28. {alissa_tools_github_genloop-0.1.3 → alissa_tools_github_genloop-0.1.4}/src/main/alissa_tools_github_genloop.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alissa-tools-github-genloop
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: ALISSA-TOOLS-GITHUB-GENLOOP
5
5
  Home-page: https://alissa.app
6
6
  Author: Fahera
@@ -13,8 +13,14 @@ same fallback orcloop's `ci-green` read uses. It reads only the run of record:
13
13
  the newest plan-lint run of the trusted event (`pull_request_target`, the job
14
14
  that publishes the `plan-lint` check run), mapped onto the check run's
15
15
  `{conclusion}` shape. The offline `pull_request` run never counts, as it never
16
- does on the check-runs path. A denied 403 ("Resource not accessible") is
17
- latched for the process -- every later read goes straight to the Actions path;
16
+ does on the check-runs path. A `failure` is red only when the job's publish
17
+ step (`Publish the plan-lint check run …`, `if: always()`) succeeded: that
18
+ step is the only thing that creates the check run, and it publishes `failure`
19
+ itself when the linter never reported. When the publish step failed (a 5xx
20
+ on its POST), no check run exists, so the fallback reads None, as the
21
+ check-runs path would; the run's jobs (`actions/runs/<id>/jobs`) say which.
22
+ A denied 403 ("Resource not accessible") is latched for the process -- every
23
+ later read goes straight to the Actions path;
18
24
  any other 403 (a secondary rate limit) falls back for that read only. When
19
25
  the Actions read fails too, the warning is logged once per PR per head and
20
26
  `LintUnreadable` is raised, which the caller skips quietly.
@@ -48,6 +54,9 @@ LINT_WORKFLOW = ".github/workflows/plan-lint.yml"
48
54
  # conclusion passes through verbatim, so only `failure` is a red lint, as on
49
55
  # the check-runs path; the offline `pull_request` run is never read.
50
56
  LINT_EVENT = "pull_request_target"
57
+ # The trusted job's step that publishes the `plan-lint` check run, matched as a
58
+ # prefix of the step name (`GitHub(lint_publish_step=...)` names another).
59
+ LINT_PUBLISH_STEP = "Publish the plan-lint check run"
51
60
  # The 403 body of a token that cannot read check runs, as against a rate limit.
52
61
  _DENIED_MARKER = "resource not accessible"
53
62
  # A plan PR thread longer than this many pages of 100 comments is read only up to here.
@@ -111,10 +120,12 @@ class Comment:
111
120
 
112
121
 
113
122
  class GitHub:
114
- def __init__(self, *, timeout: int = 60, lint_workflow: str = LINT_WORKFLOW, lint_event: str = LINT_EVENT):
123
+ def __init__(self, *, timeout: int = 60, lint_workflow: str = LINT_WORKFLOW, lint_event: str = LINT_EVENT,
124
+ lint_publish_step: str = LINT_PUBLISH_STEP):
115
125
  self.timeout = timeout
116
126
  self.lint_workflow = lint_workflow
117
127
  self.lint_event = lint_event
128
+ self.lint_publish_step = lint_publish_step
118
129
  # Latched on the first 403 from check-runs: this token cannot read them.
119
130
  self._check_runs_denied = False
120
131
  # (repo, pr) -> the head whose unreadable lint has been logged already;
@@ -274,7 +285,8 @@ class GitHub:
274
285
  def _lint_actions_run(self, repo: str, sha: str) -> dict | None:
275
286
  """The newest plan-lint run of `lint_event` on `sha` as a check run:
276
287
  its conclusion verbatim once completed, pending (None) before, and
277
- None when there is no such run yet."""
288
+ None when there is no such run yet -- or when it failed only because
289
+ its publish step did, so that no check run exists."""
278
290
  data = self._api(
279
291
  "-X", "GET", f"repos/{repo}/actions/runs", "-f", f"head_sha={sha}", "-f", "per_page=100",
280
292
  )
@@ -287,12 +299,31 @@ class GitHub:
287
299
  return None
288
300
  head = max(trusted, key=_run_order)
289
301
  completed = head.get("status") == "completed"
302
+ if completed and head.get("conclusion") == "failure" and not self._lint_published(repo, head):
303
+ return None
290
304
  return {
291
305
  "id": head.get("id"), "name": LINT_CHECK_NAME, "source": "actions",
292
306
  "status": "completed" if completed else str(head.get("status") or "") or "in_progress",
293
307
  "conclusion": head.get("conclusion") if completed else None, "html_url": head.get("html_url"),
294
308
  }
295
309
 
310
+ def _lint_published(self, repo: str, run: dict) -> bool:
311
+ """Whether the failed `run` published its `plan-lint` check run: its
312
+ publish step succeeded, or no step of its jobs is the publish step (the
313
+ failure then stands, as before this read)."""
314
+ path = f"repos/{repo}/actions/runs/{run.get('id')}/jobs"
315
+ data = self._api("-X", "GET", path, "-f", "per_page=100")
316
+ jobs = data.get("jobs") if isinstance(data, dict) else None
317
+ if not isinstance(jobs, list):
318
+ raise CommandError(["gh", "api", path], 0, "no jobs in the answer")
319
+ steps = [s for j in jobs if isinstance(j, dict) for s in (j.get("steps") or [])
320
+ if isinstance(s, dict) and str(s.get("name") or "").startswith(self.lint_publish_step)]
321
+ if not steps or any(s.get("conclusion") == "success" for s in steps):
322
+ return True
323
+ log.debug("genloop: plan-lint run %s on %s failed without publishing its check run — no lint, "
324
+ "as on the check-runs path", run.get("id"), repo)
325
+ return False
326
+
296
327
  def _is_lint_workflow(self, r: dict) -> bool:
297
328
  path = str(r.get("path") or "").split("@", 1)[0]
298
329
  return self.lint_workflow in (path, str(r.get("name") or ""))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alissa-tools-github-genloop
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: ALISSA-TOOLS-GITHUB-GENLOOP
5
5
  Home-page: https://alissa.app
6
6
  Author: Fahera