github-rest-api 0.24.5__tar.gz → 0.33.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 (30) hide show
  1. github_rest_api-0.33.0/.gemini/system.md +10 -0
  2. github_rest_api-0.33.0/.github/workflows/create_pr_dev_to_main.yml +20 -0
  3. github_rest_api-0.33.0/.github/workflows/create_pr_to_dev.yml +23 -0
  4. github_rest_api-0.33.0/.github/workflows/lint.yml +25 -0
  5. github_rest_api-0.33.0/.github/workflows/release.yml +22 -0
  6. github_rest_api-0.33.0/.gitignore +21 -0
  7. github_rest_api-0.33.0/.gitpod.yml +9 -0
  8. github_rest_api-0.33.0/GEMINI.md +56 -0
  9. github_rest_api-0.33.0/PKG-INFO +19 -0
  10. github_rest_api-0.33.0/github_rest_api/__init__.py +5 -0
  11. github_rest_api-0.33.0/github_rest_api/actions/__init__.py +1 -0
  12. {github_rest_api-0.24.5 → github_rest_api-0.33.0}/github_rest_api/actions/cargo/benchmark.py +17 -10
  13. {github_rest_api-0.24.5 → github_rest_api-0.33.0}/github_rest_api/actions/cargo/profiling.py +6 -6
  14. {github_rest_api-0.24.5 → github_rest_api-0.33.0}/github_rest_api/actions/cargo/utils.py +6 -4
  15. {github_rest_api-0.24.5 → github_rest_api-0.33.0}/github_rest_api/actions/utils.py +20 -37
  16. github_rest_api-0.33.0/github_rest_api/github.py +410 -0
  17. github_rest_api-0.33.0/github_rest_api/utils.py +63 -0
  18. github_rest_api-0.33.0/pyproject.toml +33 -0
  19. github_rest_api-0.33.0/tests/__init__.py +0 -0
  20. github_rest_api-0.33.0/tests/test_github.py +17 -0
  21. github_rest_api-0.33.0/tests/test_utils.py +33 -0
  22. github_rest_api-0.33.0/uv.lock +520 -0
  23. github_rest_api-0.24.5/PKG-INFO +0 -18
  24. github_rest_api-0.24.5/github_rest_api/__init__.py +0 -3
  25. github_rest_api-0.24.5/github_rest_api/actions/__init__.py +0 -2
  26. github_rest_api-0.24.5/github_rest_api/github.py +0 -200
  27. github_rest_api-0.24.5/github_rest_api/utils.py +0 -26
  28. github_rest_api-0.24.5/pyproject.toml +0 -34
  29. {github_rest_api-0.24.5 → github_rest_api-0.33.0}/README.md +0 -0
  30. {github_rest_api-0.24.5 → github_rest_api-0.33.0}/github_rest_api/actions/cargo/__init__.py +0 -0
