chatgh 0.2.2__tar.gz → 0.2.3__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.
- {chatgh-0.2.2/src/chatgh.egg-info → chatgh-0.2.3}/PKG-INFO +21 -54
- {chatgh-0.2.2 → chatgh-0.2.3}/README.md +20 -53
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/__init__.py +1 -1
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/cli.py +2 -1
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/github/api.py +32 -40
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/github/cli.py +80 -1
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/github/commands.py +30 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/github/requests.py +115 -0
- {chatgh-0.2.2 → chatgh-0.2.3/src/chatgh.egg-info}/PKG-INFO +21 -54
- {chatgh-0.2.2 → chatgh-0.2.3}/tests/test_version.py +1 -1
- {chatgh-0.2.2 → chatgh-0.2.3}/LICENSE +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/pyproject.toml +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/setup.cfg +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/_generated/__init__.py +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/adapters/__init__.py +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/adapters/client.py +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/commands/__init__.py +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/commands/pr.py +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/config.py +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/github/__init__.py +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/github/client.py +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh/github/render.py +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh.egg-info/SOURCES.txt +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh.egg-info/dependency_links.txt +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh.egg-info/entry_points.txt +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh.egg-info/requires.txt +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/src/chatgh.egg-info/top_level.txt +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/tests/test_adapter_client.py +0 -0
- {chatgh-0.2.2 → chatgh-0.2.3}/tests/test_cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chatgh
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: GitHub CLI and Python helpers for ChatArch
|
|
5
5
|
Author-email: ChatArch <1073853456@qq.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -43,7 +43,7 @@ pip install -e ".[dev]"
|
|
|
43
43
|
默认行为:
|
|
44
44
|
|
|
45
45
|
- `repo`:优先使用显式 `--repo owner/repo`,未传时从当前 git remote 推断。
|
|
46
|
-
- `token`:优先使用显式 `--token
|
|
46
|
+
- `token`:优先使用显式 `--token`,其次读取当前仓库 `.git/config` 中 repo-local HTTPS auth header,再回退 typed env 里的 `GITHUB_ACCESS_TOKEN`。
|
|
47
47
|
- 输出:默认是人类可读格式;支持 `--json-output` 的命令会输出稳定 JSON,适合脚本消费。
|
|
48
48
|
|
|
49
49
|
### Token 来源
|
|
@@ -51,7 +51,7 @@ pip install -e ".[dev]"
|
|
|
51
51
|
Token 解析顺序稳定为:
|
|
52
52
|
|
|
53
53
|
1. 显式 `--token`。
|
|
54
|
-
2. repo-
|
|
54
|
+
2. 当前仓库 `.git/config` 中的 repo-local HTTPS auth header,路径为规范化后的 `https://github.com/owner/repo.git`。
|
|
55
55
|
3. typed env 中的 `GITHUB_ACCESS_TOKEN`。
|
|
56
56
|
|
|
57
57
|
可以用 `chatenv` 查看或配置 typed env:
|
|
@@ -72,13 +72,14 @@ chatenv cat -t gh
|
|
|
72
72
|
- `git@github.com:octocat/Hello-World.git`
|
|
73
73
|
- `ssh://git@github.com/octocat/Hello-World.git`
|
|
74
74
|
|
|
75
|
-
写入 repo-
|
|
75
|
+
写入 repo-local HTTPS auth header 时,path 会规范化为 `https://github.com/octocat/Hello-World.git`。
|
|
76
76
|
|
|
77
77
|
## CLI 入口
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
80
|
chatgh --help
|
|
81
81
|
chatgh pr --help
|
|
82
|
+
chatgh repo --help
|
|
82
83
|
chatgh run --help
|
|
83
84
|
chatgh repo-perms --help
|
|
84
85
|
chatgh set-token --help
|
|
@@ -89,7 +90,9 @@ chatgh set-token --help
|
|
|
89
90
|
- `chatgh pr list`:generated-layer PR 列表。
|
|
90
91
|
- `chatgh pr view NUMBER`:generated-layer PR 详情。
|
|
91
92
|
- `chatgh pr checks NUMBER`:generated-layer PR head commit check runs。
|
|
92
|
-
- `chatgh pr
|
|
93
|
+
- 当前公开 `chatgh pr` 命令面只包含 `list/view/checks`;PR 创建、评论、合并和编辑流程仍由内部 helper 支持,后续作为独立 CLI 命令恢复前不在帮助与文档中承诺。
|
|
94
|
+
- `chatgh repo list`:列出 user/org 下的仓库;默认 table,支持 `--json-output`、`--limit`、`--sort updated|created|pushed|name|stars|open-prs|open-issues`、`--direction asc|desc`,字段包含 visibility、stars、open PRs、open issues、created/updated time 等。
|
|
95
|
+
- `chatgh repo create`:创建仓库;默认 private,可用 `--public` 显式创建公开仓库。
|
|
93
96
|
- `chatgh run view`:查看 workflow run 和 jobs。
|
|
94
97
|
- `chatgh run logs`:查看 job 日志,支持 tail 和落盘。
|
|
95
98
|
- `chatgh repo-perms`:查看 token 权限和派生 capabilities。
|
|
@@ -99,26 +102,7 @@ chatgh set-token --help
|
|
|
99
102
|
|
|
100
103
|
### 创建 PR
|
|
101
104
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
cat > /tmp/pr_body.md <<'EOF'
|
|
106
|
-
## Summary
|
|
107
|
-
- migrate GitHub helpers to chatgh
|
|
108
|
-
|
|
109
|
-
## Testing
|
|
110
|
-
- python -m pytest -q
|
|
111
|
-
EOF
|
|
112
|
-
|
|
113
|
-
chatgh pr create \
|
|
114
|
-
--repo octocat/Hello-World \
|
|
115
|
-
--base main \
|
|
116
|
-
--head feature-branch \
|
|
117
|
-
--title "feat: migrate github helpers" \
|
|
118
|
-
--body-file /tmp/pr_body.md
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
缺少 `base` / `head` / `title` 且终端可交互时,命令会自动补问;显式 `-I` 会禁用补问。
|
|
105
|
+
当前版本未公开 `chatgh pr create` CLI 命令。需要创建 PR 时,请使用项目既有 GitHub 流程或直接调用 GitHub API;等写操作命令恢复为稳定 CLI surface 后再补回示例。
|
|
122
106
|
|
|
123
107
|
### 查看 PR
|
|
124
108
|
|
|
@@ -135,12 +119,10 @@ chatgh pr view 123 --repo octocat/Hello-World --json-output
|
|
|
135
119
|
- `mergeable` 和 `mergeable_state`。
|
|
136
120
|
- created/updated/merged timestamps。
|
|
137
121
|
|
|
138
|
-
###
|
|
122
|
+
### 查看 CI
|
|
139
123
|
|
|
140
124
|
```bash
|
|
141
125
|
chatgh pr checks 123 --repo octocat/Hello-World
|
|
142
|
-
chatgh pr checks --repo octocat/Hello-World --number 123 --wait
|
|
143
|
-
chatgh pr checks --repo octocat/Hello-World --number 123 --wait --interval 10 --timeout 600
|
|
144
126
|
chatgh pr checks 123 --repo octocat/Hello-World --json-output
|
|
145
127
|
```
|
|
146
128
|
|
|
@@ -150,11 +132,7 @@ chatgh pr checks 123 --repo octocat/Hello-World --json-output
|
|
|
150
132
|
- check runs
|
|
151
133
|
- workflow runs
|
|
152
134
|
|
|
153
|
-
`--wait`
|
|
154
|
-
|
|
155
|
-
- 默认不设超时,会一直等。
|
|
156
|
-
- `--interval <seconds>` 控制轮询间隔。
|
|
157
|
-
- 只有显式传 `--timeout <seconds>` 时才会超时报错。
|
|
135
|
+
当前公开 CLI 不提供 `--wait` / `--interval` / `--timeout` 参数;需要等待终态时,在外层流程中轮询 `chatgh pr checks`。
|
|
158
136
|
|
|
159
137
|
如果 GitHub token 无权读取 check-runs API,命令会把 check-runs 错误放进 payload,同时仍尽量展示 combined status 和 workflow runs。
|
|
160
138
|
|
|
@@ -173,25 +151,7 @@ chatgh run logs --repo octocat/Hello-World --job-id 987654321 --tail 200 --outpu
|
|
|
173
151
|
|
|
174
152
|
### 评论、合并和编辑 PR
|
|
175
153
|
|
|
176
|
-
|
|
177
|
-
chatgh pr comment --repo octocat/Hello-World --number 123 --body "Looks good"
|
|
178
|
-
|
|
179
|
-
chatgh pr merge --repo octocat/Hello-World --number 123 --method squash
|
|
180
|
-
chatgh pr merge --repo octocat/Hello-World --number 123 --method squash --check
|
|
181
|
-
|
|
182
|
-
chatgh pr edit --repo octocat/Hello-World --number 123 --title "New title"
|
|
183
|
-
chatgh pr edit --repo octocat/Hello-World --number 123 --body-file /tmp/pr_body.md
|
|
184
|
-
chatgh pr edit --repo octocat/Hello-World --number 123 --state closed
|
|
185
|
-
chatgh pr edit --repo octocat/Hello-World --number 123 --base main
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
`pr merge --check` 会在合并前拒绝以下情况:
|
|
189
|
-
|
|
190
|
-
- PR 当前 `mergeable=False`。
|
|
191
|
-
- `mergeable_state` 为 `dirty`、`blocked`、`behind`、`draft` 或 `unknown`。
|
|
192
|
-
- combined status、check runs 或 workflow runs 存在失败、取消或未完成项。
|
|
193
|
-
|
|
194
|
-
不带 `--check` 时,命令保持直接调用 GitHub merge API 的行为。
|
|
154
|
+
当前版本未公开 `chatgh pr comment/merge/edit` CLI 命令。相关业务函数已保留在 `chatgh.github.commands` 中,后续恢复为稳定 CLI surface 前,建议通过项目既有流程或 GitHub API 执行写操作。
|
|
195
155
|
|
|
196
156
|
### 配置和检查 token
|
|
197
157
|
|
|
@@ -209,7 +169,14 @@ chatgh set-token --token "$GITHUB_ACCESS_TOKEN" --save-env
|
|
|
209
169
|
- GitHub 返回的 `permissions`。
|
|
210
170
|
- 派生 capabilities:`can_read_pr`、`can_comment_pr`、`can_merge_pr`、`can_view_checks`、`can_view_actions`。
|
|
211
171
|
|
|
212
|
-
`set-token` 只在当前目录能识别 GitHub remote
|
|
172
|
+
`set-token` 只在当前目录能识别 GitHub remote 时生效。默认只写入当前仓库自己的 `.git/config`:
|
|
173
|
+
|
|
174
|
+
```ini
|
|
175
|
+
[http "https://github.com/octocat/Hello-World.git"]
|
|
176
|
+
extraHeader = Authorization: Basic <base64(x-access-token:TOKEN)>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
不要把 token 写进 remote URL,也不要把 raw `extraHeader` 输出到日志。传 `--save-env` 时会同步写入 typed env 的 `GITHUB_ACCESS_TOKEN`。
|
|
213
180
|
|
|
214
181
|
## 交互模式
|
|
215
182
|
|
|
@@ -233,7 +200,7 @@ git fetch origin main
|
|
|
233
200
|
|
|
234
201
|
- `chatgh pr view` / `chatgh pr checks` 显示 `mergeable` 不是 `False`,`mergeable_state` 不是 `dirty`。
|
|
235
202
|
- 本地基于最新 base 做过 merge 或 rebase 演练,并在该结果上跑过最相关测试。
|
|
236
|
-
- CI
|
|
203
|
+
- CI 需要终态时,在外层流程中轮询 `chatgh pr checks`,不要只看一次快照。
|
|
237
204
|
|
|
238
205
|
## Python API
|
|
239
206
|
|
|
@@ -15,7 +15,7 @@ pip install -e ".[dev]"
|
|
|
15
15
|
默认行为:
|
|
16
16
|
|
|
17
17
|
- `repo`:优先使用显式 `--repo owner/repo`,未传时从当前 git remote 推断。
|
|
18
|
-
- `token`:优先使用显式 `--token
|
|
18
|
+
- `token`:优先使用显式 `--token`,其次读取当前仓库 `.git/config` 中 repo-local HTTPS auth header,再回退 typed env 里的 `GITHUB_ACCESS_TOKEN`。
|
|
19
19
|
- 输出:默认是人类可读格式;支持 `--json-output` 的命令会输出稳定 JSON,适合脚本消费。
|
|
20
20
|
|
|
21
21
|
### Token 来源
|
|
@@ -23,7 +23,7 @@ pip install -e ".[dev]"
|
|
|
23
23
|
Token 解析顺序稳定为:
|
|
24
24
|
|
|
25
25
|
1. 显式 `--token`。
|
|
26
|
-
2. repo-
|
|
26
|
+
2. 当前仓库 `.git/config` 中的 repo-local HTTPS auth header,路径为规范化后的 `https://github.com/owner/repo.git`。
|
|
27
27
|
3. typed env 中的 `GITHUB_ACCESS_TOKEN`。
|
|
28
28
|
|
|
29
29
|
可以用 `chatenv` 查看或配置 typed env:
|
|
@@ -44,13 +44,14 @@ chatenv cat -t gh
|
|
|
44
44
|
- `git@github.com:octocat/Hello-World.git`
|
|
45
45
|
- `ssh://git@github.com/octocat/Hello-World.git`
|
|
46
46
|
|
|
47
|
-
写入 repo-
|
|
47
|
+
写入 repo-local HTTPS auth header 时,path 会规范化为 `https://github.com/octocat/Hello-World.git`。
|
|
48
48
|
|
|
49
49
|
## CLI 入口
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
52
|
chatgh --help
|
|
53
53
|
chatgh pr --help
|
|
54
|
+
chatgh repo --help
|
|
54
55
|
chatgh run --help
|
|
55
56
|
chatgh repo-perms --help
|
|
56
57
|
chatgh set-token --help
|
|
@@ -61,7 +62,9 @@ chatgh set-token --help
|
|
|
61
62
|
- `chatgh pr list`:generated-layer PR 列表。
|
|
62
63
|
- `chatgh pr view NUMBER`:generated-layer PR 详情。
|
|
63
64
|
- `chatgh pr checks NUMBER`:generated-layer PR head commit check runs。
|
|
64
|
-
- `chatgh pr
|
|
65
|
+
- 当前公开 `chatgh pr` 命令面只包含 `list/view/checks`;PR 创建、评论、合并和编辑流程仍由内部 helper 支持,后续作为独立 CLI 命令恢复前不在帮助与文档中承诺。
|
|
66
|
+
- `chatgh repo list`:列出 user/org 下的仓库;默认 table,支持 `--json-output`、`--limit`、`--sort updated|created|pushed|name|stars|open-prs|open-issues`、`--direction asc|desc`,字段包含 visibility、stars、open PRs、open issues、created/updated time 等。
|
|
67
|
+
- `chatgh repo create`:创建仓库;默认 private,可用 `--public` 显式创建公开仓库。
|
|
65
68
|
- `chatgh run view`:查看 workflow run 和 jobs。
|
|
66
69
|
- `chatgh run logs`:查看 job 日志,支持 tail 和落盘。
|
|
67
70
|
- `chatgh repo-perms`:查看 token 权限和派生 capabilities。
|
|
@@ -71,26 +74,7 @@ chatgh set-token --help
|
|
|
71
74
|
|
|
72
75
|
### 创建 PR
|
|
73
76
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
cat > /tmp/pr_body.md <<'EOF'
|
|
78
|
-
## Summary
|
|
79
|
-
- migrate GitHub helpers to chatgh
|
|
80
|
-
|
|
81
|
-
## Testing
|
|
82
|
-
- python -m pytest -q
|
|
83
|
-
EOF
|
|
84
|
-
|
|
85
|
-
chatgh pr create \
|
|
86
|
-
--repo octocat/Hello-World \
|
|
87
|
-
--base main \
|
|
88
|
-
--head feature-branch \
|
|
89
|
-
--title "feat: migrate github helpers" \
|
|
90
|
-
--body-file /tmp/pr_body.md
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
缺少 `base` / `head` / `title` 且终端可交互时,命令会自动补问;显式 `-I` 会禁用补问。
|
|
77
|
+
当前版本未公开 `chatgh pr create` CLI 命令。需要创建 PR 时,请使用项目既有 GitHub 流程或直接调用 GitHub API;等写操作命令恢复为稳定 CLI surface 后再补回示例。
|
|
94
78
|
|
|
95
79
|
### 查看 PR
|
|
96
80
|
|
|
@@ -107,12 +91,10 @@ chatgh pr view 123 --repo octocat/Hello-World --json-output
|
|
|
107
91
|
- `mergeable` 和 `mergeable_state`。
|
|
108
92
|
- created/updated/merged timestamps。
|
|
109
93
|
|
|
110
|
-
###
|
|
94
|
+
### 查看 CI
|
|
111
95
|
|
|
112
96
|
```bash
|
|
113
97
|
chatgh pr checks 123 --repo octocat/Hello-World
|
|
114
|
-
chatgh pr checks --repo octocat/Hello-World --number 123 --wait
|
|
115
|
-
chatgh pr checks --repo octocat/Hello-World --number 123 --wait --interval 10 --timeout 600
|
|
116
98
|
chatgh pr checks 123 --repo octocat/Hello-World --json-output
|
|
117
99
|
```
|
|
118
100
|
|
|
@@ -122,11 +104,7 @@ chatgh pr checks 123 --repo octocat/Hello-World --json-output
|
|
|
122
104
|
- check runs
|
|
123
105
|
- workflow runs
|
|
124
106
|
|
|
125
|
-
`--wait`
|
|
126
|
-
|
|
127
|
-
- 默认不设超时,会一直等。
|
|
128
|
-
- `--interval <seconds>` 控制轮询间隔。
|
|
129
|
-
- 只有显式传 `--timeout <seconds>` 时才会超时报错。
|
|
107
|
+
当前公开 CLI 不提供 `--wait` / `--interval` / `--timeout` 参数;需要等待终态时,在外层流程中轮询 `chatgh pr checks`。
|
|
130
108
|
|
|
131
109
|
如果 GitHub token 无权读取 check-runs API,命令会把 check-runs 错误放进 payload,同时仍尽量展示 combined status 和 workflow runs。
|
|
132
110
|
|
|
@@ -145,25 +123,7 @@ chatgh run logs --repo octocat/Hello-World --job-id 987654321 --tail 200 --outpu
|
|
|
145
123
|
|
|
146
124
|
### 评论、合并和编辑 PR
|
|
147
125
|
|
|
148
|
-
|
|
149
|
-
chatgh pr comment --repo octocat/Hello-World --number 123 --body "Looks good"
|
|
150
|
-
|
|
151
|
-
chatgh pr merge --repo octocat/Hello-World --number 123 --method squash
|
|
152
|
-
chatgh pr merge --repo octocat/Hello-World --number 123 --method squash --check
|
|
153
|
-
|
|
154
|
-
chatgh pr edit --repo octocat/Hello-World --number 123 --title "New title"
|
|
155
|
-
chatgh pr edit --repo octocat/Hello-World --number 123 --body-file /tmp/pr_body.md
|
|
156
|
-
chatgh pr edit --repo octocat/Hello-World --number 123 --state closed
|
|
157
|
-
chatgh pr edit --repo octocat/Hello-World --number 123 --base main
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
`pr merge --check` 会在合并前拒绝以下情况:
|
|
161
|
-
|
|
162
|
-
- PR 当前 `mergeable=False`。
|
|
163
|
-
- `mergeable_state` 为 `dirty`、`blocked`、`behind`、`draft` 或 `unknown`。
|
|
164
|
-
- combined status、check runs 或 workflow runs 存在失败、取消或未完成项。
|
|
165
|
-
|
|
166
|
-
不带 `--check` 时,命令保持直接调用 GitHub merge API 的行为。
|
|
126
|
+
当前版本未公开 `chatgh pr comment/merge/edit` CLI 命令。相关业务函数已保留在 `chatgh.github.commands` 中,后续恢复为稳定 CLI surface 前,建议通过项目既有流程或 GitHub API 执行写操作。
|
|
167
127
|
|
|
168
128
|
### 配置和检查 token
|
|
169
129
|
|
|
@@ -181,7 +141,14 @@ chatgh set-token --token "$GITHUB_ACCESS_TOKEN" --save-env
|
|
|
181
141
|
- GitHub 返回的 `permissions`。
|
|
182
142
|
- 派生 capabilities:`can_read_pr`、`can_comment_pr`、`can_merge_pr`、`can_view_checks`、`can_view_actions`。
|
|
183
143
|
|
|
184
|
-
`set-token` 只在当前目录能识别 GitHub remote
|
|
144
|
+
`set-token` 只在当前目录能识别 GitHub remote 时生效。默认只写入当前仓库自己的 `.git/config`:
|
|
145
|
+
|
|
146
|
+
```ini
|
|
147
|
+
[http "https://github.com/octocat/Hello-World.git"]
|
|
148
|
+
extraHeader = Authorization: Basic <base64(x-access-token:TOKEN)>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
不要把 token 写进 remote URL,也不要把 raw `extraHeader` 输出到日志。传 `--save-env` 时会同步写入 typed env 的 `GITHUB_ACCESS_TOKEN`。
|
|
185
152
|
|
|
186
153
|
## 交互模式
|
|
187
154
|
|
|
@@ -205,7 +172,7 @@ git fetch origin main
|
|
|
205
172
|
|
|
206
173
|
- `chatgh pr view` / `chatgh pr checks` 显示 `mergeable` 不是 `False`,`mergeable_state` 不是 `dirty`。
|
|
207
174
|
- 本地基于最新 base 做过 merge 或 rebase 演练,并在该结果上跑过最相关测试。
|
|
208
|
-
- CI
|
|
175
|
+
- CI 需要终态时,在外层流程中轮询 `chatgh pr checks`,不要只看一次快照。
|
|
209
176
|
|
|
210
177
|
## Python API
|
|
211
178
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import click
|
|
4
4
|
|
|
5
5
|
from chatgh.commands.pr import pr_group
|
|
6
|
-
from chatgh.github.cli import repo_permissions, run_group, set_repo_token
|
|
6
|
+
from chatgh.github.cli import repo_group, repo_permissions, run_group, set_repo_token
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
@click.group()
|
|
@@ -12,6 +12,7 @@ def main() -> None:
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
main.add_command(pr_group, name="pr")
|
|
15
|
+
main.add_command(repo_group, name="repo")
|
|
15
16
|
main.add_command(run_group, name="run")
|
|
16
17
|
main.add_command(repo_permissions, name="repo-perms")
|
|
17
18
|
main.add_command(set_repo_token, name="set-token")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import base64
|
|
3
4
|
from urllib.parse import urlparse
|
|
4
5
|
import subprocess
|
|
5
6
|
from typing import Optional, TypedDict
|
|
@@ -74,7 +75,7 @@ def resolve_token_with_source(
|
|
|
74
75
|
credential_path, exact_only=exact_only
|
|
75
76
|
)
|
|
76
77
|
if credential_token:
|
|
77
|
-
return {"token": credential_token, "source": "git
|
|
78
|
+
return {"token": credential_token, "source": "repo git config"}
|
|
78
79
|
credential_token = read_github_token_from_credentials(
|
|
79
80
|
credential_path, exact_only=exact_only
|
|
80
81
|
)
|
|
@@ -212,20 +213,22 @@ def read_github_token_from_git(
|
|
|
212
213
|
) -> Optional[str]:
|
|
213
214
|
if not credential_path:
|
|
214
215
|
return None
|
|
215
|
-
return
|
|
216
|
+
return _read_repo_local_extraheader(credential_path)
|
|
216
217
|
|
|
217
218
|
|
|
218
|
-
def
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
219
|
+
def _github_https_url(credential: CredentialQuery) -> str:
|
|
220
|
+
path = credential["path"].strip().removeprefix("/").removesuffix(".git")
|
|
221
|
+
return f"https://{credential['host']}/{path}.git"
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _extraheader_config_key(credential: CredentialQuery) -> str:
|
|
225
|
+
return f"http.{_github_https_url(credential)}.extraHeader"
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _read_repo_local_extraheader(credential: CredentialQuery) -> Optional[str]:
|
|
225
229
|
try:
|
|
226
230
|
result = subprocess.run(
|
|
227
|
-
["git", "
|
|
228
|
-
input=credential_input,
|
|
231
|
+
["git", "config", "--local", "--get", _extraheader_config_key(credential)],
|
|
229
232
|
check=False,
|
|
230
233
|
capture_output=True,
|
|
231
234
|
text=True,
|
|
@@ -234,41 +237,30 @@ def _git_credential_fill(credential: CredentialQuery) -> Optional[str]:
|
|
|
234
237
|
return None
|
|
235
238
|
if result.returncode != 0:
|
|
236
239
|
return None
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
240
|
+
return _token_from_extraheader(result.stdout.strip())
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _token_from_extraheader(header: str) -> Optional[str]:
|
|
244
|
+
prefix = "Authorization: Basic "
|
|
245
|
+
if not header.startswith(prefix):
|
|
246
|
+
return None
|
|
247
|
+
encoded = header[len(prefix) :].strip()
|
|
248
|
+
try:
|
|
249
|
+
decoded = base64.b64decode(encoded).decode("utf-8")
|
|
250
|
+
except Exception:
|
|
251
|
+
return None
|
|
252
|
+
username, sep, password = decoded.partition(":")
|
|
253
|
+
if sep != ":" or username != "x-access-token" or not password:
|
|
244
254
|
return None
|
|
245
|
-
return
|
|
255
|
+
return password
|
|
246
256
|
|
|
247
257
|
|
|
248
258
|
def configure_github_https_token(credential: CredentialQuery, token: str) -> None:
|
|
249
259
|
try:
|
|
260
|
+
raw = f"x-access-token:{token}".encode("utf-8")
|
|
261
|
+
header = "Authorization: Basic " + base64.b64encode(raw).decode("ascii")
|
|
250
262
|
subprocess.run(
|
|
251
|
-
["git", "config", "--
|
|
252
|
-
check=True,
|
|
253
|
-
capture_output=True,
|
|
254
|
-
text=True,
|
|
255
|
-
)
|
|
256
|
-
subprocess.run(
|
|
257
|
-
["git", "config", "--global", "credential.useHttpPath", "true"],
|
|
258
|
-
check=True,
|
|
259
|
-
capture_output=True,
|
|
260
|
-
text=True,
|
|
261
|
-
)
|
|
262
|
-
credential_input = (
|
|
263
|
-
f"protocol={credential['protocol']}\n"
|
|
264
|
-
f"host={credential['host']}\n"
|
|
265
|
-
f"path={credential['path']}\n"
|
|
266
|
-
"username=x-access-token\n"
|
|
267
|
-
f"password={token}\n\n"
|
|
268
|
-
)
|
|
269
|
-
subprocess.run(
|
|
270
|
-
["git", "credential", "approve"],
|
|
271
|
-
input=credential_input,
|
|
263
|
+
["git", "config", "--local", _extraheader_config_key(credential), header],
|
|
272
264
|
check=True,
|
|
273
265
|
capture_output=True,
|
|
274
266
|
text=True,
|
|
@@ -14,7 +14,7 @@ from chatstyle.core.interactive import is_interactive_available
|
|
|
14
14
|
from chatstyle.tui.prompt import BACK_VALUE, ask_text
|
|
15
15
|
|
|
16
16
|
from chatgh.github.api import resolve_token
|
|
17
|
-
from chatgh.github.commands import repo_perms, set_token, view_job_logs, view_run
|
|
17
|
+
from chatgh.github.commands import create_repo, list_repos, repo_perms, set_token, view_job_logs, view_run
|
|
18
18
|
from chatgh.github.render import echo_workflow_job, echo_workflow_run, format_optional
|
|
19
19
|
|
|
20
20
|
|
|
@@ -44,6 +44,85 @@ def run_group() -> None:
|
|
|
44
44
|
"""GitHub Actions helpers."""
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
@cli.group(name="repo")
|
|
48
|
+
def repo_group() -> None:
|
|
49
|
+
"""Repository helpers."""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@repo_group.command(name="list")
|
|
53
|
+
@click.option("--owner", required=True, help="GitHub owner or organization.")
|
|
54
|
+
@click.option("--limit", default=50, type=click.IntRange(min=1), show_default=True)
|
|
55
|
+
@click.option(
|
|
56
|
+
"--sort",
|
|
57
|
+
type=click.Choice(["updated", "created", "pushed", "name", "stars", "open-prs", "open-issues"]),
|
|
58
|
+
default="updated",
|
|
59
|
+
show_default=True,
|
|
60
|
+
help="Sort repositories before applying --limit.",
|
|
61
|
+
)
|
|
62
|
+
@click.option("--direction", type=click.Choice(["asc", "desc"]), default="desc", show_default=True)
|
|
63
|
+
@click.option("--json-output", is_flag=True, help="Output JSON.")
|
|
64
|
+
@click.option("--token", default=None, help="GitHub token.")
|
|
65
|
+
def repo_list(owner, limit, sort, direction, json_output, token):
|
|
66
|
+
"""List repositories for an owner or organization."""
|
|
67
|
+
payload = list_repos(owner, limit, sort, direction, token)
|
|
68
|
+
if json_output:
|
|
69
|
+
click.echo(json.dumps(payload, ensure_ascii=False, indent=2))
|
|
70
|
+
return
|
|
71
|
+
_echo_repo_table(payload)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _echo_repo_table(items: list[dict]) -> None:
|
|
75
|
+
columns = [
|
|
76
|
+
("repo", "full_name"),
|
|
77
|
+
("vis", "visibility"),
|
|
78
|
+
("stars", "stars"),
|
|
79
|
+
("prs", "open_prs"),
|
|
80
|
+
("issues", "open_issues"),
|
|
81
|
+
("updated", "updated_at"),
|
|
82
|
+
("created", "created_at"),
|
|
83
|
+
]
|
|
84
|
+
rows = []
|
|
85
|
+
for item in items:
|
|
86
|
+
row = []
|
|
87
|
+
for _, key in columns:
|
|
88
|
+
value = item.get(key)
|
|
89
|
+
if key.endswith("_at"):
|
|
90
|
+
value = str(value or "")[:10]
|
|
91
|
+
row.append(str(value if value is not None else ""))
|
|
92
|
+
rows.append(row)
|
|
93
|
+
widths = [len(label) for label, _ in columns]
|
|
94
|
+
for row in rows:
|
|
95
|
+
for index, value in enumerate(row):
|
|
96
|
+
widths[index] = min(max(widths[index], len(value)), 48)
|
|
97
|
+
header = " ".join(label.ljust(widths[index]) for index, (label, _) in enumerate(columns))
|
|
98
|
+
click.echo(header)
|
|
99
|
+
click.echo(" ".join("-" * width for width in widths))
|
|
100
|
+
for row in rows:
|
|
101
|
+
clipped = [value[: widths[index]] for index, value in enumerate(row)]
|
|
102
|
+
click.echo(" ".join(value.ljust(widths[index]) for index, value in enumerate(clipped)))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@repo_group.command(name="create")
|
|
106
|
+
@click.option("--owner", required=True, help="GitHub owner or organization.")
|
|
107
|
+
@click.option("--name", required=True, help="Repository name.")
|
|
108
|
+
@click.option("--description", default=None, help="Repository description.")
|
|
109
|
+
@click.option("--public", "public_repo", is_flag=True, help="Create a public repository. Defaults to private.")
|
|
110
|
+
@click.option("--if-exists", type=click.Choice(["error", "use"]), default="error", show_default=True)
|
|
111
|
+
@click.option("--json-output", is_flag=True, help="Output JSON.")
|
|
112
|
+
@click.option("--token", default=None, help="GitHub token.")
|
|
113
|
+
def repo_create(owner, name, description, public_repo, if_exists, json_output, token):
|
|
114
|
+
"""Create a repository. Repositories are private by default."""
|
|
115
|
+
payload = create_repo(owner, name, not public_repo, description, if_exists, token)
|
|
116
|
+
if json_output:
|
|
117
|
+
click.echo(json.dumps(payload, ensure_ascii=False, indent=2))
|
|
118
|
+
return
|
|
119
|
+
created = "created" if payload.get("created") else "existing"
|
|
120
|
+
private = "private" if payload.get("private") else "public"
|
|
121
|
+
click.echo(f"{created}: {payload['full_name']} ({private})")
|
|
122
|
+
if payload.get("html_url"):
|
|
123
|
+
click.echo(payload["html_url"])
|
|
124
|
+
|
|
125
|
+
|
|
47
126
|
@run_group.command(name="view")
|
|
48
127
|
@click.option("--repo", required=False, help="Repository in owner/name form.")
|
|
49
128
|
@click.option("--run-id", required=False, type=int, help="Workflow run id.")
|
|
@@ -27,12 +27,14 @@ from chatgh.github.requests import (
|
|
|
27
27
|
get_pr_checks,
|
|
28
28
|
get_pr_list,
|
|
29
29
|
get_pr_view,
|
|
30
|
+
get_repo_list,
|
|
30
31
|
get_repo_permissions,
|
|
31
32
|
get_run_view,
|
|
32
33
|
patch_pr_edit,
|
|
33
34
|
post_pr_comment,
|
|
34
35
|
post_pr_create,
|
|
35
36
|
post_pr_merge,
|
|
37
|
+
post_repo_create,
|
|
36
38
|
)
|
|
37
39
|
|
|
38
40
|
|
|
@@ -43,6 +45,34 @@ def resolve_repo_and_credential_path(repo: Optional[str]) -> tuple[str, Credenti
|
|
|
43
45
|
return resolve_repo_from_git_remote()
|
|
44
46
|
|
|
45
47
|
|
|
48
|
+
def list_repos(
|
|
49
|
+
owner: str,
|
|
50
|
+
limit: int,
|
|
51
|
+
sort: str,
|
|
52
|
+
direction: str,
|
|
53
|
+
token: Optional[str],
|
|
54
|
+
) -> list[dict]:
|
|
55
|
+
credential_path = credential_path_from_repo(f"{owner}/_")
|
|
56
|
+
client = get_client(token, require_token=True, credential_path=credential_path)
|
|
57
|
+
return get_repo_list(client, owner, limit, sort, direction)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def create_repo(
|
|
61
|
+
owner: str,
|
|
62
|
+
name: str,
|
|
63
|
+
private: bool,
|
|
64
|
+
description: Optional[str],
|
|
65
|
+
if_exists: str,
|
|
66
|
+
token: Optional[str],
|
|
67
|
+
) -> dict:
|
|
68
|
+
credential_path = credential_path_from_repo(f"{owner}/{name}")
|
|
69
|
+
client = get_client(token, require_token=True, credential_path=credential_path)
|
|
70
|
+
try:
|
|
71
|
+
return post_repo_create(client, owner, name, private, description, if_exists)
|
|
72
|
+
except ValueError as exc:
|
|
73
|
+
raise click.ClickException(str(exc)) from exc
|
|
74
|
+
|
|
75
|
+
|
|
46
76
|
def create_pr(
|
|
47
77
|
repo: Optional[str],
|
|
48
78
|
base: str,
|
|
@@ -1,10 +1,61 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from datetime import datetime, timezone
|
|
3
4
|
from typing import Optional
|
|
4
5
|
|
|
5
6
|
from chatgh.github.api import github_api_get_json, github_api_get_text
|
|
6
7
|
|
|
7
8
|
|
|
9
|
+
def get_repo_list(
|
|
10
|
+
client,
|
|
11
|
+
owner: str,
|
|
12
|
+
limit: int,
|
|
13
|
+
sort: str,
|
|
14
|
+
direction: str,
|
|
15
|
+
) -> list[dict]:
|
|
16
|
+
owner_obj = _get_owner(client, owner)
|
|
17
|
+
items: list[dict] = []
|
|
18
|
+
for repo in owner_obj.get_repos():
|
|
19
|
+
items.append(_build_repo_payload(repo))
|
|
20
|
+
items.sort(
|
|
21
|
+
key=lambda item: _repo_sort_key(item, sort),
|
|
22
|
+
reverse=(direction == "desc"),
|
|
23
|
+
)
|
|
24
|
+
return items[:limit]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def post_repo_create(
|
|
28
|
+
client,
|
|
29
|
+
owner: str,
|
|
30
|
+
name: str,
|
|
31
|
+
private: bool,
|
|
32
|
+
description: Optional[str],
|
|
33
|
+
if_exists: str,
|
|
34
|
+
) -> dict:
|
|
35
|
+
full_name = f"{owner}/{name}"
|
|
36
|
+
try:
|
|
37
|
+
existing = client.get_repo(full_name)
|
|
38
|
+
except Exception as exc:
|
|
39
|
+
if getattr(exc, "status", None) != 404:
|
|
40
|
+
raise
|
|
41
|
+
else:
|
|
42
|
+
if if_exists == "use":
|
|
43
|
+
payload = _build_repo_payload(existing)
|
|
44
|
+
payload["created"] = False
|
|
45
|
+
return payload
|
|
46
|
+
raise ValueError(f"Repository already exists: {full_name}")
|
|
47
|
+
|
|
48
|
+
owner_obj = _get_owner(client, owner)
|
|
49
|
+
repo = owner_obj.create_repo(
|
|
50
|
+
name=name,
|
|
51
|
+
private=private,
|
|
52
|
+
description=description or "",
|
|
53
|
+
)
|
|
54
|
+
payload = _build_repo_payload(repo)
|
|
55
|
+
payload["created"] = True
|
|
56
|
+
return payload
|
|
57
|
+
|
|
58
|
+
|
|
8
59
|
def get_pr_list(client, repo: str, state: str, limit: int) -> list[dict]:
|
|
9
60
|
repo_obj = client.get_repo(repo)
|
|
10
61
|
items: list[dict] = []
|
|
@@ -21,6 +72,70 @@ def get_pr_view(client, repo: str, number: int) -> dict:
|
|
|
21
72
|
return _build_pr_view_payload(pr)
|
|
22
73
|
|
|
23
74
|
|
|
75
|
+
def _get_owner(client, owner: str):
|
|
76
|
+
try:
|
|
77
|
+
return client.get_organization(owner)
|
|
78
|
+
except Exception as exc:
|
|
79
|
+
if getattr(exc, "status", None) not in {403, 404}:
|
|
80
|
+
raise
|
|
81
|
+
return client.get_user(owner)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _build_repo_payload(repo) -> dict:
|
|
85
|
+
open_prs = _safe_count(lambda: repo.get_pulls(state="open"))
|
|
86
|
+
open_issues_reported = int(getattr(repo, "open_issues_count", 0) or 0)
|
|
87
|
+
open_issues = max(open_issues_reported - open_prs, 0) if open_prs is not None else None
|
|
88
|
+
return {
|
|
89
|
+
"name": repo.name,
|
|
90
|
+
"full_name": repo.full_name,
|
|
91
|
+
"private": repo.private,
|
|
92
|
+
"visibility": getattr(repo, "visibility", None) or ("private" if repo.private else "public"),
|
|
93
|
+
"stars": int(getattr(repo, "stargazers_count", 0) or 0),
|
|
94
|
+
"forks": int(getattr(repo, "forks_count", 0) or 0),
|
|
95
|
+
"open_prs": open_prs,
|
|
96
|
+
"open_issues": open_issues,
|
|
97
|
+
"open_issues_reported": open_issues_reported,
|
|
98
|
+
"archived": bool(getattr(repo, "archived", False)),
|
|
99
|
+
"fork": bool(getattr(repo, "fork", False)),
|
|
100
|
+
"created_at": _isoformat(getattr(repo, "created_at", None)),
|
|
101
|
+
"updated_at": _isoformat(getattr(repo, "updated_at", None)),
|
|
102
|
+
"pushed_at": _isoformat(getattr(repo, "pushed_at", None)),
|
|
103
|
+
"html_url": repo.html_url,
|
|
104
|
+
"clone_url": getattr(repo, "clone_url", None),
|
|
105
|
+
"ssh_url": getattr(repo, "ssh_url", None),
|
|
106
|
+
"default_branch": getattr(repo, "default_branch", None),
|
|
107
|
+
"description": getattr(repo, "description", None),
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _safe_count(factory) -> Optional[int]:
|
|
112
|
+
try:
|
|
113
|
+
return int(factory().totalCount)
|
|
114
|
+
except Exception:
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _repo_sort_key(item: dict, sort: str):
|
|
119
|
+
if sort in {"updated", "created", "pushed"}:
|
|
120
|
+
return _parse_time(item.get(f"{sort}_at"))
|
|
121
|
+
if sort == "stars":
|
|
122
|
+
return int(item.get("stars") or 0)
|
|
123
|
+
if sort == "open-prs":
|
|
124
|
+
return int(item.get("open_prs") or 0)
|
|
125
|
+
if sort == "open-issues":
|
|
126
|
+
return int(item.get("open_issues") or 0)
|
|
127
|
+
return str(item.get("name") or item.get("full_name") or "").lower()
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _parse_time(value: Optional[str]) -> datetime:
|
|
131
|
+
if not value:
|
|
132
|
+
return datetime.min.replace(tzinfo=timezone.utc)
|
|
133
|
+
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
134
|
+
if parsed.tzinfo is None:
|
|
135
|
+
return parsed.replace(tzinfo=timezone.utc)
|
|
136
|
+
return parsed
|
|
137
|
+
|
|
138
|
+
|
|
24
139
|
def get_pr_checks(
|
|
25
140
|
client,
|
|
26
141
|
repo: str,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chatgh
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: GitHub CLI and Python helpers for ChatArch
|
|
5
5
|
Author-email: ChatArch <1073853456@qq.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -43,7 +43,7 @@ pip install -e ".[dev]"
|
|
|
43
43
|
默认行为:
|
|
44
44
|
|
|
45
45
|
- `repo`:优先使用显式 `--repo owner/repo`,未传时从当前 git remote 推断。
|
|
46
|
-
- `token`:优先使用显式 `--token
|
|
46
|
+
- `token`:优先使用显式 `--token`,其次读取当前仓库 `.git/config` 中 repo-local HTTPS auth header,再回退 typed env 里的 `GITHUB_ACCESS_TOKEN`。
|
|
47
47
|
- 输出:默认是人类可读格式;支持 `--json-output` 的命令会输出稳定 JSON,适合脚本消费。
|
|
48
48
|
|
|
49
49
|
### Token 来源
|
|
@@ -51,7 +51,7 @@ pip install -e ".[dev]"
|
|
|
51
51
|
Token 解析顺序稳定为:
|
|
52
52
|
|
|
53
53
|
1. 显式 `--token`。
|
|
54
|
-
2. repo-
|
|
54
|
+
2. 当前仓库 `.git/config` 中的 repo-local HTTPS auth header,路径为规范化后的 `https://github.com/owner/repo.git`。
|
|
55
55
|
3. typed env 中的 `GITHUB_ACCESS_TOKEN`。
|
|
56
56
|
|
|
57
57
|
可以用 `chatenv` 查看或配置 typed env:
|
|
@@ -72,13 +72,14 @@ chatenv cat -t gh
|
|
|
72
72
|
- `git@github.com:octocat/Hello-World.git`
|
|
73
73
|
- `ssh://git@github.com/octocat/Hello-World.git`
|
|
74
74
|
|
|
75
|
-
写入 repo-
|
|
75
|
+
写入 repo-local HTTPS auth header 时,path 会规范化为 `https://github.com/octocat/Hello-World.git`。
|
|
76
76
|
|
|
77
77
|
## CLI 入口
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
80
|
chatgh --help
|
|
81
81
|
chatgh pr --help
|
|
82
|
+
chatgh repo --help
|
|
82
83
|
chatgh run --help
|
|
83
84
|
chatgh repo-perms --help
|
|
84
85
|
chatgh set-token --help
|
|
@@ -89,7 +90,9 @@ chatgh set-token --help
|
|
|
89
90
|
- `chatgh pr list`:generated-layer PR 列表。
|
|
90
91
|
- `chatgh pr view NUMBER`:generated-layer PR 详情。
|
|
91
92
|
- `chatgh pr checks NUMBER`:generated-layer PR head commit check runs。
|
|
92
|
-
- `chatgh pr
|
|
93
|
+
- 当前公开 `chatgh pr` 命令面只包含 `list/view/checks`;PR 创建、评论、合并和编辑流程仍由内部 helper 支持,后续作为独立 CLI 命令恢复前不在帮助与文档中承诺。
|
|
94
|
+
- `chatgh repo list`:列出 user/org 下的仓库;默认 table,支持 `--json-output`、`--limit`、`--sort updated|created|pushed|name|stars|open-prs|open-issues`、`--direction asc|desc`,字段包含 visibility、stars、open PRs、open issues、created/updated time 等。
|
|
95
|
+
- `chatgh repo create`:创建仓库;默认 private,可用 `--public` 显式创建公开仓库。
|
|
93
96
|
- `chatgh run view`:查看 workflow run 和 jobs。
|
|
94
97
|
- `chatgh run logs`:查看 job 日志,支持 tail 和落盘。
|
|
95
98
|
- `chatgh repo-perms`:查看 token 权限和派生 capabilities。
|
|
@@ -99,26 +102,7 @@ chatgh set-token --help
|
|
|
99
102
|
|
|
100
103
|
### 创建 PR
|
|
101
104
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
cat > /tmp/pr_body.md <<'EOF'
|
|
106
|
-
## Summary
|
|
107
|
-
- migrate GitHub helpers to chatgh
|
|
108
|
-
|
|
109
|
-
## Testing
|
|
110
|
-
- python -m pytest -q
|
|
111
|
-
EOF
|
|
112
|
-
|
|
113
|
-
chatgh pr create \
|
|
114
|
-
--repo octocat/Hello-World \
|
|
115
|
-
--base main \
|
|
116
|
-
--head feature-branch \
|
|
117
|
-
--title "feat: migrate github helpers" \
|
|
118
|
-
--body-file /tmp/pr_body.md
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
缺少 `base` / `head` / `title` 且终端可交互时,命令会自动补问;显式 `-I` 会禁用补问。
|
|
105
|
+
当前版本未公开 `chatgh pr create` CLI 命令。需要创建 PR 时,请使用项目既有 GitHub 流程或直接调用 GitHub API;等写操作命令恢复为稳定 CLI surface 后再补回示例。
|
|
122
106
|
|
|
123
107
|
### 查看 PR
|
|
124
108
|
|
|
@@ -135,12 +119,10 @@ chatgh pr view 123 --repo octocat/Hello-World --json-output
|
|
|
135
119
|
- `mergeable` 和 `mergeable_state`。
|
|
136
120
|
- created/updated/merged timestamps。
|
|
137
121
|
|
|
138
|
-
###
|
|
122
|
+
### 查看 CI
|
|
139
123
|
|
|
140
124
|
```bash
|
|
141
125
|
chatgh pr checks 123 --repo octocat/Hello-World
|
|
142
|
-
chatgh pr checks --repo octocat/Hello-World --number 123 --wait
|
|
143
|
-
chatgh pr checks --repo octocat/Hello-World --number 123 --wait --interval 10 --timeout 600
|
|
144
126
|
chatgh pr checks 123 --repo octocat/Hello-World --json-output
|
|
145
127
|
```
|
|
146
128
|
|
|
@@ -150,11 +132,7 @@ chatgh pr checks 123 --repo octocat/Hello-World --json-output
|
|
|
150
132
|
- check runs
|
|
151
133
|
- workflow runs
|
|
152
134
|
|
|
153
|
-
`--wait`
|
|
154
|
-
|
|
155
|
-
- 默认不设超时,会一直等。
|
|
156
|
-
- `--interval <seconds>` 控制轮询间隔。
|
|
157
|
-
- 只有显式传 `--timeout <seconds>` 时才会超时报错。
|
|
135
|
+
当前公开 CLI 不提供 `--wait` / `--interval` / `--timeout` 参数;需要等待终态时,在外层流程中轮询 `chatgh pr checks`。
|
|
158
136
|
|
|
159
137
|
如果 GitHub token 无权读取 check-runs API,命令会把 check-runs 错误放进 payload,同时仍尽量展示 combined status 和 workflow runs。
|
|
160
138
|
|
|
@@ -173,25 +151,7 @@ chatgh run logs --repo octocat/Hello-World --job-id 987654321 --tail 200 --outpu
|
|
|
173
151
|
|
|
174
152
|
### 评论、合并和编辑 PR
|
|
175
153
|
|
|
176
|
-
|
|
177
|
-
chatgh pr comment --repo octocat/Hello-World --number 123 --body "Looks good"
|
|
178
|
-
|
|
179
|
-
chatgh pr merge --repo octocat/Hello-World --number 123 --method squash
|
|
180
|
-
chatgh pr merge --repo octocat/Hello-World --number 123 --method squash --check
|
|
181
|
-
|
|
182
|
-
chatgh pr edit --repo octocat/Hello-World --number 123 --title "New title"
|
|
183
|
-
chatgh pr edit --repo octocat/Hello-World --number 123 --body-file /tmp/pr_body.md
|
|
184
|
-
chatgh pr edit --repo octocat/Hello-World --number 123 --state closed
|
|
185
|
-
chatgh pr edit --repo octocat/Hello-World --number 123 --base main
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
`pr merge --check` 会在合并前拒绝以下情况:
|
|
189
|
-
|
|
190
|
-
- PR 当前 `mergeable=False`。
|
|
191
|
-
- `mergeable_state` 为 `dirty`、`blocked`、`behind`、`draft` 或 `unknown`。
|
|
192
|
-
- combined status、check runs 或 workflow runs 存在失败、取消或未完成项。
|
|
193
|
-
|
|
194
|
-
不带 `--check` 时,命令保持直接调用 GitHub merge API 的行为。
|
|
154
|
+
当前版本未公开 `chatgh pr comment/merge/edit` CLI 命令。相关业务函数已保留在 `chatgh.github.commands` 中,后续恢复为稳定 CLI surface 前,建议通过项目既有流程或 GitHub API 执行写操作。
|
|
195
155
|
|
|
196
156
|
### 配置和检查 token
|
|
197
157
|
|
|
@@ -209,7 +169,14 @@ chatgh set-token --token "$GITHUB_ACCESS_TOKEN" --save-env
|
|
|
209
169
|
- GitHub 返回的 `permissions`。
|
|
210
170
|
- 派生 capabilities:`can_read_pr`、`can_comment_pr`、`can_merge_pr`、`can_view_checks`、`can_view_actions`。
|
|
211
171
|
|
|
212
|
-
`set-token` 只在当前目录能识别 GitHub remote
|
|
172
|
+
`set-token` 只在当前目录能识别 GitHub remote 时生效。默认只写入当前仓库自己的 `.git/config`:
|
|
173
|
+
|
|
174
|
+
```ini
|
|
175
|
+
[http "https://github.com/octocat/Hello-World.git"]
|
|
176
|
+
extraHeader = Authorization: Basic <base64(x-access-token:TOKEN)>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
不要把 token 写进 remote URL,也不要把 raw `extraHeader` 输出到日志。传 `--save-env` 时会同步写入 typed env 的 `GITHUB_ACCESS_TOKEN`。
|
|
213
180
|
|
|
214
181
|
## 交互模式
|
|
215
182
|
|
|
@@ -233,7 +200,7 @@ git fetch origin main
|
|
|
233
200
|
|
|
234
201
|
- `chatgh pr view` / `chatgh pr checks` 显示 `mergeable` 不是 `False`,`mergeable_state` 不是 `dirty`。
|
|
235
202
|
- 本地基于最新 base 做过 merge 或 rebase 演练,并在该结果上跑过最相关测试。
|
|
236
|
-
- CI
|
|
203
|
+
- CI 需要终态时,在外层流程中轮询 `chatgh pr checks`,不要只看一次快照。
|
|
237
204
|
|
|
238
205
|
## Python API
|
|
239
206
|
|
|
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
|
|
File without changes
|
|
File without changes
|