Skip to content
Open
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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

### 新增
- **Windows PowerShell 移植版**(`scripts/windows/`):
- `scripts/windows/*.ps1` — 薄 PowerShell 包装层,委托给 `scripts/gh-api.py`(Python 后端);需要 Python 3.8+
- `scripts/windows/*.ps1` — 薄 PowerShell 包装层,委托给 `scripts/gh-api.py`(Python 后端);需要 Python 3.7+
- `_common.ps1` — 共享辅助函数,负责构建参数并调用 Python 后端
- `gh-user.ps1` / `gh-repo.ps1` / `gh-issue.ps1` / `gh-pr.ps1` — 核心 Bash 脚本的 PowerShell 等价物
- 认证回退链:`gh auth token` → `GITHUB_TOKEN`/`GH_TOKEN` 环境变量 → `~/.github_token` 文件 → `~/.config/github-ops/token` → `~/github_token.txt`
Expand All @@ -15,8 +15,12 @@

### 变更
- README 已更新,增加各平台快速上手指南
- Windows 文档改为使用 `py -3` / `python`,不再假设存在 `py3` 或 `python3`

### 修复
- Windows Python 探测优先使用 `py -3`,避免 bare `py` 被 `py.ini`/`PY_PYTHON` 配置到 Python 2
- Python API 后端代理处理改用 `urllib.request.getproxies()`,保留小写代理变量覆盖大写变量的标准优先级
- Windows PowerShell 包装器改用 .NET UTF-8 无 BOM 写文件,兼容 Windows PowerShell 5.1
- `gh-pr-review.sh`:处理 GitHub API 返回的 `null` 类型 `pull_request_review_id` 和 `user` 字段
- `gh-pr-reviews.sh`:处理 GitHub API 返回的 `null` 类型 `user` 字段

Expand Down
34 changes: 27 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

