github-rest-api 0.45.0__py3-none-any.whl → 0.46.0__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.
- github_rest_api/pr_content.py +18 -7
- github_rest_api/scripts/github/auto_merge_pull_request.py +109 -0
- github_rest_api/scripts/github/{add_github_repo.py → create_github_repo.py} +5 -5
- {github_rest_api-0.45.0.dist-info → github_rest_api-0.46.0.dist-info}/METADATA +1 -1
- {github_rest_api-0.45.0.dist-info → github_rest_api-0.46.0.dist-info}/RECORD +7 -6
- {github_rest_api-0.45.0.dist-info → github_rest_api-0.46.0.dist-info}/WHEEL +1 -1
- {github_rest_api-0.45.0.dist-info → github_rest_api-0.46.0.dist-info}/entry_points.txt +2 -1
github_rest_api/pr_content.py
CHANGED
|
@@ -83,9 +83,15 @@ def deterministic_title(compare: dict[str, Any]) -> str:
|
|
|
83
83
|
"""Derive a Conventional-Commits title from a comparison result.
|
|
84
84
|
|
|
85
85
|
The type is the most significant type present across the commits
|
|
86
|
-
(``feat`` > ``fix`` > the most frequent parsed type
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
(``feat`` > ``fix`` > the most frequent parsed type). When no commit follows
|
|
87
|
+
the Conventional-Commits grammar, the type is ``ci`` for changes confined to
|
|
88
|
+
``.github`` and ``chore`` otherwise. The scope is the common top-level
|
|
89
|
+
directory of the changed files; ``!`` is appended for breaking changes.
|
|
90
|
+
|
|
91
|
+
The description reuses a matching commit's description when available and
|
|
92
|
+
otherwise falls back to the first commit's subject line, so a genuinely
|
|
93
|
+
descriptive (if non-conventional) subject is preserved rather than replaced
|
|
94
|
+
by a generic placeholder.
|
|
89
95
|
|
|
90
96
|
:param compare: The comparison result from `Repository.compare`.
|
|
91
97
|
"""
|
|
@@ -94,17 +100,22 @@ def deterministic_title(compare: dict[str, Any]) -> str:
|
|
|
94
100
|
parsed = [parse_conventional(subject) for subject in subjects]
|
|
95
101
|
types = [item[0] for item in parsed if item]
|
|
96
102
|
scope = _common_scope(compare)
|
|
97
|
-
prefix_scope = f"({scope})" if scope else ""
|
|
98
|
-
if not subjects:
|
|
99
|
-
return f"chore{prefix_scope}: update"
|
|
100
103
|
if "feat" in types:
|
|
101
104
|
type_ = "feat"
|
|
102
105
|
elif "fix" in types:
|
|
103
106
|
type_ = "fix"
|
|
104
107
|
elif types:
|
|
105
108
|
type_ = Counter(types).most_common(1)[0][0]
|
|
109
|
+
elif scope == ".github":
|
|
110
|
+
# Changes confined to `.github` (e.g. workflow files) are conventionally
|
|
111
|
+
# `ci`; the type already conveys the location, so drop the (redundant)
|
|
112
|
+
# `.github` scope.
|
|
113
|
+
type_, scope = "ci", None
|
|
106
114
|
else:
|
|
107
115
|
type_ = "chore"
|
|
116
|
+
prefix_scope = f"({scope})" if scope else ""
|
|
117
|
+
if not subjects:
|
|
118
|
+
return f"{type_}{prefix_scope}: update"
|
|
108
119
|
breaking = any(item[2] for item in parsed if item) or any(
|
|
109
120
|
_BREAKING_CHANGE_PATTERN.search(message) for message in messages
|
|
110
121
|
)
|
|
@@ -113,7 +124,7 @@ def deterministic_title(compare: dict[str, Any]) -> str:
|
|
|
113
124
|
else:
|
|
114
125
|
description = next(
|
|
115
126
|
(item[3] for item in parsed if item and item[0] == type_),
|
|
116
|
-
|
|
127
|
+
subjects[0],
|
|
117
128
|
)
|
|
118
129
|
return f"{type_}{prefix_scope}{'!' if breaking else ''}: {description}"
|
|
119
130
|
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Auto-merge eligible open pull requests in the repository.
|
|
2
|
+
|
|
3
|
+
Every open PR is evaluated by ``Repository.auto_merge_pull_requests`` and merged
|
|
4
|
+
only when it passes the eligibility gate (author allowlist, title type, head
|
|
5
|
+
commit age, mergeable state and an approving review or marker comment).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
from argparse import ArgumentParser, Namespace
|
|
12
|
+
|
|
13
|
+
from github_rest_api import MergeMethod, Repository
|
|
14
|
+
from github_rest_api.github import (
|
|
15
|
+
DEFAULT_AUTO_MERGE_MARKER,
|
|
16
|
+
DEFAULT_AUTO_MERGE_TYPES,
|
|
17
|
+
DEFAULT_MIN_AGE_MINUTES,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def parse_args(args=None, namespace=None) -> Namespace:
|
|
22
|
+
"""Parse command-line arguments.
|
|
23
|
+
:param args: The arguments to parse.
|
|
24
|
+
If None, the arguments from command-line are parsed.
|
|
25
|
+
:param namespace: An inital Namespace object.
|
|
26
|
+
:return: A namespace object containing parsed options.
|
|
27
|
+
"""
|
|
28
|
+
parser = ArgumentParser(description="Auto-merge eligible open pull requests.")
|
|
29
|
+
parser.add_argument(
|
|
30
|
+
"--token",
|
|
31
|
+
dest="token",
|
|
32
|
+
required=True,
|
|
33
|
+
help="The personal access token for authentication.",
|
|
34
|
+
)
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
"--authors",
|
|
37
|
+
dest="authors",
|
|
38
|
+
nargs="*",
|
|
39
|
+
default=[],
|
|
40
|
+
help="Logins whose PRs are eligible for auto-merge. An empty allowlist "
|
|
41
|
+
"makes nothing eligible (fail-safe).",
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"--approvers",
|
|
45
|
+
dest="approvers",
|
|
46
|
+
nargs="*",
|
|
47
|
+
default=[],
|
|
48
|
+
help="Logins whose reviews or marker comments grant approval.",
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"--allowed-types",
|
|
52
|
+
dest="allowed_types",
|
|
53
|
+
nargs="*",
|
|
54
|
+
default=list(DEFAULT_AUTO_MERGE_TYPES),
|
|
55
|
+
help="Conventional-Commits title types eligible for auto-merge.",
|
|
56
|
+
)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--min-age-minutes",
|
|
59
|
+
dest="min_age_minutes",
|
|
60
|
+
type=int,
|
|
61
|
+
default=DEFAULT_MIN_AGE_MINUTES,
|
|
62
|
+
help="The minimum head-commit age in minutes; set to 0 to disable the guard.",
|
|
63
|
+
)
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"--marker",
|
|
66
|
+
dest="marker",
|
|
67
|
+
default=DEFAULT_AUTO_MERGE_MARKER,
|
|
68
|
+
help="The marker substring an approver may comment to approve.",
|
|
69
|
+
)
|
|
70
|
+
parser.add_argument(
|
|
71
|
+
"--merge-method",
|
|
72
|
+
dest="merge_method",
|
|
73
|
+
choices=[m.value for m in MergeMethod],
|
|
74
|
+
default=MergeMethod.MERGE.value,
|
|
75
|
+
help="The merge method to use.",
|
|
76
|
+
)
|
|
77
|
+
parser.add_argument(
|
|
78
|
+
"--dry-run",
|
|
79
|
+
dest="dry_run",
|
|
80
|
+
action="store_true",
|
|
81
|
+
help="Log the PRs that would be merged without merging them.",
|
|
82
|
+
)
|
|
83
|
+
return parser.parse_args(args=args, namespace=namespace)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def main() -> int:
|
|
87
|
+
"""Main entrance of the script,
|
|
88
|
+
which auto-merges every eligible open pull request in the repository.
|
|
89
|
+
"""
|
|
90
|
+
args = parse_args()
|
|
91
|
+
# auto_merge_pull_requests reports its outcomes (including the dry-run
|
|
92
|
+
# listing) via logging.info, which the root logger suppresses by default;
|
|
93
|
+
# configure logging so those messages are actually emitted.
|
|
94
|
+
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
95
|
+
repo = Repository(args.token, os.environ["GITHUB_REPOSITORY"])
|
|
96
|
+
repo.auto_merge_pull_requests(
|
|
97
|
+
authors=args.authors,
|
|
98
|
+
approvers=args.approvers,
|
|
99
|
+
allowed_types=args.allowed_types,
|
|
100
|
+
min_age_minutes=args.min_age_minutes,
|
|
101
|
+
marker=args.marker,
|
|
102
|
+
merge_method=args.merge_method,
|
|
103
|
+
dry_run=args.dry_run,
|
|
104
|
+
)
|
|
105
|
+
return 0
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
sys.exit(main())
|
|
@@ -80,7 +80,7 @@ def _init_local_repo(
|
|
|
80
80
|
_add_workflow(path, language)
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
def
|
|
83
|
+
def create_github_repo(
|
|
84
84
|
repo: str,
|
|
85
85
|
private: bool,
|
|
86
86
|
language: str,
|
|
@@ -117,7 +117,7 @@ def _add_workflow(path: Path, language: str, workflow_dir: Path | None = None) -
|
|
|
117
117
|
workflow_dir = Path(__file__).parent / "workflows"
|
|
118
118
|
dir_dest = path / ".github" / "workflows"
|
|
119
119
|
dir_dest.mkdir(parents=True, exist_ok=True)
|
|
120
|
-
for yaml in workflow_dir.glob("*.
|
|
120
|
+
for yaml in workflow_dir.glob("*.yaml"):
|
|
121
121
|
if not (dir_dest / yaml.name).exists():
|
|
122
122
|
shutil.copy2(yaml, dir_dest)
|
|
123
123
|
if not language:
|
|
@@ -125,13 +125,13 @@ def _add_workflow(path: Path, language: str, workflow_dir: Path | None = None) -
|
|
|
125
125
|
lang_dir = workflow_dir / language
|
|
126
126
|
if not lang_dir.exists():
|
|
127
127
|
return
|
|
128
|
-
for yaml in lang_dir.glob("*.
|
|
128
|
+
for yaml in lang_dir.glob("*.yaml"):
|
|
129
129
|
if not (dir_dest / yaml.name).exists():
|
|
130
130
|
shutil.copy2(yaml, dir_dest)
|
|
131
131
|
|
|
132
132
|
|
|
133
133
|
def parse_args(args=None, namespace=None):
|
|
134
|
-
parser = argparse.ArgumentParser(description="
|
|
134
|
+
parser = argparse.ArgumentParser(description="Create a GitHub repository.")
|
|
135
135
|
parser.add_argument(
|
|
136
136
|
"repo",
|
|
137
137
|
help="The GitHub repo (in the format of owner/repo) to be created.",
|
|
@@ -203,7 +203,7 @@ def parse_args(args=None, namespace=None):
|
|
|
203
203
|
def main() -> int:
|
|
204
204
|
args = parse_args()
|
|
205
205
|
try:
|
|
206
|
-
|
|
206
|
+
create_github_repo(
|
|
207
207
|
repo=args.repo,
|
|
208
208
|
private=args.private,
|
|
209
209
|
language=args.language,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
github_rest_api/__init__.py,sha256=VVpiUHVLbjjJGNN88OmEvCwq4xmBnXv68T8Apk_lwVc,282
|
|
2
2
|
github_rest_api/github.py,sha256=iAfhPLH9UGPtScZfcV1TCeY5XfUzAMqenf5FrSNplQc,45146
|
|
3
|
-
github_rest_api/pr_content.py,sha256=
|
|
3
|
+
github_rest_api/pr_content.py,sha256=og1VwsMtwqwl-HUJcJrkwjUjFPw9E1k5xDtJVMAXBRo,10039
|
|
4
4
|
github_rest_api/utils.py,sha256=RspoIZik0g105KpyFXCf4epAtgu1dUsxwlwUSSGhXPI,2697
|
|
5
5
|
github_rest_api/scripts/__init__.py,sha256=NkHTngnnWXPc0JmO5mqVKjDf4JPofuwWjDW1SU3aE7Y,36
|
|
6
6
|
github_rest_api/scripts/utils.py,sha256=o5GzgSNyGHUIHhaaG5QUfXvv6fsTQPCHD9clGRNMggU,4588
|
|
@@ -13,7 +13,8 @@ github_rest_api/scripts/container/build_container_images.py,sha256=-ZPPKVUpc0v-_
|
|
|
13
13
|
github_rest_api/scripts/container/config_container.py,sha256=BtOqgZlQFM9cWZMoHKvOzNsyXxCl_DLqYWbMqu1YOWM,2835
|
|
14
14
|
github_rest_api/scripts/container/update_version_containerfile.py,sha256=1PkuJkAsJBCEMdacMSndbuZKcmgUyXy6nBArnxKoPZk,4535
|
|
15
15
|
github_rest_api/scripts/github/__init__.py,sha256=tEU1SdCS51M4M13WoqXZt3rjGEAI_QgNljt5XAyQJ_w,38
|
|
16
|
-
github_rest_api/scripts/github/
|
|
16
|
+
github_rest_api/scripts/github/auto_merge_pull_request.py,sha256=_5191Xt_b8DtxjIqHrdY-gKLxvkzWd8oWoSbF2uauc8,3527
|
|
17
|
+
github_rest_api/scripts/github/create_github_repo.py,sha256=6JiNDKYmRuAwJhBsNJOkeIHGmuP2ukii4esmdC8HQw8,6362
|
|
17
18
|
github_rest_api/scripts/github/create_pull_request.py,sha256=gXtRRsxzegGxagHb6k3kxtRd3mt4QVwSPZQpsFnzz1Y,3968
|
|
18
19
|
github_rest_api/scripts/github/release_on_github.py,sha256=9psaIv-GtrHdvE-tkw0yxCMDmW0bPts-db2vUxhoKXo,4368
|
|
19
20
|
github_rest_api/scripts/github/remove_branch.py,sha256=iKZ4Y_lQlSOIfifC4plqP-B6pEMnFrknGMvV1oLnuLc,3538
|
|
@@ -23,7 +24,7 @@ github_rest_api/scripts/github/workflows/create_pr_to_main.yaml,sha256=mgHPlewkC
|
|
|
23
24
|
github_rest_api/scripts/github/workflows/remove_branch.yaml,sha256=KDpB76ovrhuRdP0HN5j6Th9gjIQWSDdh8b4b4yAgtoI,544
|
|
24
25
|
github_rest_api/scripts/github/workflows/python/lint.yaml,sha256=toP2oUdVIKyu_n20Dr14k5cJYxN8rPksfbX4sfflXxc,699
|
|
25
26
|
github_rest_api/scripts/github/workflows/python/test.yaml,sha256=86MljYEWX1E9kJYmhMCLwu46WNEWvoyO9PwqeRew540,887
|
|
26
|
-
github_rest_api-0.
|
|
27
|
-
github_rest_api-0.
|
|
28
|
-
github_rest_api-0.
|
|
29
|
-
github_rest_api-0.
|
|
27
|
+
github_rest_api-0.46.0.dist-info/METADATA,sha256=ONbUloesiPnF-4Dvv1tzFuOq4dDNwHtAEoPNr0cQCPU,915
|
|
28
|
+
github_rest_api-0.46.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
29
|
+
github_rest_api-0.46.0.dist-info/entry_points.txt,sha256=OfbP9GuQC7SyGbymfCYrXEG-TCcKuu31v0EPNvHc1Q4,659
|
|
30
|
+
github_rest_api-0.46.0.dist-info/RECORD,,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
[console_scripts]
|
|
2
|
-
|
|
2
|
+
auto_merge_pull_request = github_rest_api.scripts.github.auto_merge_pull_request:main
|
|
3
3
|
build_container_images = github_rest_api.scripts.container.build_container_images:main
|
|
4
4
|
config_container = github_rest_api.scripts.container.config_container:main
|
|
5
|
+
create_github_repo = github_rest_api.scripts.github.create_github_repo:main
|
|
5
6
|
create_pull_request = github_rest_api.scripts.github.create_pull_request:main
|
|
6
7
|
release_on_github = github_rest_api.scripts.github.release_on_github:main
|
|
7
8
|
remove_branch = github_rest_api.scripts.github.remove_branch:main
|