github-local 0.0.3b4575__tar.gz → 0.0.3b4586__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 (30) hide show
  1. {github_local-0.0.3b4575/python_packages/github_local.egg-info → github_local-0.0.3b4586}/PKG-INFO +1 -1
  2. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/pyproject.toml +1 -1
  3. github_local-0.0.3b4586/python_packages/github_local/src/github_failed_job_reply.py +10 -0
  4. github_local-0.0.3b4586/python_packages/github_local/src/handle_github_failed_job.py +62 -0
  5. github_local-0.0.3b4586/python_packages/github_local/tests/unit/handle_github_failed_job_unit_test.py +66 -0
  6. {github_local-0.0.3b4575 → github_local-0.0.3b4586/python_packages/github_local.egg-info}/PKG-INFO +1 -1
  7. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local.egg-info/SOURCES.txt +4 -1
  8. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/setup.py +1 -1
  9. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/.coveragerc +0 -0
  10. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/.flake8 +0 -0
  11. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/README.md +0 -0
  12. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/pytest.ini +0 -0
  13. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/conftest.py +0 -0
  14. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local/__init__.py +0 -0
  15. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local/src/__init__.py +0 -0
  16. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local/src/classify_github_actions_failure.py +0 -0
  17. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local/src/constants_github_local.py +0 -0
  18. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local/src/github_actions_failure.py +0 -0
  19. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local/src/github_actions_failure_category.py +0 -0
  20. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local/src/github_local.py +0 -0
  21. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local/tests/__init__.py +0 -0
  22. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local/tests/unit/__init__.py +0 -0
  23. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local/tests/unit/classify_github_actions_failure_unit_test.py +0 -0
  24. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local/tests/unit/conftest.py +0 -0
  25. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local/tests/unit/github_local_unit_test.py +0 -0
  26. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local.egg-info/dependency_links.txt +0 -0
  27. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local.egg-info/requires.txt +0 -0
  28. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/python_packages/github_local.egg-info/top_level.txt +0 -0
  29. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/requirements.txt +0 -0
  30. {github_local-0.0.3b4575 → github_local-0.0.3b4586}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-local
3
- Version: 0.0.3b4575
3
+ Version: 0.0.3b4586
4
4
  Summary: PyPI github-local Python Package owned by Circlez.ai
5
5
  Home-page: https://github.com/circles-zone/github-local-python-package
6
6
  Author: Circles
@@ -22,7 +22,7 @@ pythonpath = ["."]
22
22
  name = "github-local"
23
23
  # After the b (beta) comes the Jira Work Item ID (sdk-local Makec.ps1 updates it)
24
24
  # I believe we are still using the version from setup.py and not from here until Potery will work
25
- version = "0.0.3b4575" # https://pypi.org/project/github-local/
25
+ version = "0.0.3b4586" # https://pypi.org/project/github-local/
26
26
  description = "github-local Python Package"
27
27
  readme = "README.md"
28
28
  authors = [
@@ -0,0 +1,10 @@
1
+ from dataclasses import dataclass
2
+
3
+ from .github_actions_failure_category import GitHubActionsFailureCategory
4
+
5
+
6
+ @dataclass(frozen=True)
7
+ class GitHubFailedJobReply:
8
+ channel_message: str
9
+ category: GitHubActionsFailureCategory
10
+ is_rerun_safe: bool
@@ -0,0 +1,62 @@
1
+ """A failed GitHub Actions job (GitHub webhook workflow_job event): classify it and tell the team in one Slack message,
2
+ so nobody has to open the log to know whether it is their code, a runner or a missing secret."""
3
+ from collections.abc import Callable
4
+ from typing import Any
5
+
6
+ from .classify_github_actions_failure import classify_github_actions_failure, main_error
7
+ from .github_actions_failure import GitHubActionsFailure
8
+ from .github_actions_failure_category import GitHubActionsFailureCategory, is_rerun_safe
9
+ from .github_failed_job_reply import GitHubFailedJobReply
10
+
11
+ WORKFLOW_JOB_EVENT = "workflow_job"
12
+ NEXT_STEP = {
13
+ GitHubActionsFailureCategory.RUNNER: "The runner, not the code: rerun the job (another runner if it repeats).",
14
+ GitHubActionsFailureCategory.CONFIG_SECRET: "A secret / variable / permission is missing or wrong.",
15
+ GitHubActionsFailureCategory.PROCESS_GATE: "A company rule stopped it on purpose - see the error.",
16
+ GitHubActionsFailureCategory.SECURITY_DEPENDENCY: "A dependency or a security alert - update the package.",
17
+ GitHubActionsFailureCategory.TEST_FAILURE: "A test failed - the commit author please check.",
18
+ GitHubActionsFailureCategory.LINT_TYPE: "Lint / type check - the commit author please fix.",
19
+ GitHubActionsFailureCategory.PUBLISH_DEPLOY: "Build and tests passed; publishing or deploying failed.",
20
+ GitHubActionsFailureCategory.INVALID_WORKFLOW: "The workflow file itself is invalid.",
21
+ GitHubActionsFailureCategory.UNKNOWN: "Could not tell from the log.",
22
+ }
23
+ MESSAGE = (":red_circle: *{repo_name}* / {workflow_name} / {job_name} failed - `{category}`\n"
24
+ "{next_step}\n> {main_error}\n{html_url}")
25
+ MAXIMUM_ERROR_LENGTH = 300
26
+
27
+
28
+ class HandleGitHubFailedJob:
29
+ def __init__(self, get_job_log: Callable[[str, int], str]):
30
+ """get_job_log(repo full name, job id) -> the job log text, e.g. from the GitHub REST API"""
31
+ self.get_job_log = get_job_log
32
+
33
+ @staticmethod
34
+ def is_event(event_name: str, payload: dict[str, Any]) -> bool:
35
+ workflow_job = payload.get("workflow_job") or {}
36
+ is_failed_job = (event_name == WORKFLOW_JOB_EVENT and payload.get("action") == "completed"
37
+ and workflow_job.get("conclusion") == "failure")
38
+ return is_failed_job
39
+
40
+ def handle(self, payload: dict[str, Any]) -> GitHubFailedJobReply:
41
+ workflow_job = payload["workflow_job"]
42
+ repo_full_name = payload["repository"]["full_name"]
43
+ try:
44
+ log = self.get_job_log(repo_full_name, workflow_job["id"])
45
+ except Exception:
46
+ # The message still goes out: the category is then UNKNOWN
47
+ log = ""
48
+ failed_step_name = next((step.get("name") for step in workflow_job.get("steps") or []
49
+ if step.get("conclusion") == "failure"), None)
50
+ failure = GitHubActionsFailure(repo_name=payload["repository"]["name"],
51
+ workflow_name=workflow_job.get("workflow_name") or "",
52
+ job_name=workflow_job.get("name"), failed_step_name=failed_step_name,
53
+ runner_name=workflow_job.get("runner_name"), log=log)
54
+ category = classify_github_actions_failure(failure)
55
+ error = main_error(log)[:MAXIMUM_ERROR_LENGTH] or f"Failed step: {failed_step_name or 'unknown'}"
56
+ channel_message = MESSAGE.format(repo_name=failure.repo_name, workflow_name=failure.workflow_name,
57
+ job_name=failure.job_name, category=category.value,
58
+ next_step=NEXT_STEP[category], main_error=error,
59
+ html_url=workflow_job.get("html_url", ""))
60
+ reply = GitHubFailedJobReply(channel_message=channel_message, category=category,
61
+ is_rerun_safe=is_rerun_safe(category))
62
+ return reply
@@ -0,0 +1,66 @@
1
+ import pytest
2
+
3
+ from github_local.src.github_actions_failure_category import GitHubActionsFailureCategory
4
+ from github_local.src.handle_github_failed_job import HandleGitHubFailedJob
5
+
6
+ JOB_URL = "https://github.com/circles-zone/sdk-local/actions/runs/1/job/2"
7
+ RUNNER_LOG = "Run tests\n##[error]No space left on device\n##[error]Process completed with exit code 1."
8
+
9
+
10
+ def workflow_job_payload(conclusion: str = "failure", action: str = "completed") -> dict:
11
+ payload = {"action": action,
12
+ "repository": {"name": "sdk-local", "full_name": "circles-zone/sdk-local"},
13
+ "workflow_job": {"id": 2, "conclusion": conclusion, "name": "build", "workflow_name": "Play1",
14
+ "runner_name": "runner-1", "html_url": JOB_URL,
15
+ "steps": [{"name": "Checkout", "conclusion": "success"},
16
+ {"name": "Run tests", "conclusion": "failure"}]}}
17
+ return payload
18
+
19
+
20
+ @pytest.mark.parametrize("event_name, payload, is_event", [
21
+ ("workflow_job", workflow_job_payload(), True),
22
+ ("workflow_job", workflow_job_payload(conclusion="success"), False),
23
+ ("workflow_job", workflow_job_payload(action="in_progress"), False),
24
+ ("push", workflow_job_payload(), False),
25
+ ("workflow_job", {"action": "completed"}, False),
26
+ ])
27
+ def test_is_event_is_a_completed_failed_job(event_name, payload, is_event):
28
+ assert HandleGitHubFailedJob.is_event(event_name, payload) is is_event
29
+
30
+
31
+ def test_a_runner_failure_says_to_rerun_and_links_the_job():
32
+ job_logs: list[tuple[str, int]] = []
33
+
34
+ def get_job_log(repo_full_name: str, job_id: int) -> str:
35
+ job_logs.append((repo_full_name, job_id))
36
+ return RUNNER_LOG
37
+
38
+ reply = HandleGitHubFailedJob(get_job_log).handle(workflow_job_payload())
39
+
40
+ assert job_logs == [("circles-zone/sdk-local", 2)]
41
+ assert reply.category == GitHubActionsFailureCategory.RUNNER
42
+ assert reply.is_rerun_safe
43
+ assert reply.channel_message.startswith(":red_circle: *sdk-local* / Play1 / build failed - `runner`\n")
44
+ assert "rerun the job" in reply.channel_message
45
+ assert "> No space left on device" in reply.channel_message
46
+ assert reply.channel_message.endswith(JOB_URL)
47
+
48
+
49
+ def test_without_a_log_the_message_names_the_failed_step():
50
+ def get_job_log(repo_full_name: str, job_id: int) -> str:
51
+ raise RuntimeError("Not Found")
52
+
53
+ reply = HandleGitHubFailedJob(get_job_log).handle(workflow_job_payload())
54
+
55
+ assert reply.category == GitHubActionsFailureCategory.UNKNOWN
56
+ assert not reply.is_rerun_safe
57
+ assert "> Failed step: Run tests" in reply.channel_message
58
+
59
+
60
+ def test_a_job_without_steps_still_gets_a_message():
61
+ payload = workflow_job_payload()
62
+ del payload["workflow_job"]["steps"]
63
+
64
+ reply = HandleGitHubFailedJob(lambda repo_full_name, job_id: "").handle(payload)
65
+
66
+ assert "> Failed step: unknown" in reply.channel_message
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-local
3
- Version: 0.0.3b4575
3
+ Version: 0.0.3b4586
4
4
  Summary: PyPI github-local Python Package owned by Circlez.ai
5
5
  Home-page: https://github.com/circles-zone/github-local-python-package
6
6
  Author: Circles
@@ -17,9 +17,12 @@ python_packages/github_local/src/classify_github_actions_failure.py
17
17
  python_packages/github_local/src/constants_github_local.py
18
18
  python_packages/github_local/src/github_actions_failure.py
19
19
  python_packages/github_local/src/github_actions_failure_category.py
20
+ python_packages/github_local/src/github_failed_job_reply.py
20
21
  python_packages/github_local/src/github_local.py
22
+ python_packages/github_local/src/handle_github_failed_job.py
21
23
  python_packages/github_local/tests/__init__.py
22
24
  python_packages/github_local/tests/unit/__init__.py
23
25
  python_packages/github_local/tests/unit/classify_github_actions_failure_unit_test.py
24
26
  python_packages/github_local/tests/unit/conftest.py
25
- python_packages/github_local/tests/unit/github_local_unit_test.py
27
+ python_packages/github_local/tests/unit/github_local_unit_test.py
28
+ python_packages/github_local/tests/unit/handle_github_failed_job_unit_test.py
@@ -32,7 +32,7 @@ setuptools.setup(
32
32
  # Updating the b{{JiraWorkItemId}} done automatically by makec.ps1 / make_py.ps1
33
33
  # According to GitFlow, in the Feature Branch, after the letter 'b' comes the
34
34
  # Jira Work Item ID, i.e., 0.0.1b2432. Semantic Version simver.org
35
- version='0.0.3b4575', # https://pypi.org/project/github-local#History
35
+ version='0.0.3b4586', # https://pypi.org/project/github-local#History
36
36
  author="Circles",
37
37
  author_email="info@circlez.ai",
38
38
  description=f"{PACKAGE_DESCRIPTION}",