@@ -0,0 +1,10 @@
1
+ You have my permissions to
2
+
3
+ - read any file on the machine if it's readable to the current user
4
+ - make changes to files under this project only
5
+ - to create and/or remove temporary files under this project only
6
+ - to run shell command `uv`, `fish -n` and `fish_indent` to lint or test Python and fish scripts
7
+ - to run the shell commands which does NOT change any files
8
+
9
+ However,
10
+ please do NOT commit changes so that I can review the diff locally.
@@ -0,0 +1,20 @@
1
+ name: Create PR From dev To main
2
+ on:
3
+ push:
4
+ branches:
5
+ - dev
6
+ jobs:
7
+ create_pr_dev_to_main:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - name: Install uv
11
+ run: |
12
+ curl -LsSf https://astral.sh/uv/install.sh | sudo env UV_INSTALL_DIR="/usr/local/bin" sh
13
+ - name: Create PR From dev To main
14
+ run: |
15
+ curl -sSL https://raw.githubusercontent.com/legendu-net/github_actions_scripts/main/create_pull_request.py \
16
+ | uv run --script - \
17
+ --head-branch dev \
18
+ --base-branch main \
19
+ --token ${{ secrets.GITHUBACTIONS }}
20
+
@@ -0,0 +1,23 @@
1
+ name: Create PR To dev
2
+ on:
3
+ push:
4
+ branches-ignore:
5
+ - main
6
+ - dev
7
+ - gh-pages*
8
+ jobs:
9
+ create_pr_to_dev:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - name: Install uv
13
+ run: |
14
+ curl -LsSf https://astral.sh/uv/install.sh | sudo env UV_INSTALL_DIR="/usr/local/bin" sh
15
+ - uses: actions/checkout@v6
16
+ - name: Create PR to dev
17
+ run: |
18
+ curl -sSL https://raw.githubusercontent.com/legendu-net/github_actions_scripts/main/create_pull_request.py \
19
+ | uv run --script - \
20
+ --head-branch ${{ github.ref_name }} \
21
+ --base-branch dev \
22
+ --token ${{ secrets.GITHUBACTIONS }}
23
+
@@ -0,0 +1,25 @@
1
+ name: Lint Code
2
+
3
+ on:
4
+ push:
5
+ branches: [ dev, main ]
6
+ pull_request:
7
+ branches: [ dev ]
8
+
9
+ jobs:
10
+ lint_code:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v6
14
+ - name: Install dependencies
15
+ run: |
16
+ curl -LsSf https://astral.sh/uv/install.sh | sudo env UV_INSTALL_DIR="/usr/local/bin" sh
17
+ uv sync --all-extras
18
+ - name: Check code format
19
+ run: uv run ruff format --check ./
20
+ - name: Lint with ruff
21
+ run: uv run ruff check github_rest_api/ tests/
22
+ - name: Lint with Ty
23
+ run: uv run ty check
24
+ - name: Analyze Dependencies
25
+ run: uv run deptry .
@@ -0,0 +1,22 @@
1
+ name: Release
2
+ on:
3
+ release:
4
+ types: [published]
5
+ jobs:
6
+ release-github_rest_api:
7
+ name: Release github_rest_api
8
+ runs-on: ubuntu-latest
9
+ permissions:
10
+ issues: write
11
+ pull-requests: write
12
+ contents: write
13
+ steps:
14
+ - uses: actions/checkout@v6
15
+ - name: Install uv
16
+ run: |
17
+ curl -LsSf https://astral.sh/uv/install.sh | sudo env UV_INSTALL_DIR="/usr/local/bin" sh
18
+ - name: Build and Publish Package to PyPI
19
+ run: |
20
+ uv build
21
+ ls -lha dist/
22
+ uv publish -u __token__ -p ${{ secrets.PYPI_GITHUB_REST_API }}
@@ -0,0 +1,21 @@
1
+ .DS_Store
2
+ .idea/
3
+ .theia/
4
+ .vscode/
5
+ *.ipr
6
+ *.iws
7
+ .ipynb_checkpoints/
8
+ .coverage
9
+ .mypy/
10
+ .mypy_cache/
11
+ .pytype/
12
+ *.crc
13
+ __pycache__/
14
+ venv/
15
+ .venv/
16
+ target/
17
+ dist/
18
+ *.egg-info/
19
+ doc*/_build/
20
+ *.prof
21
+ core
@@ -0,0 +1,9 @@
1
+ image: dclong/gitpod
2
+ ports:
3
+ - port: 8888
4
+ tasks:
5
+ - command: . /scripts/gitpod.sh
6
+ vscode:
7
+ extensions:
8
+ - vscodevim.vim@1.23.2
9
+ - ms-python.python
@@ -0,0 +1,56 @@
1
+ # GitHub REST API Project
2
+
3
+ A simple Python wrapper for GitHub REST APIs, optimized for use in GitHub Actions automation.
4
+
5
+ ## Project Overview
6
+
7
+ - **Purpose:** Provide a streamlined interface for interacting with GitHub's REST API
8
+ and performing Git operations within automation scripts.
9
+ - **Main Technologies:**
10
+ - **Python 3.11+**: Core language.
11
+ - **requests**: For HTTP interactions with the GitHub API.
12
+ - **dulwich**: A pure-Python implementation of Git for repository operations.
13
+ - **psutil**: For system and process utilities.
14
+ - **Architecture:**
15
+ - `github_rest_api/github.py`: Contains the `GitHub` class for handling API requests (GET, POST, DELETE, PUT, PATCH).
16
+ - `github_rest_api/actions/`: Focused utilities for GitHub Actions, including branch management and pushing changes.
17
+ - `github_rest_api/actions/cargo/`: Specific support for Rust projects (benchmarking and profiling).
18
+ - `github_rest_api/utils.py`: General-purpose utilities (versioning, partitioning).
19
+
20
+ ## Building and Running
21
+
22
+ This project uses `uv` for dependency and environment management.
23
+
24
+ - **Setup Environment:**
25
+ ```bash
26
+ uv sync --all-extras
27
+ ```
28
+ - **Code Formatting:**
29
+ ```bash
30
+ uv run ruff format ./
31
+ ```
32
+ - **Linting:**
33
+ ```bash
34
+ uv run ruff check github_rest_api/ tests/
35
+ ```
36
+ - **Type Checking:**
37
+ ```bash
38
+ uv run ty check
39
+ ```
40
+ - **Dependency Analysis:**
41
+ ```bash
42
+ uv run deptry .
43
+ ```
44
+ - **Running Tests:**
45
+ ```bash
46
+ uv run pytest
47
+ ```
48
+
49
+ ## Development Conventions
50
+
51
+ - **Code Style:** Strictly follows `ruff` formatting and linting rules.
52
+ - **Type Safety:** Uses `ty` (in addition to standard type hints) to ensure type correctness.
53
+ - **CI/CD:** Automated linting and formatting checks are performed
54
+ on `push` to `dev`/`main` branches and on `pull_request` to `dev`.
55
+ - **Git Operations:** Prefers `dulwich` for programmatic Git interactions
56
+ to avoid dependency on a system Git installation where possible.
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: github-rest-api
3
+ Version: 0.33.0
4
+ Summary: Simple wrapper of GitHub REST APIs.
5
+ Author-email: Ben Du <longendu@yahoo.com>
6
+ Classifier: Programming Language :: Python :: 3 :: Only
7
+ Classifier: Programming Language :: Python :: 3.11
8
+ Classifier: Programming Language :: Python :: 3.12
9
+ Classifier: Programming Language :: Python :: 3.13
10
+ Classifier: Programming Language :: Python :: 3.14
11
+ Requires-Python: <4,>=3.11
12
+ Requires-Dist: dulwich>=0.25.1
13
+ Requires-Dist: psutil>=5.9.4
14
+ Requires-Dist: requests>=2.28.2
15
+ Description-Content-Type: text/markdown
16
+
17
+ # GitHub REST APIs | [@GitHub](https://github.com/legendu-net/github_rest_api) | [@PyPI](https://pypi.org/project/github-rest-api/)
18
+
19
+ Simple wrapper of GitHub REST APIs with a focus on making GitHub Actions automation easy.
@@ -0,0 +1,5 @@
1
+ """GitHub REST APIs."""
2
+
3
+ from .github import Organization, Repository, RepositoryType, User
4
+
5
+ __all__ = ["Organization", "Repository", "RepositoryType", "User"]
@@ -0,0 +1 @@
1
+ """GitHub Actions related utils."""
@@ -1,24 +1,24 @@
1
- """Benchmark action using cargo criterion.
2
- """
1
+ """Benchmark action using cargo criterion."""
2
+
3
3
  from typing import Callable
