feat(ci): add repository CI control shortcuts
This commit is contained in:
parent
71ca2bb683
commit
0806f3096c
15
README.md
15
README.md
|
|
@ -477,13 +477,22 @@ gitlink-cli release +delete --owner Gitlink --repo forgeplus -i <version_id> --d
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# List builds
|
# List builds
|
||||||
gitlink-cli ci +list --owner Gitlink --repo forgeplus
|
gitlink-cli ci +builds --owner Gitlink --repo forgeplus
|
||||||
|
|
||||||
# View build log
|
# View build log
|
||||||
gitlink-cli ci +log --owner Gitlink --repo forgeplus -i <build_id>
|
gitlink-cli ci +logs --owner Gitlink --repo forgeplus --build <build_id>
|
||||||
|
|
||||||
# Restart a build
|
# Restart a build
|
||||||
gitlink-cli ci +restart --owner Gitlink --repo forgeplus -i <build_id>
|
gitlink-cli ci +restart --owner Gitlink --repo forgeplus --build <build_id>
|
||||||
|
|
||||||
|
# Stop a build
|
||||||
|
gitlink-cli ci +stop --owner Gitlink --repo forgeplus --build <build_id>
|
||||||
|
|
||||||
|
# Check CI authorization and safely toggle repository CI
|
||||||
|
gitlink-cli ci +authorize --owner Gitlink --repo forgeplus
|
||||||
|
gitlink-cli ci +activate --owner Gitlink --repo forgeplus --dry-run
|
||||||
|
gitlink-cli ci +activate --owner Gitlink --repo forgeplus --yes
|
||||||
|
gitlink-cli ci +deactivate --owner Gitlink --repo forgeplus --dry-run
|
||||||
```
|
```
|
||||||
|
|
||||||
### Pipeline Operations
|
### Pipeline Operations
|
||||||
|
|
|
||||||
|
|
@ -464,6 +464,26 @@ gitlink-cli release +update --owner Gitlink --repo forgeplus -i <version_id> -b
|
||||||
gitlink-cli release +delete --owner Gitlink --repo forgeplus -i <version_id> --dry-run
|
gitlink-cli release +delete --owner Gitlink --repo forgeplus -i <version_id> --dry-run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### CI/CD 操作
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看构建列表
|
||||||
|
gitlink-cli ci +builds --owner Gitlink --repo forgeplus
|
||||||
|
|
||||||
|
# 查看构建日志
|
||||||
|
gitlink-cli ci +logs --owner Gitlink --repo forgeplus --build <build_id>
|
||||||
|
|
||||||
|
# 重启或停止构建
|
||||||
|
gitlink-cli ci +restart --owner Gitlink --repo forgeplus --build <build_id>
|
||||||
|
gitlink-cli ci +stop --owner Gitlink --repo forgeplus --build <build_id>
|
||||||
|
|
||||||
|
# 查看 CI 授权状态并安全启停仓库 CI
|
||||||
|
gitlink-cli ci +authorize --owner Gitlink --repo forgeplus
|
||||||
|
gitlink-cli ci +activate --owner Gitlink --repo forgeplus --dry-run
|
||||||
|
gitlink-cli ci +activate --owner Gitlink --repo forgeplus --yes
|
||||||
|
gitlink-cli ci +deactivate --owner Gitlink --repo forgeplus --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
### 流水线管理
|
### 流水线管理
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
# 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 ./...
|
||||||
|
```
|
||||||
|
|
@ -11,7 +11,10 @@
|
||||||
"cmd.branch.protect.short": "Set branch protection",
|
"cmd.branch.protect.short": "Set branch protection",
|
||||||
"cmd.branch.short": "Branch operations",
|
"cmd.branch.short": "Branch operations",
|
||||||
"cmd.branch.unprotect.short": "Remove branch protection",
|
"cmd.branch.unprotect.short": "Remove branch protection",
|
||||||
|
"cmd.ci.activate.short": "Activate repository CI",
|
||||||
|
"cmd.ci.authorize.short": "Show repository CI authorization status",
|
||||||
"cmd.ci.builds.short": "List CI builds",
|
"cmd.ci.builds.short": "List CI builds",
|
||||||
|
"cmd.ci.deactivate.short": "Deactivate repository CI",
|
||||||
"cmd.ci.logs.short": "View build logs",
|
"cmd.ci.logs.short": "View build logs",
|
||||||
"cmd.ci.restart.short": "Restart a build",
|
"cmd.ci.restart.short": "Restart a build",
|
||||||
"cmd.ci.short": "CI/CD operations",
|
"cmd.ci.short": "CI/CD operations",
|
||||||
|
|
@ -126,7 +129,11 @@
|
||||||
"flag.auth.token": "Login by pasting an existing token",
|
"flag.auth.token": "Login by pasting an existing token",
|
||||||
"flag.branch.from": "Source branch or commit",
|
"flag.branch.from": "Source branch or commit",
|
||||||
"flag.branch.name": "Branch name",
|
"flag.branch.name": "Branch name",
|
||||||
|
"flag.ci.activate_dry_run": "Preview the CI activation request without changing remote state",
|
||||||
|
"flag.ci.activate_yes": "Confirm repository CI activation",
|
||||||
"flag.ci.build": "Build number",
|
"flag.ci.build": "Build number",
|
||||||
|
"flag.ci.deactivate_dry_run": "Preview the CI deactivation request without changing remote state",
|
||||||
|
"flag.ci.deactivate_yes": "Confirm repository CI deactivation",
|
||||||
"flag.ci.stage": "Stage number",
|
"flag.ci.stage": "Stage number",
|
||||||
"flag.ci.step": "Step number",
|
"flag.ci.step": "Step number",
|
||||||
"flag.comment.body": "Comment body",
|
"flag.comment.body": "Comment body",
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,10 @@
|
||||||
"cmd.branch.protect.short": "设置分支保护",
|
"cmd.branch.protect.short": "设置分支保护",
|
||||||
"cmd.branch.short": "分支操作",
|
"cmd.branch.short": "分支操作",
|
||||||
"cmd.branch.unprotect.short": "移除分支保护",
|
"cmd.branch.unprotect.short": "移除分支保护",
|
||||||
|
"cmd.ci.activate.short": "激活仓库 CI",
|
||||||
|
"cmd.ci.authorize.short": "显示仓库 CI 授权状态",
|
||||||
"cmd.ci.builds.short": "列出 CI 构建",
|
"cmd.ci.builds.short": "列出 CI 构建",
|
||||||
|
"cmd.ci.deactivate.short": "停用仓库 CI",
|
||||||
"cmd.ci.logs.short": "查看构建日志",
|
"cmd.ci.logs.short": "查看构建日志",
|
||||||
"cmd.ci.restart.short": "重启构建",
|
"cmd.ci.restart.short": "重启构建",
|
||||||
"cmd.ci.short": "CI/CD 操作",
|
"cmd.ci.short": "CI/CD 操作",
|
||||||
|
|
@ -126,7 +129,11 @@
|
||||||
"flag.auth.token": "通过粘贴已有 Token 登录",
|
"flag.auth.token": "通过粘贴已有 Token 登录",
|
||||||
"flag.branch.from": "源分支或 Commit",
|
"flag.branch.from": "源分支或 Commit",
|
||||||
"flag.branch.name": "分支名称",
|
"flag.branch.name": "分支名称",
|
||||||
|
"flag.ci.activate_dry_run": "预览 CI 激活请求,不修改远端状态",
|
||||||
|
"flag.ci.activate_yes": "确认激活仓库 CI",
|
||||||
"flag.ci.build": "构建编号",
|
"flag.ci.build": "构建编号",
|
||||||
|
"flag.ci.deactivate_dry_run": "预览 CI 停用请求,不修改远端状态",
|
||||||
|
"flag.ci.deactivate_yes": "确认停用仓库 CI",
|
||||||
"flag.ci.stage": "阶段编号",
|
"flag.ci.stage": "阶段编号",
|
||||||
"flag.ci.step": "步骤编号",
|
"flag.ci.step": "步骤编号",
|
||||||
"flag.comment.body": "评论内容",
|
"flag.comment.body": "评论内容",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package ci
|
package ci
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
|
@ -96,7 +97,68 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
|
||||||
return ctx.Output(env)
|
return ctx.Output(env)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "activate",
|
||||||
|
Description: tr.T("cmd.ci.activate.short"),
|
||||||
|
Flags: ciControlFlags(tr, "flag.ci.activate_dry_run", "flag.ci.activate_yes"),
|
||||||
|
Run: func(ctx *common.RuntimeContext) error {
|
||||||
|
return runCIControl(ctx, "activate_ci", "POST", "activate", "activating CI changes repository CI state; run --dry-run first, then pass --yes to execute")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "deactivate",
|
||||||
|
Description: tr.T("cmd.ci.deactivate.short"),
|
||||||
|
Flags: ciControlFlags(tr, "flag.ci.deactivate_dry_run", "flag.ci.deactivate_yes"),
|
||||||
|
Run: func(ctx *common.RuntimeContext) error {
|
||||||
|
return runCIControl(ctx, "deactivate_ci", "DELETE", "deactivate", "deactivating CI changes repository CI state; run --dry-run first, then pass --yes to execute")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "authorize",
|
||||||
|
Description: tr.T("cmd.ci.authorize.short"),
|
||||||
|
Run: func(ctx *common.RuntimeContext) error {
|
||||||
|
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
env, err := ctx.CallAPI("GET", ctx.RepoPath()+"/ci_authorize", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ctx.Output(env)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ciControlFlags(tr *i18n.Translator, dryRunKey, yesKey string) []common.Flag {
|
||||||
|
return []common.Flag{
|
||||||
|
{Name: "dry-run", Usage: tr.T(dryRunKey), Bool: true, Default: "false"},
|
||||||
|
{Name: "yes", Usage: tr.T(yesKey), Bool: true, Default: "false"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCIControl(ctx *common.RuntimeContext, action, method, suffix, confirmMessage string) error {
|
||||||
|
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
path := ctx.RepoPath() + "/" + suffix
|
||||||
|
if ctx.Arg("dry-run") == "true" {
|
||||||
|
return ctx.OutputData(map[string]interface{}{
|
||||||
|
"dry_run": true,
|
||||||
|
"action": action,
|
||||||
|
"method": method,
|
||||||
|
"path": path,
|
||||||
|
"repository": fmt.Sprintf("%s/%s", ctx.Owner, ctx.Repo),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if ctx.Arg("yes") != "true" {
|
||||||
|
return errors.New(confirmMessage)
|
||||||
|
}
|
||||||
|
env, err := ctx.CallAPI(method, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ctx.Output(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
func shortcutTranslator(translators ...*i18n.Translator) *i18n.Translator {
|
func shortcutTranslator(translators ...*i18n.Translator) *i18n.Translator {
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,110 @@ func TestCIStop(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- activate / deactivate / authorize ---
|
||||||
|
|
||||||
|
func TestCIActivateDryRunDoesNotCallAPI(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Fatalf("activate dry-run should not call remote API: %s %s", r.Method, r.URL.Path)
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
err := runShortcut(t, server, "activate", map[string]string{"dry-run": "true"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("activate dry-run failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCIActivateRequiresYes(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Fatalf("activate without --yes should not call remote API: %s %s", r.Method, r.URL.Path)
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
err := runShortcut(t, server, "activate", nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected activate to require --yes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCIActivateWithYesCallsEndpoint(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "POST" {
|
||||||
|
t.Fatalf("expected POST, got %s", r.Method)
|
||||||
|
}
|
||||||
|
if r.URL.Path != "/owner/repo/activate.json" {
|
||||||
|
t.Fatalf("unexpected path: %s", r.URL.Path)
|
||||||
|
}
|
||||||
|
writeJSON(w, map[string]interface{}{"status": 0, "message": "success"})
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
err := runShortcut(t, server, "activate", map[string]string{"yes": "true"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("activate failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCIDeactivateDryRunDoesNotCallAPI(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Fatalf("deactivate dry-run should not call remote API: %s %s", r.Method, r.URL.Path)
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
err := runShortcut(t, server, "deactivate", map[string]string{"dry-run": "true"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("deactivate dry-run failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCIDeactivateRequiresYes(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Fatalf("deactivate without --yes should not call remote API: %s %s", r.Method, r.URL.Path)
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
err := runShortcut(t, server, "deactivate", nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected deactivate to require --yes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCIDeactivateWithYesCallsEndpoint(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "DELETE" {
|
||||||
|
t.Fatalf("expected DELETE, got %s", r.Method)
|
||||||
|
}
|
||||||
|
if r.URL.Path != "/owner/repo/deactivate.json" {
|
||||||
|
t.Fatalf("unexpected path: %s", r.URL.Path)
|
||||||
|
}
|
||||||
|
writeJSON(w, map[string]interface{}{"status": 0, "message": "success"})
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
err := runShortcut(t, server, "deactivate", map[string]string{"yes": "true"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("deactivate failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCIAuthorizeCallsEndpoint(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "GET" {
|
||||||
|
t.Fatalf("expected GET, got %s", r.Method)
|
||||||
|
}
|
||||||
|
if r.URL.Path != "/owner/repo/ci_authorize.json" {
|
||||||
|
t.Fatalf("unexpected path: %s", r.URL.Path)
|
||||||
|
}
|
||||||
|
writeJSON(w, map[string]interface{}{"authorized": true})
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
err := runShortcut(t, server, "authorize", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("authorize failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- HTTP error paths ---
|
// --- HTTP error paths ---
|
||||||
|
|
||||||
func TestCIBuildsHTTPError(t *testing.T) {
|
func TestCIBuildsHTTPError(t *testing.T) {
|
||||||
|
|
@ -180,3 +284,42 @@ func TestCIStopHTTPError(t *testing.T) {
|
||||||
t.Fatal("expected error for HTTP 500")
|
t.Fatal("expected error for HTTP 500")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCIActivateHTTPError(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte("server error"))
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
err := runShortcut(t, server, "activate", map[string]string{"yes": "true"})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for HTTP 500")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCIDeactivateHTTPError(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte("server error"))
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
err := runShortcut(t, server, "deactivate", map[string]string{"yes": "true"})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for HTTP 500")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCIAuthorizeHTTPError(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte("server error"))
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
err := runShortcut(t, server, "authorize", nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for HTTP 500")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: gitlink-ci
|
name: gitlink-ci
|
||||||
version: 1.0.0
|
version: 2.0.0
|
||||||
description: "CI/CD 操作:查看构建列表、构建日志、重启/停止构建。当用户需要操作 GitLink CI 时触发。"
|
description: "CI/CD 操作:查看构建列表、构建日志、重启/停止构建、启停仓库 CI、查看 CI 授权状态。当用户需要操作 GitLink CI 时触发。"
|
||||||
metadata:
|
metadata:
|
||||||
requires:
|
requires:
|
||||||
bins: ["gitlink-cli"]
|
bins: ["gitlink-cli"]
|
||||||
|
|
@ -24,6 +24,9 @@ metadata:
|
||||||
| `ci +logs` | 构建日志 | 是 |
|
| `ci +logs` | 构建日志 | 是 |
|
||||||
| `ci +restart` | 重启构建 | 是 |
|
| `ci +restart` | 重启构建 | 是 |
|
||||||
| `ci +stop` | 停止构建 | 是 |
|
| `ci +stop` | 停止构建 | 是 |
|
||||||
|
| `ci +activate` | 激活仓库 CI,先 dry-run | 是 |
|
||||||
|
| `ci +deactivate` | 停用仓库 CI,先 dry-run | 是 |
|
||||||
|
| `ci +authorize` | 查看 CI 授权状态 | 是 |
|
||||||
|
|
||||||
## 使用示例
|
## 使用示例
|
||||||
|
|
||||||
|
|
@ -39,17 +42,21 @@ gitlink-cli ci +restart --build 42
|
||||||
|
|
||||||
# 停止构建
|
# 停止构建
|
||||||
gitlink-cli ci +stop --build 42
|
gitlink-cli ci +stop --build 42
|
||||||
|
|
||||||
|
# 查看 CI 授权状态
|
||||||
|
gitlink-cli ci +authorize --owner myuser --repo myrepo
|
||||||
|
|
||||||
|
# 激活仓库 CI,先预览再执行
|
||||||
|
gitlink-cli ci +activate --owner myuser --repo myrepo --dry-run
|
||||||
|
gitlink-cli ci +activate --owner myuser --repo myrepo --yes
|
||||||
|
|
||||||
|
# 停用仓库 CI,先预览再执行
|
||||||
|
gitlink-cli ci +deactivate --owner myuser --repo myrepo --dry-run
|
||||||
|
gitlink-cli ci +deactivate --owner myuser --repo myrepo --yes
|
||||||
```
|
```
|
||||||
|
|
||||||
## Raw API 补充
|
## 注意事项
|
||||||
|
|
||||||
```bash
|
- `ci +activate` 和 `ci +deactivate` 会修改仓库 CI 状态,Agent 必须先执行 `--dry-run` 并获得用户确认,再加 `--yes`。
|
||||||
# 激活 CI
|
- `ci +authorize` 是只读查询,可直接执行。
|
||||||
gitlink-cli api POST /:owner/:repo/activate
|
- 在 Agent 场景建议统一加 `--format json`,便于解析构建状态和授权结果。
|
||||||
|
|
||||||
# 停用 CI
|
|
||||||
gitlink-cli api DELETE /:owner/:repo/deactivate
|
|
||||||
|
|
||||||
# CI 授权状态
|
|
||||||
gitlink-cli api GET /:owner/:repo/ci_authorize
|
|
||||||
```
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue