github-local 0.0.1b3763__py3-none-any.whl

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.
File without changes
File without changes
@@ -0,0 +1,6 @@
1
+ GITHUB_TOKEN_ENV = "GITHUB_TOKEN"
2
+ GITHUB_ORGANIZATION = "circles-zone"
3
+ GITHUB_API_URL = "https://api.github.com"
4
+ GITHUB_MAX_KEYS_PER_SEARCH = 5 # GitHub search allows up to 5 OR operators
5
+ GITHUB_SEARCH_MAX_PAGES = 10 # GitHub search returns at most 1000 results (10 pages of 100)
6
+ GITHUB_REQUEST_TIMEOUT_SECONDS = 10
@@ -0,0 +1,91 @@
1
+ import re
2
+ from datetime import datetime, timezone
3
+ from typing import Any, Optional
4
+
5
+ import requests
6
+ from python_sdk_remote.utilities import our_get_env
7
+
8
+ from .constants_github_local import (
9
+ GITHUB_API_URL,
10
+ GITHUB_MAX_KEYS_PER_SEARCH,
11
+ GITHUB_ORGANIZATION,
12
+ GITHUB_REQUEST_TIMEOUT_SECONDS,
13
+ GITHUB_SEARCH_MAX_PAGES,
14
+ GITHUB_TOKEN_ENV,
15
+ )
16
+
17
+ WORK_ITEM_KEY_PATTERN = re.compile(r"\b[A-Z][A-Z0-9]+-\d+\b")
18
+ GITHUB_PAGE_SIZE = 100
19
+
20
+
21
+ class GitHubLocalException(Exception):
22
+ pass
23
+
24
+
25
+ def is_branch_of_key(branch_name: str, key: str) -> bool:
26
+ """feature/BU-1-x and BU-1 belong to BU-1, feature/BU-10-x does not."""
27
+ is_branch_of_key_result = re.fullmatch(rf"(feature/)?{re.escape(key)}([^0-9].*)?", branch_name,
28
+ re.IGNORECASE) is not None
29
+ return is_branch_of_key_result
30
+
31
+
32
+ class GitHubLocal:
33
+ """Which Jira Work Items were merged to dev, and the branches of a Jira Work Item."""
34
+
35
+ def __init__(self, token: Optional[str] = None, organization: str = GITHUB_ORGANIZATION) -> None:
36
+ self.organization = organization
37
+ github_token = token or our_get_env(GITHUB_TOKEN_ENV, raise_if_empty=True)
38
+ self.session = requests.Session()
39
+ self.session.headers.update({"Accept": "application/vnd.github+json",
40
+ "Authorization": f"Bearer {github_token}"})
41
+
42
+ def get_keys_merged_to_dev(self, keys: list[str]) -> set[str]:
43
+ """PR title = branch name = feature/BU-1234-..., so a merged PR to dev with the key in its title
44
+ means the Jira Work Item was merged."""
45
+ merged_keys: set[str] = set()
46
+ for start in range(0, len(keys), GITHUB_MAX_KEYS_PER_SEARCH):
47
+ batch = keys[start:start + GITHUB_MAX_KEYS_PER_SEARCH]
48
+ query = f"org:{self.organization} is:pr is:merged base:dev in:title " + " OR ".join(batch)
49
+ for page in range(1, GITHUB_SEARCH_MAX_PAGES + 1):
50
+ search_result = self._get("/search/issues", params={"q": query, "per_page": GITHUB_PAGE_SIZE,
51
+ "page": page})
52
+ pull_requests = search_result.get("items") or []
53
+ for pull_request in pull_requests:
54
+ merged_keys.update(key for key in WORK_ITEM_KEY_PATTERN.findall(
55
+ str(pull_request.get("title", "")).upper()) if key in batch)
56
+ if len(pull_requests) < GITHUB_PAGE_SIZE:
57
+ break
58
+ return merged_keys
59
+
60
+ def get_branches(self, repo_name: str, key: str) -> list[dict[str, Any]]:
61
+ """Branches named feature/<key>... or <key>... (exactly this key) with the age of their last commit."""
62
+ branches = []
63
+ for prefix in (f"feature/{key}", key):
64
+ refs = self._get(f"/repos/{self.organization}/{repo_name}/git/matching-refs/heads/{prefix}",
65
+ is_not_found_empty=True)
66
+ for ref in refs:
67
+ branch_name = ref["ref"].removeprefix("refs/heads/")
68
+ if not is_branch_of_key(branch_name, key):
69
+ continue
70
+ commit = self._get(f"/repos/{self.organization}/{repo_name}/commits/{ref['object']['sha']}")
71
+ last_commit_time = datetime.fromisoformat(commit["commit"]["committer"]["date"].replace("Z", "+00:00"))
72
+ branches.append({"repo_name": repo_name, "branch_name": branch_name,
73
+ "age": datetime.now(timezone.utc) - last_commit_time})
74
+ return branches
75
+
76
+ def _get(self, path: str, params: Optional[dict[str, Any]] = None, is_not_found_empty: bool = False) -> Any:
77
+ try:
78
+ response = self.session.get(f"{GITHUB_API_URL}{path}", params=params,
79
+ timeout=GITHUB_REQUEST_TIMEOUT_SECONDS)
80
+ except requests.RequestException as exception:
81
+ raise GitHubLocalException(f"GitHub is unreachable: {exception}")
82
+ if response.status_code == 404 and is_not_found_empty:
83
+ response_json: Any = []
84
+ elif not response.ok:
85
+ raise GitHubLocalException(f"GitHub GET {path} failed with {response.status_code}: {response.text[:200]}")
86
+ else:
87
+ try:
88
+ response_json = response.json()
89
+ except ValueError:
90
+ raise GitHubLocalException(f"GitHub GET {path} returned an invalid response: {response.text[:200]}")
91
+ return response_json
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.4
2
+ Name: github-local
3
+ Version: 0.0.1b3763
4
+ Summary: PyPI github-local Python Package owned by Circlez.ai
5
+ Home-page: https://github.com/circles-zone/github-local-python-package
6
+ Author: Circles
7
+ Author-email: info@circlez.ai
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: python-sdk-remote
12
+ Requires-Dist: requests
13
+ Dynamic: author
14
+ Dynamic: author-email
15
+ Dynamic: classifier
16
+ Dynamic: description
17
+ Dynamic: description-content-type
18
+ Dynamic: home-page
19
+ Dynamic: requires-dist
20
+ Dynamic: summary
21
+
22
+ PyPI github-local Python Package owned by Circlez.ai
23
+ GHA: https://github.com/circles-zone/github-local-python-package/actions
@@ -0,0 +1,8 @@
1
+ github_local/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ github_local/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ github_local/src/constants_github_local.py,sha256=DpI13rmtPCL8IGYwKgqeFwHzNSpyusma1FtFpDSvNs8,318
4
+ github_local/src/github_local.py,sha256=WJTDEiwmNECdNrNAZk6LUrIuTAUOGKlS6Ywdn53ij84,4482
5
+ github_local-0.0.1b3763.dist-info/METADATA,sha256=PL2yPFjgOKmm-gSt9vT2QLNM9tcC4hSX1UVviiDEzpA,731
6
+ github_local-0.0.1b3763.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
7
+ github_local-0.0.1b3763.dist-info/top_level.txt,sha256=kuRvIGRqZ28NWumD7H2iy9ocrSK91GES4C2OZxAS3I0,13
8
+ github_local-0.0.1b3763.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ github_local