4
4
  import tempfile
5
5
  from pathlib import Path
6
6
  import datetime
7
7
  import shutil
8
+ import subprocess as sp
9
+ from dulwich import porcelain
8
10
  from ..utils import (
9
11
  config_git,
10
- create_branch,
11
12
  switch_branch,
12
13
  push_branch,
13
14
  gen_temp_branch,
14
15
  commit_benchmarks,
15
16
  )
16
- from ...utils import run_cmd
17
17
 
18
18
 
19
19
  def _copy_last_dev_bench(bench_dir: Path) -> None:
20
20
  branch = gen_temp_branch()
21
- create_branch(branch)
21
+ porcelain.checkout(repo=".", new_branch=branch)
22
22
  switch_branch(branch="gh-pages", fetch=True)
23
23
  src = bench_dir / "dev/criterion"
24
24
  tmpdir = tempfile.mkdtemp()
@@ -30,14 +30,18 @@ def _copy_last_dev_bench(bench_dir: Path) -> None:
30
30
  shutil.copytree(tmpdir, target, dirs_exist_ok=True)
31
31
 
32
32
 
33
- def _cargo_criterion(bench_dir: Path) -> None:
33
+ def _cargo_criterion(bench_dir: Path, env: str = "") -> None:
34
34
  """Run `cargo criterion` to benchmark the specified branch.
35
35
  Notice that a temp branch is created for benchmarking.
36
36
 
37
37
  :param branch: The branch to benchmark.
38
38
  """
39
39
  _copy_last_dev_bench(bench_dir=bench_dir)
40
- run_cmd("cargo criterion --all-features --message-format=json")
40
+ sp.run(
41
+ f"{env} cargo criterion --all-features --message-format=json",
42
+ shell=True,
43
+ check=True,
44
+ )
41
45
 
42
46
 
43
47
  def _copy_bench_results(bench_dir: Path, storage: str) -> None:
@@ -63,7 +67,7 @@ def _git_push_gh_pages(bench_dir: Path, pr_number: str) -> str:
63
67
  commit_benchmarks(bench_dir=bench_dir)
64
68
  timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
65
69
  branch = f"gh-pages_{pr_number}_{timestamp}"
66
- create_branch(branch=branch)
70
+ porcelain.checkout(repo=".", new_branch=branch)
67
71
  push_branch(branch=branch)
68
72
  return branch
69
73
 
@@ -166,7 +170,7 @@ def _gen_report_ci_markdown(ci: tuple[str, str, str]) -> str:
166
170
 
167
171
 
168
172
  def _sort_cips(
169
- cips: list[tuple[tuple[str, str, str], Path]]
173
+ cips: list[tuple[tuple[str, str, str], Path]],
170
174
  ) -> list[tuple[tuple[str, str, str], Path]]:
171
175
  """Sort confidence intervals according to the average performance changes."""
172
176
 
@@ -221,6 +225,7 @@ def benchmark(
221
225
  bench_dir: str | Path = "bench",
222
226
  storage: str = "",
223
227
  extract_benchmark_name: Callable[[Path], str] = lambda path: path.stem,
228
+ env: str = "",
224
229
  ) -> str:
225
230
  """Benchmark using `cargo criterion` and push benchmark results to gh-pages.
226
231
 
@@ -230,6 +235,8 @@ def benchmark(
230
235
  :param storage: The directory relative to bench_dir for storing this benchmark results.
231
236
  If not specified (empty or None), pr_number is used.
232
237
  :param extract_benchmark_name: A function to extract a benchmark name from a path.
238
+ :param env: Environment variables configuration with the format `var1=val1 var2=val2`
239
+ for the command `cargo-criterion`).
233
240
  """
234
241
  if isinstance(bench_dir, str):
235
242
  bench_dir = Path(bench_dir)
@@ -240,7 +247,7 @@ def benchmark(
240
247
  user_email="bench-bot@github.com",
241
248
  user_name="bench-bot",
242
249
  )
243
- _cargo_criterion(bench_dir=bench_dir) # _branch_*
250
+ _cargo_criterion(bench_dir=bench_dir, env=env) # _branch_*
244
251
  _copy_bench_results(bench_dir=bench_dir, storage=storage) # gh-pages
