github-rest-api 0.49.0__tar.gz → 0.50.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.
Files changed (57) hide show
  1. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/.github/workflows/test.yaml +6 -2
  2. github_rest_api-0.49.0/GEMINI.md → github_rest_api-0.50.0/AGENTS.md +1 -1
  3. github_rest_api-0.50.0/CLAUDE.md +1 -0
  4. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/PKG-INFO +2 -2
  5. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/github.py +26 -0
  6. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/github/create_github_repo.py +3 -16
  7. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/github/release_on_github.py +16 -12
  8. github_rest_api-0.50.0/github_rest_api/scripts/github/upload_image_to_issue.py +536 -0
  9. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/utils.py +77 -1
  10. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/pyproject.toml +7 -2
  11. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/tests/test_github.py +68 -0
  12. github_rest_api-0.50.0/tests/test_release_on_github.py +121 -0
  13. github_rest_api-0.50.0/tests/test_upload_image_to_issue.py +700 -0
  14. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/tests/test_utils.py +36 -0
  15. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/uv.lock +2 -2
  16. github_rest_api-0.49.0/tests/test_release_on_github.py +0 -64
  17. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/.devcontainer/devcontainer.json +0 -0
  18. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/.github/workflows/auto_merge_pull_request.yaml +0 -0
  19. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/.github/workflows/claude-code-review.yml +0 -0
  20. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/.github/workflows/claude.yml +0 -0
  21. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/.github/workflows/create_pr_to_main.yaml +0 -0
  22. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/.github/workflows/lint.yaml +0 -0
  23. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/.github/workflows/release.yaml +0 -0
  24. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/.github/workflows/remove_branch.yaml +0 -0
  25. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/.gitignore +0 -0
  26. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/README.md +0 -0
  27. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/__init__.py +0 -0
  28. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/pr_content.py +0 -0
  29. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/__init__.py +0 -0
  30. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/cargo/__init__.py +0 -0
  31. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/cargo/benchmark.py +0 -0
  32. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/cargo/profiling.py +0 -0
  33. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/cargo/utils.py +0 -0
  34. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/container/__init__.py +0 -0
  35. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/container/build_container_images.py +0 -0
  36. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/container/config_container.py +0 -0
  37. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/container/update_version_containerfile.py +0 -0
  38. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/github/__init__.py +0 -0
  39. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/github/auto_merge_pull_request.py +0 -0
  40. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/github/create_pull_request.py +0 -0
  41. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/github/remove_branch.py +0 -0
  42. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/github/workflows/auto_merge_pull_request.yaml +0 -0
  43. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/github/workflows/create_pr_dev_to_main.yaml +0 -0
  44. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/github/workflows/create_pr_to_dev.yaml +0 -0
  45. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/github/workflows/create_pr_to_main.yaml +0 -0
  46. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/github/workflows/python/lint.yaml +0 -0
  47. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/github/workflows/python/test.yaml +0 -0
  48. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/scripts/github/workflows/remove_branch.yaml +0 -0
  49. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/github_rest_api/utils.py +0 -0
  50. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/memory/MEMORY.md +0 -0
  51. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/memory/feedback_test_runner.md +0 -0
  52. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/tests/__init__.py +0 -0
  53. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/tests/test_auto_merge_pull_request.py +0 -0
  54. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/tests/test_build_container_images.py +0 -0
  55. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/tests/test_create_github_repo.py +0 -0
  56. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/tests/test_create_pull_request.py +0 -0
  57. {github_rest_api-0.49.0 → github_rest_api-0.50.0}/tests/test_pr_content.py +0 -0
@@ -11,8 +11,12 @@ jobs:
11
11
  os:
12
12
  - macOS-latest
13
13
  - ubuntu-latest
14
- python-version:
15
- - "3.12"
14
+ # The oldest supported patch release, spelled out rather than "3.12":
15
+ # a bare "3.12" resolves to the runner's system interpreter, which is
16
+ # below `requires-python` on ubuntu-latest, and uv then fails outright
17
+ # instead of looking for a compatible interpreter to download.
18
+ python-version:
19
+ - "3.12.7"
16
20
  - "3.13"
17
21
  - "3.14"
18
22
  steps:
@@ -7,7 +7,7 @@ A simple Python wrapper for GitHub REST APIs, optimized for use in GitHub Action
7
7
  - **Purpose:** Provide a streamlined interface for interacting with GitHub's REST API
8
8
  and performing Git operations within automation scripts.
9
9
  - **Main Technologies:**
10
- - **Python 3.12+**: Core language.
10
+ - **Python 3.12.7+**: Core language.
11
11
  - **requests**: For HTTP interactions with the GitHub API.
12
12
  - **dulwich**: A pure-Python implementation of Git for repository operations.
13
13
  - **tenacity**: For retry logic on API requests.
@@ -0,0 +1 @@
1
+ AGENTS.md
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-rest-api
3
- Version: 0.49.0
3
+ Version: 0.50.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
7
7
  Classifier: Programming Language :: Python :: 3.12
8
8
  Classifier: Programming Language :: Python :: 3.13
9
9
  Classifier: Programming Language :: Python :: 3.14
10
- Requires-Python: <4,>=3.12
10
+ Requires-Python: <4,>=3.12.7
11
11
  Requires-Dist: dulwich>=0.25.1
12
12
  Requires-Dist: psutil>=5.9.4
13
13
  Requires-Dist: pynacl>=1.5
@@ -1465,6 +1465,32 @@ class Repository(GitHub):
1465
1465
  json={"body": body},
1466
1466
  ).json()
1467
1467
 
1468
+ def create_issue_comment_image(
1469
+ self, issue_number: int, url: str, alt: str = "", body: str = ""
1470
+ ) -> dict[str, Any]:
1471
+ """Add a comment containing an image to an issue.
1472
+
1473
+ GitHub has no public API for the attachment upload that its web UI
1474
+ performs on drag and drop, so the image must already be hosted
1475
+ somewhere reachable; this method only references it.
1476
+
1477
+ :param issue_number: The number of the issue.
1478
+ :param url: The URL of an already uploaded image.
1479
+ :param alt: Alt text of the image.
1480
+ :param body: Optional text placed above the image.
1481
+ """
1482
+ # A bracket in the alt text would close the markdown label early and
1483
+ # degrade the whole image to literal text, so escape both brackets
1484
+ # (and the escape character itself, which must come first).
1485
+ alt = alt.replace("\\", "\\\\").replace("[", "\\[").replace("]", "\\]")
1486
+ # The URL is wrapped in angle brackets so that spaces and unbalanced
1487
+ # parentheses, which are common in URLs derived from file names, do
1488
+ # not terminate the markdown link early.
1489
+ image = f"![{alt}](<{url}>)"
1490
+ return self.create_issue_comment(
1491
+ issue_number, f"{body}\n\n{image}" if body else image
1492
+ )
1493
+
1468
1494
  def archive(self) -> requests.Response:
1469
1495
  return self._patch(
1470
1496
  url=self._url_repo,
@@ -1,9 +1,7 @@
1
1
  """Add a GitHub repository and initialize a local Git repository with workflows."""
2
2
 
3
3
  import argparse
4
- import getpass
5
4
  import logging
6
- import os
7
5
  import shutil
8
6
  import sys
9
7
  from collections.abc import Sequence
@@ -13,17 +11,12 @@ from dulwich import porcelain
13
11
  from dulwich.refs import HEADREF, LOCAL_BRANCH_PREFIX, Ref
14
12
 
15
13
  from github_rest_api import Organization, User
14
+ from github_rest_api.scripts.utils import resolve_github_token, validate_repo
16
15
  from github_rest_api.utils import as_str_sequence
17
16
 
18
17
  logger = logging.getLogger(__name__)
19
18
 
20
19
 
21
- def _validate_repo(repo: str) -> None:
22
- parts = repo.split("/")
23
- if len(parts) != 2 or not parts[0] or not parts[1]:
24
- raise ValueError(f"Invalid repo format '{repo}'. Expected 'owner/repo'.")
25
-
26
-
27
20
  def _create_remote_repo(
28
21
  repo: str, private: bool, token: str, is_owner_user: bool
29
22
  ) -> None:
@@ -169,15 +162,9 @@ def create_github_repo(
169
162
  branches: Sequence[str] = ("main",),
170
163
  ) -> None:
171
164
  branches = as_str_sequence(branches)
172
- token = token or os.getenv("GITHUB_TOKEN", "")
173
- if not token:
174
- token = getpass.getpass("Please enter your GitHub token: ")
175
- if not token:
176
- raise ValueError(
177
- "No GitHub token is provided (via $GITHUB_TOKEN, --token or at prompt)."
178
- )
165
+ token = resolve_github_token(token)
179
166
  repo = repo.strip()
180
- _validate_repo(repo)
167
+ validate_repo(repo)
181
168
  _create_remote_repo(
182
169
  repo=repo, private=private, token=token, is_owner_user=is_owner_user
183
170
  )
@@ -1,19 +1,20 @@
1
- import argparse
2
- import getpass
3
- import os
4
1
  import re
5
2
  import sys
6
3
  from pathlib import Path
7
4
 
8
5
  from github_rest_api import Repository
9
6
  from github_rest_api.scripts.utils import (
7
+ RedactingArgumentParser,
10
8
  find_project_root,
11
9
  get_project_version,
12
10
  get_repo,
11
+ reject_github_token,
12
+ resolve_github_token,
13
13
  )
14
14
 
15
15
 
16
16
  def _get_release_tag(tag: str, root: Path, validate: bool = True) -> str:
17
+ reject_github_token("--tag", tag)
17
18
  tag = tag.strip()
18
19
  if not tag:
19
20
  tag = get_project_version(root).strip()
@@ -54,7 +55,14 @@ def release_on_github(
54
55
  :param token: GitHub token.
55
56
  If not specified, the GITHUB_TOKEN environment variable is used.
56
57
  :param validate: If True, validate the tag against semantic versioning format before creating the release.
58
+ :raises ValueError: If a value that looks like a GitHub token is specified
59
+ for anything other than the token itself.
57
60
  """
61
+ # A release is public, so a token mistyped into any of these would be
62
+ # published. -t is the tag on this script rather than the token, which
63
+ # makes that slip easy; --no-validate would otherwise let it through.
64
+ reject_github_token("--branch", branch)
65
+ reject_github_token("--notes", notes)
58
66
  root = find_project_root()
59
67
  if not root:
60
68
  raise FileNotFoundError("Could not find project root (no .git found).")
@@ -65,13 +73,7 @@ def release_on_github(
65
73
  if not repo_name:
66
74
  raise ValueError("Could not find GitHub repository name.")
67
75
 
68
- token = token or os.getenv("GITHUB_TOKEN", "")
69
- if not token:
70
- token = getpass.getpass("Please enter your GitHub token: ")
71
- if not token:
72
- raise ValueError(
73
- "No GitHub token is provided (via $GITHUB_TOKEN, --token or at prompt)."
74
- )
76
+ token = resolve_github_token(token)
75
77
  repo = Repository(token=token, repo=repo_name)
76
78
  data = {
77
79
  "tag_name": tag,
@@ -87,7 +89,7 @@ def release_on_github(
87
89
 
88
90
 
89
91
  def parse_args(args=None, namespace=None):
90
- parser = argparse.ArgumentParser(
92
+ parser = RedactingArgumentParser(
91
93
  description="Make a release of the project on GitHub."
92
94
  )
93
95
  parser.add_argument(
@@ -97,7 +99,9 @@ def parse_args(args=None, namespace=None):
97
99
  help="The branch (default to main) from which to make the release.",
98
100
  )
99
101
  parser.add_argument(
100
- "-t",
102
+ # No short option on purpose: -t reads as the token to anyone used to
103
+ # the other scripts, and the tag becomes the public name of the
104
+ # release, so that slip would publish the token.
101
105
  "--tag",
102
106
  default="",
103
107
  help="The tag for the release. If not specified, the version from project configuration is used.",