github-rest-api 0.45.0__tar.gz → 0.47.0__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.
- github_rest_api-0.47.0/.github/workflows/claude-code-review.yml +44 -0
- github_rest_api-0.47.0/.github/workflows/claude.yml +50 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/.github/workflows/lint.yaml +8 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/.github/workflows/test.yaml +1 -1
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/PKG-INFO +1 -1
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/pr_content.py +18 -7
- github_rest_api-0.47.0/github_rest_api/scripts/github/auto_merge_pull_request.py +109 -0
- github_rest_api-0.45.0/github_rest_api/scripts/github/add_github_repo.py → github_rest_api-0.47.0/github_rest_api/scripts/github/create_github_repo.py +26 -18
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/pyproject.toml +3 -2
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/tests/test_pr_content.py +48 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/uv.lock +1 -1
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/.devcontainer/devcontainer.json +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/.github/workflows/create_pr_to_main.yaml +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/.github/workflows/release.yaml +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/.github/workflows/remove_branch.yaml +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/.gitignore +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/GEMINI.md +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/README.md +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/__init__.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/github.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/__init__.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/cargo/__init__.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/cargo/benchmark.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/cargo/profiling.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/cargo/utils.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/container/__init__.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/container/build_container_images.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/container/config_container.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/container/update_version_containerfile.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/github/__init__.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/github/create_pull_request.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/github/release_on_github.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/github/remove_branch.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/github/workflows/create_pr_dev_to_main.yaml +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/github/workflows/create_pr_to_dev.yaml +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/github/workflows/create_pr_to_main.yaml +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/github/workflows/python/lint.yaml +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/github/workflows/python/test.yaml +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/github/workflows/remove_branch.yaml +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/utils.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/utils.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/memory/MEMORY.md +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/memory/feedback_test_runner.md +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/tests/__init__.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/tests/test_build_container_images.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/tests/test_create_pull_request.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/tests/test_github.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/tests/test_release_on_github.py +0 -0
- {github_rest_api-0.45.0 → github_rest_api-0.47.0}/tests/test_utils.py +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Claude Code Review
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, synchronize, ready_for_review, reopened]
|
|
6
|
+
# Optional: Only run on specific file changes
|
|
7
|
+
# paths:
|
|
8
|
+
# - "src/**/*.ts"
|
|
9
|
+
# - "src/**/*.tsx"
|
|
10
|
+
# - "src/**/*.js"
|
|
11
|
+
# - "src/**/*.jsx"
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
claude-review:
|
|
15
|
+
# Optional: Filter by PR author
|
|
16
|
+
# if: |
|
|
17
|
+
# github.event.pull_request.user.login == 'external-contributor' ||
|
|
18
|
+
# github.event.pull_request.user.login == 'new-developer' ||
|
|
19
|
+
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
|
|
20
|
+
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
pull-requests: read
|
|
25
|
+
issues: read
|
|
26
|
+
id-token: write
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout repository
|
|
30
|
+
uses: actions/checkout@v4
|
|
31
|
+
with:
|
|
32
|
+
fetch-depth: 1
|
|
33
|
+
|
|
34
|
+
- name: Run Claude Code Review
|
|
35
|
+
id: claude-review
|
|
36
|
+
uses: anthropics/claude-code-action@v1
|
|
37
|
+
with:
|
|
38
|
+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
39
|
+
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
|
|
40
|
+
plugins: 'code-review@claude-code-plugins'
|
|
41
|
+
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
|
|
42
|
+
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
43
|
+
# or https://code.claude.com/docs/en/cli-reference for available options
|
|
44
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Claude Code
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issue_comment:
|
|
5
|
+
types: [created]
|
|
6
|
+
pull_request_review_comment:
|
|
7
|
+
types: [created]
|
|
8
|
+
issues:
|
|
9
|
+
types: [opened, assigned]
|
|
10
|
+
pull_request_review:
|
|
11
|
+
types: [submitted]
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
claude:
|
|
15
|
+
if: |
|
|
16
|
+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
17
|
+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
18
|
+
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
|
19
|
+
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
pull-requests: read
|
|
24
|
+
issues: read
|
|
25
|
+
id-token: write
|
|
26
|
+
actions: read # Required for Claude to read CI results on PRs
|
|
27
|
+
steps:
|
|
28
|
+
- name: Checkout repository
|
|
29
|
+
uses: actions/checkout@v4
|
|
30
|
+
with:
|
|
31
|
+
fetch-depth: 1
|
|
32
|
+
|
|
33
|
+
- name: Run Claude Code
|
|
34
|
+
id: claude
|
|
35
|
+
uses: anthropics/claude-code-action@v1
|
|
36
|
+
with:
|
|
37
|
+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
38
|
+
|
|
39
|
+
# This is an optional setting that allows Claude to read CI results on PRs
|
|
40
|
+
additional_permissions: |
|
|
41
|
+
actions: read
|
|
42
|
+
|
|
43
|
+
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
|
|
44
|
+
# prompt: 'Update the pull request description to include a summary of changes.'
|
|
45
|
+
|
|
46
|
+
# Optional: Add claude_args to customize behavior and configuration
|
|
47
|
+
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
48
|
+
# or https://code.claude.com/docs/en/cli-reference for available options
|
|
49
|
+
# claude_args: '--allowed-tools Bash(gh pr *)'
|
|
50
|
+
|
|
@@ -24,3 +24,11 @@ jobs:
|
|
|
24
24
|
run: uv run ty check
|
|
25
25
|
- name: Analyze Dependencies
|
|
26
26
|
run: uv run deptry .
|
|
27
|
+
- name: Check workflow templates use .yaml extension
|
|
28
|
+
run: |
|
|
29
|
+
yml_files=$(find github_rest_api/scripts/github/workflows/ -name '*.yml')
|
|
30
|
+
if [ -n "$yml_files" ]; then
|
|
31
|
+
echo "The following workflow templates must use the .yaml extension instead of .yml:"
|
|
32
|
+
echo "$yml_files"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
@@ -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())
|
|
@@ -38,6 +38,13 @@ def _remote_url(repo: str, protocol: str) -> str:
|
|
|
38
38
|
return f"https://github.com/{repo}.git"
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
def _ensure_remote(path: Path, repo: str, protocol: str) -> None:
|
|
42
|
+
try:
|
|
43
|
+
porcelain.remote_add(path, "origin", _remote_url(repo, protocol))
|
|
44
|
+
except porcelain.RemoteExists:
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
41
48
|
def _init_local_repo(
|
|
42
49
|
repo: str,
|
|
43
50
|
language: str,
|
|
@@ -54,33 +61,34 @@ def _init_local_repo(
|
|
|
54
61
|
readme.write_text(f"# {repo_name}\n")
|
|
55
62
|
if not (path / ".git").exists():
|
|
56
63
|
porcelain.init(path=path)
|
|
57
|
-
porcelain.remote_add(path, "origin", _remote_url(repo, protocol))
|
|
58
64
|
initial_branch = (
|
|
59
65
|
(path / ".git" / "HEAD").read_text().strip().partition("refs/heads/")[-1]
|
|
60
66
|
)
|
|
61
67
|
porcelain.add(repo=path)
|
|
62
68
|
porcelain.commit(repo=path, message="first commit")
|
|
63
|
-
|
|
64
|
-
def _create_push_branch(branch: str):
|
|
69
|
+
for branch in branches:
|
|
65
70
|
if branch != initial_branch:
|
|
66
71
|
porcelain.branch_create(repo=path, name=branch)
|
|
67
|
-
porcelain.checkout(repo=path, target=branch)
|
|
68
|
-
porcelain.push(
|
|
69
|
-
repo=path,
|
|
70
|
-
remote_location=_remote_url(repo, "https"),
|
|
71
|
-
username="x-access-token",
|
|
72
|
-
password=token,
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
for branch in branches:
|
|
76
|
-
_create_push_branch(branch)
|
|
77
72
|
porcelain.checkout(repo=path, target=branches[0])
|
|
78
73
|
if initial_branch not in branches:
|
|
79
74
|
porcelain.branch_delete(repo=path, name=initial_branch)
|
|
75
|
+
_ensure_remote(path, repo, protocol)
|
|
76
|
+
local_branches = {b.decode() for b in porcelain.branch_list(path)}
|
|
77
|
+
branches_to_push = [branch for branch in branches if branch in local_branches]
|
|
78
|
+
if not branches_to_push:
|
|
79
|
+
branches_to_push = [porcelain.active_branch(path).decode()]
|
|
80
|
+
for branch in branches_to_push:
|
|
81
|
+
porcelain.push(
|
|
82
|
+
repo=path,
|
|
83
|
+
remote_location=_remote_url(repo, "https"),
|
|
84
|
+
refspecs=[branch.encode()],
|
|
85
|
+
username="x-access-token",
|
|
86
|
+
password=token,
|
|
87
|
+
)
|
|
80
88
|
_add_workflow(path, language)
|
|
81
89
|
|
|
82
90
|
|
|
83
|
-
def
|
|
91
|
+
def create_github_repo(
|
|
84
92
|
repo: str,
|
|
85
93
|
private: bool,
|
|
86
94
|
language: str,
|
|
@@ -117,7 +125,7 @@ def _add_workflow(path: Path, language: str, workflow_dir: Path | None = None) -
|
|
|
117
125
|
workflow_dir = Path(__file__).parent / "workflows"
|
|
118
126
|
dir_dest = path / ".github" / "workflows"
|
|
119
127
|
dir_dest.mkdir(parents=True, exist_ok=True)
|
|
120
|
-
for yaml in workflow_dir.glob("*.
|
|
128
|
+
for yaml in workflow_dir.glob("*.yaml"):
|
|
121
129
|
if not (dir_dest / yaml.name).exists():
|
|
122
130
|
shutil.copy2(yaml, dir_dest)
|
|
123
131
|
if not language:
|
|
@@ -125,13 +133,13 @@ def _add_workflow(path: Path, language: str, workflow_dir: Path | None = None) -
|
|
|
125
133
|
lang_dir = workflow_dir / language
|
|
126
134
|
if not lang_dir.exists():
|
|
127
135
|
return
|
|
128
|
-
for yaml in lang_dir.glob("*.
|
|
136
|
+
for yaml in lang_dir.glob("*.yaml"):
|
|
129
137
|
if not (dir_dest / yaml.name).exists():
|
|
130
138
|
shutil.copy2(yaml, dir_dest)
|
|
131
139
|
|
|
132
140
|
|
|
133
141
|
def parse_args(args=None, namespace=None):
|
|
134
|
-
parser = argparse.ArgumentParser(description="
|
|
142
|
+
parser = argparse.ArgumentParser(description="Create a GitHub repository.")
|
|
135
143
|
parser.add_argument(
|
|
136
144
|
"repo",
|
|
137
145
|
help="The GitHub repo (in the format of owner/repo) to be created.",
|
|
@@ -203,7 +211,7 @@ def parse_args(args=None, namespace=None):
|
|
|
203
211
|
def main() -> int:
|
|
204
212
|
args = parse_args()
|
|
205
213
|
try:
|
|
206
|
-
|
|
214
|
+
create_github_repo(
|
|
207
215
|
repo=args.repo,
|
|
208
216
|
private=args.private,
|
|
209
217
|
language=args.language,
|
|
@@ -4,7 +4,7 @@ requires = [ "hatchling" ]
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "github-rest-api"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.47.0"
|
|
8
8
|
description = "Simple wrapper of GitHub REST APIs."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [ { name = "Ben Du", email = "longendu@yahoo.com" } ]
|
|
@@ -27,9 +27,10 @@ dependencies = [
|
|
|
27
27
|
optional-dependencies.ai = [
|
|
28
28
|
"litellm>=1.50",
|
|
29
29
|
]
|
|
30
|
-
scripts.
|
|
30
|
+
scripts.auto_merge_pull_request = "github_rest_api.scripts.github.auto_merge_pull_request:main"
|
|
31
31
|
scripts.build_container_images = "github_rest_api.scripts.container.build_container_images:main"
|
|
32
32
|
scripts.config_container = "github_rest_api.scripts.container.config_container:main"
|
|
33
|
+
scripts.create_github_repo = "github_rest_api.scripts.github.create_github_repo:main"
|
|
33
34
|
scripts.create_pull_request = "github_rest_api.scripts.github.create_pull_request:main"
|
|
34
35
|
scripts.release_on_github = "github_rest_api.scripts.github.release_on_github:main"
|
|
35
36
|
scripts.remove_branch = "github_rest_api.scripts.github.remove_branch:main"
|
|
@@ -136,6 +136,54 @@ def test_deterministic_title_no_scope_for_root_files():
|
|
|
136
136
|
assert deterministic_title(compare) == "chore: bump"
|
|
137
137
|
|
|
138
138
|
|
|
139
|
+
def test_deterministic_title_non_conventional_multi_uses_first_subject():
|
|
140
|
+
compare = {
|
|
141
|
+
"commits": [
|
|
142
|
+
_commit("1", "did some work"),
|
|
143
|
+
_commit("2", "did more work"),
|
|
144
|
+
],
|
|
145
|
+
"files": [_file("pkg/a.py"), _file("pkg/b.py")],
|
|
146
|
+
}
|
|
147
|
+
# No conventional commits: fall back to the first subject rather than the
|
|
148
|
+
# generic "update N commits" placeholder.
|
|
149
|
+
assert deterministic_title(compare) == "chore(pkg): did some work"
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def test_deterministic_title_github_scope_maps_to_ci():
|
|
153
|
+
compare = {
|
|
154
|
+
"commits": [
|
|
155
|
+
_commit("1", "Claude PR Assistant workflow"),
|
|
156
|
+
_commit("2", "Claude Code Review workflow"),
|
|
157
|
+
],
|
|
158
|
+
"files": [
|
|
159
|
+
_file(".github/workflows/claude.yml", "added"),
|
|
160
|
+
_file(".github/workflows/claude-review.yml", "added"),
|
|
161
|
+
],
|
|
162
|
+
}
|
|
163
|
+
# `.github`-only changes become `ci` (with the redundant scope dropped), and
|
|
164
|
+
# the first subject supplies the description.
|
|
165
|
+
assert deterministic_title(compare) == "ci: Claude PR Assistant workflow"
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_deterministic_title_github_scope_respects_conventional_type():
|
|
169
|
+
compare = {
|
|
170
|
+
"commits": [_commit("1", "docs: update issue template")],
|
|
171
|
+
"files": [_file(".github/ISSUE_TEMPLATE/bug.md")],
|
|
172
|
+
}
|
|
173
|
+
# An explicit conventional type wins over the `.github` -> `ci` fallback.
|
|
174
|
+
assert deterministic_title(compare) == "docs(.github): update issue template"
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def test_deterministic_title_github_ci_not_applied_when_mixed_with_other_dir():
|
|
178
|
+
compare = {
|
|
179
|
+
"commits": [_commit("1", "tweak things")],
|
|
180
|
+
"files": [_file(".github/workflows/ci.yml"), _file("pkg/a.py")],
|
|
181
|
+
}
|
|
182
|
+
# Changes span `.github` and another top-level dir, so no common scope and no
|
|
183
|
+
# `ci` upgrade -- the generic `chore` fallback applies.
|
|
184
|
+
assert deterministic_title(compare) == "chore: tweak things"
|
|
185
|
+
|
|
186
|
+
|
|
139
187
|
def test_deterministic_body_sections():
|
|
140
188
|
body = deterministic_body(COMPARE)
|
|
141
189
|
assert "## Summary" in body
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/cargo/benchmark.py
RENAMED
|
File without changes
|
{github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/cargo/profiling.py
RENAMED
|
File without changes
|
|
File without changes
|
{github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/container/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/github/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{github_rest_api-0.45.0 → github_rest_api-0.47.0}/github_rest_api/scripts/github/remove_branch.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|