[![License](https://img.shields.io/badge/License-MIT-orange.svg)](LICENSE)
[![Platform](https://img.shields.io/badge/Platform-Linux%20%7C%20macOS%20%7C%20Windows-blue.svg)](#-快速开始)
[![Python](https://img.shields.io/badge/Python-3.8%2B-green.svg)](https://www.python.org/)
[![Python](https://img.shields.io/badge/Python-3.7%2B-green.svg)](https://www.python.org/)
[![Zero Dependencies](https://img.shields.io/badge/Dependencies-None-brightgreen.svg)](#技术亮点)

</div>
Expand Down Expand Up @@ -59,6 +59,7 @@
- **🎯 字段过滤** — 支持 `owner.login`、`0.name` 等点号路径提取,减少 JSON 噪音
- **🔐 安全认证** — Token 文件权限检查(Linux: `S_IRWXG|S_IRWXO` / Windows: ACL)
- **🔑 多级回退** — `gh auth token` → `GITHUB_TOKEN`/`GH_TOKEN` → `~/.github_token`
- **🌐 代理兼容** — Python 后端使用 `urllib` 标准代理发现,支持 `http_proxy`/`https_proxy` 覆盖大写环境变量
- **🐍 统一后端** — `gh-api.py` 处理所有 HTTP/JSON/分页逻辑。Linux/macOS 包装器引用同目录副本,`scripts/windows/*.ps1` 引用 `../gh-api.py`

---
Expand All @@ -67,8 +68,8 @@

### 环境要求

- **Linux / macOS**: Bash + Python 3.8+
- **Windows**: PowerShell 7+ + Python 3.8+
- **Linux / macOS**: Bash + Python 3.7+
- **Windows**: Windows PowerShell 5.1+ 或 PowerShell 7+ + Python 3.7+(优先 `py -3`,其次 `python`;不要假设存在 `py3`/`python3`)

### 1. 克隆仓库

Expand Down Expand Up @@ -112,7 +113,7 @@ chmod 600 ~/.github_token
./scripts/linux/gh-activity.sh yang12535 10
```

**Windows**(PowerShell 7+ 脚本)
**Windows**(Windows PowerShell 5.1+ / PowerShell 7+ 脚本)
```powershell
# 查看当前用户
./scripts/windows/gh-user.ps1
Expand Down Expand Up @@ -171,13 +172,17 @@ github-ops/
# 自动分页(需 Python 后端支持;curl fallback 不支持分页)
./scripts/gh-api-call.sh repos/owner/repo/issues -p

# 字段过滤(直接调用 Python 后端)
# 字段过滤(直接调用 Python 后端,Linux/macOS
python3 scripts/gh-api.py repos/owner/repo/issues -p -f "0.title"

# 字段过滤(Windows)
py -3 scripts/gh-api.py repos/owner/repo/issues -p -f "0.title"
```

### Python 底层引擎

```bash
# Linux/macOS
# 查询并提取字段
python3 scripts/gh-api.py repos/owner/repo -f owner.login

Expand All @@ -189,6 +194,14 @@ python3 scripts/gh-api.py -X PATCH -d '{"state":"closed"}' \
repos/owner/repo/issues/1
```

Windows 直接调用 Python 后端时使用 `py -3`(推荐)或 `python`:

```powershell
py -3 scripts/gh-api.py repos/owner/repo -f owner.login
py -3 scripts/gh-api.py repos/owner/repo/issues -p -c
py -3 scripts/gh-api.py -X PATCH -d '{"state":"closed"}' repos/owner/repo/issues/1
```

### PR Review 工作流

```bash
Expand Down Expand Up @@ -219,7 +232,7 @@ python3 scripts/gh-api.py -X PATCH -d '{"state":"closed"}' \
### 文件权限检查

- **Linux/macOS**(Python 后端 + PowerShell):拒绝 `group` 或 `others` 有任何权限的 token 文件(读/写/执行均不允许)
- **Windows**(仅 PowerShell 包装脚本):拒绝 `Users`/`Everyone`/`Authenticated Users` 可读取的 token 文件。注意:Windows 上直接运行 `python3 scripts/gh-api.py` 不会触发 ACL 检查
- **Windows**(仅 PowerShell 包装脚本):拒绝 `Users`/`Everyone`/`Authenticated Users` 可读取的 token 文件。注意:Windows 上直接运行 `py -3 scripts/gh-api.py` 或 `python scripts/gh-api.py` 不会触发 ACL 检查

```bash
# 正确示例
Expand Down Expand Up @@ -248,8 +261,15 @@ chmod 600 ~/.github_token
# Shell 脚本语法检查
find scripts -name "*.sh" -exec bash -n {} \;

# Python 编译检查(含 scripts/ 与 scripts/linux/)
# Python 编译检查(Linux/macOS,含 scripts/ 与 scripts/linux/)
find scripts -name "*.py" -exec python3 -m py_compile {} \;
```

```powershell
# Python 编译检查(Windows)
Get-ChildItem scripts -Recurse -Filter "*.py" | ForEach-Object {
py -3 -m py_compile $_.FullName
}

# PowerShell 语法检查(Windows)
Get-ChildItem scripts/windows -Filter "*.ps1" | ForEach-Object {
Expand Down
9 changes: 9 additions & 0 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ description: "通过已认证的 REST API(curl/urllib)进行 GitHub 仓库
**主要方式**:通过 `scripts/gh-api.py`(Python/urllib)或直接 `curl` 调用 GitHub REST API。
**快捷脚本**:`scripts/gh-*.sh` 为常见任务提供便利封装。
**认证来源**:`gh auth token`(优先)→ `GITHUB_TOKEN` / `GH_TOKEN` 环境变量回退。仅在 token 缺失时才需要执行 `gh auth login` 重新认证。
**Windows Python**:Windows 包装器优先使用 `py -3`,然后回退到 `python`;不要假设存在 `py3` 或 `python3`。

## 快速开始

Expand Down Expand Up @@ -198,11 +199,19 @@ curl -s -X POST -H "Authorization: token $TOKEN" \

示例:
```bash
# Linux/macOS
python3 scripts/gh-api.py user -f login
python3 scripts/gh-api.py repos/owner/repo/issues -p -c
python3 scripts/gh-api.py -X PATCH -d '{"state":"closed"}' repos/owner/repo/issues/1
```

Windows 直接调用 Python 后端时:
```powershell
py -3 scripts/gh-api.py user -f login
py -3 scripts/gh-api.py repos/owner/repo/issues -p -c
py -3 scripts/gh-api.py -X PATCH -d '{"state":"closed"}' repos/owner/repo/issues/1
```

## 常用 API 端点

- `user` — 认证用户资料
Expand Down
12 changes: 12 additions & 0 deletions scripts/gh-api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@
import urllib.request


def _install_proxy_handler():
"""Install urllib's standard proxy handler when proxy env vars are set."""
proxies = urllib.request.getproxies()
if proxies:
handler = urllib.request.ProxyHandler(proxies)
opener = urllib.request.build_opener(handler)
urllib.request.install_opener(opener)
Comment on lines +15 to +21


_install_proxy_handler()


def get_token():
"""Get token from gh CLI, environment, or fallback files."""
try:
Expand Down
12 changes: 12 additions & 0 deletions scripts/linux/gh-api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@
import urllib.request


def _install_proxy_handler():
"""Install urllib's standard proxy handler when proxy env vars are set."""
proxies = urllib.request.getproxies()
if proxies:
handler = urllib.request.ProxyHandler(proxies)
opener = urllib.request.build_opener(handler)
urllib.request.install_opener(opener)
Comment on lines +15 to +21


_install_proxy_handler()


def get_token():
"""Get token from gh CLI, environment, or fallback files."""
try:
Expand Down
56 changes: 37 additions & 19 deletions scripts/windows/_common.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -73,29 +73,36 @@ function Invoke-GitHubApi {
}

# Pre-flight: verify Python exists and meets version requirement.
# Note: Get-Command may still find the Microsoft Store shim on PATH.
# The actual shim rejection happens in the version-regex check below.
$pythonCmd = Get-Command python -ErrorAction SilentlyContinue
if (-not $pythonCmd) {
Write-Host "[github-ops ERROR] Python interpreter not found." -ForegroundColor Red
Write-Host " This skill requires Python 3.8+ on Windows." -ForegroundColor Yellow
# On Windows, use the Python Launcher with an explicit Python 3 selector.
# Bare 'py' can be configured to launch Python 2 via py.ini/PY_PYTHON.
$candidates = @(
[pscustomobject]@{ Command = "py"; Args = @("-3"); Display = "py -3" },
[pscustomobject]@{ Command = "python"; Args = @(); Display = "python" }
)
$script:PythonExe = $null
$script:PythonArgs = @()
foreach ($c in $candidates) {
$cmd = Get-Command $c.Command -ErrorAction SilentlyContinue
if (-not $cmd) { continue }
$candidateArgs = @($c.Args)
$verOutput = & $cmd.Source @candidateArgs --version 2>&1
if ($verOutput -notmatch "Python (\d+)\.(\d+)") { continue }
$major = [int]$matches[1]
$minor = [int]$matches[2]
if ($major -gt 3 -or ($major -eq 3 -and $minor -ge 7)) {
$script:PythonExe = $cmd.Source
$script:PythonArgs = $candidateArgs
break
}
}
if (-not $script:PythonExe) {
Write-Host "[github-ops ERROR] Python 3.7+ interpreter not found." -ForegroundColor Red
Write-Host " Tried: $(($candidates | ForEach-Object { $_.Display }) -join ', ')" -ForegroundColor Yellow
Write-Host " Install: winget install Python.Python.3" -ForegroundColor Cyan
Write-Host " Or download from: https://python.org/downloads/" -ForegroundColor Cyan
exit 127
}

$verOutput = & python --version 2>&1
if ($verOutput -notmatch "Python (\d+)\.(\d+)") {
Write-Host "[github-ops ERROR] Unable to determine Python version: $verOutput" -ForegroundColor Red
exit 1
}
$major = [int]$matches[1]
$minor = [int]$matches[2]
if ($major -lt 3 -or ($major -eq 3 -and $minor -lt 8)) {
Write-Host "[github-ops ERROR] Python $major.$minor is too old. Python 3.8+ required." -ForegroundColor Red
exit 1
}

# Pre-flight: resolve GitHub token and inject into environment so Python
# backend does not need to shell out to gh CLI again.
$token = Get-GitHubToken
Expand All @@ -108,6 +115,17 @@ function Invoke-GitHubApi {
if ($Compact) { $pyArgs += "-c" }
if ($Field) { foreach ($f in $Field) { $pyArgs += @("-f", $f) } }

& python $ApiScript @pyArgs
$backendArgs = @($script:PythonArgs) + @($ApiScript) + $pyArgs
& $script:PythonExe @backendArgs
exit $LASTEXITCODE
}

function Write-Utf8NoBomFile {
param(
[Parameter(Mandatory)][string]$Path,
[Parameter(Mandatory)][string]$Value
)

$encoding = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText($Path, $Value, $encoding)
}
2 changes: 1 addition & 1 deletion scripts/windows/gh-issue.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ switch ($Command) {
$payload = @{ title = $title }
if ($body) { $payload.body = $body }
$tmpFile = [System.IO.Path]::GetTempFileName()
$payload | ConvertTo-Json -Compress -Depth 10 | Set-Content -Path $tmpFile -Encoding utf8NoBOM -NoNewline
Write-Utf8NoBomFile -Path $tmpFile -Value ($payload | ConvertTo-Json -Compress -Depth 10)
try {
Invoke-GitHubApi "repos/$Repo/issues" -Method POST -Data "@$tmpFile"
} finally {
Expand Down
2 changes: 1 addition & 1 deletion scripts/windows/gh-pr.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ switch ($Command) {
$payload = @{ title = $title; head = $head; base = $base }
if ($body) { $payload.body = $body }
$tmpFile = [System.IO.Path]::GetTempFileName()
$payload | ConvertTo-Json -Compress -Depth 10 | Set-Content -Path $tmpFile -Encoding utf8NoBOM -NoNewline
Write-Utf8NoBomFile -Path $tmpFile -Value ($payload | ConvertTo-Json -Compress -Depth 10)
try {
Invoke-GitHubApi "repos/$Repo/pulls" -Method POST -Data "@$tmpFile"
} finally {
Expand Down