Compare commits
No commits in common. "master" and "master" have entirely different histories.
|
|
@ -1,41 +0,0 @@
|
||||||
version: 2
|
|
||||||
name: gitlink_cli_ci
|
|
||||||
description: "gitlink-cli 代码提交时自动执行 CI 检查(构建、测试、格式化)"
|
|
||||||
trigger:
|
|
||||||
webhook: gitlink@1.0.0
|
|
||||||
event:
|
|
||||||
- ref: push
|
|
||||||
ruleset-operator: AND
|
|
||||||
global:
|
|
||||||
concurrent: 1
|
|
||||||
workflow:
|
|
||||||
- ref: start
|
|
||||||
name: 开始
|
|
||||||
task: start
|
|
||||||
- ref: git_clone_0
|
|
||||||
name: 拉取代码
|
|
||||||
task: git_clone@1.2.9
|
|
||||||
input:
|
|
||||||
remote_url: '"https://gitlink.org.cn/jiangtx/gitlink-cli.git"'
|
|
||||||
ref: '"refs/heads/wyx_branch"'
|
|
||||||
commit_id: '""'
|
|
||||||
depth: 1
|
|
||||||
needs:
|
|
||||||
- start
|
|
||||||
- ref: ssh_cmd_0
|
|
||||||
name: CI 检查
|
|
||||||
task: ssh_cmd@1.1.1
|
|
||||||
input:
|
|
||||||
ssh_pass: ((gitlink_cli_ci.ssh_pass))
|
|
||||||
ssh_ip: '"121.41.212.97"'
|
|
||||||
ssh_port: '"22"'
|
|
||||||
ssh_user: '"root"'
|
|
||||||
ssh_cmd: >-
|
|
||||||
"cd /root && rm -rf gitlink-cli && git clone --depth=1 -b wyx_branch https://gitlink.org.cn/jiangtx/gitlink-cli.git && cd gitlink-cli && export PATH=$PATH:/usr/local/go/bin && export GOPROXY=https://goproxy.cn,direct && go version && go build ./... && go vet ./... && go test -race ./... && output=$(gofmt -s -l .) && if [ -n \"$output\" ]; then echo '格式化检查失败:' && echo \"$output\" && exit 1; fi && echo '所有 CI 检查通过'"
|
|
||||||
needs:
|
|
||||||
- git_clone_0
|
|
||||||
- ref: end
|
|
||||||
name: 结束
|
|
||||||
task: end
|
|
||||||
needs:
|
|
||||||
- ssh_cmd_0
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
version: 2
|
|
||||||
name: 自动部署
|
|
||||||
description: ""
|
|
||||||
global:
|
|
||||||
concurrent: 1
|
|
||||||
trigger:
|
|
||||||
webhook: gitlink@1.0.0
|
|
||||||
event:
|
|
||||||
- ref: push
|
|
||||||
ruleset-operator: AND
|
|
||||||
workflow:
|
|
||||||
- ref: start
|
|
||||||
name: 开始
|
|
||||||
task: start
|
|
||||||
- ref: git_clone_0
|
|
||||||
name: git clone
|
|
||||||
task: git_clone@1.2.9
|
|
||||||
input:
|
|
||||||
username: ((gitlink_cli.ylly_git_user))
|
|
||||||
password: ((gitlink_cli.ylly_git_pass))
|
|
||||||
remote_url: '"https://gitlink.org.cn/ylly/gitlink-cli.git"'
|
|
||||||
ref: '"refs/heads/master"'
|
|
||||||
commit_id: '""'
|
|
||||||
depth: 1
|
|
||||||
needs:
|
|
||||||
- start
|
|
||||||
- ref: ssh_cmd_0
|
|
||||||
name: ssh执行命令
|
|
||||||
task: ssh_cmd@1.1.1
|
|
||||||
input:
|
|
||||||
ssh_ip: '"8.136.61.14"'
|
|
||||||
ssh_port: '"22"'
|
|
||||||
ssh_user: '"root"'
|
|
||||||
ssh_private_key: ((gitlink_cli.ecs_ssh_key))
|
|
||||||
ssh_cmd: '"cd /opt/gitlink-cli && git pull && go build -o /usr/local/bin/gitlink-cli . && gitlink-cli version"'
|
|
||||||
needs:
|
|
||||||
- git_clone_0
|
|
||||||
- ref: end
|
|
||||||
name: 结束
|
|
||||||
task: end
|
|
||||||
needs:
|
|
||||||
- ssh_cmd_0
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
.gitattributes text eol=lf
|
|
||||||
internal/i18n/locales/*.json text eol=lf
|
|
||||||
|
|
@ -15,16 +15,11 @@ jobs:
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.26.1'
|
go-version: '1.22'
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: go build ./...
|
run: go build ./...
|
||||||
|
|
||||||
- name: Validate i18n and skill metadata
|
|
||||||
run: |
|
|
||||||
go run ./internal/i18n/cmd/check
|
|
||||||
go run ./internal/skillmeta/cmd/check
|
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: make lint
|
run: make lint
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ jobs:
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: '1.22'
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,5 @@ jobs:
|
||||||
- name: Scan i18n key references
|
- name: Scan i18n key references
|
||||||
run: go run ./internal/i18n/cmd/check --scan-code
|
run: go run ./internal/i18n/cmd/check --scan-code
|
||||||
|
|
||||||
- name: Test Feishu shortcuts
|
|
||||||
run: go test ./shortcuts/feishu
|
|
||||||
|
|
||||||
- name: Test workflow shortcuts
|
|
||||||
run: go test ./shortcuts/workflow
|
|
||||||
|
|
||||||
- name: Run Go tests
|
- name: Run Go tests
|
||||||
run: go test ./...
|
run: go test ./...
|
||||||
|
|
||||||
- name: Vet
|
|
||||||
run: go vet ./...
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
|
docs/
|
||||||
gitlink-cli.exe
|
doc/
|
||||||
/gitlink-cli
|
# Python & demo artifacts
|
||||||
.local/*
|
__pycache__/
|
||||||
!.local/
|
*.pyc
|
||||||
!.local/feishu-gitlink.env.example.ps1
|
demo/bin/
|
||||||
*.local.ps1
|
data/
|
||||||
*.secret.*
|
|
||||||
reports/feishu-real-smoke-terminal.log
|
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
# Stable Feishu webhook
|
|
||||||
$env:FEISHU_WEBHOOK_URL=""
|
|
||||||
$env:FEISHU_WEBHOOK_SECRET=""
|
|
||||||
|
|
||||||
# Feishu Open Platform
|
|
||||||
$env:FEISHU_APP_ID=""
|
|
||||||
$env:FEISHU_APP_SECRET=""
|
|
||||||
|
|
||||||
# Feishu DocX / Wiki
|
|
||||||
$env:FEISHU_WIKI_URL=""
|
|
||||||
$env:FEISHU_WIKI_NODE_TOKEN=""
|
|
||||||
$env:FEISHU_FOLDER_TOKEN=""
|
|
||||||
$env:FEISHU_DOCUMENT_ID=""
|
|
||||||
|
|
||||||
# Feishu Base / Bitable
|
|
||||||
$env:FEISHU_BASE_APP_TOKEN=""
|
|
||||||
$env:FEISHU_REPORT_TABLE_ID=""
|
|
||||||
$env:FEISHU_ISSUE_TABLE_ID=""
|
|
||||||
$env:FEISHU_PR_TABLE_ID=""
|
|
||||||
$env:FEISHU_CONTRIBUTOR_TABLE_ID=""
|
|
||||||
$env:FEISHU_TASK_TABLE_ID=""
|
|
||||||
|
|
||||||
# Feishu Task
|
|
||||||
$env:FEISHU_TASK_PROJECT_ID=""
|
|
||||||
$env:FEISHU_TASK_SECTION_ID=""
|
|
||||||
|
|
||||||
# GitLink real test input
|
|
||||||
$env:GITLINK_OWNER=""
|
|
||||||
$env:GITLINK_REPO=""
|
|
||||||
$env:GITLINK_TEST_PR_IDS=""
|
|
||||||
$env:GITLINK_TOKEN=""
|
|
||||||
139
README.md
139
README.md
|
|
@ -5,7 +5,7 @@
|
||||||
[](https://golang.org)
|
[](https://golang.org)
|
||||||
[](https://www.npmjs.com/package/@gitlink-ai/cli)
|
[](https://www.npmjs.com/package/@gitlink-ai/cli)
|
||||||
|
|
||||||
The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans and AI Agents. Supports **macOS, Linux, and Windows**. Covers repository management, wiki pages, issue tracking, pull requests, webhooks, member collaboration, CI/CD, and AI-powered workflows, with 40+ commands and AI Agent [Skills](./skills/).
|
The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans and AI Agents. Supports **macOS, Linux, and Windows**. Covers repository management, issue tracking, pull requests, webhooks, member collaboration, CI/CD, and AI-powered workflows, with 40+ commands and AI Agent [Skills](./skills/README.md).
|
||||||
|
|
||||||
**[中文文档](./README.zh-CN.md)**
|
**[中文文档](./README.zh-CN.md)**
|
||||||
|
|
||||||
|
|
@ -78,12 +78,20 @@ The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans
|
||||||
<a href="https://www.gitlink.org.cn/jiangtx" title="jiangtx"><img src="https://www.gitlink.org.cn/system/lets/letter_avatars/2/J/67_157_94/120.png" width="40" height="40" alt="jiangtx" style="border-radius: 50%;"></a>
|
<a href="https://www.gitlink.org.cn/jiangtx" title="jiangtx"><img src="https://www.gitlink.org.cn/system/lets/letter_avatars/2/J/67_157_94/120.png" width="40" height="40" alt="jiangtx" style="border-radius: 50%;"></a>
|
||||||
<br><sub><a href="https://www.gitlink.org.cn/jiangtx">jiangtx</a></sub>
|
<br><sub><a href="https://www.gitlink.org.cn/jiangtx">jiangtx</a></sub>
|
||||||
</div>
|
</div>
|
||||||
|
<div align="center">
|
||||||
|
<a href="https://www.gitlink.org.cn/luwanzhou" title="luwanzhou"><img src="https://www.gitlink.org.cn/system/lets/letter_avatars/2/L/165_135_246/120.png" width="40" height="40" alt="luwanzhou" style="border-radius: 50%;"></a>
|
||||||
|
<br><sub><a href="https://www.gitlink.org.cn/luwanzhou">luwanzhou</a></sub>
|
||||||
|
</div>
|
||||||
|
<div align="center">
|
||||||
|
<a href="https://www.gitlink.org.cn/whale_hihihi" title="whale_hihihi"><img src="https://www.gitlink.org.cn/images/avatars/User/137722?t=1778575729" width="40" height="40" alt="whale_hihihi" style="border-radius: 50%;"></a>
|
||||||
|
<br><sub><a href="https://www.gitlink.org.cn/whale_hihihi">whale_hihihi</a></sub>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Why gitlink-cli?
|
## Why gitlink-cli?
|
||||||
|
|
||||||
- **Agent-Native Design** — Structured [Skills](./skills/) out of the box, compatible with Claude Code, OpenClaw, and other AI platforms — Agents can operate GitLink with zero extra setup
|
- **Agent-Native Design** — Structured [Skills](./skills/README.md) out of the box, compatible with Claude Code, OpenClaw, and other AI platforms — Agents can operate GitLink with zero extra setup
|
||||||
- **Wide Coverage** — Repository, Wiki, Issue, PR, Webhook, Member, Branch, Release, CI, Pipeline, Org, Search, and User workflows are covered by high-level commands
|
- **Wide Coverage** — Repository, Issue, PR, Webhook, Member, Branch, Release, CI, Pipeline, Org, Search, and User workflows are covered by high-level commands
|
||||||
- **AI-Friendly & Optimized** — Every command is tested with real Agents, featuring concise parameters, smart defaults, and structured output
|
- **AI-Friendly & Optimized** — Every command is tested with real Agents, featuring concise parameters, smart defaults, and structured output
|
||||||
- **Cross-Platform** — Runs on macOS, Linux, and Windows (x64/arm64), install via `npm install -g @gitlink-ai/cli` in one command, binary auto-downloaded
|
- **Cross-Platform** — Runs on macOS, Linux, and Windows (x64/arm64), install via `npm install -g @gitlink-ai/cli` in one command, binary auto-downloaded
|
||||||
- **Open Source, Zero Barriers** — MulanPSL-2.0 license, ready to use, just `npm install`
|
- **Open Source, Zero Barriers** — MulanPSL-2.0 license, ready to use, just `npm install`
|
||||||
|
|
@ -96,8 +104,7 @@ The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans
|
||||||
| Category | Capabilities |
|
| Category | Capabilities |
|
||||||
|----------|-------------|
|
|----------|-------------|
|
||||||
| 📦 Repo | List, create, fork, delete repositories, view repo info, insights, and interactions |
|
| 📦 Repo | List, create, fork, delete repositories, view repo info, insights, and interactions |
|
||||||
| 📚 Wiki | List, view, create, update, and delete wiki pages |
|
| 🐛 Issue | Create, update, close, batch close/update/delete, comment on issues |
|
||||||
| 🐛 Issue | Create, update, close, batch close, comment on issues |
|
|
||||||
| 🔖 Label | Create, list, update, delete issue labels |
|
| 🔖 Label | Create, list, update, delete issue labels |
|
||||||
| 🔀 PR | Create, merge, review pull requests, view changed files |
|
| 🔀 PR | Create, merge, review pull requests, view changed files |
|
||||||
| 👥 Member | List, add, remove repository members, change roles, create and accept invite links |
|
| 👥 Member | List, add, remove repository members, change roles, create and accept invite links |
|
||||||
|
|
@ -107,8 +114,11 @@ The official [GitLink](https://www.gitlink.org.cn) CLI tool — built for humans
|
||||||
| 🔧 CI | View builds, logs, CI/CD operations |
|
| 🔧 CI | View builds, logs, CI/CD operations |
|
||||||
| ⚙️ Pipeline | Run, inspect, enable, disable, delete pipeline workflows and logs |
|
| ⚙️ Pipeline | Run, inspect, enable, disable, delete pipeline workflows and logs |
|
||||||
| 🔔 Webhook | Manage repo webhooks and test deliveries |
|
| 🔔 Webhook | Manage repo webhooks and test deliveries |
|
||||||
|
| 📖 Wiki | List, view, create, update, and delete wiki pages |
|
||||||
| 🔍 Search | Search repositories, users |
|
| 🔍 Search | Search repositories, users |
|
||||||
|
| 📊 Dataset | Query research datasets by project |
|
||||||
| 👤 User | View user profiles and info |
|
| 👤 User | View user profiles and info |
|
||||||
|
| 📊 Profile | User ability, role, major, activity, and contribution statistics |
|
||||||
| 📋 PM | Sprint management, kanban boards, weekly reports |
|
| 📋 PM | Sprint management, kanban boards, weekly reports |
|
||||||
| 🤖 Workflow | AI-powered issue triage, PR review, release notes |
|
| 🤖 Workflow | AI-powered issue triage, PR review, release notes |
|
||||||
|
|
||||||
|
|
@ -247,27 +257,6 @@ gitlink-cli repo +create -n my-project -d "Project description"
|
||||||
gitlink-cli repo +fork --owner Gitlink --repo forgeplus
|
gitlink-cli repo +fork --owner Gitlink --repo forgeplus
|
||||||
```
|
```
|
||||||
|
|
||||||
### Wiki Management
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# List wiki pages
|
|
||||||
gitlink-cli wiki +list --owner Gitlink --repo forgeplus
|
|
||||||
|
|
||||||
# View a wiki page
|
|
||||||
gitlink-cli wiki +view --owner Gitlink --repo forgeplus --page Home
|
|
||||||
|
|
||||||
# Create a wiki page from inline content
|
|
||||||
gitlink-cli wiki +create --owner Gitlink --repo forgeplus \
|
|
||||||
--page Home --title Home --content "Welcome to the project wiki"
|
|
||||||
|
|
||||||
# Update a wiki page from a Markdown file
|
|
||||||
gitlink-cli wiki +update --owner Gitlink --repo forgeplus \
|
|
||||||
--page Home --file docs/wiki-home.md --message "Update Home"
|
|
||||||
|
|
||||||
# Delete a wiki page
|
|
||||||
gitlink-cli wiki +delete --owner Gitlink --repo forgeplus --page Home
|
|
||||||
```
|
|
||||||
|
|
||||||
### Webhook Management
|
### Webhook Management
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -285,6 +274,28 @@ gitlink-cli webhook +test --owner Gitlink --repo forgeplus --id 68
|
||||||
gitlink-cli webhook +tasks --owner Gitlink --repo forgeplus --id 68
|
gitlink-cli webhook +tasks --owner Gitlink --repo forgeplus --id 68
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Wiki Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List wiki pages (table of contents)
|
||||||
|
gitlink-cli wiki +list --owner Gitlink --repo forgeplus --project-id 12345
|
||||||
|
|
||||||
|
# View a wiki page by page name
|
||||||
|
gitlink-cli wiki +view --owner Gitlink --repo forgeplus --project-id 12345 -n home
|
||||||
|
|
||||||
|
# Create a wiki page
|
||||||
|
gitlink-cli wiki +create --owner Gitlink --repo forgeplus --project-id 12345 \
|
||||||
|
-n getting-started -t "Getting Started" -c "# Getting Started Guide"
|
||||||
|
|
||||||
|
# Update a wiki page title and/or content
|
||||||
|
gitlink-cli wiki +update --owner Gitlink --repo forgeplus --project-id 12345 -n home -t "New Title"
|
||||||
|
gitlink-cli wiki +update --owner Gitlink --repo forgeplus --project-id 12345 -n home -c "# Updated content"
|
||||||
|
gitlink-cli wiki +update --owner Gitlink --repo forgeplus --project-id 12345 -n home -t "New Title" -c "New content"
|
||||||
|
|
||||||
|
# Delete a wiki page
|
||||||
|
gitlink-cli wiki +delete --owner Gitlink --repo forgeplus --project-id 12345 -n old-page
|
||||||
|
```
|
||||||
|
|
||||||
### Member Management
|
### Member Management
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -334,6 +345,14 @@ gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --numbers 123,12
|
||||||
# Batch close issues from a CSV file
|
# Batch close issues from a CSV file
|
||||||
gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --from issues.csv
|
gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --from issues.csv
|
||||||
|
|
||||||
|
# Preview batch metadata update by API issue IDs
|
||||||
|
# Note: --ids uses API issue IDs, not web URL issue numbers.
|
||||||
|
gitlink-cli issue +batch-update --owner Gitlink --repo forgeplus --ids 101,102 --status-id 3 --priority-id 2 --dry-run
|
||||||
|
|
||||||
|
# Destructive batch delete requires both dry-run first and --yes for real execution
|
||||||
|
gitlink-cli issue +batch-delete --owner Gitlink --repo forgeplus --ids 101,102 --dry-run
|
||||||
|
gitlink-cli issue +batch-delete --owner Gitlink --repo forgeplus --ids 101,102 --yes
|
||||||
|
|
||||||
# Add a comment
|
# Add a comment
|
||||||
gitlink-cli issue +comment --owner Gitlink --repo forgeplus -i 123 -b "Fixed"
|
gitlink-cli issue +comment --owner Gitlink --repo forgeplus -i 123 -b "Fixed"
|
||||||
|
|
||||||
|
|
@ -489,6 +508,16 @@ gitlink-cli pipeline +disable --owner Gitlink --repo forgeplus --id 7 --workflow
|
||||||
gitlink-cli pipeline +delete --owner Gitlink --repo forgeplus --id 7 --dry-run
|
gitlink-cli pipeline +delete --owner Gitlink --repo forgeplus --id 7 --dry-run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Ignore File Templates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all available .gitignore templates
|
||||||
|
gitlink-cli ignore +list
|
||||||
|
|
||||||
|
# Filter templates by name
|
||||||
|
gitlink-cli ignore +list --name Go
|
||||||
|
```
|
||||||
|
|
||||||
### Search
|
### Search
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -499,6 +528,29 @@ gitlink-cli search +repos -k "machine learning"
|
||||||
gitlink-cli search +users -k "zhangsan"
|
gitlink-cli search +users -k "zhangsan"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### User Profile
|
||||||
|
|
||||||
|
`profile` surfaces GitLink's native user statistics (ability, role, major, activity,
|
||||||
|
contribution). When `--user` is omitted it defaults to the authenticated user.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Development ability scores + language breakdown
|
||||||
|
gitlink-cli profile +ability --user zhangsan
|
||||||
|
|
||||||
|
# Role positioning / major (discipline) categories
|
||||||
|
gitlink-cli profile +role --user zhangsan
|
||||||
|
gitlink-cli profile +major --user zhangsan
|
||||||
|
|
||||||
|
# Ability within a time window (Unix timestamps)
|
||||||
|
gitlink-cli profile +ability --user zhangsan --start-time 1704067200 --end-time 1735689600
|
||||||
|
|
||||||
|
# Recent activity (issues / PRs / commits per day) for the current user
|
||||||
|
gitlink-cli profile +activity
|
||||||
|
|
||||||
|
# Contribution heatmap for a given year
|
||||||
|
gitlink-cli profile +contribution --user zhangsan --year 2025
|
||||||
|
```
|
||||||
|
|
||||||
### Workflow Agent Commands
|
### Workflow Agent Commands
|
||||||
|
|
||||||
`workflow` provides rule-based repository analysis for maintainers and AI Agents. It currently supports:
|
`workflow` provides rule-based repository analysis for maintainers and AI Agents. It currently supports:
|
||||||
|
|
@ -598,6 +650,32 @@ Safety:
|
||||||
- `workflow +pr-summary` does not comment, approve, reject, or merge pull requests.
|
- `workflow +pr-summary` does not comment, approve, reject, or merge pull requests.
|
||||||
- `workflow +repo-report` aggregates health, issue triage, and PR review summary signals without remote writes.
|
- `workflow +repo-report` aggregates health, issue triage, and PR review summary signals without remote writes.
|
||||||
|
|
||||||
|
### Dataset
|
||||||
|
|
||||||
|
`dataset` manages and queries GitLink research datasets (title, description,
|
||||||
|
paper content, license, owning project).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List datasets for one or more projects (by numeric project ID)
|
||||||
|
gitlink-cli dataset +list --ids 5988
|
||||||
|
|
||||||
|
# View a repository's dataset and attachments
|
||||||
|
gitlink-cli dataset +view --owner Gitlink --repo forgeplus
|
||||||
|
|
||||||
|
# Create / update a repository's dataset (preview first with --dry-run)
|
||||||
|
gitlink-cli dataset +create --owner me --repo proj -t "My dataset" -d "..." --license-id 359 --dry-run
|
||||||
|
gitlink-cli dataset +update --owner me --repo proj -t "My dataset" -d "updated"
|
||||||
|
|
||||||
|
# Delete a dataset attachment (destructive: preview, then confirm with --yes)
|
||||||
|
gitlink-cli dataset +delete-attachment --owner me --repo proj --uuid <uuid> --dry-run
|
||||||
|
gitlink-cli dataset +delete-attachment --owner me --repo proj --uuid <uuid> --yes
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: `dataset +list` (platform dataset query) is verified on production
|
||||||
|
> gitlink.org.cn. The per-repo `+view`/`+create`/`+update` routes follow the
|
||||||
|
> published OpenAPI contract but are not yet deployed on production (they return
|
||||||
|
> 404 there); they will work once the platform enables them.
|
||||||
|
|
||||||
### Raw API
|
### Raw API
|
||||||
|
|
||||||
For endpoints not covered by shortcuts, use the Raw API directly:
|
For endpoints not covered by shortcuts, use the Raw API directly:
|
||||||
|
|
@ -654,14 +732,13 @@ git push gitlink
|
||||||
|
|
||||||
The `skills/` directory contains Agent Skill files for AI-automated GitLink operations.
|
The `skills/` directory contains Agent Skill files for AI-automated GitLink operations.
|
||||||
|
|
||||||
See [skills/README.md](skills/README.md) for details.
|
See [skills/README.md](./skills/README.md) for details.
|
||||||
|
|
||||||
| Skill | Description |
|
| Skill | Description |
|
||||||
|-------|-------------|
|
|-------|-------------|
|
||||||
| `gitlink-shared` | Authentication, global parameters, safety rules, API notes |
|
| `gitlink-shared` | Authentication, global parameters, safety rules, API notes |
|
||||||
| `gitlink-repo` | Repository operations (create, view, delete, fork, insights, etc.) |
|
| `gitlink-repo` | Repository operations (create, view, delete, fork, insights, etc.) |
|
||||||
| `gitlink-wiki` | Wiki operations (list, view, create, update, delete) |
|
| `gitlink-issue` | Issue operations (create, update, close, batch update/delete, comment, etc.) |
|
||||||
| `gitlink-issue` | Issue operations (create, update, close, comment, etc.) |
|
|
||||||
| `gitlink-pr` | Pull request operations (create, merge, review, etc.) |
|
| `gitlink-pr` | Pull request operations (create, merge, review, etc.) |
|
||||||
| `gitlink-member` | Repository member and invite link management |
|
| `gitlink-member` | Repository member and invite link management |
|
||||||
| `gitlink-branch` | Branch management (create, delete, list, protect, unprotect) |
|
| `gitlink-branch` | Branch management (create, delete, list, protect, unprotect) |
|
||||||
|
|
@ -725,7 +802,7 @@ gitlink-cli/
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- [Skills Guide](skills/README.md) — AI Agent Skills detailed documentation
|
- [Skills Guide](./skills/README.md) — AI Agent Skills detailed documentation
|
||||||
- [Design Document](doc/design.md) — Architecture design and development plan
|
- [Design Document](doc/design.md) — Architecture design and development plan
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
@ -790,7 +867,7 @@ gitlink-cli uses Windows Credential Manager for secure token storage. If Credent
|
||||||
|
|
||||||
### Q: Where can I find the full API reference?
|
### Q: Where can I find the full API reference?
|
||||||
|
|
||||||
See [skills/gitlink-shared/REFERENCE.md](skills/gitlink-shared/REFERENCE.md).
|
See [skills/gitlink-shared/references/api-reference.md](./skills/gitlink-shared/references/api-reference.md).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
||||||
134
README.zh-CN.md
134
README.zh-CN.md
|
|
@ -5,7 +5,7 @@
|
||||||
[](https://golang.org)
|
[](https://golang.org)
|
||||||
[](https://www.npmjs.com/package/@gitlink-ai/cli)
|
[](https://www.npmjs.com/package/@gitlink-ai/cli)
|
||||||
|
|
||||||
[GitLink(确实开源)](https://www.gitlink.org.cn) 官方 CLI 工具 — 为人类和 AI Agent 双重设计。支持 **macOS、Linux、Windows**,覆盖仓库管理、Wiki、Issue 追踪、Pull Request、Webhook、成员协作、CI/CD 和 AI 自动化工作流,包含 40+ 命令和 AI Agent [Skills](./skills/)。
|
[GitLink(确实开源)](https://www.gitlink.org.cn) 官方 CLI 工具 — 为人类和 AI Agent 双重设计。支持 **macOS、Linux、Windows**,覆盖仓库管理、Issue 追踪、Pull Request、Webhook、成员协作、CI/CD 和 AI 自动化工作流,包含 40+ 命令和 AI Agent [Skills](./skills/README.md)。
|
||||||
|
|
||||||
**[English](./README.md)**
|
**[English](./README.md)**
|
||||||
|
|
||||||
|
|
@ -78,12 +78,20 @@
|
||||||
<a href="https://www.gitlink.org.cn/jiangtx" title="jiangtx"><img src="https://www.gitlink.org.cn/system/lets/letter_avatars/2/J/67_157_94/120.png" width="40" height="40" alt="jiangtx" style="border-radius: 50%;"></a>
|
<a href="https://www.gitlink.org.cn/jiangtx" title="jiangtx"><img src="https://www.gitlink.org.cn/system/lets/letter_avatars/2/J/67_157_94/120.png" width="40" height="40" alt="jiangtx" style="border-radius: 50%;"></a>
|
||||||
<br><sub><a href="https://www.gitlink.org.cn/jiangtx">jiangtx</a></sub>
|
<br><sub><a href="https://www.gitlink.org.cn/jiangtx">jiangtx</a></sub>
|
||||||
</div>
|
</div>
|
||||||
|
<div align="center">
|
||||||
|
<a href="https://www.gitlink.org.cn/luwanzhou" title="luwanzhou"><img src="https://www.gitlink.org.cn/system/lets/letter_avatars/2/L/165_135_246/120.png" width="40" height="40" alt="luwanzhou" style="border-radius: 50%;"></a>
|
||||||
|
<br><sub><a href="https://www.gitlink.org.cn/luwanzhou">luwanzhou</a></sub>
|
||||||
|
</div>
|
||||||
|
<div align="center">
|
||||||
|
<a href="https://www.gitlink.org.cn/whale_hihihi" title="whale_hihihi"><img src="https://www.gitlink.org.cn/images/avatars/User/137722?t=1778575729" width="40" height="40" alt="whale_hihihi" style="border-radius: 50%;"></a>
|
||||||
|
<br><sub><a href="https://www.gitlink.org.cn/whale_hihihi">whale_hihihi</a></sub>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## 为什么选择 gitlink-cli?
|
## 为什么选择 gitlink-cli?
|
||||||
|
|
||||||
- **Agent-Native 设计** — 开箱即用结构化 [Skills](./skills/),兼容 Claude Code — Agent 零配置即可操作 GitLink
|
- **Agent-Native 设计** — 开箱即用结构化 [Skills](./skills/README.md),兼容 Claude Code — Agent 零配置即可操作 GitLink
|
||||||
- **广泛覆盖** — 仓库、Wiki、Issue、PR、Webhook、成员、分支、Release、CI、Pipeline、组织、搜索、用户等常用工作流均提供高层命令
|
- **广泛覆盖** — 仓库、Issue、PR、Webhook、成员、分支、Release、CI、Pipeline、组织、搜索、用户等常用工作流均提供高层命令
|
||||||
- **AI 友好 & 优化** — 每条命令都经过真实 Agent 测试,简洁参数、智能默认值、结构化输出
|
- **AI 友好 & 优化** — 每条命令都经过真实 Agent 测试,简洁参数、智能默认值、结构化输出
|
||||||
- **跨平台** — macOS、Linux、Windows (x64/arm64) 全支持,`npm` 一条命令安装
|
- **跨平台** — macOS、Linux、Windows (x64/arm64) 全支持,`npm` 一条命令安装
|
||||||
- **开源零门槛** — 木兰宽松许可证第2版(MulanPSL-2.0),`npm install` 即用
|
- **开源零门槛** — 木兰宽松许可证第2版(MulanPSL-2.0),`npm install` 即用
|
||||||
|
|
@ -96,8 +104,7 @@
|
||||||
| 分类 | 能力 |
|
| 分类 | 能力 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 📦 仓库 | 列出、创建、Fork、删除仓库,查看仓库信息、洞察数据和互动状态 |
|
| 📦 仓库 | 列出、创建、Fork、删除仓库,查看仓库信息、洞察数据和互动状态 |
|
||||||
| 📚 Wiki | 列出、查看、创建、更新、删除 Wiki 页面 |
|
| 🐛 Issue | 创建、更新、关闭、批量关闭/更新/删除、评论 Issue |
|
||||||
| 🐛 Issue | 创建、更新、关闭、批量关闭、评论 Issue |
|
|
||||||
| 🔖 标签 | 创建、列出、更新、删除 Issue 标签 |
|
| 🔖 标签 | 创建、列出、更新、删除 Issue 标签 |
|
||||||
| 🔀 PR | 创建、合并、Review Pull Request,查看变更文件 |
|
| 🔀 PR | 创建、合并、Review Pull Request,查看变更文件 |
|
||||||
| 👥 成员 | 列出、添加、移除仓库成员,调整角色,生成和接受邀请链接 |
|
| 👥 成员 | 列出、添加、移除仓库成员,调整角色,生成和接受邀请链接 |
|
||||||
|
|
@ -106,8 +113,11 @@
|
||||||
| 🏢 组织 | 管理组织、成员、团队 |
|
| 🏢 组织 | 管理组织、成员、团队 |
|
||||||
| 🔧 CI | 查看构建、日志、CI/CD 操作 |
|
| 🔧 CI | 查看构建、日志、CI/CD 操作 |
|
||||||
| ⚙️ Pipeline | 运行、查看、启停、删除流水线工作流并查询日志 |
|
| ⚙️ Pipeline | 运行、查看、启停、删除流水线工作流并查询日志 |
|
||||||
|
| 📖 Wiki | 列出、查看、创建、更新、删除 Wiki 页面 |
|
||||||
| 🔍 搜索 | 搜索仓库、用户 |
|
| 🔍 搜索 | 搜索仓库、用户 |
|
||||||
|
| 📊 数据集 | 按项目查询科研数据集 |
|
||||||
| 👤 用户 | 查看用户资料和信息 |
|
| 👤 用户 | 查看用户资料和信息 |
|
||||||
|
| 📊 画像 | 用户开发能力、角色定位、专业定位、近期活动、贡献热力图统计 |
|
||||||
| 📋 项目管理 | Sprint 管理、看板、周报 |
|
| 📋 项目管理 | Sprint 管理、看板、周报 |
|
||||||
| 🤖 工作流 | AI 驱动的 Issue 分类、PR Review、Release Notes |
|
| 🤖 工作流 | AI 驱动的 Issue 分类、PR Review、Release Notes |
|
||||||
|
|
||||||
|
|
@ -258,27 +268,6 @@ gitlink-cli repo +create -n my-project -d "项目描述"
|
||||||
gitlink-cli repo +fork --owner Gitlink --repo forgeplus
|
gitlink-cli repo +fork --owner Gitlink --repo forgeplus
|
||||||
```
|
```
|
||||||
|
|
||||||
### Wiki 管理
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 列出 Wiki 页面
|
|
||||||
gitlink-cli wiki +list --owner Gitlink --repo forgeplus
|
|
||||||
|
|
||||||
# 查看 Wiki 页面
|
|
||||||
gitlink-cli wiki +view --owner Gitlink --repo forgeplus --page Home
|
|
||||||
|
|
||||||
# 使用命令行内容创建 Wiki 页面
|
|
||||||
gitlink-cli wiki +create --owner Gitlink --repo forgeplus \
|
|
||||||
--page Home --title Home --content "欢迎来到项目 Wiki"
|
|
||||||
|
|
||||||
# 使用 Markdown 文件更新 Wiki 页面
|
|
||||||
gitlink-cli wiki +update --owner Gitlink --repo forgeplus \
|
|
||||||
--page Home --file docs/wiki-home.md --message "更新 Home"
|
|
||||||
|
|
||||||
# 删除 Wiki 页面
|
|
||||||
gitlink-cli wiki +delete --owner Gitlink --repo forgeplus --page Home
|
|
||||||
```
|
|
||||||
|
|
||||||
### Webhook 管理
|
### Webhook 管理
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -296,6 +285,28 @@ gitlink-cli webhook +test --owner Gitlink --repo forgeplus --id 68
|
||||||
gitlink-cli webhook +tasks --owner Gitlink --repo forgeplus --id 68
|
gitlink-cli webhook +tasks --owner Gitlink --repo forgeplus --id 68
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Wiki 管理
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 列出 Wiki 页面(目录结构)
|
||||||
|
gitlink-cli wiki +list --owner Gitlink --repo forgeplus --project-id 12345
|
||||||
|
|
||||||
|
# 查看 Wiki 页面
|
||||||
|
gitlink-cli wiki +view --owner Gitlink --repo forgeplus --project-id 12345 -n home
|
||||||
|
|
||||||
|
# 创建 Wiki 页面
|
||||||
|
gitlink-cli wiki +create --owner Gitlink --repo forgeplus --project-id 12345 \
|
||||||
|
-n getting-started -t "快速开始" -c "# 快速开始指南"
|
||||||
|
|
||||||
|
# 更新 Wiki 页面标题和/或内容
|
||||||
|
gitlink-cli wiki +update --owner Gitlink --repo forgeplus --project-id 12345 -n home -t "新标题"
|
||||||
|
gitlink-cli wiki +update --owner Gitlink --repo forgeplus --project-id 12345 -n home -c "# 更新后的内容"
|
||||||
|
gitlink-cli wiki +update --owner Gitlink --repo forgeplus --project-id 12345 -n home -t "新标题" -c "新内容"
|
||||||
|
|
||||||
|
# 删除 Wiki 页面
|
||||||
|
gitlink-cli wiki +delete --owner Gitlink --repo forgeplus --project-id 12345 -n old-page
|
||||||
|
```
|
||||||
|
|
||||||
### 成员管理
|
### 成员管理
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -345,6 +356,14 @@ gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --numbers 123,12
|
||||||
# 从 CSV 文件批量关闭 Issue
|
# 从 CSV 文件批量关闭 Issue
|
||||||
gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --from issues.csv
|
gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --from issues.csv
|
||||||
|
|
||||||
|
# 按 API issue id 预览批量更新元数据
|
||||||
|
# 注意:--ids 是 API issue id,不是网页 URL 中的 Issue 编号。
|
||||||
|
gitlink-cli issue +batch-update --owner Gitlink --repo forgeplus --ids 101,102 --status-id 3 --priority-id 2 --dry-run
|
||||||
|
|
||||||
|
# 危险批量删除必须先 dry-run,真实执行还要显式 --yes
|
||||||
|
gitlink-cli issue +batch-delete --owner Gitlink --repo forgeplus --ids 101,102 --dry-run
|
||||||
|
gitlink-cli issue +batch-delete --owner Gitlink --repo forgeplus --ids 101,102 --yes
|
||||||
|
|
||||||
# 添加评论
|
# 添加评论
|
||||||
gitlink-cli issue +comment --owner Gitlink --repo forgeplus -i 123 -b "已修复"
|
gitlink-cli issue +comment --owner Gitlink --repo forgeplus -i 123 -b "已修复"
|
||||||
|
|
||||||
|
|
@ -467,6 +486,16 @@ gitlink-cli pipeline +disable --owner Gitlink --repo forgeplus --id 7 --workflow
|
||||||
gitlink-cli pipeline +delete --owner Gitlink --repo forgeplus --id 7 --dry-run
|
gitlink-cli pipeline +delete --owner Gitlink --repo forgeplus --id 7 --dry-run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 忽略文件模板
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 列出所有可用的 .gitignore 模板
|
||||||
|
gitlink-cli ignore +list
|
||||||
|
|
||||||
|
# 按名称筛选模板
|
||||||
|
gitlink-cli ignore +list --name Go
|
||||||
|
```
|
||||||
|
|
||||||
### 搜索
|
### 搜索
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -477,6 +506,50 @@ gitlink-cli search +repos -k "machine learning"
|
||||||
gitlink-cli search +users -k "zhangsan"
|
gitlink-cli search +users -k "zhangsan"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 用户画像
|
||||||
|
|
||||||
|
`profile` 暴露 GitLink 原生的用户画像统计(开发能力、角色定位、专业定位、近期活动、贡献热力图)。
|
||||||
|
省略 `--user` 时默认使用当前认证用户。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 开发能力评分 + 语言分布
|
||||||
|
gitlink-cli profile +ability --user zhangsan
|
||||||
|
|
||||||
|
# 角色定位 / 专业(学科)定位
|
||||||
|
gitlink-cli profile +role --user zhangsan
|
||||||
|
gitlink-cli profile +major --user zhangsan
|
||||||
|
|
||||||
|
# 指定时间范围的开发能力(Unix 时间戳)
|
||||||
|
gitlink-cli profile +ability --user zhangsan --start-time 1704067200 --end-time 1735689600
|
||||||
|
|
||||||
|
# 当前用户的近期活动(每日 疑修 / 合并请求 / 提交)
|
||||||
|
gitlink-cli profile +activity
|
||||||
|
|
||||||
|
# 指定年份的贡献热力图
|
||||||
|
gitlink-cli profile +contribution --user zhangsan --year 2025
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据集
|
||||||
|
|
||||||
|
`dataset` 管理并查询 GitLink 科研数据集(标题、描述、论文内容、许可证、所属项目)。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 按数字项目 ID 列出一个或多个项目的数据集
|
||||||
|
gitlink-cli dataset +list --ids 5988
|
||||||
|
|
||||||
|
# 查看仓库的数据集及其附件
|
||||||
|
gitlink-cli dataset +view --owner Gitlink --repo forgeplus
|
||||||
|
|
||||||
|
# 创建 / 更新仓库数据集(先用 --dry-run 预览)
|
||||||
|
gitlink-cli dataset +create --owner me --repo proj -t "我的数据集" -d "..." --license-id 359 --dry-run
|
||||||
|
gitlink-cli dataset +update --owner me --repo proj -t "我的数据集" -d "更新"
|
||||||
|
|
||||||
|
# 删除数据集附件(破坏性:先预览,再用 --yes 确认)
|
||||||
|
gitlink-cli dataset +delete-attachment --owner me --repo proj --uuid <uuid> --dry-run
|
||||||
|
gitlink-cli dataset +delete-attachment --owner me --repo proj --uuid <uuid> --yes
|
||||||
|
```
|
||||||
|
|
||||||
|
> 注意:`dataset +list`(平台数据集查询)已在生产 gitlink.org.cn 验证可用。按仓库的 `+view`/`+create`/`+update` 遵循已发布的 OpenAPI 契约,但生产环境尚未部署(当前返回 404),待平台上线后即可生效。
|
||||||
### Raw API
|
### Raw API
|
||||||
|
|
||||||
Shortcuts 未覆盖的接口可通过 Raw API 直接调用:
|
Shortcuts 未覆盖的接口可通过 Raw API 直接调用:
|
||||||
|
|
@ -533,14 +606,13 @@ git push gitlink
|
||||||
|
|
||||||
`skills/` 目录包含 Claude Code Agent Skill 文件,支持 AI 自动化操作 GitLink 平台。
|
`skills/` 目录包含 Claude Code Agent Skill 文件,支持 AI 自动化操作 GitLink 平台。
|
||||||
|
|
||||||
详见 [skills/README.md](skills/README.md)
|
详见 [skills/README.md](./skills/README.md)
|
||||||
|
|
||||||
| Skill | 说明 |
|
| Skill | 说明 |
|
||||||
|-------|------|
|
|-------|------|
|
||||||
| `gitlink-shared` | 认证、全局参数、安全规则、API 注意事项 |
|
| `gitlink-shared` | 认证、全局参数、安全规则、API 注意事项 |
|
||||||
| `gitlink-repo` | 仓库操作(创建、查看、删除、Fork、洞察数据等) |
|
| `gitlink-repo` | 仓库操作(创建、查看、删除、Fork、洞察数据等) |
|
||||||
| `gitlink-wiki` | Wiki 操作(列出、查看、创建、更新、删除) |
|
| `gitlink-issue` | Issue 操作(创建、更新、关闭、批量更新/删除、评论等) |
|
||||||
| `gitlink-issue` | Issue 操作(创建、更新、关闭、评论等) |
|
|
||||||
| `gitlink-pr` | Pull Request 操作(创建、合并、Review 等) |
|
| `gitlink-pr` | Pull Request 操作(创建、合并、Review 等) |
|
||||||
| `gitlink-member` | 仓库成员与邀请链接管理 |
|
| `gitlink-member` | 仓库成员与邀请链接管理 |
|
||||||
| `gitlink-release` | 发布管理(创建、编辑、更新、查看、删除等) |
|
| `gitlink-release` | 发布管理(创建、编辑、更新、查看、删除等) |
|
||||||
|
|
@ -602,7 +674,7 @@ gitlink-cli/
|
||||||
|
|
||||||
## 文档
|
## 文档
|
||||||
|
|
||||||
- [Skills 使用指南](skills/README.md) — AI Agent Skills 详细说明
|
- [Skills 使用指南](./skills/README.md) — AI Agent Skills 详细说明
|
||||||
- [设计文档](doc/design.md) — 架构设计和开发计划
|
- [设计文档](doc/design.md) — 架构设计和开发计划
|
||||||
|
|
||||||
## 常见问题
|
## 常见问题
|
||||||
|
|
@ -665,7 +737,7 @@ gitlink-cli 使用 Windows Credential Manager 安全存储 Token。如果 Creden
|
||||||
|
|
||||||
### Q: 如何查看完整的 API 参考?
|
### Q: 如何查看完整的 API 参考?
|
||||||
|
|
||||||
查看 [skills/gitlink-shared/REFERENCE.md](skills/gitlink-shared/REFERENCE.md)
|
查看 [skills/gitlink-shared/references/api-reference.md](./skills/gitlink-shared/references/api-reference.md)
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
package alias
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
|
|
||||||
"github.com/gitlink-org/gitlink-cli/internal/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AliasConfig represents the aliases section of the CLI config.
|
|
||||||
type AliasConfig struct {
|
|
||||||
Aliases map[string]string `yaml:"aliases,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAliasCmd creates the alias command with subcommands.
|
|
||||||
func NewAliasCmd() *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "alias",
|
|
||||||
Short: "管理命令别名(把长命令变短)",
|
|
||||||
Long: `管理 gitlink-cli 的命令别名。
|
|
||||||
|
|
||||||
别名允许你为常用命令创建简短的名称,例如:
|
|
||||||
gitlink-cli alias +set rl "repo +list"
|
|
||||||
之后可以使用: gitlink-cli rl
|
|
||||||
|
|
||||||
别名存储在 ~/.config/gitlink-cli/aliases.yaml 中。`,
|
|
||||||
Example: ` gitlink-cli alias +list
|
|
||||||
gitlink-cli alias +set rl "repo +list"
|
|
||||||
gitlink-cli alias +set ri "repo +info --owner Gitlink --repo gitlink-cli"
|
|
||||||
gitlink-cli alias +delete rl`,
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.AddCommand(
|
|
||||||
&cobra.Command{
|
|
||||||
Use: "+list",
|
|
||||||
Short: "列出所有已定义的别名",
|
|
||||||
Long: "列出所有已定义的命令别名。如果没有任何别名,会给出创建提示。",
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
aliases, _ := loadAliases()
|
|
||||||
if len(aliases) == 0 {
|
|
||||||
fmt.Println("(未定义任何别名)")
|
|
||||||
fmt.Println("使用 alias +set <名称> <命令> 来创建别名")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for k, v := range aliases {
|
|
||||||
fmt.Printf(" %-15s → %s\n", k, v)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&cobra.Command{
|
|
||||||
Use: "+set <name> <command>",
|
|
||||||
Short: "设置别名",
|
|
||||||
Long: "为一条命令设置别名。如果别名已存在,会被覆盖。",
|
|
||||||
Args: cobra.ExactArgs(2),
|
|
||||||
Example: ` gitlink-cli alias +set rl "repo +list"
|
|
||||||
gitlink-cli alias +set ri "repo +info"`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
aliases, _ := loadAliases()
|
|
||||||
aliases[args[0]] = args[1]
|
|
||||||
if err := saveAliases(aliases); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("别名已设置: %s → %s\n", args[0], args[1])
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&cobra.Command{
|
|
||||||
Use: "+delete <name>",
|
|
||||||
Short: "删除别名",
|
|
||||||
Long: "删除一个已定义的命令别名。",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
aliases, _ := loadAliases()
|
|
||||||
if _, ok := aliases[args[0]]; !ok {
|
|
||||||
return fmt.Errorf("别名 %s 不存在", args[0])
|
|
||||||
}
|
|
||||||
delete(aliases, args[0])
|
|
||||||
if err := saveAliases(aliases); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("别名已删除: %s\n", args[0])
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func aliasesPath() string {
|
|
||||||
return config.ConfigDir() + "/aliases.yaml"
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadAliases() (map[string]string, error) {
|
|
||||||
data, err := os.ReadFile(aliasesPath())
|
|
||||||
if err != nil {
|
|
||||||
return make(map[string]string), nil
|
|
||||||
}
|
|
||||||
var ac AliasConfig
|
|
||||||
if err := yaml.Unmarshal(data, &ac); err != nil {
|
|
||||||
return make(map[string]string), nil
|
|
||||||
}
|
|
||||||
if ac.Aliases == nil {
|
|
||||||
ac.Aliases = make(map[string]string)
|
|
||||||
}
|
|
||||||
return ac.Aliases, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveAliases(a map[string]string) error {
|
|
||||||
data, err := yaml.Marshal(AliasConfig{Aliases: a})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
os.MkdirAll(config.ConfigDir(), 0700)
|
|
||||||
return os.WriteFile(aliasesPath(), data, 0600)
|
|
||||||
}
|
|
||||||
|
|
@ -1,193 +0,0 @@
|
||||||
package alias
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLoadAliasesEmpty(t *testing.T) {
|
|
||||||
// 设置临时配置目录
|
|
||||||
tmpDir := t.TempDir()
|
|
||||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
|
||||||
|
|
||||||
aliases, err := loadAliases()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("loadAliases failed: %v", err)
|
|
||||||
}
|
|
||||||
if len(aliases) != 0 {
|
|
||||||
t.Fatalf("expected empty aliases, got %d", len(aliases))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSaveAndLoadAliases(t *testing.T) {
|
|
||||||
tmpDir := t.TempDir()
|
|
||||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
|
||||||
|
|
||||||
// 保存
|
|
||||||
original := map[string]string{
|
|
||||||
"rl": "repo +list",
|
|
||||||
"ri": "repo +info",
|
|
||||||
}
|
|
||||||
if err := saveAliases(original); err != nil {
|
|
||||||
t.Fatalf("saveAliases failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载
|
|
||||||
loaded, err := loadAliases()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("loadAliases failed: %v", err)
|
|
||||||
}
|
|
||||||
if len(loaded) != 2 {
|
|
||||||
t.Fatalf("expected 2 aliases, got %d", len(loaded))
|
|
||||||
}
|
|
||||||
if loaded["rl"] != "repo +list" {
|
|
||||||
t.Errorf("expected rl -> repo +list, got %s", loaded["rl"])
|
|
||||||
}
|
|
||||||
if loaded["ri"] != "repo +info" {
|
|
||||||
t.Errorf("expected ri -> repo +info, got %s", loaded["ri"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSaveAliasesOverwrite(t *testing.T) {
|
|
||||||
tmpDir := t.TempDir()
|
|
||||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
|
||||||
|
|
||||||
// 第一次保存
|
|
||||||
saveAliases(map[string]string{"rl": "repo +list"})
|
|
||||||
|
|
||||||
// 覆盖保存
|
|
||||||
saveAliases(map[string]string{"rl": "repo +list --owner Gitlink"})
|
|
||||||
|
|
||||||
loaded, _ := loadAliases()
|
|
||||||
if loaded["rl"] != "repo +list --owner Gitlink" {
|
|
||||||
t.Errorf("alias should be overwritten, got %s", loaded["rl"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLoadAliasesInvalidYAML(t *testing.T) {
|
|
||||||
tmpDir := t.TempDir()
|
|
||||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
|
||||||
|
|
||||||
// 写入无效 YAML
|
|
||||||
os.WriteFile(tmpDir+"/aliases.yaml", []byte("{{invalid yaml}}"), 0600)
|
|
||||||
|
|
||||||
aliases, err := loadAliases()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not error on invalid YAML, got: %v", err)
|
|
||||||
}
|
|
||||||
if len(aliases) != 0 {
|
|
||||||
t.Fatalf("should return empty map on invalid YAML, got %d", len(aliases))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewAliasCmd(t *testing.T) {
|
|
||||||
cmd := NewAliasCmd()
|
|
||||||
if cmd.Use != "alias" {
|
|
||||||
t.Errorf("expected Use 'alias', got %s", cmd.Use)
|
|
||||||
}
|
|
||||||
if !cmd.HasSubCommands() {
|
|
||||||
t.Error("alias command should have subcommands")
|
|
||||||
}
|
|
||||||
subcmds := cmd.Commands()
|
|
||||||
if len(subcmds) != 3 {
|
|
||||||
t.Fatalf("expected 3 subcommands, got %d", len(subcmds))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAliasListSubcommand(t *testing.T) {
|
|
||||||
tmpDir := t.TempDir()
|
|
||||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
|
||||||
|
|
||||||
cmd := NewAliasCmd()
|
|
||||||
// 找到 +list 子命令
|
|
||||||
var listCmd *cobra.Command
|
|
||||||
for _, sub := range cmd.Commands() {
|
|
||||||
if sub.Use == "+list" {
|
|
||||||
listCmd = sub
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if listCmd == nil {
|
|
||||||
t.Fatal("+list subcommand not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 无别名时运行
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
listCmd.SetOut(buf)
|
|
||||||
listCmd.SetArgs([]string{})
|
|
||||||
if err := listCmd.Execute(); err != nil {
|
|
||||||
t.Fatalf("list failed: %v", err)
|
|
||||||
}
|
|
||||||
if !strings.Contains(buf.String(), "未定义任何别名") {
|
|
||||||
t.Errorf("expected hint for no aliases, got: %s", buf.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAliasSetAndDeleteSubcommands(t *testing.T) {
|
|
||||||
tmpDir := t.TempDir()
|
|
||||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
|
||||||
|
|
||||||
cmd := NewAliasCmd()
|
|
||||||
|
|
||||||
// 找到 +set 子命令
|
|
||||||
var setCmd, deleteCmd *cobra.Command
|
|
||||||
for _, sub := range cmd.Commands() {
|
|
||||||
if strings.HasPrefix(sub.Use, "+set") {
|
|
||||||
setCmd = sub
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(sub.Use, "+delete") {
|
|
||||||
deleteCmd = sub
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// +set
|
|
||||||
setCmd.SetArgs([]string{"rl", "repo +list"})
|
|
||||||
if err := setCmd.Execute(); err != nil {
|
|
||||||
t.Fatalf("set failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证文件写入
|
|
||||||
aliases, _ := loadAliases()
|
|
||||||
if aliases["rl"] != "repo +list" {
|
|
||||||
t.Fatalf("alias not saved correctly: %v", aliases)
|
|
||||||
}
|
|
||||||
|
|
||||||
// +delete
|
|
||||||
deleteCmd.SetArgs([]string{"rl"})
|
|
||||||
if err := deleteCmd.Execute(); err != nil {
|
|
||||||
t.Fatalf("delete failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证已删除
|
|
||||||
aliases, _ = loadAliases()
|
|
||||||
if _, ok := aliases["rl"]; ok {
|
|
||||||
t.Fatal("alias should have been deleted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAliasDeleteNonExistent(t *testing.T) {
|
|
||||||
tmpDir := t.TempDir()
|
|
||||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
|
||||||
|
|
||||||
cmd := NewAliasCmd()
|
|
||||||
var deleteCmd *cobra.Command
|
|
||||||
for _, sub := range cmd.Commands() {
|
|
||||||
if strings.HasPrefix(sub.Use, "+delete") {
|
|
||||||
deleteCmd = sub
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteCmd.SetArgs([]string{"nonexistent"})
|
|
||||||
err := deleteCmd.Execute()
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error when deleting nonexistent alias")
|
|
||||||
}
|
|
||||||
if !strings.Contains(err.Error(), "不存在") {
|
|
||||||
t.Errorf("error should mention alias does not exist: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,25 +7,16 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/gitlink-org/gitlink-cli/cmd/cmdutil"
|
"github.com/gitlink-org/gitlink-cli/cmd/cmdutil"
|
||||||
"github.com/gitlink-org/gitlink-cli/internal/client"
|
"github.com/gitlink-org/gitlink-cli/internal/client"
|
||||||
"github.com/gitlink-org/gitlink-cli/internal/context"
|
|
||||||
"github.com/gitlink-org/gitlink-cli/internal/i18n"
|
"github.com/gitlink-org/gitlink-cli/internal/i18n"
|
||||||
"github.com/gitlink-org/gitlink-cli/internal/output"
|
"github.com/gitlink-org/gitlink-cli/internal/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
// apiOwnerPlaceholder and apiRepoPlaceholder match the REST-style :owner / :repo
|
|
||||||
// path placeholders used throughout the GitLink API docs and shortcut commands.
|
|
||||||
var (
|
|
||||||
apiOwnerPlaceholder = regexp.MustCompile(`:owner\b`)
|
|
||||||
apiRepoPlaceholder = regexp.MustCompile(`:repo\b`)
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewAPICmd(translators ...*i18n.Translator) *cobra.Command {
|
func NewAPICmd(translators ...*i18n.Translator) *cobra.Command {
|
||||||
tr := i18n.Default()
|
tr := i18n.Default()
|
||||||
if len(translators) > 0 && translators[0] != nil {
|
if len(translators) > 0 && translators[0] != nil {
|
||||||
|
|
@ -69,32 +60,6 @@ func validateAPIArgs(c *cobra.Command, args []string) error {
|
||||||
return cobra.ExactArgs(2)(c, args)
|
return cobra.ExactArgs(2)(c, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// msysPathRe matches Windows drive-letter prefixes produced by MSYS2/Git Bash
|
|
||||||
// path conversion, e.g. "C:/Program Files/Git/v1/owner/repo" for input "/v1/owner/repo".
|
|
||||||
var msysPathRe = regexp.MustCompile(`^[A-Za-z]:/`)
|
|
||||||
|
|
||||||
// restoreAPIPath restores an API path polluted by MSYS2/Git Bash path
|
|
||||||
// conversion on Windows, e.g. "C:/Program Files/Git/v1/owner/repo" -> "/v1/owner/repo".
|
|
||||||
// If the path does not start with a drive letter, or no known API prefix is
|
|
||||||
// found, the original path is returned unchanged.
|
|
||||||
func restoreAPIPath(path string) string {
|
|
||||||
if !msysPathRe.MatchString(path) {
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
// Pick the EARLIEST occurrence among known API prefixes, so a path like
|
|
||||||
// ".../api/v1/users" restores to "/api/v1/users" rather than "/v1/users".
|
|
||||||
bestIdx := -1
|
|
||||||
for _, prefix := range []string{"/v1/", "/v2/", "/api/", "/users/", "/projects/"} {
|
|
||||||
if idx := strings.Index(path, prefix); idx >= 0 && (bestIdx == -1 || idx < bestIdx) {
|
|
||||||
bestIdx = idx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if bestIdx >= 0 {
|
|
||||||
return path[bestIdx:]
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
func runAPI(c *cobra.Command, args []string) error {
|
func runAPI(c *cobra.Command, args []string) error {
|
||||||
batchFile, _ := c.Flags().GetString("batch-file")
|
batchFile, _ := c.Flags().GetString("batch-file")
|
||||||
if batchFile != "" {
|
if batchFile != "" {
|
||||||
|
|
@ -102,14 +67,10 @@ func runAPI(c *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
method := strings.ToUpper(args[0])
|
method := strings.ToUpper(args[0])
|
||||||
|
path := args[1]
|
||||||
|
|
||||||
// Fix MSYS2/Git Bash path auto-conversion on Windows first:
|
if !strings.HasPrefix(path, "/") {
|
||||||
// "/v1/owner/repo" is rewritten to "C:/Program Files/Git/v1/owner/repo".
|
path = "/" + path
|
||||||
rawPath := restoreAPIPath(args[1])
|
|
||||||
|
|
||||||
path, err := resolveAPIPath(c, rawPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cli, err := client.New()
|
cli, err := client.New()
|
||||||
|
|
@ -146,40 +107,6 @@ func runAPI(c *cobra.Command, args []string) error {
|
||||||
return output.Print(env, resolveFormat())
|
return output.Print(env, resolveFormat())
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveAPIPath prepares a single-call path: it renders {{var}} templates
|
|
||||||
// supplied via --var (consistent with batch mode), substitutes the REST-style
|
|
||||||
// :owner / :repo placeholders (resolved from --owner/--repo or the git remote,
|
|
||||||
// exactly like the shortcut commands), and ensures a leading slash.
|
|
||||||
func resolveAPIPath(c *cobra.Command, rawPath string) (string, error) {
|
|
||||||
path := rawPath
|
|
||||||
|
|
||||||
overrides, err := parseBatchVars(c)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if len(overrides) > 0 {
|
|
||||||
rendered, rerr := renderTemplate(path, overrides)
|
|
||||||
if rerr != nil {
|
|
||||||
return "", rerr
|
|
||||||
}
|
|
||||||
path = rendered
|
|
||||||
}
|
|
||||||
|
|
||||||
if apiOwnerPlaceholder.MatchString(path) || apiRepoPlaceholder.MatchString(path) {
|
|
||||||
owner, repo, rerr := context.ResolveOwnerRepo(cmdutil.Owner, cmdutil.Repo)
|
|
||||||
if rerr != nil {
|
|
||||||
return "", fmt.Errorf("path contains :owner/:repo placeholders but they could not be resolved: %w", rerr)
|
|
||||||
}
|
|
||||||
path = apiOwnerPlaceholder.ReplaceAllLiteralString(path, owner)
|
|
||||||
path = apiRepoPlaceholder.ReplaceAllLiteralString(path, repo)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(path, "/") {
|
|
||||||
path = "/" + path
|
|
||||||
}
|
|
||||||
return path, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readJSONBody(c *cobra.Command) (interface{}, error) {
|
func readJSONBody(c *cobra.Command) (interface{}, error) {
|
||||||
bodyStr, _ := c.Flags().GetString("body")
|
bodyStr, _ := c.Flags().GetString("body")
|
||||||
bodyFile, _ := c.Flags().GetString("body-file")
|
bodyFile, _ := c.Flags().GetString("body-file")
|
||||||
|
|
|
||||||
|
|
@ -131,63 +131,6 @@ func TestRunAPIBadQuery(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunAPIPaginate(t *testing.T) {
|
|
||||||
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
page := r.URL.Query().Get("page")
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
switch page {
|
|
||||||
case "1":
|
|
||||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
||||||
"total_count": 3,
|
|
||||||
"issues": []interface{}{
|
|
||||||
map[string]interface{}{"id": 1},
|
|
||||||
map[string]interface{}{"id": 2},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
||||||
"total_count": 3,
|
|
||||||
"issues": []interface{}{
|
|
||||||
map[string]interface{}{"id": 3},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
cmdutil.Format = "json"
|
|
||||||
|
|
||||||
cmd := NewAPICmd()
|
|
||||||
cmd.SetArgs([]string{"GET", "/owner/repo/issues", "--paginate", "--query", "limit=2"})
|
|
||||||
if err := cmd.Execute(); err != nil {
|
|
||||||
t.Fatalf("runAPI paginate error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunAPIPaginateRejectsNonGET(t *testing.T) {
|
|
||||||
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatal("server should not be reached")
|
|
||||||
})
|
|
||||||
cmdutil.Format = "json"
|
|
||||||
|
|
||||||
cmd := NewAPICmd()
|
|
||||||
cmd.SetArgs([]string{"POST", "/owner/repo/issues", "--paginate"})
|
|
||||||
if err := cmd.Execute(); err == nil {
|
|
||||||
t.Fatal("expected error for --paginate with POST")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunAPIPaginateRejectsBatchFile(t *testing.T) {
|
|
||||||
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatal("server should not be reached")
|
|
||||||
})
|
|
||||||
cmdutil.Format = "json"
|
|
||||||
|
|
||||||
cmd := NewAPICmd()
|
|
||||||
cmd.SetArgs([]string{"--batch-file", "plan.json", "--paginate"})
|
|
||||||
if err := cmd.Execute(); err == nil {
|
|
||||||
t.Fatal("expected error for --paginate with --batch-file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunAPIHTTPError(t *testing.T) {
|
func TestRunAPIHTTPError(t *testing.T) {
|
||||||
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
|
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
|
@ -259,105 +202,6 @@ func TestRunAPINoPrefix(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunAPIRendersOwnerRepoColonPlaceholders(t *testing.T) {
|
|
||||||
oldOwner, oldRepo := cmdutil.Owner, cmdutil.Repo
|
|
||||||
cmdutil.Owner, cmdutil.Repo = "Gitlink", "gitlink-cli"
|
|
||||||
defer func() {
|
|
||||||
cmdutil.Owner, cmdutil.Repo = oldOwner, oldRepo
|
|
||||||
}()
|
|
||||||
|
|
||||||
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.URL.Path != "/Gitlink/gitlink-cli/issues.json" {
|
|
||||||
t.Fatalf("unexpected path: %s", r.URL.Path)
|
|
||||||
}
|
|
||||||
json.NewEncoder(w).Encode(map[string]interface{}{"ok": true})
|
|
||||||
})
|
|
||||||
cmdutil.Format = "json"
|
|
||||||
|
|
||||||
cmd := NewAPICmd()
|
|
||||||
cmd.SetArgs([]string{"GET", "/:owner/:repo/issues"})
|
|
||||||
if err := cmd.Execute(); err != nil {
|
|
||||||
t.Fatalf("runAPI placeholder error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunAPIRendersVarsInQueryAndBody(t *testing.T) {
|
|
||||||
var gotBody map[string]interface{}
|
|
||||||
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.URL.Path != "/v1/Gitlink/gitlink-cli/issues/42/journals.json" {
|
|
||||||
t.Fatalf("unexpected path: %s", r.URL.Path)
|
|
||||||
}
|
|
||||||
if r.URL.Query().Get("notify") != "true" {
|
|
||||||
t.Fatalf("notify query = %q", r.URL.Query().Get("notify"))
|
|
||||||
}
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil {
|
|
||||||
t.Fatalf("decode body: %v", err)
|
|
||||||
}
|
|
||||||
json.NewEncoder(w).Encode(map[string]interface{}{"id": 99})
|
|
||||||
})
|
|
||||||
cmdutil.Format = "json"
|
|
||||||
|
|
||||||
bodyPath := filepath.Join(t.TempDir(), "body.json")
|
|
||||||
if err := os.WriteFile(bodyPath, []byte(`{"notes":"hello {{actor}}","meta":{"repo":"{{repo}}"}}`), 0600); err != nil {
|
|
||||||
t.Fatalf("write body: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := NewAPICmd()
|
|
||||||
cmd.SetArgs([]string{
|
|
||||||
"POST", "/v1/{{owner}}/{{repo}}/issues/{{number}}/journals",
|
|
||||||
"--query", "notify={{notify}}",
|
|
||||||
"--body-file", bodyPath,
|
|
||||||
"--var", "owner=Gitlink",
|
|
||||||
"--var", "repo=gitlink-cli",
|
|
||||||
"--var", "number=42",
|
|
||||||
"--var", "notify=true",
|
|
||||||
"--var", "actor=bot",
|
|
||||||
})
|
|
||||||
if err := cmd.Execute(); err != nil {
|
|
||||||
t.Fatalf("runAPI rendered vars error: %v", err)
|
|
||||||
}
|
|
||||||
if gotBody["notes"] != "hello bot" {
|
|
||||||
t.Fatalf("notes = %#v", gotBody["notes"])
|
|
||||||
}
|
|
||||||
meta := gotBody["meta"].(map[string]interface{})
|
|
||||||
if meta["repo"] != "gitlink-cli" {
|
|
||||||
t.Fatalf("meta.repo = %#v", meta["repo"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunAPIDryRunDoesNotReachServer(t *testing.T) {
|
|
||||||
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatal("dry-run should not reach server")
|
|
||||||
})
|
|
||||||
cmdutil.Format = "json"
|
|
||||||
|
|
||||||
cmd := NewAPICmd()
|
|
||||||
cmd.SetArgs([]string{
|
|
||||||
"POST", "/v1/{{owner}}/{{repo}}/issues",
|
|
||||||
"--body", `{"subject":"{{title}}"}`,
|
|
||||||
"--dry-run",
|
|
||||||
"--var", "owner=Gitlink",
|
|
||||||
"--var", "repo=gitlink-cli",
|
|
||||||
"--var", "title=Bug report",
|
|
||||||
})
|
|
||||||
if err := cmd.Execute(); err != nil {
|
|
||||||
t.Fatalf("runAPI dry-run error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunAPIMissingSingleRequestVar(t *testing.T) {
|
|
||||||
setupAPITest(t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatal("should not reach server")
|
|
||||||
})
|
|
||||||
cmdutil.Format = "json"
|
|
||||||
|
|
||||||
cmd := NewAPICmd()
|
|
||||||
cmd.SetArgs([]string{"GET", "/v1/{{owner}}/{{repo}}/issues/{{number}}"})
|
|
||||||
if err := cmd.Execute(); err == nil {
|
|
||||||
t.Fatal("expected missing variable error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRenderBatchRequestsTemplateVars(t *testing.T) {
|
func TestRenderBatchRequestsTemplateVars(t *testing.T) {
|
||||||
requests, err := renderBatchRequests([]batchRequest{
|
requests, err := renderBatchRequests([]batchRequest{
|
||||||
{
|
{
|
||||||
|
|
|
||||||
101
cmd/auth/auth.go
101
cmd/auth/auth.go
|
|
@ -2,15 +2,11 @@ package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
|
|
@ -38,7 +34,6 @@ func NewAuthCmd(translators ...*i18n.Translator) *cobra.Command {
|
||||||
cmd.AddCommand(newLoginCmd(tr))
|
cmd.AddCommand(newLoginCmd(tr))
|
||||||
cmd.AddCommand(newLogoutCmd(tr))
|
cmd.AddCommand(newLogoutCmd(tr))
|
||||||
cmd.AddCommand(newStatusCmd(tr))
|
cmd.AddCommand(newStatusCmd(tr))
|
||||||
cmd.AddCommand(newCheckinCmd(tr))
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,99 +181,3 @@ func newStatusCmd(tr *i18n.Translator) *cobra.Command {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCheckinCmd(tr *i18n.Translator) *cobra.Command {
|
|
||||||
var intervalMinutes int
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "checkin",
|
|
||||||
Short: tr.T("cmd.auth.checkin.short"),
|
|
||||||
Long: tr.T("cmd.auth.checkin.long"),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
return runCheckin(cmd.OutOrStdout(), intervalMinutes, tr)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
cmd.Flags().IntVarP(&intervalMinutes, "time", "t", 30, tr.T("flag.auth.checkin.time"))
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func runCheckin(out io.Writer, intervalMinutes int, tr *i18n.Translator) error {
|
|
||||||
// Check if user is logged in
|
|
||||||
token, err := loadToken()
|
|
||||||
if err != nil || token == "" {
|
|
||||||
if os.Getenv(envTokenVar) == "" {
|
|
||||||
return errors.New(tr.T("error.auth.not_logged_in"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert minutes to duration
|
|
||||||
interval := time.Duration(intervalMinutes) * time.Minute
|
|
||||||
|
|
||||||
// Print startup message
|
|
||||||
fmt.Fprintln(out, tr.Tf("output.auth.checkin.start", i18n.Args{"interval": intervalMinutes}))
|
|
||||||
fmt.Fprintln(out, tr.T("output.auth.checkin.stop_hint"))
|
|
||||||
|
|
||||||
// Setup signal handling for graceful shutdown
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
sigChan := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-sigChan
|
|
||||||
fmt.Fprintln(out, "\n"+tr.T("output.auth.checkin.stopping"))
|
|
||||||
cancel()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Create ticker
|
|
||||||
ticker := time.NewTicker(interval)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
// Do first check immediately
|
|
||||||
if err := doCheckin(out, tr, interval); err != nil {
|
|
||||||
fmt.Fprintln(out, tr.Tf("error.auth.checkin.failed", i18n.Args{"message": err.Error()}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then check periodically
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
fmt.Fprintln(out, tr.T("output.auth.checkin.stopped"))
|
|
||||||
return nil
|
|
||||||
case <-ticker.C:
|
|
||||||
if err := doCheckin(out, tr, interval); err != nil {
|
|
||||||
fmt.Fprintln(out, tr.Tf("error.auth.checkin.failed", i18n.Args{"message": err.Error()}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func doCheckin(out io.Writer, tr *i18n.Translator, interval time.Duration) error {
|
|
||||||
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
|
||||||
fmt.Fprintf(out, "[%s] "+tr.T("output.auth.checkin.checking")+"\n", timestamp)
|
|
||||||
|
|
||||||
user, err := internalAuth.GetCurrentUser()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
login, _ := user["login"].(string)
|
|
||||||
name, _ := user["name"].(string)
|
|
||||||
|
|
||||||
if login != "" {
|
|
||||||
msg := tr.Tf("output.auth.checkin.success", i18n.Args{"login": login})
|
|
||||||
if name != "" {
|
|
||||||
msg = fmt.Sprintf("%s (%s)", msg, name)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(out, "[%s] %s\n", timestamp, msg)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(out, "[%s] "+tr.T("output.auth.checkin.success_no_user")+"\n", timestamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print next refresh time
|
|
||||||
nextTime := time.Now().Add(interval).Format("2006-01-02 15:04:05")
|
|
||||||
fmt.Fprintln(out, tr.Tf("output.auth.checkin.interval", i18n.Args{"time": nextTime}))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ func TestNewAuthCmd(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedSubs := map[string]bool{
|
expectedSubs := map[string]bool{
|
||||||
"login": false, "logout": false, "status": false, "checkin": false,
|
"login": false, "logout": false, "status": false,
|
||||||
}
|
}
|
||||||
for _, sub := range cmd.Commands() {
|
for _, sub := range cmd.Commands() {
|
||||||
if _, ok := expectedSubs[sub.Use]; !ok {
|
if _, ok := expectedSubs[sub.Use]; !ok {
|
||||||
|
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
package browse
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"github.com/gitlink-org/gitlink-cli/internal/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewBrowseCmd creates the browse command for opening GitLink pages in a browser.
|
|
||||||
func NewBrowseCmd() *cobra.Command {
|
|
||||||
return &cobra.Command{
|
|
||||||
Use: "browse [resource]",
|
|
||||||
Short: "在浏览器中打开 GitLink 页面",
|
|
||||||
Long: `打开当前仓库(或指定资源)的 GitLink 页面。
|
|
||||||
|
|
||||||
如果不带参数,打开当前仓库主页。
|
|
||||||
资源格式: issues/42, pulls/42, wiki
|
|
||||||
|
|
||||||
浏览器打开命令:
|
|
||||||
- macOS: open
|
|
||||||
- Windows: start
|
|
||||||
- Linux: xdg-open`,
|
|
||||||
Example: ` gitlink-cli browse
|
|
||||||
gitlink-cli browse issues/42
|
|
||||||
gitlink-cli browse pulls/128
|
|
||||||
gitlink-cli browse wiki`,
|
|
||||||
Args: cobra.MaximumNArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
owner, repo, err := context.ResolveOwnerRepo("", "")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("无法推断仓库信息: %w", err)
|
|
||||||
}
|
|
||||||
url := fmt.Sprintf("https://gitlink.org.cn/%s/%s", owner, repo)
|
|
||||||
if len(args) > 0 {
|
|
||||||
url += "/" + args[0]
|
|
||||||
}
|
|
||||||
fmt.Printf("正在打开: %s\n", url)
|
|
||||||
return openBrowser(url)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func openBrowser(url string) error {
|
|
||||||
var cmd *exec.Cmd
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "darwin":
|
|
||||||
cmd = exec.Command("open", url)
|
|
||||||
case "windows":
|
|
||||||
cmd = exec.Command("cmd", "/c", "start", url)
|
|
||||||
default:
|
|
||||||
cmd = exec.Command("xdg-open", url)
|
|
||||||
}
|
|
||||||
return cmd.Start()
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
package browse
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewBrowseCmd(t *testing.T) {
|
|
||||||
cmd := NewBrowseCmd()
|
|
||||||
if cmd.Use != "browse [resource]" {
|
|
||||||
t.Errorf("expected Use 'browse [resource]', got %s", cmd.Use)
|
|
||||||
}
|
|
||||||
if cmd.Short == "" {
|
|
||||||
t.Error("Short description should not be empty")
|
|
||||||
}
|
|
||||||
if cmd.Long == "" {
|
|
||||||
t.Error("Long description should not be empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBrowseCmdHasCorrectArgs(t *testing.T) {
|
|
||||||
cmd := NewBrowseCmd()
|
|
||||||
// MaximumNArgs(1) should allow 0 or 1 args
|
|
||||||
if err := cmd.Args(cmd, []string{}); err != nil {
|
|
||||||
t.Errorf("should accept 0 args: %v", err)
|
|
||||||
}
|
|
||||||
if err := cmd.Args(cmd, []string{"issues/42"}); err != nil {
|
|
||||||
t.Errorf("should accept 1 arg: %v", err)
|
|
||||||
}
|
|
||||||
if err := cmd.Args(cmd, []string{"a", "b"}); err == nil {
|
|
||||||
t.Error("should reject more than 1 arg")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBrowseCmdSubcommandStructure(t *testing.T) {
|
|
||||||
cmd := NewBrowseCmd()
|
|
||||||
// browse 不应该有子命令
|
|
||||||
if cmd.HasSubCommands() {
|
|
||||||
t.Error("browse should not have subcommands")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBrowseCmdExample(t *testing.T) {
|
|
||||||
cmd := NewBrowseCmd()
|
|
||||||
if cmd.Example == "" {
|
|
||||||
t.Error("Example should not be empty")
|
|
||||||
}
|
|
||||||
if !strings.Contains(cmd.Example, "browse") {
|
|
||||||
t.Error("Example should contain 'browse'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOpenBrowserReturnsNoError(t *testing.T) {
|
|
||||||
// openBrowser 在所有平台都应该返回 nil 或一个 error
|
|
||||||
// 在无头环境下可能会失败,但不应该 panic
|
|
||||||
_ = openBrowser("https://gitlink.org.cn")
|
|
||||||
// 只要不 panic 就行
|
|
||||||
}
|
|
||||||
|
|
@ -55,9 +55,9 @@ func TestRootCmdHasSubcommands(t *testing.T) {
|
||||||
}
|
}
|
||||||
names := map[string]bool{}
|
names := map[string]bool{}
|
||||||
for _, sub := range root.Commands() {
|
for _, sub := range root.Commands() {
|
||||||
names[sub.Name()] = true
|
names[sub.Use] = true
|
||||||
}
|
}
|
||||||
for _, want := range []string{"auth", "completion", "config", "doctor", "version"} {
|
for _, want := range []string{"auth", "config", "doctor", "version"} {
|
||||||
if !names[want] {
|
if !names[want] {
|
||||||
t.Fatalf("missing subcommand: %s", want)
|
t.Fatalf("missing subcommand: %s", want)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCompletionCmdGeneratesSupportedShells(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
shell string
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{shell: "bash", want: "__gitlink-cli"},
|
|
||||||
{shell: "zsh", want: "#compdef gitlink-cli"},
|
|
||||||
{shell: "fish", want: "complete -c gitlink-cli"},
|
|
||||||
{shell: "powershell", want: "Register-ArgumentCompleter"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
root, err := NewRootCmd(RootOptions{Version: "test", Args: []string{"completion", tc.shell}}, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
var out bytes.Buffer
|
|
||||||
root.SetOut(&out)
|
|
||||||
root.SetErr(&out)
|
|
||||||
if err := root.Execute(); err != nil {
|
|
||||||
t.Fatalf("%s completion error: %v", tc.shell, err)
|
|
||||||
}
|
|
||||||
if !strings.Contains(out.String(), tc.want) {
|
|
||||||
t.Fatalf("%s completion missing %q, got:\n%s", tc.shell, tc.want, out.String()[:min(len(out.String()), 400)])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCompletionCmdNoDescriptions(t *testing.T) {
|
|
||||||
root, err := NewRootCmd(RootOptions{
|
|
||||||
Version: "test",
|
|
||||||
Args: []string{"completion", "bash", "--no-descriptions"},
|
|
||||||
}, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
var out bytes.Buffer
|
|
||||||
root.SetOut(&out)
|
|
||||||
root.SetErr(&out)
|
|
||||||
if err := root.Execute(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if strings.Contains(out.String(), "GitLink CLI - command-line tool for GitLink") {
|
|
||||||
t.Fatalf("expected descriptions to be omitted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCompletionCmdRejectsUnsupportedShell(t *testing.T) {
|
|
||||||
root, err := NewRootCmd(RootOptions{Version: "test", Args: []string{"completion", "xonsh"}}, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
var out bytes.Buffer
|
|
||||||
root.SetOut(&out)
|
|
||||||
root.SetErr(&out)
|
|
||||||
err = root.Execute()
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected unsupported shell error")
|
|
||||||
}
|
|
||||||
if !strings.Contains(err.Error(), "invalid argument") {
|
|
||||||
t.Fatalf("expected invalid argument error, got %q", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
208
cmd/do.go
208
cmd/do.go
|
|
@ -1,208 +0,0 @@
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NLCommand 自然语言命令路由
|
|
||||||
// 用法: gitlink-cli do "列出issue" 或 gitlink-cli do wiki
|
|
||||||
// 关键词匹配 → 推荐命令 + 显示参数 + 简短/完全示例
|
|
||||||
// 输入模块名(如 wiki)→ 列出该模块全部命令
|
|
||||||
|
|
||||||
func newDoCmd() *cobra.Command {
|
|
||||||
return &cobra.Command{
|
|
||||||
Use: `do "自然语言描述或模块名"`,
|
|
||||||
Short: "Natural language command helper (e.g. do \"list issues\" or do wiki)",
|
|
||||||
Long: `用自然语言描述你想做的事,自动匹配命令并显示参数。
|
|
||||||
也可以直接输入模块名查看该模块全部命令。
|
|
||||||
|
|
||||||
示例:
|
|
||||||
gitlink-cli do 列出issue # 匹配到 issue +list
|
|
||||||
gitlink-cli do wiki # 列出 wiki 全部命令
|
|
||||||
gitlink-cli do 创建标签 # 匹配到 label +create`,
|
|
||||||
Args: cobra.MinimumNArgs(1),
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
input := strings.ToLower(strings.Join(args, " "))
|
|
||||||
input = strings.TrimSpace(input)
|
|
||||||
exePath, _ := os.Executable()
|
|
||||||
|
|
||||||
// 1. 先检查是否是模块名(如 wiki/issue/pr/label...)
|
|
||||||
modules := []string{"wiki", "issue", "pr", "label", "notification", "snippet",
|
|
||||||
"repo", "release", "branch", "member", "milestone", "webhook",
|
|
||||||
"ci", "search", "org", "user", "compare", "workflow"}
|
|
||||||
for _, mod := range modules {
|
|
||||||
if input == mod {
|
|
||||||
fmt.Printf("📦 %s 模块全部命令:\n", mod)
|
|
||||||
fmt.Println(strings.Repeat("=", 50))
|
|
||||||
helpCmd := exec.Command(exePath, mod, "--help")
|
|
||||||
helpCmd.Stdout = os.Stdout
|
|
||||||
helpCmd.Stderr = os.Stderr
|
|
||||||
helpCmd.Run()
|
|
||||||
fmt.Println(strings.Repeat("=", 50))
|
|
||||||
fmt.Println("\n💡 选择一个命令运行,例如:")
|
|
||||||
fmt.Printf(" gitlink-cli %s +list\n", mod)
|
|
||||||
fmt.Printf(" gitlink-cli %s +list --owner ylly --repo gitlink-cli --format json (完全版)\n", mod)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 关键词匹配
|
|
||||||
matched := matchNL(input)
|
|
||||||
if matched == "" {
|
|
||||||
fmt.Println("❌ 未识别。试试这些:")
|
|
||||||
fmt.Println(" 输入模块名(wiki/issue/pr/label/notification/snippet/repo...)")
|
|
||||||
fmt.Println(" 或描述操作(列出issue/创建标签/登录/搜索仓库...)")
|
|
||||||
fmt.Println("\n示例:")
|
|
||||||
fmt.Println(" gitlink-cli do wiki # 查看 wiki 全部命令")
|
|
||||||
fmt.Println(" gitlink-cli do 列出issue # 匹配 issue +list")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
parts := strings.Fields(matched)
|
|
||||||
fmt.Printf("✅ 匹配到命令: %s\n\n", matched)
|
|
||||||
|
|
||||||
// 显示该命令的 --help
|
|
||||||
if len(parts) >= 2 {
|
|
||||||
group := parts[0]
|
|
||||||
sub := parts[1]
|
|
||||||
fmt.Println("📋 命令参数说明:")
|
|
||||||
fmt.Println(strings.Repeat("-", 50))
|
|
||||||
helpCmd := exec.Command(exePath, group, sub, "--help")
|
|
||||||
helpCmd.Stdout = os.Stdout
|
|
||||||
helpCmd.Stderr = os.Stderr
|
|
||||||
helpCmd.Run()
|
|
||||||
fmt.Println(strings.Repeat("-", 50))
|
|
||||||
|
|
||||||
// 显示简短版 + 完全版示例
|
|
||||||
fmt.Println("\n💡 命令示例:")
|
|
||||||
fmt.Printf(" 简短版(在自己的仓库目录里):\n")
|
|
||||||
fmt.Printf(" gitlink-cli %s\n", matched)
|
|
||||||
fmt.Printf(" 完全版(任何目录都能用):\n")
|
|
||||||
fmt.Printf(" gitlink-cli %s --owner ylly --repo gitlink-cli --format json\n", matched)
|
|
||||||
} else {
|
|
||||||
// auth login / auth status 等无子命令的
|
|
||||||
fmt.Printf("\n💡 运行:gitlink-cli %s\n", matched)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// matchNL 关键词匹配自然语言 → 命令
|
|
||||||
func matchNL(input string) string {
|
|
||||||
type rule struct {
|
|
||||||
keywords []string
|
|
||||||
cmd string
|
|
||||||
}
|
|
||||||
rules := []rule{
|
|
||||||
// Issue
|
|
||||||
{[]string{"issue", "疑修", "问题", "list", "列", "查看"}, "issue +list"},
|
|
||||||
{[]string{"issue", "create", "新建", "创建", "提"}, "issue +create"},
|
|
||||||
{[]string{"issue", "view", "详情", "看"}, "issue +view"},
|
|
||||||
{[]string{"issue", "close", "关闭"}, "issue +close"},
|
|
||||||
{[]string{"issue", "update", "更新", "修改"}, "issue +update"},
|
|
||||||
{[]string{"issue", "comment", "评论", "回复"}, "issue +comment"},
|
|
||||||
{[]string{"issue", "batch", "批量", "关闭"}, "issue +batch-close"},
|
|
||||||
{[]string{"issue", "assigners", "负责人", "分配", "候选"}, "issue +assigners"},
|
|
||||||
{[]string{"issue", "authors", "作者"}, "issue +authors"},
|
|
||||||
// PR
|
|
||||||
{[]string{"pr", "pull", "合并请求", "merge", "list"}, "pr +list"},
|
|
||||||
{[]string{"pr", "create", "新建", "提交"}, "pr +create"},
|
|
||||||
{[]string{"pr", "view", "详情", "看"}, "pr +view"},
|
|
||||||
{[]string{"pr", "merge", "合并"}, "pr +merge"},
|
|
||||||
{[]string{"pr", "diff", "差异", "变更"}, "pr +diff"},
|
|
||||||
{[]string{"pr", "files", "文件", "变更文件"}, "pr +files"},
|
|
||||||
{[]string{"pr", "review", "审查", "review"}, "pr +review"},
|
|
||||||
{[]string{"pr", "close", "关闭"}, "pr +close"},
|
|
||||||
{[]string{"pr", "reopen", "重开", "重新打开"}, "pr +reopen"},
|
|
||||||
// Label
|
|
||||||
{[]string{"label", "tag", "标签", "list"}, "label +list"},
|
|
||||||
{[]string{"label", "tag", "标签", "create", "新建", "创建"}, "label +create"},
|
|
||||||
{[]string{"label", "tag", "标签", "update", "更新", "修改"}, "label +update"},
|
|
||||||
{[]string{"label", "tag", "标签", "delete", "删除"}, "label +delete"},
|
|
||||||
{[]string{"label", "tag", "标签", "batch", "批量"}, "label +batch-create"},
|
|
||||||
// Wiki
|
|
||||||
{[]string{"wiki", "文档", "知识库", "list"}, "wiki +list"},
|
|
||||||
{[]string{"wiki", "文档", "create", "新建", "创建"}, "wiki +create"},
|
|
||||||
{[]string{"wiki", "文档", "view", "查看"}, "wiki +view"},
|
|
||||||
{[]string{"wiki", "文档", "update", "更新"}, "wiki +update"},
|
|
||||||
{[]string{"wiki", "文档", "delete", "删除"}, "wiki +delete"},
|
|
||||||
// Release
|
|
||||||
{[]string{"release", "发布", "版本", "list"}, "release +list"},
|
|
||||||
{[]string{"release", "发布", "create", "新建"}, "release +create"},
|
|
||||||
{[]string{"release", "发布", "view", "查看"}, "release +view"},
|
|
||||||
// Repo
|
|
||||||
{[]string{"repo", "仓库", "info", "信息"}, "repo +info"},
|
|
||||||
{[]string{"repo", "仓库", "create", "新建", "创建"}, "repo +create"},
|
|
||||||
{[]string{"repo", "仓库", "readme"}, "repo +readme"},
|
|
||||||
{[]string{"repo", "仓库", "fork", "复刻"}, "repo +fork"},
|
|
||||||
{[]string{"repo", "仓库", "list", "列"}, "repo +list"},
|
|
||||||
// Auth
|
|
||||||
{[]string{"auth", "login", "登录", "认证"}, "auth login"},
|
|
||||||
{[]string{"auth", "status", "状态"}, "auth status"},
|
|
||||||
// Snippet
|
|
||||||
{[]string{"snippet", "片段", "代码片段", "list"}, "snippet +list"},
|
|
||||||
{[]string{"snippet", "片段", "代码片段", "create", "新建", "保存"}, "snippet +create"},
|
|
||||||
{[]string{"snippet", "片段", "代码片段", "view", "查看"}, "snippet +view"},
|
|
||||||
{[]string{"snippet", "片段", "代码片段", "delete", "删除"}, "snippet +delete"},
|
|
||||||
// Notification
|
|
||||||
{[]string{"notification", "通知", "消息", "list"}, "notification +list"},
|
|
||||||
{[]string{"notification", "通知", "read", "已读"}, "notification +read"},
|
|
||||||
{[]string{"notification", "通知", "delete", "删除"}, "notification +delete"},
|
|
||||||
// Member
|
|
||||||
{[]string{"member", "成员", "list"}, "member +list"},
|
|
||||||
{[]string{"member", "成员", "add", "添加"}, "member +add"},
|
|
||||||
{[]string{"member", "成员", "invite", "邀请"}, "member +invite-link"},
|
|
||||||
// Branch
|
|
||||||
{[]string{"branch", "分支", "list"}, "branch +list"},
|
|
||||||
{[]string{"branch", "分支", "create", "新建"}, "branch +create"},
|
|
||||||
// CI
|
|
||||||
{[]string{"ci", "构建", "流水线", "list"}, "ci +list"},
|
|
||||||
{[]string{"ci", "构建", "log", "日志"}, "ci +logs"},
|
|
||||||
// Search
|
|
||||||
{[]string{"search", "搜索", "查找", "repos"}, "search +repos"},
|
|
||||||
{[]string{"search", "搜索", "查找", "user", "用户"}, "search +users"},
|
|
||||||
// Milestone
|
|
||||||
{[]string{"milestone", "里程碑", "list"}, "milestone +list"},
|
|
||||||
{[]string{"milestone", "里程碑", "create", "新建"}, "milestone +create"},
|
|
||||||
// Webhook
|
|
||||||
{[]string{"webhook", "钩子", "list"}, "webhook +list"},
|
|
||||||
{[]string{"webhook", "钩子", "create", "新建"}, "webhook +create"},
|
|
||||||
}
|
|
||||||
|
|
||||||
bestMatch := ""
|
|
||||||
bestScore := 0
|
|
||||||
for _, r := range rules {
|
|
||||||
score := 0
|
|
||||||
for _, kw := range r.keywords {
|
|
||||||
if strings.Contains(input, kw) {
|
|
||||||
score++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if score >= 2 && score > bestScore {
|
|
||||||
bestScore = score
|
|
||||||
bestMatch = r.cmd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 降级:只匹配1个关键词
|
|
||||||
if bestMatch == "" {
|
|
||||||
for _, r := range rules {
|
|
||||||
for _, kw := range r.keywords {
|
|
||||||
if strings.Contains(input, kw) {
|
|
||||||
bestMatch = r.cmd
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if bestMatch != "" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bestMatch
|
|
||||||
}
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
internalConfig "github.com/gitlink-org/gitlink-cli/internal/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
// expandAlias rewrites args when the first positional token names a saved alias.
|
|
||||||
// Built-in commands always take precedence, so an alias can never shadow a real
|
|
||||||
// command; a colliding alias simply never expands.
|
|
||||||
func expandAlias(root *cobra.Command, args []string) ([]string, bool) {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return args, false
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
if name == "" || strings.HasPrefix(name, "-") {
|
|
||||||
return args, false
|
|
||||||
}
|
|
||||||
if isBuiltinCommand(root, name) {
|
|
||||||
return args, false
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg, err := internalConfig.Load()
|
|
||||||
if err != nil {
|
|
||||||
return args, false
|
|
||||||
}
|
|
||||||
expansion, ok := cfg.Aliases[name]
|
|
||||||
if !ok {
|
|
||||||
return args, false
|
|
||||||
}
|
|
||||||
parts := splitArgs(expansion)
|
|
||||||
if len(parts) == 0 {
|
|
||||||
return args, false
|
|
||||||
}
|
|
||||||
return append(parts, args[1:]...), true
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBuiltinCommand(root *cobra.Command, name string) bool {
|
|
||||||
for _, c := range root.Commands() {
|
|
||||||
if c.Name() == name {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, alias := range c.Aliases {
|
|
||||||
if alias == name {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// splitArgs breaks an alias expansion into argv tokens, honoring single and
|
|
||||||
// double quotes so expansions can carry multi-word flag values.
|
|
||||||
func splitArgs(s string) []string {
|
|
||||||
var args []string
|
|
||||||
var buf strings.Builder
|
|
||||||
inWord := false
|
|
||||||
var quote rune
|
|
||||||
|
|
||||||
for _, r := range s {
|
|
||||||
switch {
|
|
||||||
case quote != 0:
|
|
||||||
if r == quote {
|
|
||||||
quote = 0
|
|
||||||
} else {
|
|
||||||
buf.WriteRune(r)
|
|
||||||
}
|
|
||||||
inWord = true
|
|
||||||
case r == '\'' || r == '"':
|
|
||||||
quote = r
|
|
||||||
inWord = true
|
|
||||||
case r == ' ' || r == '\t' || r == '\n':
|
|
||||||
if inWord {
|
|
||||||
args = append(args, buf.String())
|
|
||||||
buf.Reset()
|
|
||||||
inWord = false
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
buf.WriteRune(r)
|
|
||||||
inWord = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if inWord {
|
|
||||||
args = append(args, buf.String())
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
internalConfig "github.com/gitlink-org/gitlink-cli/internal/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSplitArgs(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
in string
|
|
||||||
want []string
|
|
||||||
}{
|
|
||||||
{"", nil},
|
|
||||||
{"pr +view", []string{"pr", "+view"}},
|
|
||||||
{" issue +list ", []string{"issue", "+list"}},
|
|
||||||
{`pr +view --title "needs review"`, []string{"pr", "+view", "--title", "needs review"}},
|
|
||||||
{`repo +create --name 'my repo'`, []string{"repo", "+create", "--name", "my repo"}},
|
|
||||||
}
|
|
||||||
for _, tc := range cases {
|
|
||||||
if got := splitArgs(tc.in); !reflect.DeepEqual(got, tc.want) {
|
|
||||||
t.Fatalf("splitArgs(%q) = %#v, want %#v", tc.in, got, tc.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExpandAlias(t *testing.T) {
|
|
||||||
t.Setenv("GITLINK_CONFIG_DIR", t.TempDir())
|
|
||||||
|
|
||||||
cfg := internalConfig.DefaultConfig()
|
|
||||||
cfg.Aliases = map[string]string{
|
|
||||||
"co": "pr +view",
|
|
||||||
"config": "should never expand",
|
|
||||||
}
|
|
||||||
if err := internalConfig.Save(cfg); err != nil {
|
|
||||||
t.Fatalf("Save error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
root := &cobra.Command{Use: "gitlink-cli"}
|
|
||||||
root.AddCommand(&cobra.Command{Use: "config"})
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
name string
|
|
||||||
args []string
|
|
||||||
want []string
|
|
||||||
expand bool
|
|
||||||
}{
|
|
||||||
{"alias match", []string{"co", "42"}, []string{"pr", "+view", "42"}, true},
|
|
||||||
{"builtin wins", []string{"config", "list"}, nil, false},
|
|
||||||
{"flag first", []string{"--lang", "zh-CN"}, nil, false},
|
|
||||||
{"unknown token", []string{"nope"}, nil, false},
|
|
||||||
{"empty args", nil, nil, false},
|
|
||||||
}
|
|
||||||
for _, tc := range cases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
got, ok := expandAlias(root, tc.args)
|
|
||||||
if ok != tc.expand {
|
|
||||||
t.Fatalf("expandAlias ok = %v, want %v", ok, tc.expand)
|
|
||||||
}
|
|
||||||
if tc.expand && !reflect.DeepEqual(got, tc.want) {
|
|
||||||
t.Fatalf("expandAlias = %#v, want %#v", got, tc.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
10
cmd/root.go
10
cmd/root.go
|
|
@ -7,15 +7,14 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
aliasCmd "github.com/gitlink-org/gitlink-cli/cmd/alias"
|
|
||||||
apiCmd "github.com/gitlink-org/gitlink-cli/cmd/api"
|
apiCmd "github.com/gitlink-org/gitlink-cli/cmd/api"
|
||||||
browseCmd "github.com/gitlink-org/gitlink-cli/cmd/browse"
|
|
||||||
statusCmd "github.com/gitlink-org/gitlink-cli/cmd/status"
|
|
||||||
authCmd "github.com/gitlink-org/gitlink-cli/cmd/auth"
|
authCmd "github.com/gitlink-org/gitlink-cli/cmd/auth"
|
||||||
"github.com/gitlink-org/gitlink-cli/cmd/cmdutil"
|
"github.com/gitlink-org/gitlink-cli/cmd/cmdutil"
|
||||||
configCmd "github.com/gitlink-org/gitlink-cli/cmd/config"
|
configCmd "github.com/gitlink-org/gitlink-cli/cmd/config"
|
||||||
|
doctorCmd "github.com/gitlink-org/gitlink-cli/cmd/doctor"
|
||||||
internalConfig "github.com/gitlink-org/gitlink-cli/internal/config"
|
internalConfig "github.com/gitlink-org/gitlink-cli/internal/config"
|
||||||
"github.com/gitlink-org/gitlink-cli/internal/i18n"
|
"github.com/gitlink-org/gitlink-cli/internal/i18n"
|
||||||
|
serverCmd "github.com/gitlink-org/gitlink-cli/cmd/server"
|
||||||
"github.com/gitlink-org/gitlink-cli/shortcuts"
|
"github.com/gitlink-org/gitlink-cli/shortcuts"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -59,10 +58,9 @@ func NewRootCmd(opts RootOptions, tr *i18n.Translator) (*cobra.Command, error) {
|
||||||
rootCmd.AddCommand(authCmd.NewAuthCmd(tr))
|
rootCmd.AddCommand(authCmd.NewAuthCmd(tr))
|
||||||
rootCmd.AddCommand(apiCmd.NewAPICmd(tr))
|
rootCmd.AddCommand(apiCmd.NewAPICmd(tr))
|
||||||
rootCmd.AddCommand(configCmd.NewConfigCmd(tr))
|
rootCmd.AddCommand(configCmd.NewConfigCmd(tr))
|
||||||
|
rootCmd.AddCommand(doctorCmd.NewDoctorCmd(tr))
|
||||||
|
rootCmd.AddCommand(serverCmd.NewServerCmd())
|
||||||
rootCmd.AddCommand(newVersionCmd(version, tr))
|
rootCmd.AddCommand(newVersionCmd(version, tr))
|
||||||
rootCmd.AddCommand(aliasCmd.NewAliasCmd())
|
|
||||||
rootCmd.AddCommand(browseCmd.NewBrowseCmd())
|
|
||||||
rootCmd.AddCommand(statusCmd.NewStatusCmd())
|
|
||||||
|
|
||||||
shortcuts.RegisterAll(rootCmd, tr)
|
shortcuts.RegisterAll(rootCmd, tr)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
package status
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"github.com/gitlink-org/gitlink-cli/internal/auth"
|
|
||||||
"github.com/gitlink-org/gitlink-cli/internal/config"
|
|
||||||
"github.com/gitlink-org/gitlink-cli/internal/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewStatusCmd creates the status command that displays login state and context.
|
|
||||||
func NewStatusCmd() *cobra.Command {
|
|
||||||
return &cobra.Command{
|
|
||||||
Use: "status",
|
|
||||||
Short: "显示当前登录状态和上下文信息",
|
|
||||||
Long: `显示 gitlink-cli 的当前状态,包括:
|
|
||||||
- 认证状态(是否已登录、Token 来源)
|
|
||||||
- API 地址
|
|
||||||
- 当前目录
|
|
||||||
- 自动推断的仓库信息`,
|
|
||||||
Example: ` gitlink-cli status`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
cfg, _ := config.Load()
|
|
||||||
token, _ := auth.LoadToken()
|
|
||||||
if token == "" {
|
|
||||||
token = os.Getenv("GITLINK_TOKEN")
|
|
||||||
}
|
|
||||||
cwd, _ := os.Getwd()
|
|
||||||
|
|
||||||
fmt.Println("GitLink CLI 状态")
|
|
||||||
fmt.Println("───────────────")
|
|
||||||
|
|
||||||
// 认证状态
|
|
||||||
if token != "" {
|
|
||||||
fmt.Println(" 认证状态: 已登录")
|
|
||||||
fmt.Printf(" Token 来源: %s\n", tokenSource(token))
|
|
||||||
} else {
|
|
||||||
fmt.Println(" 认证状态: 未登录(运行 gitlink-cli auth login)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// API 地址
|
|
||||||
fmt.Printf(" API 地址: %s\n", cfg.BaseURL)
|
|
||||||
|
|
||||||
// 当前目录
|
|
||||||
fmt.Printf(" 当前目录: %s\n", cwd)
|
|
||||||
|
|
||||||
// 推断的仓库
|
|
||||||
owner, repo, err := context.ResolveOwnerRepo("", "")
|
|
||||||
if err == nil {
|
|
||||||
fmt.Printf(" 推断仓库: %s/%s\n", owner, repo)
|
|
||||||
} else {
|
|
||||||
fmt.Println(" 推断仓库: (不在 Git 仓库中)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func tokenSource(token string) string {
|
|
||||||
if token == os.Getenv("GITLINK_TOKEN") {
|
|
||||||
return "环境变量 GITLINK_TOKEN"
|
|
||||||
}
|
|
||||||
return "keyring / 配置文件"
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
package status
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewStatusCmd(t *testing.T) {
|
|
||||||
cmd := NewStatusCmd()
|
|
||||||
if cmd.Use != "status" {
|
|
||||||
t.Errorf("expected Use 'status', got %s", cmd.Use)
|
|
||||||
}
|
|
||||||
if cmd.Short == "" {
|
|
||||||
t.Error("Short description should not be empty")
|
|
||||||
}
|
|
||||||
if cmd.Long == "" {
|
|
||||||
t.Error("Long description should not be empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewStatusCmdExample(t *testing.T) {
|
|
||||||
cmd := NewStatusCmd()
|
|
||||||
if !strings.Contains(cmd.Example, "status") {
|
|
||||||
t.Errorf("Example should contain 'status', got: %s", cmd.Example)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewStatusCmdHasNoSubcommands(t *testing.T) {
|
|
||||||
cmd := NewStatusCmd()
|
|
||||||
if cmd.HasSubCommands() {
|
|
||||||
t.Error("status should not have subcommands")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTokenSourceFromEnv(t *testing.T) {
|
|
||||||
t.Setenv("GITLINK_TOKEN", "test-token-123")
|
|
||||||
result := tokenSource("test-token-123")
|
|
||||||
if result != "环境变量 GITLINK_TOKEN" {
|
|
||||||
t.Errorf("expected env source, got: %s", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTokenSourceFromKeyring(t *testing.T) {
|
|
||||||
// 不设置环境变量,或用不同的值
|
|
||||||
t.Setenv("GITLINK_TOKEN", "")
|
|
||||||
result := tokenSource("some-stored-token")
|
|
||||||
if result != "keyring / 配置文件" {
|
|
||||||
t.Errorf("expected keyring source, got: %s", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTokenSourceMismatch(t *testing.T) {
|
|
||||||
t.Setenv("GITLINK_TOKEN", "env-token")
|
|
||||||
result := tokenSource("different-token")
|
|
||||||
if result != "keyring / 配置文件" {
|
|
||||||
t.Errorf("should fallback to keyring when token differs from env, got: %s", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
# 新增命令别名(alias)
|
|
||||||
|
|
||||||
新增 `gitlink-cli alias` 根命令组,用于把常用命令行保存为用户自定义的快捷方式,行为对齐 `gh alias`。别名统一存放在配置文件(`config.yaml`)的 `aliases` 字段,通过 yaml.v3 与其他配置项一起读写,无需额外文件。
|
|
||||||
|
|
||||||
## 子命令
|
|
||||||
|
|
||||||
- `alias set <name> <expansion>...`:创建或更新别名。`name` 之后的所有参数会拼成展开内容,因此多词展开在 shell 上加不加引号都可以,例如 `alias set bugs issue +list --label bug`。
|
|
||||||
- `alias list`:按名称排序列出全部别名;没有别名时给出提示。
|
|
||||||
- `alias delete <name>`:删除别名,别名不存在时返回明确错误。
|
|
||||||
- `alias import`:从 YAML 的 `name: expansion` 映射批量导入,来源可用 `--file` 指定文件,或从标准输入读取(便于管道注入);导入采用合并语义,同名覆盖。
|
|
||||||
|
|
||||||
## 别名展开
|
|
||||||
|
|
||||||
别名展开已接入根命令分发:在 `cmd.Execute()` 里,若 `os.Args` 的第一个位置参数命中已保存的别名,就把该 token 替换为别名展开(按 shell 风格拆分,识别单双引号)后再交给 cobra 分发。展开发生在翻译器解析之后,因此 `--lang` 等全局标志不受影响。
|
|
||||||
|
|
||||||
内置命令始终优先:展开前会先检查该 token 是否为已注册的根命令(或其 cobra 别名),命中则不展开。这样别名无法遮蔽真实命令——与内置命令同名的别名只是永远不会被触发,因此 `set` 不做额外的命名冲突校验。
|
|
||||||
|
|
||||||
## 本次变更
|
|
||||||
|
|
||||||
`internal/config` 的 `Config` 增加 `Aliases map[string]string` 字段并验证读写往返;新增 `cmd/alias` 命令组与 `cmd/expand.go` 展开逻辑,并在 `cmd/root.go` 注册与接线;中英文帮助与提示文案补齐到 `en-US.json` 与 `zh-CN.json`。测试覆盖配置往返、set/list/delete、文件与标准输入导入、非法 YAML,以及展开的别名命中、内置优先、首个为标志、未知 token 等分支和引号拆分。
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
# Fix: Windows Git Bash 下 `gitlink-cli api` 路径被 MSYS2 污染导致 404
|
|
||||||
|
|
||||||
## 问题
|
|
||||||
|
|
||||||
在 Windows Git Bash(MSYS2)环境下执行:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli api GET /v1/owner/repo
|
|
||||||
```
|
|
||||||
|
|
||||||
路径参数 `/v1/owner/repo` 会被 MSYS2 自动改写为类似 `C:/Program Files/Git/v1/owner/repo` 的 Windows 路径——MSYS2 把以 `/` 开头的命令行参数当成 Unix 路径,转换为 Git 安装目录。结果 API 请求路径错误,返回 404。影响所有 Windows Git Bash 用户。
|
|
||||||
|
|
||||||
## 根因
|
|
||||||
|
|
||||||
MSYS2 的 POSIX→Windows 路径转换会对命令行参数中以 `/` 开头的字符串生效,且无法通过 shell 转义稳定规避(`MSYS_NO_PATHCONV` 等环境变量依赖用户配置,不可靠)。
|
|
||||||
|
|
||||||
## 修复
|
|
||||||
|
|
||||||
在 `cmd/api` 的 `runAPI` 中,对取到的 `path` 调用 `restoreAPIPath` 还原:
|
|
||||||
|
|
||||||
- 检测首部是否为盘符(正则 `^[A-Za-z]:/`);
|
|
||||||
- 若是,按常见 API 前缀(`/v1/` `/v2/` `/api/` `/users/` `/projects/`)在污染后的路径里定位原始起点并截取;
|
|
||||||
- 无盘符或无匹配前缀时原样返回,不影响其他平台与正常路径。
|
|
||||||
|
|
||||||
`restoreAPIPath` 为纯函数,便于单元测试。
|
|
||||||
|
|
||||||
## 影响
|
|
||||||
|
|
||||||
仅 Windows 受益,其他平台行为不变。改动集中在 `cmd/api/api.go`(约 +25 行,含函数与注释)。
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./cmd/api/... -run TestRestoreAPIPath -v
|
|
||||||
```
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
# 新增 `api --paginate` 自动翻页
|
|
||||||
|
|
||||||
`gitlink-cli api GET <PATH> --paginate` 对齐 `gh api --paginate`:自动逐页抓取并把所有条目拼接成一个数组输出,免去手动传 `page`/`limit` 逐页拉取。仅支持 GET,其它方法会明确报错而不是静默退化。
|
|
||||||
|
|
||||||
配套修复了 `PaginateAll` 无法解包真实 GitLink 列表响应的问题。此前它只认顶层裸数组或 `data` 键下的数组,而 GitLink 列表接口把数组包在资源专属键下(`{"total_count":N,"pulls":[...]}`、`{"issues":[...]}`、`{"branches":[...]}` 等),这类响应会被当成单个对象直接返回、根本不翻页。现在解析顺序为:优先取 `data` 数组;否则取 map 中唯一的数组字段(覆盖 pulls/issues/branches/labels 等);无数组字段或存在多个数组字段(歧义)时,保留“单对象作为单元素返回”的旧行为。短页终止(本页条目数小于 limit 即停止)与既有的裸数组、`data` 包裹用例保持不变。
|
|
||||||
|
|
||||||
本次变更包含 `PaginateAll` 解包逻辑修复、`--paginate` 标志与 `runAPIPaginate` 路由、中英文帮助文案,以及单元测试:client 层验证 `{total_count, issues:[...]}` 两页拼接并正确解包 `issues`;cmd 层端到端验证 `--paginate` 合并多页输出与非 GET 报错。
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
# 修复 `api` 命令单次调用不替换 `:owner/:repo` 占位符
|
|
||||||
|
|
||||||
## 背景
|
|
||||||
|
|
||||||
`gitlink-cli api <METHOD> <PATH>` 单次调用此前直接把 `<PATH>` 原样发送给服务端,**不会替换 REST 风格的 `:owner` / `:repo` 占位符**。这导致:
|
|
||||||
|
|
||||||
- 该命令自身的帮助 `Example`(如 `api POST /:owner/:repo/issues`)跑不通;
|
|
||||||
- 依赖 `:owner/:repo` 写法的 Skill / 文档(如 `api GET /:owner/:repo/commits`)报错;
|
|
||||||
- 占位符替换能力此前只存在于 `--batch-file` 批处理模式的 `{{var}}` 模板中,单次调用无法复用。
|
|
||||||
|
|
||||||
对应 Issue:`bug: api 命令单次调用不替换 :owner/:repo 占位符(0.2.0)`。
|
|
||||||
|
|
||||||
## 变更
|
|
||||||
|
|
||||||
`api` 单次调用现在按以下顺序处理路径:
|
|
||||||
|
|
||||||
1. **`{{var}}` 模板渲染**:若提供了 `--var key=value`,复用与批处理模式相同的模板引擎渲染 `<PATH>` 中的 `{{key}}`,缺失变量时报错。
|
|
||||||
2. **`:owner` / `:repo` 占位符替换**:当路径包含 `:owner` / `:repo` 时,使用与所有 shortcut 一致的解析逻辑 `context.ResolveOwnerRepo(--owner, --repo → git remote origin)` 解析仓库归属并替换;无法解析时给出明确错误提示。
|
|
||||||
3. 保持原有的「缺失前导 `/` 自动补全」行为。
|
|
||||||
|
|
||||||
不含占位符、且未传 `--var` 的调用(如 `api GET /users/me`)行为完全不变。
|
|
||||||
|
|
||||||
## 示例
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 自动从当前 git 仓库或 --owner/--repo 解析
|
|
||||||
gitlink-cli api GET /:owner/:repo/commits --owner Gitlink --repo gitlink-cli
|
|
||||||
|
|
||||||
# 单次调用也支持 {{var}} 模板
|
|
||||||
gitlink-cli api GET /v1/{{owner}}/gitlink-cli/issues --var owner=Gitlink
|
|
||||||
```
|
|
||||||
|
|
||||||
## 实现与测试
|
|
||||||
|
|
||||||
- 改动集中在 `cmd/api/api.go`:新增 `resolveAPIPath` 辅助函数与 `:owner` / `:repo` 占位符正则(`\b` 边界避免误伤 `:owner_id` 等更长 token;`ReplaceAllLiteralString` 避免 `$` 被当作正则替换引用)。
|
|
||||||
- 复用既有 `parseBatchVars` / `renderTemplate`(`cmd/api/batch.go`)与 `internal/context.ResolveOwnerRepo`,无新增依赖。
|
|
||||||
- 更新命令 `Example` 帮助文案。
|
|
||||||
- 新增单元测试:`:owner/:repo` 解析替换、单次调用 `{{var}}` 渲染、缺失模板变量报错;既有测试全部通过。
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
# api 单次调用模板变量与预演能力
|
|
||||||
|
|
||||||
这次改动把 `gitlink-cli api` 的单次调用模式和 batch 模式拉齐了。
|
|
||||||
|
|
||||||
- 单次调用现在支持 `--var key=value`,可以在路径、查询参数和 JSON 请求体里复用 `{{var}}` 模板变量。
|
|
||||||
- 路径里的 `:owner` 和 `:repo` 会自动使用当前 `--owner` / `--repo` 或 git remote 上下文渲染,修复了单次调用不替换占位符的问题。
|
|
||||||
- `--dry-run` 不再只属于 batch 模式,单次调用也可以先预览渲染后的 method、path、query、body 和 variables,再决定是否真正发请求。
|
|
||||||
|
|
||||||
这样做的目的不是单纯补一个 bug,而是让 Raw API 更适合脚本和 Agent 复用:同一份模板写法既能用在 `api --batch-file`,也能平滑退化成一次性的单条请求。
|
|
||||||
|
|
||||||
本地验证:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./cmd/api
|
|
||||||
go test ./...
|
|
||||||
go build ./...
|
|
||||||
git diff --check
|
|
||||||
go run . api --help
|
|
||||||
```
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
# Attachment Shortcut
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
This change adds a new `attachment` shortcut group to `gitlink-cli` so users can upload and delete standalone attachments without dropping down to raw API calls.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli attachment +upload -f ./build.log -d "CI build log"
|
|
||||||
gitlink-cli attachment +upload -f ./release-notes.md --container-id 42 --container-type VersionRelease
|
|
||||||
gitlink-cli attachment +delete -i 791eccbf-2e35-4301-ad95-8c937a117f40
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Coverage
|
|
||||||
|
|
||||||
- `POST /api/attachments.json`
|
|
||||||
- `DELETE /api/attachments/{uuid}.json`
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- Upload uses multipart form data and works with the same `GITLINK_TOKEN` access token flow already used by the CLI.
|
|
||||||
- Delete accepts the attachment UUID returned by the upload API.
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
# Branch Lifecycle Shortcuts
|
|
||||||
|
|
||||||
This change expands branch management coverage so repository maintainers can complete more of the branch lifecycle from `gitlink-cli` without falling back to manual API calls.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
- `branch +all`
|
|
||||||
- `branch +set-default`
|
|
||||||
- `branch +restore`
|
|
||||||
|
|
||||||
## Improvements
|
|
||||||
|
|
||||||
- `branch +list` now supports `--state` so users can inspect visible branches, deleted branches, or all branch records.
|
|
||||||
- `branch +list` now supports `--keyword` to filter branches by name on the server side.
|
|
||||||
- The README examples document the deleted-branch recovery flow so users can retrieve `branch_id` and restore the branch in one CLI workflow.
|
|
||||||
|
|
||||||
## API Mapping
|
|
||||||
|
|
||||||
| Shortcut | Method | API path |
|
|
||||||
|----------|--------|----------|
|
|
||||||
| `branch +all` | GET | `/api/v1/{owner}/{repo}/branches/all.json` |
|
|
||||||
| `branch +set-default` | PATCH | `/api/v1/{owner}/{repo}/branches/update_default_branch.json?name=...` |
|
|
||||||
| `branch +restore` | POST | `/api/v1/{owner}/{repo}/branches/restore.json` |
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
- Unit tests cover request methods, paths, query parameters, restore payloads, invalid `branch-id` validation, and HTTP error handling.
|
|
||||||
- Documentation now includes branch filtering, default-branch switching, and deleted-branch restore examples.
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
# Branch OpenAPI Shortcuts
|
|
||||||
|
|
||||||
补齐 GitLink 分支 OpenAPI 的生命周期操作,并增强现有 branch shortcut 的安全性和参数能力。
|
|
||||||
|
|
||||||
## 新增 / 增强命令
|
|
||||||
|
|
||||||
- `branch +list`:新增 `--keyword` 和 `--state all|deleted`,对齐 OpenAPI 查询参数。
|
|
||||||
- `branch +all`:调用无分页分支列表接口。
|
|
||||||
- `branch +create`:新增 `--dry-run`,预览创建分支请求。
|
|
||||||
- `branch +delete`:切换到 OpenAPI 文档中的 `DELETE /api/v1/{owner}/{repo}/branches/{branch}.json`,并新增 `--dry-run`。
|
|
||||||
- `branch +set-default`:设置仓库默认分支,支持 `--dry-run`。
|
|
||||||
- `branch +restore`:恢复已删除分支,支持 `--dry-run`。
|
|
||||||
|
|
||||||
## OpenAPI 对齐
|
|
||||||
|
|
||||||
- `GET /api/v1/{owner}/{repo}/branches.json`
|
|
||||||
- `POST /api/v1/{owner}/{repo}/branches.json`
|
|
||||||
- `GET /api/v1/{owner}/{repo}/branches/all.json`
|
|
||||||
- `DELETE /api/v1/{owner}/{repo}/branches/{branch}.json`
|
|
||||||
- `PATCH /api/v1/{owner}/{repo}/branches/update_default_branch.json`
|
|
||||||
- `POST /api/v1/{owner}/{repo}/branches/restore.json`
|
|
||||||
|
|
||||||
## 安全设计
|
|
||||||
|
|
||||||
- `branch +create`、`branch +delete`、`branch +set-default`、`branch +restore` 都支持 `--dry-run`。
|
|
||||||
- `branch +delete` 会对包含 `/` 的分支名进行路径转义,避免把 `feature/foo` 误解析为多级路径。
|
|
||||||
- `branch +restore` 校验 `--branch-id` 必须为正整数。
|
|
||||||
- `branch +list --state` 仅允许 `all` 或 `deleted`,避免无效状态参数。
|
|
||||||
|
|
||||||
## 测试
|
|
||||||
|
|
||||||
新增单元测试覆盖:
|
|
||||||
|
|
||||||
- list 查询参数。
|
|
||||||
- all endpoint。
|
|
||||||
- create payload 与 dry-run。
|
|
||||||
- delete v1 endpoint 与路径转义。
|
|
||||||
- set-default query 参数。
|
|
||||||
- restore payload。
|
|
||||||
- 无效 state / branch-id 不触发 API。
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
# Browse Shortcut
|
|
||||||
|
|
||||||
新增 `browse` Shortcut 组,在浏览器中打开仓库的各类页面,对标 `gh browse`:
|
|
||||||
|
|
||||||
- `browse +repo`
|
|
||||||
- `browse +issue --number <n>`
|
|
||||||
- `browse +pr --number <n>`
|
|
||||||
- `browse +commit --sha <sha>`
|
|
||||||
- `browse +branch [--name <branch>]`
|
|
||||||
- `browse +file --path <path> [--ref <branch>]`
|
|
||||||
- `browse +releases`
|
|
||||||
- `browse +wiki`
|
|
||||||
|
|
||||||
说明:
|
|
||||||
|
|
||||||
- Web 地址由配置的 `base_url` 推导(去掉 `/api` 后缀),因此自建实例同样适用。
|
|
||||||
- `-n/--no-browser` 只打印地址而不打开浏览器,便于脚本取用与 CI 环境。
|
|
||||||
- 浏览器启动优先使用 `$BROWSER`,否则回退到平台默认(Windows/macOS/Linux)。
|
|
||||||
|
|
||||||
同时补充了 `internal/browser` 跨平台启动器与 URL 构造的单元测试。
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
# Capability shortcut
|
|
||||||
|
|
||||||
新增 `capability` 命令组 + `internal/capability` 包,提供 GitLink 后端 API 能力探测:
|
|
||||||
|
|
||||||
- `capability +check` — 向后端发送探测请求,检查各命令模块依赖的 API 是否就绪,结果缓存 24 小时
|
|
||||||
- `capability +list` — 查看已缓存的能力探测结果
|
|
||||||
|
|
||||||
实现要点:
|
|
||||||
|
|
||||||
- 新增 `internal/capability` 包:`Registry` 记录各域(label/notification/pm/wiki/pipeline/webhook/member/milestone/export/search/workflow 等)的可用状态(Available/Unavailable/Error/Unknown),带 24 小时缓存(`~/.config/gitlink-cli/capabilities.json`)。
|
|
||||||
- 探测复用 `internal/client`,对需要 owner/repo 上下文的域自动从 git remote 推断或 `--owner/--repo` 指定。
|
|
||||||
- `+check` 输出表格(模块 / 状态 / 说明);`+list` 直接读缓存不发请求,过期会提示。
|
|
||||||
|
|
||||||
背景:不同 GitLink 实例后端能力不一致,命令调用前无法预知某模块是否可用。`capability` 组让用户/Agent 在调用前自检,提升跨实例兼容性与错误可诊断性。含完整单元测试(状态判定、探测 200/401/403/404、HTML 响应、repo 上下文等)。
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
# Catalog Shortcuts
|
|
||||||
|
|
||||||
Adds a `catalog` shortcut group for GitLink platform template lookups:
|
|
||||||
|
|
||||||
- `catalog +licenses` lists repository license templates.
|
|
||||||
- `catalog +ignores` lists repository `.gitignore` templates.
|
|
||||||
- Both commands support `--name` filtering and return the original API response in the configured output format.
|
|
||||||
|
|
||||||
This closes a small but useful OpenAPI coverage gap for repository bootstrap workflows. Agents and scripts can now discover valid license and ignore template names before creating repositories, without falling back to raw API paths.
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
# CI control shortcuts
|
|
||||||
|
|
||||||
## Background
|
|
||||||
|
|
||||||
The CI shortcut group already supported build listing, log inspection, restart,
|
|
||||||
and stop operations. Repository-level CI activation, deactivation, and
|
|
||||||
authorization checks were still documented as Raw API calls in `gitlink-ci`.
|
|
||||||
|
|
||||||
This change adds first-class CI control shortcuts.
|
|
||||||
|
|
||||||
## New shortcuts
|
|
||||||
|
|
||||||
- `ci +activate` activates CI for a repository.
|
|
||||||
- `ci +deactivate` deactivates CI for a repository.
|
|
||||||
- `ci +authorize` shows CI authorization state for a repository.
|
|
||||||
|
|
||||||
## Safety model
|
|
||||||
|
|
||||||
`ci +authorize` is read-only and can run directly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli ci +authorize --owner Gitlink --repo forgeplus
|
|
||||||
```
|
|
||||||
|
|
||||||
`ci +activate` and `ci +deactivate` change repository CI state, so they require
|
|
||||||
an explicit confirmation flag and support dry-run previews:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli ci +activate --owner Gitlink --repo forgeplus --dry-run
|
|
||||||
gitlink-cli ci +activate --owner Gitlink --repo forgeplus --yes
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli ci +deactivate --owner Gitlink --repo forgeplus --dry-run
|
|
||||||
gitlink-cli ci +deactivate --owner Gitlink --repo forgeplus --yes
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation updates
|
|
||||||
|
|
||||||
- README and README.zh-CN include CI control examples.
|
|
||||||
- `skills/gitlink-ci` now prefers `ci +activate`, `ci +deactivate`, and
|
|
||||||
`ci +authorize` instead of Raw API calls.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Unit tests cover:
|
|
||||||
|
|
||||||
- endpoint method/path mapping for activate, deactivate, and authorize;
|
|
||||||
- dry-run behavior for state-changing commands;
|
|
||||||
- `--yes` confirmation guards;
|
|
||||||
- HTTP error propagation.
|
|
||||||
|
|
||||||
Suggested verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./shortcuts/ci ./shortcuts
|
|
||||||
```
|
|
||||||
|
|
||||||
Full project verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./...
|
|
||||||
```
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
# Code history and batch file shortcuts
|
|
||||||
|
|
||||||
## Background
|
|
||||||
|
|
||||||
Agent workflows such as PR review, commit quality checks, repository health
|
|
||||||
reports, and research reproducibility audits need commit timelines, changed
|
|
||||||
files, commit diffs, and sometimes controlled multi-file updates. Before this
|
|
||||||
change, several of these operations required Raw API calls.
|
|
||||||
|
|
||||||
This change adds a larger Subtask 1 feature set around repository code history
|
|
||||||
and file operations.
|
|
||||||
|
|
||||||
## New shortcuts
|
|
||||||
|
|
||||||
Repository shortcuts:
|
|
||||||
|
|
||||||
- `repo +files` searches repository files with optional `--search` and `--ref`.
|
|
||||||
- `repo +commits` lists commits for a branch, tag, or commit ref with pagination.
|
|
||||||
- `repo +commit-files` lists files changed by a commit, with optional file-path filtering.
|
|
||||||
- `repo +commit-diff` returns a commit diff.
|
|
||||||
- `repo +tags` lists repository tags with pagination and optional name filtering.
|
|
||||||
- `repo +tag` returns one tag's metadata and target commit.
|
|
||||||
- `repo +delete-tag` deletes a repository tag after explicit confirmation.
|
|
||||||
- `repo +batch-commit` creates, updates, or deletes multiple files in one commit.
|
|
||||||
|
|
||||||
Pull request shortcuts:
|
|
||||||
|
|
||||||
- `pr +commits` lists commits included in a pull request.
|
|
||||||
|
|
||||||
## Safety model
|
|
||||||
|
|
||||||
All history and file inspection commands are read-only.
|
|
||||||
|
|
||||||
`repo +delete-tag` and `repo +batch-commit` can modify repository content, so
|
|
||||||
they require an explicit confirmation flag for remote writes:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli repo +delete-tag --owner me --repo proj --name v0.1.0 --dry-run
|
|
||||||
gitlink-cli repo +delete-tag --owner me --repo proj --name v0.1.0 --yes
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli repo +batch-commit --owner me --repo proj \
|
|
||||||
--branch master --message "docs: update" \
|
|
||||||
--files 'update:README.md:# Updated' \
|
|
||||||
--dry-run
|
|
||||||
|
|
||||||
gitlink-cli repo +batch-commit --owner me --repo proj \
|
|
||||||
--branch master --message "docs: update" \
|
|
||||||
--files 'update:README.md:# Updated' \
|
|
||||||
--yes
|
|
||||||
```
|
|
||||||
|
|
||||||
The `--files` format is:
|
|
||||||
|
|
||||||
```text
|
|
||||||
action:path[:content][;action:path[:content]...]
|
|
||||||
```
|
|
||||||
|
|
||||||
Supported actions are `create`, `update`, and `delete`. `create` and `update`
|
|
||||||
require content; `delete` does not.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Unit tests cover:
|
|
||||||
|
|
||||||
- repository file search query mapping;
|
|
||||||
- commit list pagination and ref mapping;
|
|
||||||
- commit changed-file and diff endpoints;
|
|
||||||
- repository tag list/detail/delete endpoint mapping;
|
|
||||||
- PR commit list endpoint;
|
|
||||||
- `repo +batch-commit` dry-run behavior;
|
|
||||||
- `repo +batch-commit` remote write protection without `--yes`;
|
|
||||||
- batch file operation payload construction and validation.
|
|
||||||
|
|
||||||
Suggested verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./shortcuts/repo ./shortcuts/pr
|
|
||||||
```
|
|
||||||
|
|
||||||
Full project verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make test
|
|
||||||
```
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
# Commit Inspect Shortcuts
|
|
||||||
|
|
||||||
Added `gitlink-cli commit` for read-only commit inspection.
|
|
||||||
|
|
||||||
| Command | Endpoint |
|
|
||||||
| --- | --- |
|
|
||||||
| `commit +list` | `GET /v1/{owner}/{repo}/commits` |
|
|
||||||
| `commit +files` | `GET /v1/{owner}/{repo}/commits/{sha}/files` |
|
|
||||||
| `commit +diff` | `GET /v1/{owner}/{repo}/commits/{sha}/diff` |
|
|
||||||
| `commit +blame` | `GET /v1/{owner}/{repo}/blame` |
|
|
||||||
|
|
||||||
Validation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
GOPROXY=https://goproxy.cn,direct go test ./shortcuts/commit ./shortcuts
|
|
||||||
go vet ./shortcuts/commit ./shortcuts
|
|
||||||
go run . commit --help
|
|
||||||
```
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
# Commit shortcut
|
|
||||||
|
|
||||||
新增 `commit` Shortcut 组,封装 GitLink 仓库提交(Commit)相关 OpenAPI 的常用操作,支持查看提交列表、单条提交变更文件、提交 Diff 与文件 Blame:
|
|
||||||
|
|
||||||
- `commit +list`
|
|
||||||
- `commit +view`
|
|
||||||
- `commit +diff`
|
|
||||||
- `commit +blame`
|
|
||||||
|
|
||||||
实现要点:
|
|
||||||
|
|
||||||
- `+list` 映射 `GET /v1/{owner}/{repo}/commits`,支持 `--sha`(分支 / 标签 / 提交 SHA 过滤)、`--page`(默认 `1`)、`--limit`(默认 `20`),通过查询参数传给 API。
|
|
||||||
- `+view` 映射 `GET /v1/{owner}/{repo}/commits/{sha}/files`,返回某次提交涉及的文件清单;`--sha` 为必填,同时支持分页参数。
|
|
||||||
- `+diff` 映射 `GET /v1/{owner}/{repo}/commits/{sha}/diff`,返回指定提交的 Diff 内容;`--sha` 为必填。
|
|
||||||
- `+blame` 映射 `GET /v1/{owner}/{repo}/blame`,按文件展示逐行归属;`--path` 为必填,`--sha` 缺省为 `master`,二者以 `filepath` / `sha` 查询参数提交。
|
|
||||||
- 四个命令均通过 `ResolveOwnerRepo()` 解析 `--owner` / `--repo`(支持 `-R owner/repo` 缩写与仓库默认推断),结果统一经 `ctx.Output(env)` 输出,兼容 `--format`(table / json)等全局参数。
|
|
||||||
- 路径沿用 `/v1/` 前缀约定,与 webhook / milestone / label 等组保持一致;`.json` 后缀由底层 client 自动补全。
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 列出 develop 分支最近 10 条提交
|
|
||||||
gitlink-cli commit +list --owner Gitlink --repo forgeplus --sha develop --limit 10
|
|
||||||
|
|
||||||
# 查看某次提交涉及的文件
|
|
||||||
gitlink-cli commit +view --owner Gitlink --repo forgeplus --sha abc123def
|
|
||||||
|
|
||||||
# 查看提交 Diff
|
|
||||||
gitlink-cli commit +diff --owner Gitlink --repo forgeplus --sha abc123def
|
|
||||||
|
|
||||||
# 查看 README.md 在 master 分支的 Blame 信息
|
|
||||||
gitlink-cli commit +blame --owner Gitlink --repo forgeplus --path README.md
|
|
||||||
|
|
||||||
# 使用 -R 缩写并以 JSON 输出
|
|
||||||
gitlink-cli commit +list -R Gitlink/forgeplus --page 2 --format json
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
GOPROXY=https://goproxy.cn,direct go test ./shortcuts/commit/...
|
|
||||||
go test ./...
|
|
||||||
go run . commit +list --help
|
|
||||||
```
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
# Compare Summary Shortcuts
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Adds higher-level compare shortcuts so users and AI agents can inspect commit lists and summarize branch differences without manually stitching together raw compare responses.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
| Command | Purpose |
|
|
||||||
|---------|---------|
|
|
||||||
| `gitlink-cli compare +commits` | List commits between two refs with optional author, keyword, limit, and reverse filters. |
|
|
||||||
| `gitlink-cli compare +summary` | Summarize compare metadata, commit sample, changed file totals, file status counts, top files, path groups, and extension groups. |
|
|
||||||
|
|
||||||
## Behavior
|
|
||||||
|
|
||||||
- Reuse the existing compare endpoint so branch, tag, and commit refs keep the same URL-safe encoding behavior.
|
|
||||||
- Normalize commit output into stable fields such as `subject`, `author_login`, and `committer_login`.
|
|
||||||
- Aggregate compare file data into top changed files, directory groups, extension groups, and created / modified / deleted / renamed counts.
|
|
||||||
- Mark truncated summaries when `--max-files` analyzes only part of a large compare result.
|
|
||||||
- Validate `compare +files`, `compare +commits`, and `compare +summary` numeric flags before sending API requests.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- `go test ./shortcuts/compare/...`
|
|
||||||
- `go build ./...`
|
|
||||||
- `go test ./...`
|
|
||||||
- `go run . compare +summary --owner Gitlink --repo gitlink-cli --head Mengz:mengz/compare-summary-shortcuts --base master --format json`
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
# Export shortcut
|
|
||||||
|
|
||||||
新增 `export` Shortcut 组,将仓库数据导出为 CSV / JSON 文件,支撑离线分析、科研数据抽取与外部报表:
|
|
||||||
|
|
||||||
- `export +issues` — 导出 Issue 列表(GET `/v1/:owner/:repo/issues`)
|
|
||||||
- `export +prs` — 导出 PR 列表(GET `/v1/:owner/:repo/pulls`)
|
|
||||||
- `export +contributors` — 导出贡献者统计(GET `/:owner/:repo/contributors`)
|
|
||||||
|
|
||||||
实现要点:
|
|
||||||
|
|
||||||
- 统一 `--format csv|json`(默认 csv)与 `--output` 输出路径;`issues`/`prs` 额外支持 `--state open|closed|all` 过滤与 `--page/--limit` 分页。
|
|
||||||
- CSV 表头固定(`id,title,state,created_at` 等),便于直接导入 Excel / pandas;JSON 保留原始字段,供 `workflow` 模块与科研 Skill 二次处理。
|
|
||||||
- 导出过程只读、分页拉取全量,避免一次性请求超限。
|
|
||||||
|
|
||||||
背景:此前要做仓库数据导出只能手工拼 Raw API 并自行解析分页。`export` 组将其提升为一等命令,是子任务四科研场景(贡献排行、Issue 趋势、PR 效率)的数据入口,并与 `gitlink-contributor-insight`、`gitlink-research-tracker` 等 Skill 衔接。关联 PR #15。
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
# PR View Merged Timestamp
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
`pr +view` now surfaces the merge timestamp at the top level of its output.
|
|
||||||
|
|
||||||
The non-v1 detail endpoint `/{owner}/{repo}/pulls/{id}` nests the merge time under
|
|
||||||
`pull_request.merged_at` (an ISO-8601 string such as `2026-07-05T12:52:05+08:00`),
|
|
||||||
but the CLI previously only lifted `closed_at`. Merged PRs therefore showed no merge
|
|
||||||
time, mirroring upstream issue #14.
|
|
||||||
|
|
||||||
The `closed_at` enrichment is renamed to `enrichPullRequestTimestamps` and extended so
|
|
||||||
that, when `pull_request.merged_at` is present, it is copied to `merged_at` at the top
|
|
||||||
level (and the boolean `merged`, when present, is surfaced alongside it). This matches
|
|
||||||
`gh pr view`, which exposes `mergedAt`. The existing `closed_at` behavior is unchanged.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli pr +view --owner Gitlink --repo forgeplus --id 42
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"merged_at": "2026-07-05T12:52:05+08:00",
|
|
||||||
"merged": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
# Feedback shortcut
|
|
||||||
|
|
||||||
This change adds a dedicated `feedback` shortcut group for submitting GitLink platform feedback from the CLI.
|
|
||||||
|
|
||||||
New command:
|
|
||||||
|
|
||||||
- `feedback +create`
|
|
||||||
|
|
||||||
The command wraps `POST /api/v1/{owner}/feedbacks.json` and improves the CLI experience around the narrow API payload:
|
|
||||||
|
|
||||||
- Resolves the current authenticated user with `GET /users/me` when `--user` is omitted.
|
|
||||||
- Accepts feedback text from `--content`, `--from`, and `--stdin`, combining multiple sources with blank lines.
|
|
||||||
- Adds optional metadata lines for `--category`, `--contact`, and `--repo-ref` before the body.
|
|
||||||
- Supports `--dry-run` to preview method, path, payload, and content length without submitting.
|
|
||||||
- Rejects empty feedback before making any API request.
|
|
||||||
|
|
||||||
Documentation was added to README, README.zh-CN, and `skills/gitlink-feedback/SKILL.md`.
|
|
||||||
|
|
||||||
Verification:
|
|
||||||
|
|
||||||
- `go test ./shortcuts/feedback`
|
|
||||||
- `go test ./shortcuts`
|
|
||||||
- `go test ./...`
|
|
||||||
- `go build ./...`
|
|
||||||
- `git diff --check`
|
|
||||||
- UTF-8 mojibake scan on touched files
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
# File shortcut
|
|
||||||
|
|
||||||
新增 `file` Shortcut 组,补齐 GitLink 仓库文件与目录内容操作的常用封装:
|
|
||||||
|
|
||||||
- `file +list` 列出仓库文件(`--ref` 指定分支/标签/commit,`--search` 关键词过滤)
|
|
||||||
- `file +tree` 列出文件树(`--sha` 默认 master,`--recursive` 递归,支持分页)
|
|
||||||
- `file +get` 获取文件或目录内容(`--path` 必填,`--ref` 默认 master)
|
|
||||||
- `file +create` 创建文件(`--path`/`--content`/`--message` 必填,content 自动 Base64 编码)
|
|
||||||
- `file +delete` 删除文件(`--path`/`--sha`/`--message` 必填,SHA 取自 `file +list`)
|
|
||||||
|
|
||||||
实现要点:
|
|
||||||
|
|
||||||
- `+tree` 走 `/v1/{owner}/{repo}/git/trees/{sha}`,与 git 树对象语义一致,支持 `--recursive` 与分页。
|
|
||||||
- `+get` / `+list` 经 `/sub_entries`、`/files` 等接口读取文件或目录内容。
|
|
||||||
- `+create` 调用 `/create_file`,文件内容 Base64 编码后提交;`+delete` 调用 `/delete_file`,需先从 `file +list` 取得文件 blob SHA。
|
|
||||||
- 路径统一使用 `/v1/{owner}/{repo}/` 前缀,与现有 Shortcut 组保持一致。
|
|
||||||
|
|
||||||
补充单元测试 `shortcuts/file/file_test.go`,覆盖各命令的参数解析与路径构造。
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli file +list --owner Gitlink --repo gitlink-cli
|
|
||||||
gitlink-cli file +tree --owner Gitlink --repo gitlink-cli --recursive
|
|
||||||
gitlink-cli file +get --owner Gitlink --repo gitlink-cli --path README.md
|
|
||||||
gitlink-cli file +create --owner Gitlink --repo gitlink-cli --path docs/note.md --content "hello" --message "add note"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./shortcuts/file/...
|
|
||||||
```
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
# File Content Shortcuts
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Adds a `file` shortcut group so users and AI agents can read, search, and write
|
|
||||||
repository file contents without cloning or falling back to Raw API calls.
|
|
||||||
Directory listing and README viewing remain covered by `repo +tree` and
|
|
||||||
`repo +readme`.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
| Command | Purpose |
|
|
||||||
|---------|---------|
|
|
||||||
| `gitlink-cli file +view` | View a file's contents; `--raw` prints only the decoded content |
|
|
||||||
| `gitlink-cli file +search` | Search repository files by name |
|
|
||||||
| `gitlink-cli file +create` | Create a file and commit it to a branch |
|
|
||||||
| `gitlink-cli file +update` | Update a file and commit it to a branch |
|
|
||||||
| `gitlink-cli file +delete` | Delete a file and commit the removal to a branch |
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
- `file +view` accepts `--ref` (branch, tag, or commit SHA) and `--raw`; `--raw`
|
|
||||||
fails with a clear error when the path is a directory.
|
|
||||||
- Write commands require `--path` and `--branch`; `--message` defaults to
|
|
||||||
`<action> <path>` when omitted.
|
|
||||||
- `file +create` / `file +update` accept exactly one of `--content` or
|
|
||||||
`--content-file`; providing both or neither is rejected before any request.
|
|
||||||
- `--new-branch` commits the change to a new branch created from `--branch`.
|
|
||||||
- File content is transported with `text` encoding (verified against production
|
|
||||||
gitlink.org.cn; the documented `base64` encoding is rejected there).
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Unit tests cover endpoint paths, query parameter mapping, request payload
|
|
||||||
construction, content-source validation, default commit messages, `--new-branch`
|
|
||||||
propagation, and raw content extraction from entries/README-shaped responses.
|
|
||||||
|
|
||||||
## 中文说明
|
|
||||||
|
|
||||||
### 变更内容
|
|
||||||
|
|
||||||
- 新增 `file` 快捷命令组:`+view`(查看文件内容,`--raw` 仅输出解码后的正文)、
|
|
||||||
`+search`(按文件名搜索)、`+create` / `+update` / `+delete`(通过
|
|
||||||
contents/batch API 直接提交文件增删改)。
|
|
||||||
- 无需克隆仓库即可读写文件,适合 AI Agent 读取 README、修改单个文件等场景
|
|
||||||
(响应社区 issue:API 是否支持自动读取仓库内文件)。
|
|
||||||
- 内容支持 `--content` 内联或 `--content-file` 从本地文件读取(text 编码,
|
|
||||||
已在生产环境验证,文档中的 base64 编码在生产环境会被拒绝);支持
|
|
||||||
`--new-branch` 提交到新分支。
|
|
||||||
- 更新 README 与 README.zh-CN 的功能表和使用示例。
|
|
||||||
|
|
||||||
### 国际化
|
|
||||||
|
|
||||||
命令与全部 flag 文案已接入 i18n(`cmd.file.*` / `flag.file.*`,含 en-US 与
|
|
||||||
zh-CN 两套 locale),`GITLINK_LANG=zh-CN` 下 `file --help` 输出中文帮助。
|
|
||||||
|
|
||||||
### 验证
|
|
||||||
|
|
||||||
- `go test ./...`
|
|
||||||
- `go vet ./...`
|
|
||||||
- `go run . file --help`
|
|
||||||
- `go run . file +view --help`
|
|
||||||
- 在生产 gitlink.org.cn 真实仓库验证 `+view --raw`、`+search`、`+create`、
|
|
||||||
`+update`、`+delete` 全链路
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
# Ignore group i18n key
|
|
||||||
|
|
||||||
`register.go` 用 `tr.T("cmd.ignore.short")` 设置 `ignore` 组描述,但该键在两个语言包中都缺失,导致 `--help` 里直接显示字面量 `cmd.ignore.short`:
|
|
||||||
|
|
||||||
- 补齐 `en-US.json` / `zh-CN.json` 的 `cmd.ignore.short`
|
|
||||||
- `TestRegisterAllGroupDescriptions` 增加断言:组描述不得是未解析的 i18n 键
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
# Ignore Template Shortcuts
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Added `gitlink-cli ignore +list` to expose GitLink's `.gitignore` template catalog from the CLI. This fills the OpenAPI wrapper gap for `GET /ignores` and helps repository bootstrap workflows pick a valid ignore template before creating a project.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
| Command | Method / Endpoint | Purpose |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `ignore +list` | `GET /ignores` | List all built-in `.gitignore` templates |
|
|
||||||
| `ignore +list --name Go` | `GET /ignores?name=Go` | Filter templates by name |
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
GOPROXY=https://goproxy.cn,direct go test ./shortcuts/ignore ./shortcuts
|
|
||||||
go vet ./shortcuts/ignore ./shortcuts
|
|
||||||
go run . ignore --help
|
|
||||||
go run . ignore +list --help
|
|
||||||
```
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
# Issue batch operations enhancement
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Add new Issue batch operation shortcuts to enhance issue management capabilities:
|
|
||||||
|
|
||||||
- `issue +batch-reopen` — Batch reopen closed issues by web URL issue numbers.
|
|
||||||
- `issue +batch-label` — Batch add/remove labels from issues by API issue IDs.
|
|
||||||
- `issue +batch-assign` — Batch assign/unassign users from issues by API issue IDs.
|
|
||||||
- `issue +batch-comment` — Batch add comments to issues by web URL issue numbers.
|
|
||||||
- `issue +batch-export` — Export issues to CSV or JSON format with optional filters.
|
|
||||||
- `issue +batch-import` — Create issues from CSV file.
|
|
||||||
|
|
||||||
These commands complement the existing `issue +batch-close`, `issue +batch-update`, and `issue +batch-delete` commands.
|
|
||||||
|
|
||||||
## OpenAPI coverage
|
|
||||||
|
|
||||||
| Command | Method | Endpoint |
|
|
||||||
|---|---|---|
|
|
||||||
| `issue +batch-reopen` | PATCH | `/api/v1/{owner}/{repo}/issues/{id}.json` |
|
|
||||||
| `issue +batch-label` | GET + PATCH | `/api/v1/{owner}/{repo}/issues/{id}.json` + `/api/v1/{owner}/{repo}/issues/batch_update.json` |
|
|
||||||
| `issue +batch-assign` | GET + PATCH | `/api/v1/{owner}/{repo}/issues/{id}.json` + `/api/v1/{owner}/{repo}/issues/batch_update.json` |
|
|
||||||
| `issue +batch-comment` | POST | `/api/v1/{owner}/{repo}/issues/{number}/journals.json` |
|
|
||||||
| `issue +batch-export` | GET | `/api/v1/{owner}/{repo}/issues.json` |
|
|
||||||
| `issue +batch-import` | POST | `/api/v1/{owner}/{repo}/issues.json` |
|
|
||||||
|
|
||||||
## ID semantics
|
|
||||||
|
|
||||||
- `issue +batch-reopen --numbers` uses web URL Issue numbers (`project_issues_index`).
|
|
||||||
- `issue +batch-comment --numbers` uses web URL Issue numbers (`project_issues_index`).
|
|
||||||
- `issue +batch-label --ids` uses API Issue IDs returned by Issue APIs.
|
|
||||||
- `issue +batch-assign --ids` uses API Issue IDs returned by Issue APIs.
|
|
||||||
|
|
||||||
The docs and help text explicitly call this out to avoid mixing the two ID types.
|
|
||||||
|
|
||||||
## Safety and usability
|
|
||||||
|
|
||||||
- All commands support `--dry-run` for preview.
|
|
||||||
- `issue +batch-label` and `issue +batch-assign` preserve existing labels/assigners and only add/remove specified ones.
|
|
||||||
- `issue +batch-export` supports filtering by status, assigner, milestone, keyword, and more.
|
|
||||||
- `issue +batch-import` requires a CSV file with `subject` column (required) and optional columns (`description`, `priority_id`, etc.).
|
|
||||||
- ID lists are validated as positive integers and de-duplicated.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Batch reopen issues
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli issue +batch-reopen \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--numbers 42,43,44 \
|
|
||||||
--dry-run
|
|
||||||
|
|
||||||
gitlink-cli issue +batch-reopen \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--numbers 42,43,44
|
|
||||||
```
|
|
||||||
|
|
||||||
### Batch add/remove labels
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Add labels to issues
|
|
||||||
gitlink-cli issue +batch-label \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--ids 101,102,103 \
|
|
||||||
--add 1,2 \
|
|
||||||
--dry-run
|
|
||||||
|
|
||||||
# Remove labels from issues
|
|
||||||
gitlink-cli issue +batch-label \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--ids 101,102,103 \
|
|
||||||
--remove 3,4
|
|
||||||
|
|
||||||
# Add and remove labels in one command
|
|
||||||
gitlink-cli issue +batch-label \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--ids 101,102,103 \
|
|
||||||
--add 1,2 \
|
|
||||||
--remove 3,4
|
|
||||||
```
|
|
||||||
|
|
||||||
### Batch assign/unassign users
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Assign users to issues
|
|
||||||
gitlink-cli issue +batch-assign \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--ids 101,102,103 \
|
|
||||||
--add 5,6 \
|
|
||||||
--dry-run
|
|
||||||
|
|
||||||
# Unassign users from issues
|
|
||||||
gitlink-cli issue +batch-assign \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--ids 101,102,103 \
|
|
||||||
--remove 5,6
|
|
||||||
```
|
|
||||||
|
|
||||||
### Batch add comments
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli issue +batch-comment \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--numbers 42,43,44 \
|
|
||||||
--message "This issue has been resolved in v2.0.0" \
|
|
||||||
--dry-run
|
|
||||||
|
|
||||||
gitlink-cli issue +batch-comment \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--numbers 42,43,44 \
|
|
||||||
--message "Closing as duplicate of #100"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Export issues
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Export to CSV (default)
|
|
||||||
gitlink-cli issue +batch-export \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--output issues.csv
|
|
||||||
|
|
||||||
# Export to JSON
|
|
||||||
gitlink-cli issue +batch-export \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--format json \
|
|
||||||
--output issues.json
|
|
||||||
|
|
||||||
# Export with filters
|
|
||||||
gitlink-cli issue +batch-export \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--status-id 5 \
|
|
||||||
--assigner-id 10 \
|
|
||||||
--keyword "bug" \
|
|
||||||
--output closed_bugs.csv
|
|
||||||
```
|
|
||||||
|
|
||||||
### Import issues from CSV
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create issues from CSV file
|
|
||||||
gitlink-cli issue +batch-import \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--file issues.csv \
|
|
||||||
--dry-run
|
|
||||||
|
|
||||||
gitlink-cli issue +batch-import \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--file issues.csv
|
|
||||||
```
|
|
||||||
|
|
||||||
CSV file format:
|
|
||||||
|
|
||||||
```csv
|
|
||||||
subject,description,priority_id
|
|
||||||
"Fix login bug","Users cannot login with special characters",1
|
|
||||||
"Add dark mode","Implement dark mode for the UI",2
|
|
||||||
"Update documentation","Add API reference for new endpoints",3
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
GOPROXY=https://goproxy.cn,direct go test -v -run "TestBatch" ./shortcuts/issue/...
|
|
||||||
go vet ./...
|
|
||||||
go run . issue +batch-reopen --help
|
|
||||||
go run . issue +batch-label --help
|
|
||||||
go run . issue +batch-assign --help
|
|
||||||
go run . issue +batch-comment --help
|
|
||||||
go run . issue +batch-export --help
|
|
||||||
go run . issue +batch-import --help
|
|
||||||
```
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
# issue 批量运维能力增强
|
|
||||||
|
|
||||||
本次变更把 Issue 的批量运维能力从“只能批量关闭”扩展为更完整的日常工作流:
|
|
||||||
|
|
||||||
- 新增 `issue +batch-comment`,支持按 `--numbers` 或 `--from issues.csv` 给多个 Issue 统一追加评论。
|
|
||||||
- 新增 `issue +batch-update`,支持批量更新状态、优先级、标签、负责人、关联分支、开始日期和截止日期。
|
|
||||||
- `issue +create`、`issue +update`、`issue +comment` 现在支持 `--body-file`,适合读取 Markdown 文件中的长文本。
|
|
||||||
|
|
||||||
设计上延续了现有 `issue +batch-close` 的安全思路:
|
|
||||||
|
|
||||||
- 批量命令统一支持 `--dry-run`;
|
|
||||||
- `--body` 与 `--body-file` 互斥;
|
|
||||||
- 批量更新会先读取当前 Issue,再保留已有标题、描述和元数据,避免误清空字段;
|
|
||||||
- 输出统一包含逐条结果汇总,便于 Agent 或脚本继续处理。
|
|
||||||
|
|
||||||
相关文档已同步更新:
|
|
||||||
|
|
||||||
- `README.md`
|
|
||||||
- `skills/gitlink-issue/SKILL.md`
|
|
||||||
|
|
||||||
本地验证:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./shortcuts/issue/...
|
|
||||||
go test ./shortcuts/...
|
|
||||||
go build ./...
|
|
||||||
git diff --check
|
|
||||||
go run . issue +batch-comment --help
|
|
||||||
go run . issue +batch-update --help
|
|
||||||
```
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
# Issue 批量重开 / 批量评论 shortcuts
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
为 `issue` shortcut 组新增两条批量操作命令,补齐 Issue 生命周期批量运维能力:
|
|
||||||
|
|
||||||
- `issue +batch-reopen` — 按网页 Issue 编号或 CSV 文件批量重新打开已关闭的 Issue。
|
|
||||||
- `issue +batch-comment` — 按网页 Issue 编号或 CSV 文件对多个 Issue 批量追加同一段评论。
|
|
||||||
|
|
||||||
两条命令复用了 `issue +batch-close` 的基础设施(`collectIssueNumbers`、`--numbers` / `--from` / `--dry-run`、`batchCloseSummary` 汇总结构),仅替换最后的写操作,保持与批量关闭一致的使用体验。
|
|
||||||
|
|
||||||
## 命令清单
|
|
||||||
|
|
||||||
- issue +batch-reopen
|
|
||||||
- issue +batch-comment
|
|
||||||
|
|
||||||
## OpenAPI coverage
|
|
||||||
|
|
||||||
| Command | Method | Endpoint |
|
|
||||||
|---|---|---|
|
|
||||||
| `issue +batch-reopen` | PATCH | `/api/v1/{owner}/{repo}/issues/{number}.json` |
|
|
||||||
| `issue +batch-comment` | POST | `/api/v1/{owner}/{repo}/issues/{number}/journals.json` |
|
|
||||||
|
|
||||||
## 实现要点
|
|
||||||
|
|
||||||
- 输入与 `batch-close` 一致:`--numbers/-n` 接逗号分隔的网页 Issue 编号,`--from` 读 CSV(识别 `number` / `issue_number` / `project_issues_index` 列或无表头首列),二者可叠加并自动去重;编号统一校验为正整数。
|
|
||||||
- `batch-reopen`:先 `GET` 取回 Issue 当前的 `subject` / `description`,再 `PATCH /v1/{owner}/{repo}/issues/{number}` 回传原内容并把 `status_id` 设为打开状态常量 `openIssueStatusID = 1`,避免重开时丢失标题与描述。
|
|
||||||
- `batch-comment`:`--body/-b` 为必填,对应 journal 的 `notes` 字段,逐条 `POST /v1/{owner}/{repo}/issues/{number}/journals`。
|
|
||||||
- 两条命令均支持 `--dry-run`:不发起写请求,逐条返回 `planned` 计划态,便于预览影响范围。
|
|
||||||
- 结果以 `batchCloseSummary`(`repository` / `dry_run` / `total` / `succeeded` / `failed` / `results`)输出,逐条记录 `action` / `status` / `error`;存在失败时以非零错误码退出并报失败计数。
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 批量重开指定编号的 Issue
|
|
||||||
gitlink-cli issue +batch-reopen \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--numbers 1,2,3
|
|
||||||
|
|
||||||
# 先预览,不实际改动
|
|
||||||
gitlink-cli issue +batch-reopen \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--from issues.csv \
|
|
||||||
--dry-run
|
|
||||||
|
|
||||||
# 批量给多个 Issue 追加同一段评论
|
|
||||||
gitlink-cli issue +batch-comment \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--numbers 4,5 \
|
|
||||||
--body "已在新版本修复,请验证。"
|
|
||||||
|
|
||||||
# 输出 JSON 便于脚本处理
|
|
||||||
gitlink-cli issue +batch-comment \
|
|
||||||
--owner Gitlink \
|
|
||||||
--repo forgeplus \
|
|
||||||
--numbers 4,5 \
|
|
||||||
--body "已在新版本修复,请验证。" \
|
|
||||||
--format json
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
GOPROXY=https://goproxy.cn,direct go test ./shortcuts/issue/...
|
|
||||||
go vet ./...
|
|
||||||
go run . issue +batch-reopen --help
|
|
||||||
go run . issue +batch-comment --help
|
|
||||||
go run . issue +batch-reopen --owner Gitlink --repo forgeplus --numbers 1,2,3 --dry-run --format json
|
|
||||||
go run . issue +batch-comment --owner Gitlink --repo forgeplus --numbers 4,5 --body "test" --dry-run --format json
|
|
||||||
```
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
# Issue comment management shortcuts
|
|
||||||
|
|
||||||
This change expands issue comment support from create-only to a full comment
|
|
||||||
management workflow.
|
|
||||||
|
|
||||||
- `issue +comment` now supports threaded replies through `--parent-id` and
|
|
||||||
`--reply-id`, attachment IDs, and mentioned users.
|
|
||||||
- `issue +comments` lists comments and operation records with category,
|
|
||||||
keyword, sorting, and pagination filters.
|
|
||||||
- `issue +comment-update` and `issue +comment-delete` edit or remove existing
|
|
||||||
issue comments.
|
|
||||||
- `issue +comment-replies` lists child comments for threaded conversations.
|
|
||||||
|
|
||||||
The implementation keeps the existing `issue +comment -b` behavior compatible
|
|
||||||
and adds validation for numeric comment, parent, reply, and attachment IDs
|
|
||||||
before any API request is sent.
|
|
||||||
|
|
||||||
Verification:
|
|
||||||
|
|
||||||
- `go test ./shortcuts/issue`
|
|
||||||
- `go test ./shortcuts`
|
|
||||||
- `go build ./...`
|
|
||||||
- `git diff --check`
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
# Issue 批量导出命令
|
|
||||||
|
|
||||||
新增 `gitlink-cli issue +export`,用于把筛选后的 Issue 跨页导出为 CSV、JSON 或 Markdown。维护者经常需要把 Issue 列表带出 GitLink,用于周报、迁移、离线排查或交给脚本/AI Agent 做进一步分析;过去只能手动翻页复制或依赖原始 API 拼参数,容易漏页,也不方便统一字段。
|
|
||||||
|
|
||||||
命令复用 `issue +list` 的常用筛选条件,包括状态、关键词、参与范围、作者、负责人、里程碑、状态 ID、标签和排序参数;同时增加 `--limit`、`--max` 控制导出规模,`--fields` 控制输出字段,`--export-format` 选择 CSV/JSON/Markdown,`--output` 写入文件。不传 `--output` 时会直接把导出内容输出到 stdout,方便管道处理。
|
|
||||||
|
|
||||||
实现上新增独立的 Issue 导出分页逻辑,兼容 GitLink Issue 列表返回的 `issues` 包装结构,并把嵌套的状态、优先级、作者、负责人、标签等字段规范化为稳定列。已补充单元测试覆盖筛选参数、多页导出、`--max` 截断、Markdown 转义和非法参数校验。
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
# Issue journal shortcuts
|
|
||||||
|
|
||||||
## Background
|
|
||||||
|
|
||||||
Issue comments and journal events are useful for stale issue detection, audit
|
|
||||||
trails, and triage workflows. Those reads were still documented as Raw API calls
|
|
||||||
against `/v1/:owner/:repo/issues/:number/journals`.
|
|
||||||
|
|
||||||
This change adds first-class read-only shortcuts for issue journals.
|
|
||||||
|
|
||||||
## New shortcuts
|
|
||||||
|
|
||||||
- `issue +journals` lists raw issue journal records.
|
|
||||||
- `issue +activity` uses the same journal endpoint as an activity-oriented
|
|
||||||
alias.
|
|
||||||
|
|
||||||
Both commands preserve the existing issue number convention:
|
|
||||||
|
|
||||||
- `--number` / `-n` is the preferred web-visible issue number.
|
|
||||||
- `--id` / `-i` remains a compatibility alias for the same web-visible number,
|
|
||||||
not the database ID.
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
- `--category` filters journal category, for example `comment`.
|
|
||||||
- `--page` defaults to `1`.
|
|
||||||
- `--limit` defaults to `50`.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli issue +journals --owner Gitlink --repo forgeplus --number 123 --page 1 --limit 50
|
|
||||||
gitlink-cli issue +journals --owner Gitlink --repo forgeplus --number 123 --category comment
|
|
||||||
gitlink-cli issue +activity --owner Gitlink --repo forgeplus --number 123 --category comment
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation updates
|
|
||||||
|
|
||||||
- README and README.zh-CN include journal and activity examples.
|
|
||||||
- `skills/gitlink-issue` documents the new read-only shortcuts.
|
|
||||||
- `skills/gitlink-stale-issue-manager` now uses `issue +journals` instead of
|
|
||||||
Raw API for comment history lookup.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Unit tests cover:
|
|
||||||
|
|
||||||
- endpoint path and query mapping;
|
|
||||||
- `--number` and `--id` alias behavior;
|
|
||||||
- missing issue number validation;
|
|
||||||
- HTTP error propagation.
|
|
||||||
|
|
||||||
Suggested verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./shortcuts/issue ./shortcuts
|
|
||||||
```
|
|
||||||
|
|
||||||
Full project verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./...
|
|
||||||
```
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
# Issue Reopen
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
`issue +reopen` reopens a closed issue, mirroring `issue +close`. It brings the
|
|
||||||
issue shortcut group to parity with `milestone +reopen` and `pr +reopen`, which
|
|
||||||
already had the counterpart to their close command.
|
|
||||||
|
|
||||||
Like `issue +close`, the v1 PATCH is read-modify-write, so the current issue is
|
|
||||||
fetched first and its metadata (priority, tags, assigners, linked branch, dates)
|
|
||||||
is replayed alongside the new status so unrelated fields are not reset. Only
|
|
||||||
`status_id` is flipped: `1` (open) for reopen, `5` (closed) for close. Both
|
|
||||||
commands share the same helper, so `+reopen` preserves exactly the fields
|
|
||||||
`+close` already does.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli issue +reopen --owner Gitlink --repo forgeplus --number 123
|
|
||||||
gitlink-cli issue +reopen --owner Gitlink --repo forgeplus -i 123
|
|
||||||
```
|
|
||||||
|
|
||||||
`--number` / `-n` is the project-level issue number from the web URL; `--id` /
|
|
||||||
`-i` is accepted as a compatibility alias.
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
# issue 详情增强与更新保护
|
|
||||||
|
|
||||||
这个变更聚焦修复 `issue` 快捷命令里两个容易影响实际使用的问题。
|
|
||||||
|
|
||||||
`issue +view` 之前只读取 v1 详情接口,返回结果里缺少网页端常见的状态、优先级、跟踪器和标签信息,用户很难直接把 CLI 输出和网页上的 issue 页面对应起来。这次调整后,命令会继续以 v1 接口为主,再补充读取旧版详情与编辑接口,在不影响主流程可用性的前提下,把 `number`、`database_id`、`tracker_id`、`issue_type`、`issue_tag_ids`、`issue_tag_names` 等信息一起带出来。
|
|
||||||
|
|
||||||
`issue +update`、`issue +close` 和 `issue +batch-close` 之前只保留了部分字段,更新时可能把现有 issue 的 `tracker_id`、`fixed_version_id`、`assigned_to_id`、`issue_type` 等服务端依赖字段丢掉,导致网页上出现状态异常或字段被误清空。现在这些命令会先读取 issue 的编辑元数据,再把关键字段一并回写;如果编辑元数据拉取失败,就直接终止更新,避免发送不完整的 PATCH 请求。
|
|
||||||
|
|
||||||
为了防止这类问题回归,这次补充了 `shortcuts/issue` 的单元测试,覆盖了详情增强、元数据保留、编辑元数据失败时停止写入,以及批量关闭复用同一套保护逻辑的场景。
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
# Issue and PR Journal Shortcuts
|
|
||||||
|
|
||||||
补齐 Issue 评论/操作记录与 Pull Request Review 评论相关 OpenAPI 封装。
|
|
||||||
|
|
||||||
## Issue comments / journals
|
|
||||||
|
|
||||||
新增或增强:
|
|
||||||
|
|
||||||
- `issue +comment`:添加评论,新增 `--parent-id`、`--reply-id`、`--attachment-ids`、`--receivers`、`--dry-run`。
|
|
||||||
- `issue +comments`:查看 Issue 评论和操作记录,支持 `category`、关键字、排序、分页。
|
|
||||||
- `issue +comment-update`:更新 Issue 评论,支持附件、@接收人和 `--dry-run`。
|
|
||||||
- `issue +comment-delete`:删除 Issue 评论,支持 `--dry-run`。
|
|
||||||
- `issue +comment-children`:查看指定评论的子评论。
|
|
||||||
|
|
||||||
覆盖 OpenAPI:
|
|
||||||
|
|
||||||
- `GET /api/v1/{owner}/{repo}/issues/{index}/journals.json`
|
|
||||||
- `POST /api/v1/{owner}/{repo}/issues/{index}/journals.json`
|
|
||||||
- `PATCH /api/v1/{owner}/{repo}/issues/{index}/journals/{id}.json`
|
|
||||||
- `DELETE /api/v1/{owner}/{repo}/issues/{index}/journals/{id}.json`
|
|
||||||
- `GET /api/v1/{owner}/{repo}/issues/{index}/journals/{id}/children_journals.json`
|
|
||||||
|
|
||||||
## PR review comments
|
|
||||||
|
|
||||||
新增:
|
|
||||||
|
|
||||||
- `pr +review-comments`:查看 PR Review 行评论,支持 review/state/path/parent/keyword 等过滤。
|
|
||||||
- `pr +review-comment`:创建 PR Review 行评论,支持 `comment` / `problem` 类型和 `--dry-run`。
|
|
||||||
- `pr +review-comment-update`:更新 Review 评论内容、commit 或状态,支持 `--dry-run`。
|
|
||||||
- `pr +review-comment-delete`:删除 Review 评论,支持 `--dry-run`。
|
|
||||||
|
|
||||||
覆盖 OpenAPI:
|
|
||||||
|
|
||||||
- `GET /api/v1/{owner}/{repo}/pulls/{index}/journals.json`
|
|
||||||
- `POST /api/v1/{owner}/{repo}/pulls/{index}/journals.json`
|
|
||||||
- `PUT /api/v1/{owner}/{repo}/pulls/{index}/journals/{id}.json`
|
|
||||||
- `DELETE /api/v1/{owner}/{repo}/pulls/{index}/journals/{id}.json`
|
|
||||||
|
|
||||||
## 安全设计
|
|
||||||
|
|
||||||
- 所有写入/删除评论的命令支持 `--dry-run`,先输出 method/path/body,不直接改远端数据。
|
|
||||||
- PR Review 行评论创建支持 `--diff-json` / `--diff-file`,复杂 diff payload 由用户或 Agent 明确传入,避免 CLI 猜测 line diff。
|
|
||||||
- 参数校验覆盖 Issue journal category、PR review comment type/state、布尔查询参数、正整数 ID 和 JSON diff。
|
|
||||||
|
|
||||||
## 测试
|
|
||||||
|
|
||||||
新增单元测试覆盖:
|
|
||||||
|
|
||||||
- Issue comments list/create/update/delete/children 的 method、path、query、payload。
|
|
||||||
- PR review comments list/create/update/delete 的 method、path、query、payload。
|
|
||||||
- dry-run 不触发 API。
|
|
||||||
- 参数校验失败不触发 API。
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
# Label batch safety shortcuts
|
|
||||||
|
|
||||||
## Background
|
|
||||||
|
|
||||||
The label shortcut group already supported listing, creating, updating, and
|
|
||||||
deleting issue labels. Deletion executed immediately, and larger taxonomy setup
|
|
||||||
or cleanup workflows still required repeated manual commands or Raw API calls.
|
|
||||||
|
|
||||||
This change adds safer destructive operations and first-class batch helpers.
|
|
||||||
|
|
||||||
## New and changed shortcuts
|
|
||||||
|
|
||||||
- `label +delete` now supports `--dry-run` and requires `--yes` for real
|
|
||||||
deletion.
|
|
||||||
- `label +batch-create` creates multiple labels from a semicolon-separated
|
|
||||||
`name:color:description` list.
|
|
||||||
- `label +batch-delete` deletes multiple labels from a comma-separated ID list.
|
|
||||||
|
|
||||||
## Safety model
|
|
||||||
|
|
||||||
Destructive or multi-write commands should be previewed first:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli label +delete --owner Gitlink --repo forgeplus -i 42 --dry-run
|
|
||||||
gitlink-cli label +batch-create --owner Gitlink --repo forgeplus \
|
|
||||||
--labels 'bug:#ee0701:Bug fixes;feature:#0075ca:New features' --dry-run
|
|
||||||
gitlink-cli label +batch-delete --owner Gitlink --repo forgeplus --ids 3,5,8 --dry-run
|
|
||||||
```
|
|
||||||
|
|
||||||
After confirmation, pass `--yes`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli label +delete --owner Gitlink --repo forgeplus -i 42 --yes
|
|
||||||
gitlink-cli label +batch-create --owner Gitlink --repo forgeplus \
|
|
||||||
--labels 'bug:#ee0701:Bug fixes;feature:#0075ca:New features' --yes
|
|
||||||
gitlink-cli label +batch-delete --owner Gitlink --repo forgeplus --ids 3,5,8 --yes
|
|
||||||
```
|
|
||||||
|
|
||||||
## Parsing rules
|
|
||||||
|
|
||||||
- `label +batch-create --labels` uses semicolons between labels and colons
|
|
||||||
inside each label spec: `name:color:description`.
|
|
||||||
- Missing colors default to `#1E90FF`.
|
|
||||||
- Colors are validated as `#RGB` or `#RRGGBB` before any API call.
|
|
||||||
- `label +batch-delete --ids` accepts comma-separated positive integer IDs and
|
|
||||||
removes duplicates before making requests.
|
|
||||||
|
|
||||||
## Documentation updates
|
|
||||||
|
|
||||||
- README and README.zh-CN include safe delete and batch examples.
|
|
||||||
- `skills/gitlink-label` documents the new shortcuts and safety model.
|
|
||||||
- `skills/gitlink-issue-tag` now recommends label shortcuts instead of Raw API
|
|
||||||
calls for common label workflows.
|
|
||||||
- `skills/gitlink-stale-issue-manager` uses `label +batch-create` for stale
|
|
||||||
label bootstrap steps.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Unit tests cover:
|
|
||||||
|
|
||||||
- single delete dry-run and `--yes` confirmation;
|
|
||||||
- batch-create dry-run/default preview and real API calls;
|
|
||||||
- batch-delete dry-run/default preview, de-duplication, and real API calls;
|
|
||||||
- parser validation for label specs, colors, and ID lists;
|
|
||||||
- partial batch failure reporting.
|
|
||||||
|
|
||||||
Suggested verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./shortcuts/label ./shortcuts
|
|
||||||
```
|
|
||||||
|
|
||||||
Full project verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./...
|
|
||||||
```
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
# Label clone shortcut
|
|
||||||
|
|
||||||
新增 `label +clone`,对齐 `gh label clone`:把源仓库的全部 Issue 标签复制到当前仓库。
|
|
||||||
|
|
||||||
- 用法:`label +clone --source owner/repo [--force]`。
|
|
||||||
- 语义与 `gh` 一致:按**名称**判重,目标已存在的同名标签默认**跳过**;仅在 `--force` 下就地**覆盖**(`PATCH` 标签 id,保留标签 id 与其 Issue 关联)。
|
|
||||||
- 纯组合已有端点:`GET issue_tags` 列举 + `POST` 新建 / `PATCH` 更新,不新增 API。
|
|
||||||
- 返回 `created` / `updated` / `skipped` 三组名称,便于查看每个标签的去向。
|
|
||||||
|
|
||||||
实现要点:
|
|
||||||
|
|
||||||
- 新增自包含的 `fetchLabelsForRepo(ctx, owner, repo)`,按 `page`/`limit` 翻页遍历 `issue_tags` 数组(与 `workflow` 的 `fetchAllListItems` 同一翻页范式),源仓库或目标仓库标签超过一页也能完整镜像。
|
|
||||||
- **未改动既有 `fetchLabel`**:上游 PR #363(`fix/label-update-pagination`)正在为 `fetchLabel` 加翻页,clone 走独立的 `fetchLabelsForRepo` 以避免合并冲突、也不重新引入单页 bug。
|
|
||||||
- 补充路径辅助 `repoLabelPath` / `repoLabelItemPath` 支持任意 owner/repo,原 `labelPath` / `labelItemPath` 改为其薄封装;`splitOwnerRepo` 解析 `owner/repo`(容忍首尾斜杠与多余尾部路径)。
|
|
||||||
- 单测覆盖:默认跳过同名、新建缺失标签、`--force` 就地 `PATCH`,以及 `fetchLabelsForRepo` 翻页遍历两页。
|
|
||||||
|
|
@ -1,10 +1,18 @@
|
||||||
# label shortcut
|
# Label shortcut
|
||||||
|
|
||||||
新增 `label` 命令组,支持仓库标签管理:
|
新增 `label` Shortcut 组,补齐 GitLink Issue 标签(项目标记 / `issue_tags`)OpenAPI 的常用操作封装:
|
||||||
|
|
||||||
| 命令 | 功能 |
|
- `label +list`
|
||||||
|------|------|
|
- `label +create`
|
||||||
| `label +list` | 列出仓库所有标签 |
|
- `label +update`
|
||||||
| `label +create --name <name> --color <hex>` | 创建标签 |
|
- `label +delete`
|
||||||
| `label +update --id <id> [--name <name>] [--color <hex>]` | 更新标签 |
|
|
||||||
| `label +delete --id <id>` | 删除标签 |
|
实现要点:
|
||||||
|
|
||||||
|
- 列表支持 `--keyword` 关键词过滤、`--only-name` 精简返回、`--sort-by` / `--sort-direction` 排序,映射到 API 的 `order_by` / `order_direction`。
|
||||||
|
- `+create` 的 `--color` 缺省为 `#1E90FF`;颜色统一做十六进制(`#RGB` / `#RRGGBB`)客户端校验,非法颜色在调用 API 前即报错。
|
||||||
|
- `+update` 先从列表接口取标签当前值并与传入字段合并,避免漏传字段被清空(更新接口要求 `name`/`description`/`color` 同时提交);无任何变更字段时直接报错。
|
||||||
|
- 路径使用 `/api/v1/{owner}/{repo}/issue_tags`,与 webhook/milestone 等组保持一致的 `/v1/` 前缀约定。
|
||||||
|
- 补充单元测试覆盖各命令的 HTTP 方法、路径、查询参数、payload,以及颜色校验和 id 归一化逻辑。
|
||||||
|
|
||||||
|
背景:在此之前,Issue 标签只能通过 Raw API(`issue_tags`)手工管理;`gitlink-code-review`、`gitlink-insight` 等 Skill 在做 Issue 分拣 / 打标签时都需要拼接原始请求。`label` 组将其提升为一等命令,并配套 `skills/gitlink-label/` Skill 文档,方便人类与 AI Agent 直接复用。
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
# label +update 分页取值修复
|
|
||||||
|
|
||||||
修复 `label +update` 在标签数量超过一页时静默覆盖服务端数据的问题。
|
|
||||||
|
|
||||||
`+update` 需要先取标签当前的 `name`/`description`/`color`(更新接口要求三者同时提交),此前 `fetchLabel` 只对列表接口做单次 `GET`,没有翻页;虽然注释声称“分页匹配 id”,实际当目标标签落在第二页及以后时返回“未找到”。随后 `+update` 用空描述与缺省颜色 `#1E90FF` 回填,PATCH 便把服务端真实的描述与颜色抹掉,造成数据丢失。
|
|
||||||
|
|
||||||
变更:
|
|
||||||
|
|
||||||
- `fetchLabel` 改为真正翻页(按 `page`/`limit=50` 循环,页内条数少于一页即视为末页),与仓库其他列表接口的翻页方式一致。
|
|
||||||
- 只有用户显式传入的字段才会覆盖,未传字段一律保留服务端现值,不再用缺省值静默重置。
|
|
||||||
- 全量翻页后仍找不到该 id 时直接返回明确错误,不再带缺省值发起 PATCH。
|
|
||||||
- 单元测试新增“目标标签在第二页”用例,断言 PATCH 报文保留原描述与颜色;并补充“标签不存在时报错、不 PATCH”用例。
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
# list 命令 --all 自动翻页
|
|
||||||
|
|
||||||
## 背景
|
|
||||||
|
|
||||||
`issue +list`、`pr +list`、`branch +list`、`release +list` 此前一次只能取一页,
|
|
||||||
用户或 AI Agent 想拿到全量列表必须手动循环 `--page`。代码中虽有 `PaginateAll`
|
|
||||||
翻页助手,但它只识别 `data` 包裹键;而 GitLink 生产 API 的列表响应实际用
|
|
||||||
资源名包裹数组(如 `{"total_count":N,"issues":[...]}`、`"pulls"`、`"branches"`、
|
|
||||||
`"releases"`),导致该助手在真实端点上退化为「单对象」返回,从未被任何命令使用。
|
|
||||||
|
|
||||||
## 变更内容
|
|
||||||
|
|
||||||
- `internal/client`:翻页助手对齐生产响应形状
|
|
||||||
- 新增 `PaginateAllKey(path, params, listKey)`:按指定资源键提取数组;
|
|
||||||
`listKey` 为空时自动探测(顶层数组 / `data` 包裹 / 唯一数组字段)。
|
|
||||||
- 遵循 `total_count`:达到总数即停止;另设最大页数护栏,防止
|
|
||||||
忽略 `page` 参数的端点造成死循环。
|
|
||||||
- `PaginateAll` 保持原签名,委托给 `PaginateAllKey`。
|
|
||||||
- 十九个分页 list 命令新增 `--all` 布尔参数(默认 false):
|
|
||||||
- `issue +list --all`(合并结果同样应用 number/database_id 规范化)
|
|
||||||
- `pr +list --all`、`branch +list --all`、`release +list --all`
|
|
||||||
- `milestone +list --all`、`org +list --all`、`repo +list --all`
|
|
||||||
- `search +repos --all`、`search +users --all`
|
|
||||||
- `label +list --all`、`member +list --all`、`webhook +list --all`
|
|
||||||
- `issue +comments --all`(新增子命令,见下)
|
|
||||||
- `tag +list --all`、`commit +list --all`(新增命令组,见下)
|
|
||||||
- `repo +watchers/+stargazers/+forks --all`、`org +members --all`
|
|
||||||
(资源键 `organization_users`,生产实测 gitlink 组织 71 名成员全量合并)
|
|
||||||
- `org +repos --all`(新增子命令:组织仓库列表
|
|
||||||
`/organizations/:name/projects`,此前只能裸 api 访问;
|
|
||||||
生产实测 gitlink 组织 28 个仓库全量合并)
|
|
||||||
- 对应资源键:`issues`/`pulls`/`branches`/`releases`/`milestones`/
|
|
||||||
`organizations`/`projects`/`users`/`issue_tags`/`collaborators`/`webhooks`
|
|
||||||
(均生产实测确认)
|
|
||||||
- 输出与单页响应同构:`{"total_count": N, "<资源名>": [...]}`。
|
|
||||||
- 总数字段兼容 `total_count` 与 `count`(如 `/users/:login/projects`)。
|
|
||||||
- 修复三个既有分页语义缺口(均生产实测确认端点本身分页):
|
|
||||||
- `label +list` 完全没有 `--page/--limit`(端点实际返回 `total_count`),现已补齐;
|
|
||||||
- `member +list` 既无分页又走遗留路径(非管理员直接 403),现改走
|
|
||||||
`/v1/:owner/:repo/collaborators`(支持分页且普通成员可读);
|
|
||||||
- `webhook +list` 完全没有 `--page/--limit`(探针实测:建 3 个 webhook 后
|
|
||||||
`page=2&limit=1` 返回第二条,确认端点分页),现已补齐;
|
|
||||||
- `repo +watchers/+stargazers` 完全没有分页 flag(端点实测分页,
|
|
||||||
总数键 `count`,forgeplus watchers 264 / stargazers 577),现已补齐。
|
|
||||||
- 新增 `tag +list` 命令组:平台暴露分页的 `/v1/:owner/:repo/tags`
|
|
||||||
端点(轻量 tag 与 release 不同),但 CLI 此前完全没有 tag 命令;
|
|
||||||
生产实测 forgeplus 16 个 tag 分页与 --all 合并均通过。
|
|
||||||
- 新增 `commit +list` 命令组:分页的 `/v1/:owner/:repo/commits`
|
|
||||||
端点此前只能通过裸 api 命令访问;支持 `--ref` 指定分支/tag/SHA
|
|
||||||
(映射 sha 参数,生产实测 forgeplus 6762 commits、develop 5346);
|
|
||||||
另新增 `commit +view --sha` 单提交详情(v1 无此端点,走遗留
|
|
||||||
`/api/:owner/:repo/commits/:sha`,生产实测含 diff 统计与文件列表)。
|
|
||||||
- 新增 `repo +forks`:分页的 forks 列表(总数键 `count`,
|
|
||||||
生产实测 forgeplus 77 个 fork 全量合并);此前只有 fork 创建命令。
|
|
||||||
- 修复翻页助手服务端封顶 limit 丢数据 bug:当端点把请求的 limit
|
|
||||||
封顶(如请求 100 每页只返 20)时,旧逻辑因「页内条数 < limit」提前
|
|
||||||
终止只拿到首页;现已知 total 时以 total 为准(watchers 264 条全量
|
|
||||||
合并生产实测),新增回归单测。
|
|
||||||
- 新增 `issue +comments` 子命令(对标 `gh issue view --comments`):
|
|
||||||
此前 CLI 只能发评论(`issue +comment`)无法读评论流,Agent 无法获取
|
|
||||||
issue 讨论上下文;现接 `/v1/:owner/:repo/issues/:number/journals`
|
|
||||||
(资源键 `journals`,生产实测分页 + --all 合并通过),
|
|
||||||
复用 --number/--id 语义。
|
|
||||||
- 新增 `pr +commits` 子命令:读 PR 的提交列表(commits 端点);
|
|
||||||
生产实测该端点同样忽略 page/limit 始终全量,故不暴露分页 flag。
|
|
||||||
- 新增 `pr +comments` 子命令:读 PR 评论流
|
|
||||||
(`/v1/:owner/:repo/pulls/:number/journals`)。生产实测该端点
|
|
||||||
忽略 page/limit 始终返回全量,故不暴露分页 flag(避免假分页语义)。
|
|
||||||
- 未加 `--all` 的 list 端点均经生产验证为非分页或未部署:
|
|
||||||
`licenses`/`ignores` 返回全量数组;`pm/pipelines` 404 未部署;
|
|
||||||
`pipeline +runs` 用 `total_data` 非标准包裹;dataset 端点未部署(平台 issue #144255)。
|
|
||||||
- 中英文 i18n 新增 `flag.all` 文案。
|
|
||||||
|
|
||||||
## 命令示例
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 拉取仓库全部 open issue(自动翻页合并)
|
|
||||||
gitlink-cli issue +list --state open --all --format json
|
|
||||||
|
|
||||||
# 全部分支 / 全部 PR / 全部 release / 全部里程碑
|
|
||||||
gitlink-cli branch +list --all
|
|
||||||
gitlink-cli pr +list --state all --all
|
|
||||||
gitlink-cli release +list --all
|
|
||||||
gitlink-cli milestone +list --all
|
|
||||||
|
|
||||||
# 全部组织 / 某用户全部仓库 / 搜索结果全量
|
|
||||||
gitlink-cli org +list --all
|
|
||||||
gitlink-cli repo +list --user Taoyouce --all
|
|
||||||
gitlink-cli search +repos --keyword gitlink --all
|
|
||||||
```
|
|
||||||
|
|
||||||
## 测试
|
|
||||||
|
|
||||||
- `internal/client/pagination_test.go`:资源键包裹多页合并、`total_count`
|
|
||||||
截断(模拟忽略 page 的异常端点)、`data` 包裹、唯一数组字段自动探测、
|
|
||||||
单对象回退、指定键缺失回退,共 6 个用例。
|
|
||||||
- `shortcuts/issue`:`--all` 端到端用例验证按页请求序列与合并。
|
|
||||||
- `go test ./...`、`go vet`、`gofmt` 全部通过。
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
# 维护者工作流 Skills
|
|
||||||
|
|
||||||
本次补充了两条面向仓库维护者的新 Skill,重点解决“交接信息散落”和“旧分支不敢清理”这两类高频但容易被忽略的问题。
|
|
||||||
|
|
||||||
## 新增内容
|
|
||||||
|
|
||||||
### 1. `gitlink-maintainer-handoff`
|
|
||||||
|
|
||||||
- 汇总站内消息、仓库健康度、开放 PR、开放 Issue 和最近发布
|
|
||||||
- 输出可直接交给下一位维护者的 Markdown 交接摘要
|
|
||||||
- 默认只读,适合值班交接、周报、比赛期间的维护汇总
|
|
||||||
|
|
||||||
### 2. `gitlink-branch-hygiene`
|
|
||||||
|
|
||||||
- 面向分支治理场景,识别默认分支、主分支别名、保护分支、无 PR 分支和可删除候选
|
|
||||||
- 把 compare 的零差异返回 `[-2] 分支内容相同,无需创建合并请求` 解释为正常治理信号,而不是失败
|
|
||||||
- 先输出计划,再要求用户确认删除,避免误删分支
|
|
||||||
|
|
||||||
## 文档与示例
|
|
||||||
|
|
||||||
- 新增 `skills/gitlink-maintainer-handoff/SKILL.md`
|
|
||||||
- 新增 `skills/gitlink-maintainer-handoff/examples/gitlink-cli-maintainer-handoff.md`
|
|
||||||
- 新增 `skills/gitlink-branch-hygiene/SKILL.md`
|
|
||||||
- 新增 `skills/gitlink-branch-hygiene/examples/gitlink-cli-branch-hygiene.md`
|
|
||||||
- 更新 `skills/README.md`
|
|
||||||
|
|
||||||
## 平台验证
|
|
||||||
|
|
||||||
本次示例在 Codex 中完成验证,触发方式为自然语言请求 + `gitlink-cli` 实际命令执行。Skill 本身仅依赖 Markdown 规则和标准 CLI 调用,也兼容 Claude Code、Cursor 等可读取 `SKILL.md` 的主流 Agent 平台。
|
|
||||||
|
|
||||||
## 本地验证命令
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go run . user +me --format json
|
|
||||||
go run . api GET "users/Mengz/messages.json" --query "status=1&limit=20" --format json
|
|
||||||
go run . workflow +repo-report --owner Gitlink --repo gitlink-cli --lang zh-CN --format markdown
|
|
||||||
go run . workflow +pr-summary --owner Gitlink --repo gitlink-cli --number 209 --lang zh-CN --format markdown
|
|
||||||
go run . issue +list --owner Gitlink --repo gitlink-cli --state open --limit 10 --format json
|
|
||||||
go run . release +list --owner Gitlink --repo gitlink-cli --limit 3 --format json
|
|
||||||
go run . branch +list --owner Gitlink --repo gitlink-cli --format json
|
|
||||||
go run . compare +view --owner Gitlink --repo gitlink-cli --head fix/pr-review-journal-sync --base master --format json
|
|
||||||
go run . compare +view --owner Gitlink --repo gitlink-cli --head main --base master --format json
|
|
||||||
```
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
# Member application shortcuts
|
|
||||||
|
|
||||||
This change extends `gitlink-cli member` from direct collaborator and invite-link operations to the project membership application workflow.
|
|
||||||
|
|
||||||
New shortcuts:
|
|
||||||
|
|
||||||
- `member +applications` lists project membership applications for a user inbox with `--user`, `--page`, and `--per-page`.
|
|
||||||
- `member +accept-application` accepts an application by `applied_projects[].id` and supports `--dry-run`.
|
|
||||||
- `member +refuse-application` refuses an application by `applied_projects[].id` and supports `--dry-run`.
|
|
||||||
- `member +apply` applies to join a project with an application code and requested role, also supporting `--dry-run`.
|
|
||||||
|
|
||||||
The implementation follows the documented GitLink OpenAPI endpoints:
|
|
||||||
|
|
||||||
- `GET /api/users/{owner}/applied_projects.json`
|
|
||||||
- `POST /api/users/{owner}/applied_projects/{id}/accept.json`
|
|
||||||
- `POST /api/users/{owner}/applied_projects/{id}/refuse.json`
|
|
||||||
- `POST /api/applied_projects.json`
|
|
||||||
|
|
||||||
Safety details:
|
|
||||||
|
|
||||||
- Application decisions validate positive integer IDs before calling the API.
|
|
||||||
- Application role values are normalized to `manager`, `developer`, or `reporter`.
|
|
||||||
- Dry-run output includes the method, path, and request body where applicable.
|
|
||||||
- When `--user` is omitted, the shortcut uses `--owner` first and falls back to `GET /users/me`.
|
|
||||||
|
|
||||||
Verification:
|
|
||||||
|
|
||||||
- `go test ./shortcuts/member`
|
|
||||||
- `go test ./shortcuts`
|
|
||||||
- `go test ./...`
|
|
||||||
- `go build ./...`
|
|
||||||
- `go run ./internal/i18n/cmd/check --scan-code`
|
|
||||||
- `git diff --check`
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
# Message Settings Shortcut
|
|
||||||
|
|
||||||
Added a new `message-settings` shortcut group for managing notification delivery preferences from the CLI.
|
|
||||||
|
|
||||||
Included commands:
|
|
||||||
|
|
||||||
- `message-settings +catalog`
|
|
||||||
- `message-settings +view`
|
|
||||||
- `message-settings +update`
|
|
||||||
- `message-settings +preset`
|
|
||||||
|
|
||||||
This change normalizes the setting catalog into stable `Group::Key` identifiers, enriches user settings with group and display names, supports safe partial updates with `--dry-run`, and preserves unspecified settings while updating only the selected keys or groups.
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
# Message Center Shortcuts
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
This change adds a new `message` shortcut group for personal inbox management in GitLink CLI.
|
|
||||||
It covers message listing, unread counters, batch mark-as-read, and batch delete workflows without forcing users to drop down to raw API calls.
|
|
||||||
|
|
||||||
## Included Commands
|
|
||||||
|
|
||||||
- `message +list` filters inbox items by message type, read status, page, and limit.
|
|
||||||
- `message +stats` returns unread counters for notifications and `@me` messages.
|
|
||||||
- `message +read` marks selected message IDs, or all unread messages of a given type, as read.
|
|
||||||
- `message +delete` deletes selected message IDs, or all unread messages of a given type.
|
|
||||||
|
|
||||||
## Usability Details
|
|
||||||
|
|
||||||
- `--login` defaults to the authenticated user when omitted.
|
|
||||||
- `--dry-run` is supported for write operations so users can inspect destructive requests first.
|
|
||||||
- `content_text` is added to list output to expose HTML-free plain text that is easier to grep, diff, and script.
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
- Added shortcut tests for list, stats, read, delete, ID parsing, and content normalization.
|
|
||||||
- Verified registration by wiring the `message` group into the global shortcut registry.
|
|
||||||
- Updated `README.md` with feature coverage and usage examples.
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
# Meta and Attachment Shortcuts
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
This change adds high-level shortcuts for GitLink OpenAPI endpoints that were previously only reachable through Raw API:
|
|
||||||
|
|
||||||
- `meta +licenses` → `GET /api/licenses.json`
|
|
||||||
- `meta +ignores` → `GET /api/ignores.json`
|
|
||||||
- `attachment +upload` → `POST /api/attachments.json`
|
|
||||||
- `attachment +delete` → `DELETE /api/attachments/{uuid}.json`
|
|
||||||
|
|
||||||
## User Value
|
|
||||||
|
|
||||||
- Maintainers can query license and `.gitignore` templates before creating repositories.
|
|
||||||
- Agents can upload files once, capture the returned attachment UUID/URL, and reuse it in Issue/PR/comment workflows.
|
|
||||||
- Destructive attachment deletion supports `--dry-run` to preview the request before remote mutation.
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
- Unit tests cover query parameters, multipart upload fields, dry-run behavior, missing local files, and deletion.
|
|
||||||
- README and Skill docs include command examples and Agent safety guidance.
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
# milestone +create 放宽必填参数
|
|
||||||
|
|
||||||
## 背景
|
|
||||||
|
|
||||||
`milestone +create` 之前把 `--description` 与 `--due-date` 标记为必填,
|
|
||||||
但平台 v1 API 创建里程碑仅要求 `name`(已在生产环境
|
|
||||||
`POST /api/v1/:owner/:repo/milestones` 用仅含 `{"name": ...}` 的请求验证成功)。
|
|
||||||
CLI 侧的额外限制迫使用户为可选字段编造内容。
|
|
||||||
|
|
||||||
## 变更
|
|
||||||
|
|
||||||
- `--description` 与 `--due-date` 改为可选;未提供时请求体中省略对应字段。
|
|
||||||
- `milestonePayload` 的 `requireAll` 参数收敛为 `requireName`,仅校验 `--name`。
|
|
||||||
|
|
||||||
## 验证
|
|
||||||
|
|
||||||
- `go test ./shortcuts/milestone/`(新增 `TestMilestoneCreateNameOnly`
|
|
||||||
断言未提供的可选字段不出现在请求体中)
|
|
||||||
- `go vet ./...`
|
|
||||||
- 生产 gitlink.org.cn 实测:`milestone +create -n <name>` 仅带名称创建成功。
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
# milestone +report shortcut
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This change adds a new `gitlink-cli milestone +report` shortcut for repository
|
|
||||||
maintainers and AI agents who need a quick milestone health summary before a
|
|
||||||
release or iteration close-out.
|
|
||||||
|
|
||||||
The command accepts either `--id` or `--name`, resolves the target milestone,
|
|
||||||
collects all linked issues across pages, and outputs a structured report with:
|
|
||||||
|
|
||||||
- milestone metadata and completion percentage
|
|
||||||
- total/open/closed issue counts
|
|
||||||
- close-readiness blockers and warnings
|
|
||||||
- open issue breakdown by status, priority, assignee, and tag
|
|
||||||
- sample open issues for recent activity, unassigned work, and commented threads
|
|
||||||
|
|
||||||
## Why it matters
|
|
||||||
|
|
||||||
The existing milestone shortcuts cover CRUD and status changes, but they do not
|
|
||||||
help a maintainer answer practical questions such as:
|
|
||||||
|
|
||||||
- Is this milestone ready to close?
|
|
||||||
- How many open issues are still unassigned?
|
|
||||||
- Which priorities or tags dominate the remaining work?
|
|
||||||
- Which open issues should I inspect first?
|
|
||||||
|
|
||||||
`milestone +report` turns those checks into one command and keeps the output
|
|
||||||
machine-friendly for scripts and AI agents.
|
|
||||||
|
|
||||||
## Pagination safeguard
|
|
||||||
|
|
||||||
The report implementation now fetches milestone issues until the API-reported
|
|
||||||
total is fully collected, instead of assuming the server always honors the
|
|
||||||
requested `limit`.
|
|
||||||
|
|
||||||
This avoids undercounting when the service caps each response page below the
|
|
||||||
requested size. For filtered milestone issue views, the implementation uses the
|
|
||||||
matching filtered totals (`opened_issues_count` / `closed_issues_count`) so it
|
|
||||||
does not over-fetch extra pages.
|
|
||||||
|
|
||||||
## Example commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli milestone +report --owner Gitlink --repo forgeplus --id 2438 --sample-limit 3
|
|
||||||
gitlink-cli milestone +report --owner Gitlink --repo forgeplus --name "v1.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Files changed
|
|
||||||
|
|
||||||
- `shortcuts/milestone/milestone.go`
|
|
||||||
- `shortcuts/milestone/report.go`
|
|
||||||
- `shortcuts/milestone/report_test.go`
|
|
||||||
- `README.md`
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./shortcuts/milestone/...
|
|
||||||
go test ./shortcuts/...
|
|
||||||
go test ./...
|
|
||||||
go build ./...
|
|
||||||
go run . milestone +report --owner Gitlink --repo forgeplus --id 2438 --sample-limit 3 --format json
|
|
||||||
```
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
# 变更说明:新增 Shortcut 模块(wiki/commit/file/star/watch)+ 批量操作
|
|
||||||
|
|
||||||
## 概述
|
|
||||||
|
|
||||||
本 PR 新增 5 个 Shortcut 模块(共 23 个命令)和 3 个批量操作模块。
|
|
||||||
|
|
||||||
## 新增模块
|
|
||||||
|
|
||||||
### 1. Wiki 模块(5 个命令)
|
|
||||||
|
|
||||||
| 命令 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| `wiki +list` | 列出 Wiki 页面 |
|
|
||||||
| `wiki +view` | 查看 Wiki 页面内容 |
|
|
||||||
| `wiki +create` | 创建 Wiki 页面 |
|
|
||||||
| `wiki +update` | 更新 Wiki 页面 |
|
|
||||||
| `wiki +delete` | 删除 Wiki 页面(自动清理 Sidebar) |
|
|
||||||
|
|
||||||
**技术要点**:
|
|
||||||
- Wiki API 使用独立网关 `gateway.gitlink.org.cn`,不带 `.json` 后缀
|
|
||||||
- 使用 `client.DoRaw()` 方法处理非标准 API 响应
|
|
||||||
- delete 命令会自动清理 `_Sidebar` 中的残留链接
|
|
||||||
|
|
||||||
### 2. Commit 模块(4 个命令)
|
|
||||||
|
|
||||||
| 命令 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| `commit +list` | 提交历史列表 |
|
|
||||||
| `commit +view` | 查看提交详情 |
|
|
||||||
| `commit +diff` | 查看提交差异 |
|
|
||||||
| `commit +blame` | 代码追溯 |
|
|
||||||
|
|
||||||
### 3. File 模块(5 个命令)
|
|
||||||
|
|
||||||
| 命令 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| `file +list` | 列出目录文件 |
|
|
||||||
| `file +tree` | 文件树 |
|
|
||||||
| `file +get` | 获取文件内容 |
|
|
||||||
| `file +create` | 创建文件(自动 base64 编码) |
|
|
||||||
| `file +delete` | 删除文件 |
|
|
||||||
|
|
||||||
### 4. Star 模块(3 个命令)
|
|
||||||
|
|
||||||
| 命令 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| `star +star` | 点赞仓库 |
|
|
||||||
| `star +unstar` | 取消点赞 |
|
|
||||||
| `star +stars` | 查看点赞列表 |
|
|
||||||
|
|
||||||
### 5. Watch 模块(3 个命令)
|
|
||||||
|
|
||||||
| 命令 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| `watch +watch` | 关注仓库 |
|
|
||||||
| `watch +unwatch` | 取消关注 |
|
|
||||||
| `watch +watchers` | 查看关注者列表 |
|
|
||||||
|
|
||||||
## 新增批量操作
|
|
||||||
|
|
||||||
| 模块 | 命令 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| member | `batch-add` | 批量添加成员 |
|
|
||||||
| org | `batch-invite` | 批量邀请成员(自动解析用户名→ID) |
|
|
||||||
| repo | `batch-create` | 批量创建仓库 |
|
|
||||||
| repo | `batch-fork` | 批量 Fork 仓库 |
|
|
||||||
| repo | `batch-delete` | 批量删除仓库 |
|
|
||||||
|
|
||||||
所有批量操作支持 `--dry-run` 预览模式和 `--from CSV` 文件输入。
|
|
||||||
|
|
||||||
## 测试覆盖
|
|
||||||
|
|
||||||
| 模块 | 测试数 |
|
|
||||||
|------|--------|
|
|
||||||
| wiki | 12 |
|
|
||||||
| commit | 4+ |
|
|
||||||
| file | 5+ |
|
|
||||||
| star | 3+ |
|
|
||||||
| watch | 3+ |
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
> ⚠️ 本 PR 的代码基于旧版 API 签名(`Shortcuts()` 无参数),需适配上游新版 i18n 翻译器接口(`Shortcuts(tr *i18n.Translator)`)后方可编译通过。
|
|
||||||
>
|
|
||||||
> Wiki 模块依赖 `client.DoRaw()` 方法(用于不带 `.json` 后缀的网关 API),需合入 client.go 的相关变更。
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
# notification shortcut
|
|
||||||
|
|
||||||
新增 `notification` 命令组,支持用户通知管理:
|
|
||||||
|
|
||||||
| 命令 | 功能 |
|
|
||||||
|------|------|
|
|
||||||
| `notification +list` | 列出通知列表 |
|
|
||||||
| `notification +view --id <id>` | 查看通知详情 |
|
|
||||||
| `notification +read --id <id>` | 标记通知已读 |
|
|
||||||
| `notification +delete --id <id>` | 删除通知 |
|
|
||||||
|
|
||||||
修复:`issue +create` 命令的 `--label` 参数现在会正确传入请求 body。
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
# Notification shortcuts
|
|
||||||
|
|
||||||
## Background
|
|
||||||
|
|
||||||
GitLink exposes user messages and notifications through the messages API. The
|
|
||||||
existing `gitlink-notification-digest` Skill had to call Raw API paths directly
|
|
||||||
to list notifications and mark messages as read. That made agent workflows more
|
|
||||||
fragile and forced users to remember GitLink's "messages" terminology.
|
|
||||||
|
|
||||||
This change adds a first-class `notification` shortcut group.
|
|
||||||
|
|
||||||
## New shortcuts
|
|
||||||
|
|
||||||
- `notification +list` lists messages for the authenticated user or a specified
|
|
||||||
user, with type/status/page/limit filters.
|
|
||||||
- `notification +read` marks selected messages as read, or marks all unread
|
|
||||||
messages of a selected type as read.
|
|
||||||
- `notification +delete` deletes selected messages.
|
|
||||||
- `notification +send-atme` sends @ mention messages for `Journal`, `Issue`, or
|
|
||||||
`PullRequest` targets.
|
|
||||||
|
|
||||||
## Safety model
|
|
||||||
|
|
||||||
Read-only listing runs directly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli notification +list --status unread --limit 20
|
|
||||||
```
|
|
||||||
|
|
||||||
Remote write operations require explicit confirmation and support dry-run
|
|
||||||
previews:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli notification +read --ids 740214,740213 --dry-run
|
|
||||||
gitlink-cli notification +read --ids 740214,740213 --yes
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli notification +delete --ids 740214,740213 --dry-run
|
|
||||||
gitlink-cli notification +delete --ids 740214,740213 --yes
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli notification +send-atme --receivers alice,bob \
|
|
||||||
--atmeable-type Issue --atmeable-id 123 --dry-run
|
|
||||||
```
|
|
||||||
|
|
||||||
`notification +read --all-unread` maps to GitLink's `ids: [-1]` convention.
|
|
||||||
The delete command does not expose `--all-unread` to avoid accidental broad
|
|
||||||
deletion.
|
|
||||||
|
|
||||||
## Documentation updates
|
|
||||||
|
|
||||||
- README and README.zh-CN include notification usage examples.
|
|
||||||
- `skills/gitlink-notification/` documents the new shortcut group.
|
|
||||||
- `skills/gitlink-notification-digest` now prefers `notification +list` and
|
|
||||||
`notification +read` instead of Raw API calls.
|
|
||||||
- The Skills overview lists the new notification Skill.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Unit tests cover:
|
|
||||||
|
|
||||||
- list endpoint path, filters, pagination, and current-user fallback;
|
|
||||||
- dry-run behavior for all write operations;
|
|
||||||
- confirmation guard without `--yes`;
|
|
||||||
- read payload construction for selected IDs and all unread messages;
|
|
||||||
- delete payload construction;
|
|
||||||
- send-atme payload construction and validation;
|
|
||||||
- invalid argument handling before remote calls.
|
|
||||||
|
|
||||||
Suggested verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./shortcuts/notification
|
|
||||||
```
|
|
||||||
|
|
||||||
Full project verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./...
|
|
||||||
```
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
# org team shortcuts
|
|
||||||
|
|
||||||
This change expands the `org` shortcut group from basic organization lookup into an operational workflow for team and membership management.
|
|
||||||
|
|
||||||
The updated command set keeps `org +list`, `org +info`, and `org +create`, and adds stronger organization administration coverage:
|
|
||||||
|
|
||||||
- `org +members` now supports `--all`, `--team`, `--login`, and `--keyword`, and automatically fetches all pages before applying filters.
|
|
||||||
- `org +teams` lists organization teams with `--authorize`, `--unit`, `--keyword`, and `--include-users` support, returning normalized team summaries.
|
|
||||||
- `org +team-create` creates a team and supports `--dry-run` for request preview.
|
|
||||||
- `org +member-remove` removes an organization member by `--user-id` or resolves `--login` automatically, with `--dry-run` support.
|
|
||||||
|
|
||||||
The output shape is normalized for automation use. Member results now include matched counts, team summaries, and consistent user fields. Team results expose permission-level aggregation, unit information, and optional normalized user details.
|
|
||||||
|
|
||||||
The command examples in `README.md` were extended so the new team and member management flows are discoverable from the main project documentation.
|
|
||||||
|
|
||||||
Validation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./shortcuts/org/...
|
|
||||||
go test ./shortcuts/...
|
|
||||||
go test ./...
|
|
||||||
go build ./...
|
|
||||||
```
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
# 全局 `--jq` 输出字段提取
|
|
||||||
|
|
||||||
## 动机
|
|
||||||
|
|
||||||
对标 `gh --jq`:Agent 与 shell 脚本消费 CLI 输出时经常只需要单个字段
|
|
||||||
(如最新 commit 的 sha、issue 总数),此前必须依赖外部 `jq` 或自行解析
|
|
||||||
完整 JSON 信封。新增零依赖的点分路径提取,纯 Go 标准库实现,
|
|
||||||
不引入任何第三方 JSON 查询库(避免供应链风险)。
|
|
||||||
|
|
||||||
## 行为
|
|
||||||
|
|
||||||
- 新增全局持久 flag `--jq`,对所有命令生效(不占用 `--query`,与 api 子命令的查询参数 flag 无冲突)。
|
|
||||||
- 路径语法:点分段;段为对象键,或非负整数作为数组下标。
|
|
||||||
例:`--jq data.commits.0.sha`、`--jq data.total_issues_count`。
|
|
||||||
- 标量(字符串/数字/布尔/null)输出裸值,方便 shell 管道直接消费;
|
|
||||||
对象与数组输出缩进 JSON。
|
|
||||||
- 错误信息可操作:键不存在时列出该层全部可用键(排序后);
|
|
||||||
数组下标越界/非数字段给出数组长度提示。
|
|
||||||
|
|
||||||
## 生产实测
|
|
||||||
|
|
||||||
- `issue +list --limit 2 --jq data.issues.0.subject` → 裸标题字符串
|
|
||||||
- `issue +list --jq data.total_issues_count` → `4197`
|
|
||||||
- 误键 `--jq data.issues.0.name` → 报错并列出 29 个可用键(含 `subject`)
|
|
||||||
|
|
||||||
## 测试
|
|
||||||
|
|
||||||
`internal/output/query_test.go` 6 个单测:字符串/数字标量裸值输出、
|
|
||||||
对象 JSON 输出、缺键错误含可用键列表、下标越界、数组段非数字。
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
# 表格输出确定性排序与错误状态码展示
|
|
||||||
|
|
||||||
## 背景
|
|
||||||
|
|
||||||
`--format table` 的渲染依赖 Go map 的遍历顺序,而 Go 的 map 遍历是**随机的**:
|
|
||||||
|
|
||||||
- `printMapTable`(单对象 KEY/VALUE 表)每次运行的行顺序都不一样;
|
|
||||||
- `collectKeys` 在补全非优先列时直接 `for k := range m`,导致 `printSliceTable`(列表表)优先列之后的**列顺序**也随机。
|
|
||||||
|
|
||||||
这会让同一条命令两次运行的表格输出不一致,难以肉眼对比、`diff`、截图或在脚本/测试中稳定断言。
|
|
||||||
|
|
||||||
此外,table 模式的错误输出只显示 `Error: <message>`,**不显示状态码**,用户难以快速区分 404 / 422 / 500。
|
|
||||||
|
|
||||||
## 变更
|
|
||||||
|
|
||||||
- `collectKeys`:优先列(id/name/login/title/status/state/created_at/updated_at)之后的剩余列改为 `sort.Strings` 排序,列顺序稳定且可预期。
|
|
||||||
- `printMapTable`:改为按 `collectKeys` 的顺序输出,行顺序确定,并与列表表的列顺序保持一致。
|
|
||||||
- 错误输出:当存在错误码时显示 `Error [<code>]: <message>`(无错误码时保持 `Error: <message>`),便于快速识别状态码。
|
|
||||||
|
|
||||||
不影响 json / yaml 输出,也不改变成功数据的内容,仅稳定其呈现顺序与丰富错误提示。
|
|
||||||
|
|
||||||
## 测试
|
|
||||||
|
|
||||||
`internal/output/formatter_test.go` 新增:
|
|
||||||
- `collectKeys` 非优先列按字典序排序;
|
|
||||||
- 单对象表渲染 25 次输出完全一致(确定性);
|
|
||||||
- 列表表表头顺序 25 次渲染一致;
|
|
||||||
- 错误输出包含状态码 `Error [500]: ...`,无错误码时回退为 `Error: ...`。
|
|
||||||
|
|
||||||
`go build ./...`、`go vet ./...`、`go test ./...`、`gofmt -s` 全部通过。
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
# PM (项目管理) shortcut
|
|
||||||
|
|
||||||
新增 `pm` Shortcut 组,封装 GitLink 项目管理相关只读接口,补齐仓库协作元数据的命令化访问:
|
|
||||||
|
|
||||||
- `pm +dashboards` — 查看项目仪表盘数据
|
|
||||||
- `pm +sprints` — 查看 Sprint 任务列表
|
|
||||||
- `pm +weekly` — 查看周报任务
|
|
||||||
- `pm +tags` — 查看项目 Issue 标签
|
|
||||||
- `pm +pipelines` — 查看项目 CI/CD 流水线列表
|
|
||||||
- `pm +runs` — 查看项目 Action 运行记录
|
|
||||||
|
|
||||||
实现要点:
|
|
||||||
|
|
||||||
- 全部为只读(GET)命令,通过 Raw API 访问项目管理后端,统一 `owner/repo` 自动解析与 `--format json|table|yaml` 输出。
|
|
||||||
- 面向「项目经理 / 科研课题负责人」视角:一条命令拿到仪表盘、Sprint、周报、流水线运行等聚合视图,无需在 Web 上多次跳转。
|
|
||||||
- 与 `gitlink-pm` Skill 配套,供 AI Agent 做项目健康巡检与进度跟踪。
|
|
||||||
|
|
||||||
背景:项目管理数据此前散落在多个 Web 页面,无命令行入口。`pm` 组将其收敛为 6 条命令,是子任务三「项目一键初始化 / 进度跟踪」与子任务四「科研进度智能跟踪与预警」的基础数据层。关联 PR #12。
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
# PM shortcuts
|
|
||||||
|
|
||||||
## Background
|
|
||||||
|
|
||||||
The `gitlink-pm` Skill documented GitLink project-management workflows but had
|
|
||||||
to call Raw API paths under `/pm/...` directly. That made Sprint reports, weekly
|
|
||||||
issue summaries, board inspection, and PM pipeline checks less discoverable for
|
|
||||||
users and agents.
|
|
||||||
|
|
||||||
This change adds a first-class `pm` shortcut group for read-only PM data.
|
|
||||||
|
|
||||||
## New shortcuts
|
|
||||||
|
|
||||||
- `pm +dashboards` lists PM dashboards for a project.
|
|
||||||
- `pm +sprint-issues` lists Sprint issues.
|
|
||||||
- `pm +weekly-issues` lists weekly issues.
|
|
||||||
- `pm +issue-tags` lists PM issue tags.
|
|
||||||
- `pm +pipelines` lists PM pipelines.
|
|
||||||
- `pm +action-runs` lists PM action run records.
|
|
||||||
|
|
||||||
All commands require `--project-id` and accept `--page`/`--limit`.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli pm +dashboards --project-id 123 --limit 20
|
|
||||||
gitlink-cli pm +sprint-issues --project-id 123 --page 1 --limit 20
|
|
||||||
gitlink-cli pm +weekly-issues --project-id 123
|
|
||||||
gitlink-cli pm +issue-tags --project-id 123
|
|
||||||
gitlink-cli pm +pipelines --project-id 123
|
|
||||||
gitlink-cli pm +action-runs --project-id 123
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation updates
|
|
||||||
|
|
||||||
- README and README.zh-CN include PM usage examples.
|
|
||||||
- `skills/gitlink-pm` now prefers `pm +...` shortcuts instead of Raw API calls.
|
|
||||||
- The Skills overview lists the new PM commands.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Unit tests cover:
|
|
||||||
|
|
||||||
- all PM shortcut endpoint mappings;
|
|
||||||
- `project_id`, `page`, and `limit` query construction;
|
|
||||||
- default pagination;
|
|
||||||
- required `--project-id` validation;
|
|
||||||
- HTTP error propagation.
|
|
||||||
|
|
||||||
Suggested verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./shortcuts/pm ./shortcuts
|
|
||||||
```
|
|
||||||
|
|
||||||
Full project verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./...
|
|
||||||
```
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
# PR 本地检出命令
|
|
||||||
|
|
||||||
新增 `gitlink-cli pr +checkout`,用于把指定 PR 的源分支拉取到本地并切换到 review 分支。维护者和贡献者在审查 PR 时,经常需要从“查看 PR 元信息”进入“本地构建、测试、复现”的流程,之前只能手动查看 fork 信息、拼接远端地址、fetch 分支,再 checkout;新命令把这套流程收敛为一个 Shortcut。
|
|
||||||
|
|
||||||
命令会读取 GitLink PR 详情,自动识别同仓库 PR 与 fork PR 的源仓库和源分支,不依赖本地 remote 必须叫 `origin`。默认创建 `pr-<id>` 本地分支,也支持 `--branch` 指定分支名、`--force` 重置已存在分支、`--dry-run` 预览将执行的 git 命令。
|
|
||||||
|
|
||||||
本次变更补充了单元测试、README 示例、PR Skill 说明和独立参考文档,覆盖 fork PR 解析、同仓库 head 解析、dry-run、force checkout 以及非法分支名校验。
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
# pr +checks 合并请求 CI 构建状态命令
|
|
||||||
|
|
||||||
## 背景
|
|
||||||
|
|
||||||
`gitlink-cli pr` 已覆盖合并请求的列表、详情、评审、评论等操作,`gitlink-cli ci +builds` 可列出仓库的 CI 构建,但两者相互独立。用户或 AI Agent 想确认「某个合并请求的最新提交是否通过了 CI」,此前需要手动读取 PR 的源分支/源提交,再逐条比对构建列表。
|
|
||||||
|
|
||||||
本次变更新增 `pr +checks`,对齐 `gh pr checks` 的语义:解析合并请求 head,自动关联并汇总对应的 CI 构建状态。
|
|
||||||
|
|
||||||
## 变更内容
|
|
||||||
|
|
||||||
- 新增 `gitlink-cli pr +checks --id N` Shortcut。
|
|
||||||
- 先 `GET /{owner}/{repo}/pulls/{id}` 读取合并请求详情,取源分支 `head` 与源提交 `head_commit_sha`(字段依据 API 文档「获取一个合并请求」章节)。
|
|
||||||
- 再 `GET /{owner}/{repo}/builds` 拉取 CI 构建列表,在客户端按 head 提交/分支筛选。
|
|
||||||
- 输出规范化的状态摘要:`matched_by`、`total_builds` 以及每条构建的 `id / stage / status / conclusion / branch / sha`。
|
|
||||||
- 复用现有仓库上下文解析、API 调用与统一输出封装;新增中英文 i18n 文案。
|
|
||||||
|
|
||||||
## 匹配策略
|
|
||||||
|
|
||||||
| 优先级 | 条件 | `matched_by` |
|
|
||||||
|--------|------|--------------|
|
|
||||||
| 1 | 构建提交 SHA 与 head 提交一致(支持缩写前缀比对) | `sha` |
|
|
||||||
| 2 | 无 SHA 命中,但构建分支等于 head 分支 | `branch` |
|
|
||||||
| 3 | 构建未暴露任何分支/提交字段,无法建立关联 | `unlinkable` |
|
|
||||||
| 4 | 构建暴露了分支/提交字段但均不匹配 | `none` |
|
|
||||||
|
|
||||||
## 命令示例
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli pr +checks --owner Gitlink --repo forgeplus --id 42
|
|
||||||
gitlink-cli pr +checks --id 42 --format json
|
|
||||||
```
|
|
||||||
|
|
||||||
## 已知限制
|
|
||||||
|
|
||||||
GitLink 的 `/{owner}/{repo}/builds` 端点未纳入官方 OpenAPI 参考文档,构建对象中承载分支与提交的字段名无法从文档确证。为避免臆造字段:
|
|
||||||
|
|
||||||
- 分支字段按 `branch / head_branch / source_branch / ref` 依次探测(`ref` 会去除 `refs/heads/` 前缀)。
|
|
||||||
- 提交字段按 `head_commit_sha / commit_sha / commit_id / sha / after / revision` 依次探测。
|
|
||||||
- 若某次构建两类字段均缺失,则判定为无法关联(`matched_by = unlinkable`),此时**降级返回全部最近构建并附带说明**,由使用者依据 `head_sha` 手动核对,而非丢弃结果或猜测字段。
|
|
||||||
|
|
||||||
后续若 `/builds` 响应结构被官方文档化,可据实收敛探测键集合。
|
|
||||||
|
|
||||||
## 测试覆盖
|
|
||||||
|
|
||||||
- 表驱动 httptest:先 mock `GET pulls/{id}`、再 mock `GET builds`,断言四种 `matched_by` 分支(sha 优先于 branch、缩写 SHA 命中、branch 回退、unlinkable 全量降级、none 无命中)与选中的构建 id。
|
|
||||||
- head 字段缺失、builds 请求 HTTP 失败的错误路径。
|
|
||||||
- 纯函数单测:`extractPullRequestHead`、`buildsFromEnvelope`(含客户端把顶层数组作为字符串返回的情形)、`commitMatches`、仅有分支的 PR。
|
|
||||||
|
|
||||||
验证命令:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go build ./...
|
|
||||||
go test ./shortcuts/pr/ ./shortcuts/ci/
|
|
||||||
```
|
|
||||||
|
|
||||||
## 交付要求核对
|
|
||||||
|
|
||||||
- 功能代码:`shortcuts/pr/pr.go`、`shortcuts/pr/checks.go`
|
|
||||||
- 单元测试:`shortcuts/pr/checks_test.go`
|
|
||||||
- i18n 文案:`internal/i18n/locales/en-US.json`、`internal/i18n/locales/zh-CN.json`
|
|
||||||
- 变更说明文档:`doc/changes/pr-checks.md`
|
|
||||||
|
|
||||||
## 兼容性
|
|
||||||
|
|
||||||
该变更只新增 Shortcut、辅助函数、单元测试、i18n 文案与文档,不修改任何已有命令的参数或输出结构。
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
# PR Create Fork Support
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
This change makes `gitlink-cli pr +create` work with the fork syntax already documented in the README:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli pr +create --owner Gitlink --repo forgeplus \
|
|
||||||
-t "feat: New feature" \
|
|
||||||
--head your_username/forgeplus:feature/my-feature \
|
|
||||||
--base master
|
|
||||||
```
|
|
||||||
|
|
||||||
## What changed
|
|
||||||
|
|
||||||
- Parse `owner/repo:branch` fork heads in `pr +create`
|
|
||||||
- Auto-resolve the fork repository metadata required by GitLink:
|
|
||||||
- `merge_user_login`
|
|
||||||
- `merge_project_identifier`
|
|
||||||
- `fork_project_id`
|
|
||||||
- Auto-fill compare counts when available so the request matches GitLink's real PR create flow more closely
|
|
||||||
- Keep same-repo PR creation behavior unchanged
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
- `go test ./...`
|
|
||||||
- Unit tests for:
|
|
||||||
- same-repo PR creation payload
|
|
||||||
- fork PR creation payload
|
|
||||||
- invalid fork head syntax
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
# PR Edit Shortcut
|
|
||||||
|
|
||||||
This change adds a `pr +edit` verb, bringing pull requests in line with `issue`, `label`, `release`, `milestone`, and `webhook`, which already expose an edit/update command.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
- Add `pr +edit` for `PUT /api/{owner}/{repo}/pulls/{index}.json`.
|
|
||||||
|
|
||||||
## Behavior
|
|
||||||
|
|
||||||
- `pr +edit` first GETs the current PR (the same `/pulls/{index}` path used by `pr +view`), then merges the requested changes onto its current values before the PUT. The update endpoint requires `title`, `body`, `head`, `base`, `issue_tag_ids`, and `receivers_login` together, so unspecified flags fall back to the existing values to avoid clobbering them.
|
|
||||||
- Flags: `--title`, `--body`, `--base`, `--head`, and `--tag-ids` (comma-separated). At least one is required.
|
|
||||||
- `--tag-ids` replaces the tag set when given; otherwise the PR's current tag IDs are preserved.
|
|
||||||
- PR detail fields are read defensively across the top-level, `pull_request`, and `issue` scopes, matching how the health checks parse the same endpoint.
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
- Unit tests cover field preservation, tag-id override, the nested `pull_request` response shape, the missing-field guard, and the HTTP error path.
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
# PR 行级审查评论快捷命令
|
|
||||||
|
|
||||||
这次改动把 `pull request` 里的行级审查评论补成了一套完整的快捷命令,而不只是停留在普通会话评论或 review 总览。
|
|
||||||
|
|
||||||
- 新增 `pr +review-comments`,可以按 `review_id`、`state`、`path`、`need_respond` 等条件筛选 inline review comments。
|
|
||||||
- 新增 `pr +review-comment`、`pr +update-review-comment`、`pr +delete-review-comment`,把创建、更新、删除行级评论的常用操作补齐。
|
|
||||||
- `pr +review-comment` 默认会自动调用 PR files 接口,按 `--path` 提取对应文件的 diff 并转换成评论接口可用的结构,减少手工拼接大段 `diff` JSON 的负担;如果需要完全自定义,也可以通过 `--diff-file` 直接提供 diff JSON。
|
|
||||||
- 创建、更新、删除都支持 `--dry-run`,方便在脚本或 Agent 场景里先预览最终请求内容。
|
|
||||||
|
|
||||||
这条能力比较适合代码审查自动化、Agent 辅助 review、或者把外部静态分析结果回写到具体变更行上,比单纯暴露原始接口更容易直接落到实际工作流里。
|
|
||||||
|
|
||||||
本地验证:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./shortcuts/pr
|
|
||||||
go test ./...
|
|
||||||
go build ./...
|
|
||||||
git diff --check
|
|
||||||
go run . pr --help
|
|
||||||
```
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
# PR 列表按作者过滤
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
`pr +list` 新增 `--login` 参数,可按 Issue 作者(`issue.author.login`)过滤返回的 Pull Request 列表。
|
|
||||||
|
|
||||||
## 新增 Flag
|
|
||||||
|
|
||||||
| Flag | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| `--login` | 按 Issue 作者的 login 过滤 PR(不区分大小写) |
|
|
||||||
|
|
||||||
## 实现细节
|
|
||||||
|
|
||||||
GitLink PR 列表 API 不支持服务端按作者过滤,因此本功能在客户端对返回结果进行过滤。过滤逻辑:
|
|
||||||
|
|
||||||
- 匹配 `issue.author.login` 字段
|
|
||||||
- 不区分大小写
|
|
||||||
- 过滤后自动更新 `total_count` 和 `meta.total_count` 以反映实际数量
|
|
||||||
|
|
||||||
## 示例
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 列出 alice 提交的所有 PR
|
|
||||||
gitlink-cli pr +list --owner Gitlink --repo gitlink-cli --login alice
|
|
||||||
|
|
||||||
# 结合其他筛选条件
|
|
||||||
gitlink-cli pr +list --owner Gitlink --repo gitlink-cli --login alice --state open
|
|
||||||
|
|
||||||
# 列出已合并的 PR
|
|
||||||
gitlink-cli pr +list --login bob --state merged
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
## PR list output now shows the user-facing PR number
|
|
||||||
|
|
||||||
`pr +list` already returned the GitLink PR sequence as `index`, but the default
|
|
||||||
table output did not make that value easy to spot. This change copies the same
|
|
||||||
value into a stable `number` field during list normalization and prioritizes the
|
|
||||||
`number` column in table rendering.
|
|
||||||
|
|
||||||
As a result:
|
|
||||||
|
|
||||||
- `gitlink-cli pr +list --format table` shows the PR number in a dedicated
|
|
||||||
leading column.
|
|
||||||
- JSON and YAML output also include `number`, making the list output align with
|
|
||||||
`pr +view --id <number>` semantics and with the PR number shown in the web UI.
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
## PR list supports direct lookup by PR number
|
|
||||||
|
|
||||||
`pr +list` previously only exposed keyword-based search, which made it awkward
|
|
||||||
to jump to a known PR from the web UI or from review notes. This change adds
|
|
||||||
`--number` / `-n` and a compatibility alias `--id` / `-i` to the list command.
|
|
||||||
|
|
||||||
When a PR number is provided, the CLI now reads that PR through the dedicated
|
|
||||||
detail endpoint and wraps the result into the usual list payload shape. This
|
|
||||||
keeps the output stable for automation while making exact-number lookup work
|
|
||||||
even when the target PR is not on the current list page.
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
# PR readiness shortcuts
|
|
||||||
|
|
||||||
## Background
|
|
||||||
|
|
||||||
Pull request workflows already support list, create, view, merge, review,
|
|
||||||
comments, changed files, and patchset/version inspection. Two helper endpoints
|
|
||||||
were still documented as Raw API calls:
|
|
||||||
|
|
||||||
- `GET /:owner/:repo/pulls/get_branches`
|
|
||||||
- `POST /:owner/:repo/pulls/check_can_merge`
|
|
||||||
|
|
||||||
This change adds first-class shortcuts for those PR preparation workflows.
|
|
||||||
|
|
||||||
## New shortcuts
|
|
||||||
|
|
||||||
- `pr +branches` lists PR source/target branch candidates.
|
|
||||||
- `pr +check-can-merge` checks whether a source branch can merge into a target
|
|
||||||
branch.
|
|
||||||
|
|
||||||
## Safety model
|
|
||||||
|
|
||||||
`pr +branches` is read-only and can run directly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli pr +branches --owner Gitlink --repo forgeplus
|
|
||||||
```
|
|
||||||
|
|
||||||
`pr +check-can-merge` uses a remote POST endpoint, so it supports dry-run and
|
|
||||||
requires explicit confirmation for real execution:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli pr +check-can-merge --owner Gitlink --repo forgeplus \
|
|
||||||
--head feature/search --base master --dry-run
|
|
||||||
gitlink-cli pr +check-can-merge --owner Gitlink --repo forgeplus \
|
|
||||||
--head feature/search --base master --yes
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation updates
|
|
||||||
|
|
||||||
- README and README.zh-CN include branch helper and merge readiness examples.
|
|
||||||
- `skills/gitlink-pr` now prefers `pr +branches` and `pr +check-can-merge`
|
|
||||||
over Raw API calls.
|
|
||||||
- `skills/gitlink-gatekeeper` includes merge readiness as a PR preflight signal.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Unit tests cover:
|
|
||||||
|
|
||||||
- endpoint method/path mapping for `pr +branches`;
|
|
||||||
- `pr +check-can-merge` dry-run behavior;
|
|
||||||
- `--yes` confirmation guard;
|
|
||||||
- request payload and default `--base master`;
|
|
||||||
- HTTP error propagation for both shortcuts.
|
|
||||||
|
|
||||||
Suggested verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./shortcuts/pr ./shortcuts
|
|
||||||
```
|
|
||||||
|
|
||||||
Full project verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./...
|
|
||||||
```
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
# Pull request review comment management shortcuts
|
|
||||||
|
|
||||||
This change adds first-class shortcuts for GitLink pull request review comment
|
|
||||||
threads. It complements `pr +review`, which creates an overall review decision,
|
|
||||||
with commands for line-level and threaded discussion records.
|
|
||||||
|
|
||||||
- `pr +review-comments` lists review comments with keyword, review ID,
|
|
||||||
need-response, state, parent, path, full-thread, and sorting filters.
|
|
||||||
- `pr +review-comment` creates review comments or replies and supports
|
|
||||||
`comment`/`problem` types, review IDs, line codes, commit IDs, paths,
|
|
||||||
parent IDs, raw diff JSON, and dry-run previews.
|
|
||||||
- `pr +review-comment-update` edits the note, commit, or state (`opened`,
|
|
||||||
`resolved`, `disabled`) with dry-run support.
|
|
||||||
- `pr +review-comment-delete` deletes a review comment by ID.
|
|
||||||
|
|
||||||
The implementation validates numeric IDs, boolean filters, comment type, state,
|
|
||||||
and diff JSON before sending API requests.
|
|
||||||
|
|
||||||
Verification:
|
|
||||||
|
|
||||||
- `go test ./shortcuts/pr`
|
|
||||||
- `go test ./shortcuts`
|
|
||||||
- `go test ./...`
|
|
||||||
- `go build ./...`
|
|
||||||
- `git diff --check`
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
# pr +status 与我相关的合并请求概览命令
|
|
||||||
|
|
||||||
## 背景
|
|
||||||
|
|
||||||
`gitlink-cli pr` 已经提供列表、创建、查看、合并、评审等能力,但缺少一个类似 `gh pr status` 的入口,用于快速回答“当前登录用户在本仓库里有哪些待处理的合并请求”。用户或 AI Agent 过去需要先查自己的身份,再手动拼 `pr +list` 的过滤参数,还要区分“我提的”和“等我评审的”。
|
|
||||||
|
|
||||||
本次变更把这一常见诉求封装为 `pr +status`,一次调用给出两组结果:你创建的、以及请求你评审的开启中合并请求。
|
|
||||||
|
|
||||||
## 变更内容
|
|
||||||
|
|
||||||
- 新增 `gitlink-cli pr +status` Shortcut(只读)。
|
|
||||||
- 先调用 `GET /users/me` 解析当前用户的 `login` 与数值 `id`(复用 `user +me` 的接口)。
|
|
||||||
- 复用合并请求列表接口 `GET /v1/{owner}/{repo}/pulls`(api_ref「获取合并请求列表」)拉取数据:
|
|
||||||
- **你创建的**:以 `status=0` 拉取开启中的合并请求,再按 `issue.author.login` 与当前用户在**客户端**匹配。该列表接口没有 author 过滤参数,故只能客户端过滤。
|
|
||||||
- **请求你评审的**:以 `status=0` 加 `reviewer_id={当前用户 id}` 由**服务端**过滤(`reviewer_id` 是列表接口文档化的审查人员过滤参数)。
|
|
||||||
- 输出统一封装为结构化数据:`login`、`created`、`review_requested` 两组合并请求数组,沿用现有输出格式(json/table/yaml)。
|
|
||||||
- 补充中英文 i18n 文案(`cmd.pr.status.short` / `cmd.pr.status.long`),避免命令帮助信息硬编码。
|
|
||||||
|
|
||||||
## 命令示例
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 查看与你相关的合并请求(owner/repo 可从 git remote 自动解析)
|
|
||||||
gitlink-cli pr +status --owner Gitlink --repo forgeplus
|
|
||||||
|
|
||||||
# Agent 场景建议 JSON 输出
|
|
||||||
gitlink-cli pr +status --owner Gitlink --repo forgeplus --format json
|
|
||||||
```
|
|
||||||
|
|
||||||
## 输出结构
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"ok": true,
|
|
||||||
"data": {
|
|
||||||
"login": "currentuser",
|
|
||||||
"created": [ /* 你创建的开启中合并请求 */ ],
|
|
||||||
"review_requested": [ /* 请求你评审的开启中合并请求 */ ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 设计说明
|
|
||||||
|
|
||||||
- 该命令刻意只使用列表接口文档化的查询参数(`status`、`reviewer_id`),不引入未在 api_ref 中出现的参数。
|
|
||||||
- author 侧过滤放在客户端,是因为列表接口只支持 `reviewer_id` / `assign_user_id` 等数值过滤,没有 author 过滤参数;这一点在上文与代码注释中都做了说明。
|
|
||||||
- 全流程只读,不修改任何合并请求状态。
|
|
||||||
|
|
||||||
## 测试覆盖
|
|
||||||
|
|
||||||
`shortcuts/pr/pr_test.go` 中新增表驱动单元测试(mock `/users/me` 与合并请求列表接口):
|
|
||||||
|
|
||||||
- 分组正确:混合作者的开启中合并请求被正确拆分为“你创建的”与“请求你评审的”。
|
|
||||||
- author 客户端过滤:他人创建的合并请求不进入“你创建的”分组。
|
|
||||||
- 空仓库:两组均为空。
|
|
||||||
- `reviewer_id` 断言:确认按当前用户数值 id 向服务端发起评审过滤查询。
|
|
||||||
- 错误路径:`/users/me` 返回 500、或响应缺少 `login` 时命令报错。
|
|
||||||
- 端到端:`pr +status` 走完整 Run 路径(含输出)不报错。
|
|
||||||
|
|
||||||
验证命令:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go build ./...
|
|
||||||
go test ./shortcuts/pr/
|
|
||||||
```
|
|
||||||
|
|
||||||
## 交付要求核对
|
|
||||||
|
|
||||||
- 功能代码:`shortcuts/pr/pr.go`
|
|
||||||
- 单元测试:`shortcuts/pr/pr_test.go`
|
|
||||||
- i18n 文案:`internal/i18n/locales/en-US.json`、`internal/i18n/locales/zh-CN.json`
|
|
||||||
- 变更说明文档:`doc/changes/pr-status.md`
|
|
||||||
|
|
||||||
## 兼容性
|
|
||||||
|
|
||||||
该变更只新增一个只读 Shortcut、对应单元测试、i18n 文案与文档,不修改已有命令的参数或输出结构,对现有功能无破坏性影响。
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
# PR View Timestamp Normalization
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
`gitlink-cli pr +view` now normalizes pull request lifecycle timestamps so merged and closed pull requests expose stable top-level time fields in JSON output.
|
|
||||||
|
|
||||||
## Command
|
|
||||||
|
|
||||||
| Command | Purpose |
|
|
||||||
|---------|---------|
|
|
||||||
| `gitlink-cli pr +view` | Return PR detail and normalize `created_at`, `merged_at`, `closed_at`, and `closed_on` when GitLink provides them directly or journals can infer them. |
|
|
||||||
|
|
||||||
## Behavior
|
|
||||||
|
|
||||||
- Promote `created_at` and `merged_at` from nested response objects to the top-level payload.
|
|
||||||
- Backfill `closed_at` and `closed_on` for merged pull requests when GitLink omits an explicit close timestamp.
|
|
||||||
- Read issue journals only when a merged or closed pull request is still missing lifecycle timestamps.
|
|
||||||
- Recognize merge and close journal operations after stripping HTML tags and whitespace.
|
|
||||||
- Support both `pull_request_status` and `pull_request_staus` status shapes returned by GitLink APIs.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- `go test ./shortcuts/pr/...`
|
|
||||||
- `go build ./...`
|
|
||||||
- `go test ./...`
|
|
||||||
- `go run . pr +view --owner Gitlink --repo gitlink-cli -i 15 --format json`
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
# Reaction Shortcut
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Adds a `reaction` shortcut group for repository social interactions. The group exposes watcher and stargazer lists plus follow/unfollow and like/unlike actions.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
| Command | Purpose |
|
|
||||||
|---------|---------|
|
|
||||||
| `gitlink-cli reaction +watchers` | List repository watchers |
|
|
||||||
| `gitlink-cli reaction +stargazers` | List repository stargazers |
|
|
||||||
| `gitlink-cli reaction +follow` | Follow a repository |
|
|
||||||
| `gitlink-cli reaction +unfollow` | Unfollow a repository |
|
|
||||||
| `gitlink-cli reaction +like` | Like a repository |
|
|
||||||
| `gitlink-cli reaction +unlike` | Unlike a repository |
|
|
||||||
|
|
||||||
## Behavior
|
|
||||||
|
|
||||||
- `+watchers` and `+stargazers` support optional `--start-at` and `--end-at` Unix timestamp filters.
|
|
||||||
- Write actions accept optional `--project-id`; if omitted, the shortcut resolves the project ID from `--owner/--repo` automatically.
|
|
||||||
- Timestamp and project ID inputs are validated before API calls are sent.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
The unit tests verify list query parameters, project ID auto-resolution, explicit project ID handling, follow/unfollow endpoints, like/unlike endpoints, and validation failures.
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
# Release Asset Shortcuts
|
|
||||||
|
|
||||||
Submitter: Mengz
|
|
||||||
|
|
||||||
This change fills the missing release asset workflow in `gitlink-cli`, so users can upload files, attach existing assets, inspect attached assets, and detach assets without dropping the rest of the release metadata.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
- Add `release +assets` to inspect the assets currently attached to a release.
|
|
||||||
- Add `release +attach` to merge existing attachment IDs into a release.
|
|
||||||
- Add `release +detach` to remove attachment IDs from a release.
|
|
||||||
- Add `release +upload` to upload a local file and attach it to a release in one command.
|
|
||||||
|
|
||||||
## Behavior
|
|
||||||
|
|
||||||
- Add multipart upload support to `internal/client` and expose it through `shortcuts/common.RuntimeContext`.
|
|
||||||
- Keep the `RuntimeContext` encapsulation instead of calling the low-level client directly from shortcuts.
|
|
||||||
- Preserve existing release fields when attaching, detaching, or uploading assets, so these commands do not accidentally overwrite `name`, `tag_name`, `body`, `target_commitish`, `draft`, or `prerelease`.
|
|
||||||
- Accept release tags for asset operations by resolving them to the internal release version ID before write operations.
|
|
||||||
- Clean up the uploaded attachment automatically if the follow-up release update fails, avoiding orphaned assets.
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
- Add multipart client tests covering field values, uploaded filename, content type, and file body.
|
|
||||||
- Add release shortcut tests covering asset listing, attach/detach dry-run behavior, attachment merging, upload-and-attach flow, and cleanup after failed release update.
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
# 新增 Release 资产下载能力
|
|
||||||
|
|
||||||
Release 命令组现在支持 `release +assets` 和 `release +download`,可以先查看某个发布包含的附件、zip/tar 源码包,再按附件 ID、附件文件名或源码包类型下载到本地目录。
|
|
||||||
|
|
||||||
下载链路新增了 client 层原始字节获取能力,并通过 `RuntimeContext.Download()` 暴露给 shortcut,避免 release 命令直接绕过统一客户端封装。附件 URL 支持 GitLink 返回的 `/api/attachments/<id>` 形式,源码包也支持完整 URL。
|
|
||||||
|
|
||||||
`release +download` 默认不覆盖已有文件;发布只有一个附件时可以直接下载,存在多个附件时要求显式传入 `--asset`,减少自动化脚本误下文件的风险。本次变更补充了 client、release shortcut、帮助文案、README 和 Skill reference 测试与文档。
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
# Release Enhance Shortcuts
|
|
||||||
|
|
||||||
Submitter: Wang Yue
|
|
||||||
|
|
||||||
This change adds two new release shortcuts for getting the latest release and auto-generating release notes.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
- Add `release +latest` for getting the latest release version.
|
|
||||||
- Add `release +auto-notes` for auto-generating release notes from commits and issues.
|
|
||||||
|
|
||||||
## Behavior
|
|
||||||
|
|
||||||
### release +latest
|
|
||||||
|
|
||||||
- Fetches releases from the repository and returns the first matching release.
|
|
||||||
- By default, skips draft and prerelease versions.
|
|
||||||
- Supports `--include-prerelease` flag to include prerelease versions.
|
|
||||||
- Supports `--include-draft` flag to include draft versions.
|
|
||||||
- Returns error if no matching release is found.
|
|
||||||
|
|
||||||
### release +auto-notes
|
|
||||||
|
|
||||||
- Generates formatted release notes from commit messages and closed issues.
|
|
||||||
- Automatically categorizes commits by prefix:
|
|
||||||
- `feat:` → 🚀 新功能 (New Features)
|
|
||||||
- `fix:` → 🐛 Bug 修复 (Bug Fixes)
|
|
||||||
- Others → 📝 其他变更 (Other Changes)
|
|
||||||
- Supports `--from-tag` to specify the starting tag for comparison.
|
|
||||||
- If `--from-tag` is not specified, uses the last 50 commits.
|
|
||||||
- Includes closed issues in the "Related Issues" section.
|
|
||||||
- Supports `--format json` to output with statistics (commits_count, issues_count).
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
- Unit tests cover:
|
|
||||||
- `TestReleaseLatest`: Basic latest release retrieval
|
|
||||||
- `TestReleaseLatestWithPrerelease`: Including prerelease versions
|
|
||||||
- `TestReleaseLatestSkipsDraft`: Skipping draft versions
|
|
||||||
- `TestReleaseLatestNoReleases`: Error handling when no releases
|
|
||||||
- `TestReleaseAutoNotes`: Basic auto-notes generation
|
|
||||||
- `TestReleaseAutoNotesWithFromTag`: Using from-tag parameter
|
|
||||||
- `TestReleaseAutoNotesJSONFormat`: JSON format output with statistics
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
# Repository File Read, Search, and Batch Commit Shortcuts
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
This change adds repository file workflow shortcuts for users and automation agents that need to read file content, find files, and commit multiple file changes without manually assembling Raw API calls.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
| Command | Purpose |
|
|
||||||
|---------|---------|
|
|
||||||
| `gitlink-cli repo +file` | Read one repository file with content, commit info, and download metadata |
|
|
||||||
| `gitlink-cli repo +files` | Search repository files by name with optional branch, tag, or commit filtering |
|
|
||||||
| `gitlink-cli repo +commit-files` | Commit one file operation or a batch JSON operation list through the `contents/batch` API |
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
Search files on a branch:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli repo +files --owner Gitlink --repo forgeplus --search README --ref main
|
|
||||||
```
|
|
||||||
|
|
||||||
Read one file from a branch:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli repo +file --owner Gitlink --repo forgeplus --path README.md --ref main
|
|
||||||
```
|
|
||||||
|
|
||||||
Update one text file:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli repo +commit-files --owner Gitlink --repo forgeplus \
|
|
||||||
--branch main --message "docs: update README" \
|
|
||||||
--path README.md --content "# Project"
|
|
||||||
```
|
|
||||||
|
|
||||||
Create or update a binary file by reading local bytes and encoding them as base64:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli repo +commit-files --owner Gitlink --repo forgeplus \
|
|
||||||
--branch main --message "assets: update logo" \
|
|
||||||
--action update --path assets/logo.png --from ./logo.png --encoding base64
|
|
||||||
```
|
|
||||||
|
|
||||||
Preview a multi-file commit before sending it:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli repo +commit-files --owner Gitlink --repo forgeplus \
|
|
||||||
--branch main --new-branch docs/batch-update \
|
|
||||||
--message "docs: batch update" --ops changes.json --dry-run
|
|
||||||
```
|
|
||||||
|
|
||||||
`changes.json` can be either an array of file operations:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"action_type": "create",
|
|
||||||
"file_path": "docs/guide.md",
|
|
||||||
"content": "# Guide\n",
|
|
||||||
"encoding": "text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"action_type": "delete",
|
|
||||||
"file_path": "docs/old-guide.md"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
or an object with a `files` array. The CLI supplies `branch`, `message`, optional author and committer fields, and optional `new_branch` from command flags.
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
- `repo +commit-files` requires `--branch` and `--message`.
|
|
||||||
- Single-file mode requires `--path`; `create` and `update` require exactly one of `--content` or `--from`.
|
|
||||||
- `delete` operations reject `content` and `encoding` so the request body matches the API intent.
|
|
||||||
- `--encoding` accepts only `text` and `base64`.
|
|
||||||
- `--ops` cannot be combined with single-file flags.
|
|
||||||
- Author and committer names must be provided together with their matching email fields.
|
|
||||||
- `--dry-run` prints the resolved request and does not call the remote API.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Unit tests cover direct file reads through `sub_entries`, file search query mapping, single-file request bodies, base64 local file reading, JSON batch operation files, dry-run behavior, and validation failures that must not perform an API request.
|
|
||||||
|
|
||||||
## 中文说明
|
|
||||||
|
|
||||||
本次变更把仓库文件工作流补成了一个更完整的闭环:先用 `repo +file` 直接读取指定文件内容和元数据,用 `repo +files` 按文件名和分支搜索仓库文件,再用 `repo +commit-files` 把单个或多个文件变更提交到目标分支。批量提交支持创建新分支、设置提交信息、指定作者和提交者、从本地文件读取内容、对二进制内容做 base64 编码,并提供 `--dry-run` 预览请求体,适合脚本、CI 和 AI Agent 在真正写入仓库前检查即将提交的内容。验证覆盖了文件读取、端点路径、查询参数、请求体字段、JSON 批量文件、base64 编码和无效参数不触网等关键路径。
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
# repo +file 仓库文件读取快捷命令
|
|
||||||
|
|
||||||
`gitlink-cli repo` 已经支持查看仓库信息、README 和目录树,但当用户想直接读取 `go.mod`、`.gitignore`、配置文件、脚本或许可证内容时,仍然需要回退到 Raw API。对于脚本、Agent 和日常排查来说,这是一个很常见的能力缺口。
|
|
||||||
|
|
||||||
这次新增 `gitlink-cli repo +file`,把仓库任意文件读取封装成高层 Shortcut。命令基于 `GET /{owner}/{repo}/sub_entries` 的文件模式实现,支持 `--path` 指定仓库内文件路径,支持 `--ref` 读取指定分支、标签或提交,也支持 `--content-only` 只输出文件正文,方便直接做管道消费或作为 Agent 上下文输入。
|
|
||||||
|
|
||||||
为了让这个命令在真实使用里更顺手,这次还补了两个常见边界处理:
|
|
||||||
|
|
||||||
- `--path` 设为必填,并对空路径或仅 `/` 这类无效输入给出明确报错。
|
|
||||||
- 如果用户传入的是目录路径,而不是文件路径,命令会直接提示改用 `repo +tree`,避免得到难以理解的 API 结果。
|
|
||||||
|
|
||||||
测试覆盖了默认分支、显式 `--ref`、路径归一化、帮助参数注册、目录误传报错、`--content-only` 缺少内容报错以及结果扁平化输出等关键分支。
|
|
||||||
|
|
||||||
本次交付包含:
|
|
||||||
|
|
||||||
- 功能代码:`shortcuts/repo/repo.go`
|
|
||||||
- 单元测试:`shortcuts/repo/repo_test.go`
|
|
||||||
- 帮助文档更新:`README.md`、`README.zh-CN.md`、`skills/gitlink-repo/SKILL.md`、`skills/gitlink-repo/references/gitlink-repo-file.md`
|
|
||||||
- 变更说明:本文档
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
# Repo Mirror Sync Shortcut
|
|
||||||
|
|
||||||
Added `gitlink-cli repo-mirror +sync` for `POST /repositories/{id}/sync_mirror`.
|
|
||||||
|
|
||||||
Safety: supports `--dry-run`; validates `--id` as integer.
|
|
||||||
|
|
||||||
Validation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
GOPROXY=https://goproxy.cn,direct go test ./shortcuts/repomirror ./shortcuts
|
|
||||||
go vet ./shortcuts/repomirror ./shortcuts
|
|
||||||
go run . repo-mirror --help
|
|
||||||
```
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
# Repository raw file shortcuts
|
|
||||||
|
|
||||||
## Background
|
|
||||||
|
|
||||||
Repository inspection workflows often need to read files such as `LICENSE`,
|
|
||||||
`README.md`, dependency manifests, and source files. Those workflows previously
|
|
||||||
had to use Raw API calls for `/raw/<ref>/<path>` and `sub_entries`.
|
|
||||||
|
|
||||||
This change adds first-class read-only shortcuts for raw file content and common
|
|
||||||
manifest discovery.
|
|
||||||
|
|
||||||
## New shortcuts
|
|
||||||
|
|
||||||
- `repo +raw` reads raw file content at a branch, tag, or commit ref.
|
|
||||||
- `repo +file-exists` checks whether a repository file exists without returning
|
|
||||||
the full content.
|
|
||||||
- `repo +manifest` reads common dependency manifests for `go`, `node`,
|
|
||||||
`python`, `rust`, and `java`.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gitlink-cli repo +raw --owner Gitlink --repo forgeplus --path LICENSE --ref master
|
|
||||||
gitlink-cli repo +file-exists --owner Gitlink --repo forgeplus --path package.json --ref master
|
|
||||||
gitlink-cli repo +manifest --owner Gitlink --repo forgeplus --kind go --ref master
|
|
||||||
```
|
|
||||||
|
|
||||||
## Manifest mapping
|
|
||||||
|
|
||||||
| Kind | Candidate files |
|
|
||||||
|------|-----------------|
|
|
||||||
| `go` | `go.mod` |
|
|
||||||
| `node` | `package.json` |
|
|
||||||
| `python` | `requirements.txt`, `pyproject.toml`, `Pipfile`, `setup.py` |
|
|
||||||
| `rust` | `Cargo.toml` |
|
|
||||||
| `java` | `pom.xml`, `build.gradle`, `build.gradle.kts` |
|
|
||||||
|
|
||||||
## Safety and path handling
|
|
||||||
|
|
||||||
- These shortcuts are read-only.
|
|
||||||
- `--ref` defaults to `master`.
|
|
||||||
- File paths are cleaned of leading/trailing slashes.
|
|
||||||
- Parent path segments (`..`) are rejected before making API requests.
|
|
||||||
- Raw paths are escaped segment by segment for paths containing spaces or other
|
|
||||||
special characters.
|
|
||||||
|
|
||||||
## Documentation updates
|
|
||||||
|
|
||||||
- README and README.zh-CN include raw file, file existence, and manifest examples.
|
|
||||||
- `skills/gitlink-repo` documents the new shortcuts.
|
|
||||||
- `skills/gitlink-compliance` and `skills/gitlink-code-review` now prefer
|
|
||||||
shortcuts over Raw API calls for file tree and content reads.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Unit tests cover:
|
|
||||||
|
|
||||||
- raw file path construction and default refs;
|
|
||||||
- file existence checks through `sub_entries`;
|
|
||||||
- manifest kind mapping and fallback behavior;
|
|
||||||
- missing manifests;
|
|
||||||
- invalid paths and unknown manifest kinds;
|
|
||||||
- HTTP error propagation.
|
|
||||||
|
|
||||||
Suggested verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./shortcuts/repo ./shortcuts
|
|
||||||
```
|
|
||||||
|
|
||||||
Full project verification:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./...
|
|
||||||
```
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
# 完善仓库 README 快捷命令
|
|
||||||
|
|
||||||
`repo +readme` 现在补齐了中英文帮助文案、README 使用示例和 `gitlink-repo` Skill 说明,日常查看仓库 README 或子目录 README 时不再需要退回到 Raw API。
|
|
||||||
|
|
||||||
命令会把 `--ref` 传给 README API 的 `ref` 查询参数,把 `--path` 规范化后传给 `filepath` 查询参数,支持类似 `--path docs`、`--path /docs/guide/` 的子目录 README 查询方式。
|
|
||||||
|
|
||||||
本次变更同时补充了仓库 README 快捷命令的单元测试,覆盖默认查询、指定分支和子目录、HTTP 错误返回等路径,确保后续调整不会破坏请求路径或查询参数。
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
# repo +rename 仓库重命名命令
|
|
||||||
|
|
||||||
## 背景
|
|
||||||
|
|
||||||
`gitlink-cli repo` 已提供创建、Fork、删除等仓库管理能力,但缺少对标 `gh repo rename` 的重命名命令。用户或 AI Agent 想改名当前仓库时,过去只能手动调用 PATCH 更新项目接口。
|
|
||||||
|
|
||||||
本次变更把重命名封装为 `repo +rename`,与 GitHub CLI 的 `gh repo rename` 语义对齐。
|
|
||||||
|
|
||||||
## 变更内容
|
|
||||||
|
|
||||||
- 新增 `gitlink-cli repo +rename` Shortcut。
|
|
||||||
- 调用 `PATCH /{owner}/{repo}` 更新项目,请求体同时设置 `name`(项目名称)与 `identifier`(项目标识)。
|
|
||||||
- 支持 `--name, -n` 指定新名称,必填;空值或纯空白会在调用接口前被拒绝。
|
|
||||||
- 复用现有仓库上下文解析、API 调用和统一输出格式。
|
|
||||||
- 补充中英文 i18n 文案,避免命令帮助信息硬编码。
|
|
||||||
|
|
||||||
## 命令示例
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 重命名当前仓库(owner/repo 从 git remote 自动解析)
|
|
||||||
gitlink-cli repo +rename --name new-name
|
|
||||||
|
|
||||||
# 显式指定仓库
|
|
||||||
gitlink-cli repo +rename --owner Gitlink --repo forgeplus --name new-name
|
|
||||||
```
|
|
||||||
|
|
||||||
## 参数说明
|
|
||||||
|
|
||||||
| 参数 | 必填 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| `--name, -n` | 是 | 新的仓库名称与标识 |
|
|
||||||
| `--owner` | 否 | 全局参数,仓库所有者,可从 git remote 自动解析 |
|
|
||||||
| `--repo` | 否 | 全局参数,仓库名称,可从 git remote 自动解析 |
|
|
||||||
| `--format` | 否 | 全局参数,输出格式:`json`、`table` 或 `yaml` |
|
|
||||||
|
|
||||||
## API 映射
|
|
||||||
|
|
||||||
| Shortcut | Method | API path | 请求体字段 |
|
|
||||||
|----------|--------|----------|-----------|
|
|
||||||
| `repo +rename` | PATCH | `/api/{owner}/{repo}.json` | `name`、`identifier` |
|
|
||||||
|
|
||||||
参照 API 文档「PATCH 更新项目」与「PATCH 更新项目(完整)」,`name` 与 `identifier` 均为必填。`name` 是展示用项目名称,`identifier` 是 URL 标识(等价于 `gh repo rename` 改动的仓库 URL 标识),两者都从 `--name` 取值。
|
|
||||||
|
|
||||||
## 兼容性提示
|
|
||||||
|
|
||||||
重命名会同时修改 `identifier`,也就是仓库的 URL 标识,因此**克隆地址会随之变化**(与 `gh repo rename` 行为一致)。执行后本地已有的 remote 需要相应更新。
|
|
||||||
|
|
||||||
## 测试覆盖
|
|
||||||
|
|
||||||
- 表驱动单元测试断言请求方法为 `PATCH`、路径为 `/owner/repo.json`,且请求体 `name` 与 `identifier` 均为新名称。
|
|
||||||
- 覆盖前后空白被裁剪的场景。
|
|
||||||
- 缺失或纯空白的 `--name` 在调用接口前即返回校验错误,不触发任何 HTTP 请求。
|
|
||||||
|
|
||||||
验证命令:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go build ./...
|
|
||||||
go test ./shortcuts/repo/
|
|
||||||
```
|
|
||||||
|
|
||||||
## 交付要求核对
|
|
||||||
|
|
||||||
- 功能代码:`shortcuts/repo/repo.go`
|
|
||||||
- 单元测试:`shortcuts/repo/repo_test.go`
|
|
||||||
- i18n 文案:`internal/i18n/locales/en-US.json`、`internal/i18n/locales/zh-CN.json`
|
|
||||||
- 变更说明文档:`doc/changes/repo-rename.md`
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue