diff --git a/showcase/Dockerfile b/showcase/Dockerfile new file mode 100644 index 0000000..794e2ea --- /dev/null +++ b/showcase/Dockerfile @@ -0,0 +1,28 @@ +# Stage 1: Build +FROM golang:1.26-alpine AS builder + +WORKDIR /build +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . + +# Build gitlink-cli binary +RUN CGO_ENABLED=0 GOOS=linux go build -o gitlink-cli . + +# Build showcase server binary +RUN CGO_ENABLED=0 GOOS=linux go build -o showcase-server ./showcase/ + +# Stage 2: Runtime +FROM alpine:latest + +RUN apk add --no-cache ca-certificates git + +WORKDIR /app + +COPY --from=builder /build/gitlink-cli . +COPY --from=builder /build/showcase-server . + +EXPOSE 9090 + +CMD ["./showcase-server"] diff --git a/showcase/index.html b/showcase/index.html index e70db5a..887267c 100644 --- a/showcase/index.html +++ b/showcase/index.html @@ -6,337 +6,272 @@ gitlink-cli 功能增强展示 -
-

gitlink-cli 功能增强

-

软件演化与运维 课程实践 — 进阶任务 子任务一

- -
-
-
40+
-
原有命令
-
-
-
-
-
-
70+
-
增强后命令
-
-
-
12
-
Shortcut 分组
-
-
-
36+
-
单元测试
-
-
+
+

gitlink-cli 功能增强

+

软件演化与运维 课程实践 — 进阶任务 子任务一

+
演示仓库:chroe/gitlink-cli — 点击下方命令按钮查看真实运行结果
+
- -

团队分工

-
-
-
同学 A(组长)
-
- milestone - webhook - label - commit -
+ 展示工程部署 -
-
20 个新命令
-
-
-
同学 B
-
- file - member - watch/star -
-
14 个新命令
-
-
-
同学 C
-
- batch 增强 - table 格式 - 错误优化 - 测试补全 -
-
4 新命令 + 全局优化
-
-
- - -

三层架构设计

-
-
-┌──────────────────────────────────────────────────────────┐
-│              Shortcuts Layer(人 + AI 友好)              │
-│                                                          │
-│  milestone · webhook · label · commit · file · member    │
-│  watch · star · batch-label · batch-milestone · ...      │
-├──────────────────────────────────────────────────────────┤
-│              Raw API Layer(全覆盖)                      │
-│                                                          │
-│  gitlink-cli api GET /v1/{owner}/{repo}/...              │
-├──────────────────────────────────────────────────────────┤
-│              Config Layer(配置管理)                      │
-│                                                          │
-│  auth login · config set · GITLINK_TOKEN 环境变量         │
-└──────────────────────────────────────────────────────────┘
-            
-
- - -

新增功能清单

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
模块新增命令API 端点负责人
milestonelist, view, create, update, close, delete/v1/{owner}/{repo}/milestonesA
webhooklist, create, view, update, delete, test/v1/{owner}/{repo}/webhooksA
labellist, create, update, delete/v1/{owner}/{repo}/issue_tagsA
commitlist, view, diff, blame/v1/{owner}/{repo}/commits, blameA
filelist, tree, get, create, delete/{owner}/{repo}/files, create_file, delete_fileB
memberlist, add, remove, update/{owner}/{repo}/collaboratorsB
watch/starwatch, unwatch, star, unstar, watchers, stars/watchers/follow, /praise_treadB
batch 增强batch-label, batch-milestone, batch-assign, batch-close 优化基于现有 issue batch 框架扩展C
全局优化table 输出格式增强、错误提示优化、测试补全跨所有模块C
- - -

运行效果演示

- -
-

milestone +list

-
$ gitlink-cli milestone +list --owner chroe --repo gitlink_help_center
-{
-  "ok": true,
-  "data": {
-    "milestones": [
-      {"id": 2756, "name": "v2.0", "status": "open", "effective_date": "2026-06-30", "issues_count": 0}
-    ],
-    "opening_milestone_count": 1,
-    "total_count": 1
-  }
-}
-
- -
-

milestone +create

-
$ gitlink-cli milestone +create --owner chroe --repo gitlink_help_center --name "v2.0" --description "CLI test milestone" --due "2026-06-30"
-{
-  "ok": true,
-  "data": {"status": 0, "message": "success"}
-}
-
- -
-

webhook +list

-
$ gitlink-cli webhook +list --owner chroe --repo gitlink_help_center
-{
-  "ok": true,
-  "data": {
-    "total_count": 3,
-    "webhooks": [
-      {"id": 50035, "url": "https://jianmu.gitlink.org.cn/webhook/projects/sync", "is_active": true, "last_status": "succeed"},
-      {"id": 50201, "url": "https://jianmu.gitlink.org.cn/webhook/50201", "is_active": true, "last_status": "fail"}
-    ]
-  }
-}
-
- -
-