245
252
  gen_index_markdown(
246
253
  bench_dir=bench_dir, history=1, extract_benchmark_name=extract_benchmark_name
@@ -1,5 +1,5 @@
1
- """Utils for profiling Rust applications.
2
- """
1
+ """Utils for profiling Rust applications."""
2
+
3
3
  from typing import Iterable
4
4
  from pathlib import Path
5
5
  import time
@@ -8,7 +8,7 @@ import subprocess as sp
8
8
  import psutil
9
9
  from .utils import build_project
10
10
  from ..utils import config_git, switch_branch, push_branch, commit_profiling
11
- from ...utils import partition, run_cmd
11
+ from ...utils import partition
12
12
 
13
13
 
14
14
  def launch_application(cmd: list[str]) -> int:
@@ -64,13 +64,13 @@ def nperf(pid: int, prof_name: str, prof_dir: str | Path = ".") -> Path:
64
64
  yymmdd = time.strftime("%Y%m%d")
65
65
  prof_dir.mkdir(exist_ok=True, parents=True)
66
66
  data_file = prof_dir / f"{yymmdd}_{prof_name}"
67
- run_cmd(f"nperf record -p {pid} -o '{data_file}'")
67
+ sp.run(f"nperf record -p {pid} -o '{data_file}'", shell=True, check=True)
68
68
  return _gen_flamegraph(data_file)
69
69
 
70
70
 
71
71
  def _gen_flamegraph(data_file: Path) -> Path:
72
72
  flamegraph = data_file.with_name(data_file.name + ".svg")
73
- run_cmd(f"nperf flamegraph '{data_file}' > '{flamegraph}'")
73
+ sp.run(f"nperf flamegraph '{data_file}' > '{flamegraph}'", shell=True, check=True)
74
74
  return flamegraph
75
75
 
76
76
 
@@ -95,7 +95,7 @@ def _save_flamegraph(prof_dir: Path, history: int = 5):
95
95
 
96
96
  def _gen_markdown(svgs: Iterable[Path], prof_dir: Path) -> None:
97
97
  def _gen_link(svg: Path):
98
- svg = svg.name
98
+ svg: str = svg.name
99
99
  yyyymmdd = svg[:8]
100
100
  prof_name = svg[9:-4]
101
101
  return f"- [{prof_name} - {yyyymmdd}]({svg})"
@@ -1,10 +1,12 @@
1
- """Util functions for building GitHub Actions for Rust projects.
2
- """
3
- from ...utils import run_cmd
1
+ """Util functions for building GitHub Actions for Rust projects."""
2
+
3
+ import subprocess as sp
4
4
 
5
5
 
6
6
  def build_project(profile: str = "release") -> None:
7
7
  """Build the Rust project.
8
8
  :param profile: The profile for building.
9
9
  """
10
- run_cmd(f"RUSTFLAGS=-Awarnings cargo build --profile {profile}")
10
+ sp.run(
11
+ f"RUSTFLAGS=-Awarnings cargo build --profile {profile}", shell=True, check=True
12
+ )
@@ -1,39 +1,21 @@
1
- """Util functions for GitHub actions.
2
- """
1
+ """Util functions for GitHub actions."""
2
+
3
3
  from typing import Iterable
4
4
  from pathlib import Path
5
5
  import random
6
- from ..utils import run_cmd
7
-
8
-
9
- class FailToPushToGitHubException(Exception):
10
- """Exception for failure to push a branch to GitHub."""
11
-
12
- def __init__(self, branch: str, branch_alt: str):
13
- msg = f"Failed to push the branch {branch} to GitHub!"
14
- if branch_alt:
15
- msg += f" Pushed to {branch_alt} instead."
16
- super().__init__(msg)
6
+ from dulwich import porcelain
7
+ from dulwich.repo import Repo
17
8
 
18
9
 
19
- def config_git(local_repo_dir: str, user_email: str, user_name: str):
10
+ def config_git(local_repo_dir: str | Path, user_email: str, user_name: str):
20
11
  """Config Git.
21
12
  :param local_repo_dir: The root directory of the project.
22
13
  :param user_email: The email of the user (no need to be a valid one).
23
14
  :param user_name: The name of the user.
24
15
  """
25
- cmd = f"""git config --global --add safe.directory {local_repo_dir} \
26
- && git config --global user.email "{user_email}" \
27
- && git config --global user.name "{user_name}"
28
- """
29
- run_cmd(cmd)
30
-
31
-
32
- def create_branch(branch: str) -> None:
33
- """Create a new local branch.
34
- :param branch: The new local branch to create.
35
- """
36
- run_cmd(f"git checkout -b {branch}")
16
+ config = Repo(local_repo_dir).get_config()
17
+ config.set(b"user", b"email", user_email.encode())
18
+ config.set(b"user", b"name", user_name.encode())
37
19
 
38
20
 
39
21
  def switch_branch(branch: str, fetch: bool) -> None:
@@ -42,8 +24,8 @@ def switch_branch(branch: str, fetch: bool) -> None:
42
24
  :param fetch: If true, fetch the branch from remote first.
43
25
  """
44
26
  if fetch:
45
- run_cmd(f"git fetch origin {branch}")
46
- run_cmd(f"git checkout {branch}")
27
+ porcelain.fetch(repo=".")
28
+ porcelain.checkout(repo=".", target=branch)
47
29
 
48
30
 
49
31
  def gen_temp_branch(
@@ -67,26 +49,27 @@ def push_branch(branch: str, branch_alt: str = ""):
67
49
  :param branch_alt: An alternative branch name to push to GitHub.
68
50
  """
69
51
  try:
70
- run_cmd(f"git push origin {branch}")
52
+ porcelain.push(repo=".", refspecs=branch)
71
53
  except Exception as err:
72
54
  if branch_alt:
73
- cmd = f"""git checkout {branch} \
74
- && git checkout -b {branch_alt} \
75
- && git push origin {branch_alt}
76
- """
77
- run_cmd(cmd)
78
- raise FailToPushToGitHubException(branch, branch_alt) from err
55
+ porcelain.checkout(repo=".", target=branch)
56
+ porcelain.checkout(repo=".", new_branch=branch_alt)
57
+ porcelain.push(repo=".", refspecs=branch_alt)
58
+ else:
59
+ raise err
79
60
 
80
61
 
81
62
  def commit_benchmarks(bench_dir: str | Path):
82
63
  """Commit changes in the benchmark directory.
83
64
  :param bench_dir: The benchmark directory.
84
65
  """
85
- run_cmd(f"git add {bench_dir} && git commit -m 'add benchmarks'")
66
+ porcelain.add(paths=bench_dir)
67
+ porcelain.commit(message="Add benchmarks.")
86
68
 
87
69
 
88
70
  def commit_profiling(prof_dir: str | Path):
89
71
  """Commit changes in the profiling directory.
90
72
  :param prof_dir: The profiling directory.
91
73
  """
92
- run_cmd(f"git add {prof_dir} && git commit -m 'update profiling results'")
74
+ porcelain.add(paths=prof_dir)
75
+ porcelain.commit(message="Updating profiling results.")