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.
@@ -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 > ``chore``); the scope
87
- is the common top-level directory of the changed files; ``!`` is appended
88
- for breaking changes.
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
- f"update {len(subjects)} commits",
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 add_github_repo(
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("*.yml"):
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("*.yml"):
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="Add a GitHub repository.")
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
- add_github_repo(
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
  Metadata-Version: 2.4
2
2
  Name: github-rest-api
3
- Version: 0.45.0
3
+ Version: 0.46.0
4
4
  Summary: Simple wrapper of GitHub REST APIs.
5
5
  Author-email: Ben Du <longendu@yahoo.com>
6
6
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -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=8SJKdTOCTt1pZasmh3MEQ2sRXlVX2dYGpYHZP9H5K3g,9415
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/add_github_repo.py,sha256=iFuDGXYz_bCXo9_1Y4TMleSZ4HFHriNOT9oj4-XAnno,6351
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.45.0.dist-info/METADATA,sha256=wvu0t0cqklG5gPmrq_0QmbyOR5frDuhAjyMbAmf8SVI,915
27
- github_rest_api-0.45.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
28
- github_rest_api-0.45.0.dist-info/entry_points.txt,sha256=eXMEdbMEpuE1nsTfUiKSILgS4awe09NwKn0N2_fZ9H0,567
29
- github_rest_api-0.45.0.dist-info/RECORD,,
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,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.30.1
2
+ Generator: hatchling 1.31.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,7 +1,8 @@
1
1
  [console_scripts]
2
- add_github_repo = github_rest_api.scripts.github.add_github_repo:main
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