label +list

-
$ gitlink-cli label +list --owner chroe --repo gitlink_help_center
-{
-  "ok": true,
-  "data": {
-    "total_count": 0,
-    "issue_tags": []
-  }
-}
-
- -
-

commit +list

-
$ gitlink-cli commit +list --owner chroe --repo gitlink_help_center --limit 2
-{
-  "ok": true,
-  "data": {
-    "total_count": 500,
-    "commits": [
-      {"sha": "af3b0d4...", "commit_message": "refactor: delete .devops/...", "author": {"login": "yetja"}},
-      {"sha": "854ce5d...", "commit_message": "feat: .devops/...", "author": {"login": "yetja"}}
-    ]
-  }
-}
-
- -
-

commit +view(查看某次提交的变更文件)

-
$ gitlink-cli commit +view --owner chroe --repo gitlink_help_center --sha af3b0d4
-{
-  "ok": true,
-  "data": {
-    "file_nums": 1,
-    "total_addition": 0,
-    "total_deletion": 43,
-    "files": [
-      {"filename": ".devops/灵枢自动部署.yml", "additions": 0, "deletions": 43, "is_deleted": true}
-    ]
-  }
-}
-
- -
-

commit +blame(查看文件逐行追溯)

-
$ gitlink-cli commit +blame --owner chroe --repo gitlink_help_center --path README.md
-{
-  "ok": true,
-  "data": {
-    "file_name": "README.md",
-    "num_lines": 28,
-    "blame_parts": [
-      {"commit": {"sha": "8679343...", "author": {"login": "caishi"}, "commit_message": "search"}, "lines": ["# gitlink_help_center", ...]}
-    ]
-  }
-}
-
- - -

单元测试

