Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gemini/system.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
You have my permissions to

- read any file on the machine if it's readable to the current user
- make changes to files under this project only
- to create and/or remove temporary files under this project only
- to run shell command `uv`, `fish -n` and `fish_indent` to lint or test Python and fish scripts
- to run the shell commands which does NOT change any files

However,
please do NOT commit changes so that I can review the diff locally.
56 changes: 56 additions & 0 deletions GEMINI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# GitHub REST API Project

A simple Python wrapper for GitHub REST APIs, optimized for use in GitHub Actions automation.

## Project Overview

- **Purpose:** Provide a streamlined interface for interacting with GitHub's REST API
and performing Git operations within automation scripts.
- **Main Technologies:**
- **Python 3.11+**: Core language.
- **requests**: For HTTP interactions with the GitHub API.
- **dulwich**: A pure-Python implementation of Git for repository operations.
- **psutil**: For system and process utilities.
- **Architecture:**
- `github_rest_api/github.py`: Contains the `GitHub` class for handling API requests (GET, POST, DELETE, PUT, PATCH).
- `github_rest_api/actions/`: Focused utilities for GitHub Actions, including branch management and pushing changes.
- `github_rest_api/actions/cargo/`: Specific support for Rust projects (benchmarking and profiling).
- `github_rest_api/utils.py`: General-purpose utilities (versioning, partitioning).

## Building and Running

This project uses `uv` for dependency and environment management.

- **Setup Environment:**
```bash
uv sync --all-extras
```
- **Code Formatting:**
```bash
uv run ruff format ./
```
- **Linting:**
```bash
uv run ruff check github_rest_api/ tests/
```
- **Type Checking:**
```bash
uv run ty check
```
- **Dependency Analysis:**
```bash
uv run deptry .
```
- **Running Tests:**
```bash
uv run pytest
```

## Development Conventions

- **Code Style:** Strictly follows `ruff` formatting and linting rules.
- **Type Safety:** Uses `ty` (in addition to standard type hints) to ensure type correctness.
- **CI/CD:** Automated linting and formatting checks are performed
on `push` to `dev`/`main` branches and on `pull_request` to `dev`.
- **Git Operations:** Prefers `dulwich` for programmatic Git interactions
to avoid dependency on a system Git installation where possible.
45 changes: 37 additions & 8 deletions github_rest_api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,43 @@ def partition(pred, iterable):


def strip_patch_version(version: str) -> str:
parts = version.split(".")
match len(parts):
case 1:
return parts[0] + ".0.0"
case 2 | 3:
return ".".join(parts[:2]) + ".0"
case _:
raise ValueError("Invalid version semantic provided!")
"""Strip the patch version.

For example,
- strip_patch_version("5.4.0") ==> "5.4.0"
- strip_patch_version("5.4.6") ==> "5.4.0"
- strip_patch_version("2.3") ==> "2.3.0"
- strip_patch_version("1") ==> "1.0.0"
"""
parts = version.strip().split(".")
n = len(parts)
if n < 1 or n > 3:
raise ValueError("Invalid version semantic provided!")
while len(parts) < 3:
parts.append("0")
parts[2] = "0"
return ".".join(parts)


def next_minor_or_strip_patch(version: str, patch_to_bump: int) -> str:
"""Get the next minor version of strip the patch version.

If the patch version is less than patch_to_bump, then strip the patch version;
otherwise, bump version to the next minor one.
For example,
- next_minor_or_strip_patch("5.4.6", 4) ==> "5.5.0"
- next_minor_or_strip_patch("5.4.6", 8) ==> "5.4.0"
"""
parts = version.strip().split(".")
n = len(parts)
if n < 1 or n > 3:
raise ValueError("Invalid version semantic provided!")
while len(parts) < 3:
parts.append("0")
major, minor, patch = map(int, parts)
if patch >= patch_to_bump:
minor += 1
return f"{major}.{minor}.0"


def strip_minor_version(version: str) -> str:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "github_rest_api"
version = "0.31.0"
version = "0.32.0"
description = "Simple wrapper of GitHub REST APIs."
authors = [{ name = "Ben Du", email = "longendu@yahoo.com" }]
requires-python = ">=3.11,<4"
Expand Down
33 changes: 33 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import pytest
from github_rest_api.utils import next_minor_or_strip_patch, strip_patch_version


def test_next_minor_or_strip_patch():
# Examples from docstring
assert next_minor_or_strip_patch("5.4.6", 4) == "5.5.0"
assert next_minor_or_strip_patch("5.4.6", 8) == "5.4.0"

# Edge cases
assert next_minor_or_strip_patch("1.0.0", 0) == "1.1.0"
assert next_minor_or_strip_patch("1.0.0", 1) == "1.0.0"
assert next_minor_or_strip_patch("1.2.3", 3) == "1.3.0"
assert next_minor_or_strip_patch("1.2.3", 4) == "1.2.0"

# Different lengths
assert next_minor_or_strip_patch("1", 0) == "1.1.0"
assert next_minor_or_strip_patch("1", 1) == "1.0.0"
assert next_minor_or_strip_patch("1.2", 0) == "1.3.0"
assert next_minor_or_strip_patch("1.2", 1) == "1.2.0"

with pytest.raises(ValueError):
next_minor_or_strip_patch("1.2.3.4", 1)
with pytest.raises(ValueError):
next_minor_or_strip_patch("", 1)


def test_strip_patch_version():
assert strip_patch_version("1") == "1.0.0"
assert strip_patch_version("1.2") == "1.2.0"
assert strip_patch_version("1.2.3") == "1.2.0"
with pytest.raises(ValueError):
strip_patch_version("1.2.3.4")