-
$ go test ./... -v
-=== RUN   TestCommitList                --- PASS
-=== RUN   TestCommitListWithSHA         --- PASS
-=== RUN   TestCommitView                --- PASS
-=== RUN   TestCommitDiff                --- PASS
-=== RUN   TestCommitBlame               --- PASS
-=== RUN   TestMilestoneList             --- PASS
-=== RUN   TestMilestoneCreate           --- PASS
-=== RUN   TestMilestoneUpdate           --- PASS
-=== RUN   TestMilestoneClose            --- PASS
-=== RUN   TestMilestoneDelete           --- PASS
-=== RUN   TestMilestoneUpdateRequiresAtLeastOneField --- PASS
-=== RUN   TestWebhookList               --- PASS
-=== RUN   TestWebhookCreate             --- PASS
-=== RUN   TestWebhookDelete             --- PASS
-=== RUN   TestWebhookTest               --- PASS
-=== RUN   TestWebhookUpdateRequiresAtLeastOneField --- PASS
-=== RUN   TestLabelList                 --- PASS
-=== RUN   TestLabelCreate               --- PASS
-=== RUN   TestLabelCreateWithDefaultColor --- PASS
-=== RUN   TestLabelUpdate               --- PASS
-=== RUN   TestLabelUpdateRequiresAtLeastOneField --- PASS
-=== RUN   TestLabelDelete               --- PASS
-=== RUN   TestIssueClosePreservesCurrentDescription --- PASS
-=== RUN   TestIssueUpdatePreservesCurrentDescriptionWhenChangingTitleAndState --- PASS
-=== RUN   TestIssueUpdatePreservesCurrentSubjectWhenChangingDescription --- PASS
-=== RUN   TestBatchClosePreservesCurrentDescription --- PASS
-=== RUN   TestPRCommentPostsToCorrectIssueJournal --- PASS
-=== RUN   TestPRCommentFailsWhenPRNotFound --- PASS
-=== RUN   TestPRCommentFailsWhenIssueFieldMissing --- PASS
-=== RUN   TestMountShortcutSupportsBoolFlags --- PASS
-
-36 tests — ALL PASSED
- - +
+
+
4
+
新增模块
+
+
20
+
新增命令
+
+
+
36+
+
单元测试
+
+
+
41
+
总提交数
+
+
+ +
+
+
+ + + + + diff --git a/showcase/main.go b/showcase/main.go new file mode 100644 index 0000000..7eea5d9 --- /dev/null +++ b/showcase/main.go @@ -0,0 +1,113 @@ +package main + +import ( + _ "embed" + "encoding/json" + "fmt" + "log" + "net/http" + "os" + "os/exec" + "path/filepath" + "strings" +) + +//go:embed index.html +var indexHTML []byte + +type RunResult struct { + OK bool `json:"ok"` + Command string `json:"command"` + Output interface{} `json:"output"` + Error string `json:"error,omitempty"` +} + +func main() { + port := os.Getenv("PORT") + if port == "" { + port = "9090" + } + cliBin := findCLIBinary() + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.Write(indexHTML) + }) + + http.HandleFunc("/api/run", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + + module := r.URL.Query().Get("module") + command := r.URL.Query().Get("command") + owner := r.URL.Query().Get("owner") + repo := r.URL.Query().Get("repo") + extraArgs := r.URL.Query().Get("args") + + if module == "" || command == "" { + json.NewEncoder(w).Encode(RunResult{Error: "missing module or command"}) + return + } + if owner == "" { + owner = "chroe" + } + if repo == "" { + repo = "gitlink-cli" + } + + args := []string{module, "+" + command, "--owner", owner, "--repo", repo, "--format", "json"} + if extraArgs != "" { + args = append(args, strings.Split(extraArgs, " ")...) + } + + cmdStr := "gitlink-cli " + strings.Join(args, " ") + log.Printf("Running: %s", cmdStr) + + cmd := exec.Command(cliBin, args...) + output, err := cmd.CombinedOutput() + + result := RunResult{ + Command: cmdStr, + } + + if err != nil { + result.Error = strings.TrimSpace(string(output)) + result.Output = nil + } else { + result.OK = true + var parsed interface{} + if json.Unmarshal(output, &parsed) == nil { + result.Output = parsed + } else { + result.Output = strings.TrimSpace(string(output)) + } + } + + json.NewEncoder(w).Encode(result) + }) + + fmt.Printf("Showcase Dashboard running at http://localhost:%s\n", port) + log.Fatal(http.ListenAndServe(":"+port, nil)) +} + +func findCLIBinary() string { + exe, _ := os.Executable() + exeDir := filepath.Dir(exe) + + candidates := []string{ + filepath.Join(exeDir, "gitlink-cli.exe"), + filepath.Join(exeDir, "gitlink-cli"), + filepath.Join(exeDir, "..", "gitlink-cli.exe"), + filepath.Join(exeDir, "..", "gitlink-cli"), + "./gitlink-cli.exe", + "./gitlink-cli", + "../gitlink-cli.exe", + "../gitlink-cli", + } + for _, c := range candidates { + if _, err := os.Stat(c); err == nil { + abs, _ := filepath.Abs(c) + return abs + } + } + return "gitlink-cli" +} diff --git a/showcase/pipeline.yml b/showcase/pipeline.yml new file mode 100644 index 0000000..7583556 --- /dev/null +++ b/showcase/pipeline.yml @@ -0,0 +1,56 @@ +version: 2 +name: 构建部署Showcase +description: "代码提交自动触发:构建gitlink-cli + Showcase Dashboard并部署到ECS" +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: + remote_url: '"https://gitlink.org.cn/chroe/gitlink-cli.git"' + ref: '"refs/heads/master"' + commit_id: '""' + depth: 1 + needs: + - start + - ref: docker_image_build_0 + name: 构建Docker镜像 + task: docker_image_build@1.6.0 + input: + docker_username: ((aliyun_docker.docker_username)) + docker_password: ((aliyun_docker.docker_password)) + image_name: '"crpi-r6c0crt00jo9rvaq.cn-hangzhou.personal.cr.aliyuncs.com/chroe/gitlink-cli-showcase"' + image_tag: '"latest"' + registry_address: '"crpi-r6c0crt00jo9rvaq.cn-hangzhou.personal.cr.aliyuncs.com"' + docker_file: '"showcase/Dockerfile"' + docker_build_path: '"."' + workspace: git_clone_0.git_path + image_push: true + build_args: '""' + needs: + - git_clone_0 + - ref: ssh_cmd_0 + name: SSH部署到服务器 + task: ssh_cmd@1.1.1 + input: + ssh_pass: ((deploy_server.server_password)) + ssh_ip: '"121.41.210.165"' + ssh_port: '"22"' + ssh_user: '"root"' + ssh_cmd: '"docker stop gitlink-cli-showcase || true && docker rm gitlink-cli-showcase || true && docker pull crpi-r6c0crt00jo9rvaq.cn-hangzhou.personal.cr.aliyuncs.com/chroe/gitlink-cli-showcase:latest && docker run -d -p 9090:9090 --name gitlink-cli-showcase --restart unless-stopped crpi-r6c0crt00jo9rvaq.cn-hangzhou.personal.cr.aliyuncs.com/chroe/gitlink-cli-showcase:latest"' + needs: + - docker_image_build_0 + - ref: end + name: 结束 + task: end + needs: + - ssh_cmd_0