feat(repo): add mirror sync shortcut
This commit is contained in:
parent
71ca2bb683
commit
4a263c1ec0
|
|
@ -0,0 +1,13 @@
|
|||
# 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
|
||||
```
|
||||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/gitlink-org/gitlink-cli/shortcuts/profile"
|
||||
"github.com/gitlink-org/gitlink-cli/shortcuts/release"
|
||||
"github.com/gitlink-org/gitlink-cli/shortcuts/repo"
|
||||
"github.com/gitlink-org/gitlink-cli/shortcuts/repomirror"
|
||||
"github.com/gitlink-org/gitlink-cli/shortcuts/search"
|
||||
"github.com/gitlink-org/gitlink-cli/shortcuts/user"
|
||||
"github.com/gitlink-org/gitlink-cli/shortcuts/webhook"
|
||||
|
|
@ -36,53 +37,55 @@ func RegisterAll(root *cobra.Command, translators ...*i18n.Translator) {
|
|||
tr = translators[0]
|
||||
}
|
||||
groups := map[string][]*common.Shortcut{
|
||||
"repo": repo.Shortcuts(tr),
|
||||
"issue": issue.Shortcuts(tr),
|
||||
"label": label.Shortcuts(),
|
||||
"license": license.Shortcuts(),
|
||||
"member": member.Shortcuts(),
|
||||
"milestone": milestone.Shortcuts(),
|
||||
"pipeline": pipeline.Shortcuts(),
|
||||
"pr": pr.Shortcuts(tr),
|
||||
"profile": profile.Shortcuts(tr),
|
||||
"release": release.Shortcuts(tr),
|
||||
"branch": branch.Shortcuts(tr),
|
||||
"org": org.Shortcuts(tr),
|
||||
"user": user.Shortcuts(tr),
|
||||
"search": search.Shortcuts(tr),
|
||||
"ci": ci.Shortcuts(tr),
|
||||
"compare": compare.Shortcuts(),
|
||||
"dataset": dataset.Shortcuts(tr),
|
||||
"webhook": webhook.Shortcuts(tr),
|
||||
"wiki": wiki.Shortcuts(),
|
||||
"health": health.Shortcuts(tr),
|
||||
"ignore": ignore.Shortcuts(),
|
||||
"workflow": workflow.Shortcuts(),
|
||||
"repo": repo.Shortcuts(tr),
|
||||
"issue": issue.Shortcuts(tr),
|
||||
"label": label.Shortcuts(),
|
||||
"license": license.Shortcuts(),
|
||||
"member": member.Shortcuts(),
|
||||
"milestone": milestone.Shortcuts(),
|
||||
"pipeline": pipeline.Shortcuts(),
|
||||
"pr": pr.Shortcuts(tr),
|
||||
"profile": profile.Shortcuts(tr),
|
||||
"release": release.Shortcuts(tr),
|
||||
"branch": branch.Shortcuts(tr),
|
||||
"org": org.Shortcuts(tr),
|
||||
"user": user.Shortcuts(tr),
|
||||
"search": search.Shortcuts(tr),
|
||||
"ci": ci.Shortcuts(tr),
|
||||
"compare": compare.Shortcuts(),
|
||||
"dataset": dataset.Shortcuts(tr),
|
||||
"webhook": webhook.Shortcuts(tr),
|
||||
"wiki": wiki.Shortcuts(),
|
||||
"health": health.Shortcuts(tr),
|
||||
"ignore": ignore.Shortcuts(),
|
||||
"repo-mirror": repomirror.Shortcuts(),
|
||||
"workflow": workflow.Shortcuts(),
|
||||
}
|
||||
|
||||
descriptions := map[string]string{
|
||||
"repo": tr.T("cmd.repo.short"),
|
||||
"issue": tr.T("cmd.issue.short"),
|
||||
"label": "Issue label operations",
|
||||
"license": "License operations",
|
||||
"member": "Repository member operations",
|
||||
"milestone": "Milestone operations",
|
||||
"pipeline": "Pipeline operations",
|
||||
"pr": tr.T("cmd.pr.short"),
|
||||
"profile": tr.T("cmd.profile.short"),
|
||||
"release": tr.T("cmd.release.short"),
|
||||
"branch": tr.T("cmd.branch.short"),
|
||||
"org": tr.T("cmd.org.short"),
|
||||
"user": tr.T("cmd.user.short"),
|
||||
"search": tr.T("cmd.search.short"),
|
||||
"ci": tr.T("cmd.ci.short"),
|
||||
"compare": "Compare branches, tags, or commits",
|
||||
"dataset": tr.T("cmd.dataset.short"),
|
||||
"webhook": tr.T("cmd.webhook.short"),
|
||||
"wiki": "Wiki page management",
|
||||
"health": "Project health data collection",
|
||||
"ignore": tr.T("cmd.ignore.short"),
|
||||
"workflow": "AI agent workflow analysis",
|
||||
"repo": tr.T("cmd.repo.short"),
|
||||
"issue": tr.T("cmd.issue.short"),
|
||||
"label": "Issue label operations",
|
||||
"license": "License operations",
|
||||
"member": "Repository member operations",
|
||||
"milestone": "Milestone operations",
|
||||
"pipeline": "Pipeline operations",
|
||||
"pr": tr.T("cmd.pr.short"),
|
||||
"profile": tr.T("cmd.profile.short"),
|
||||
"release": tr.T("cmd.release.short"),
|
||||
"branch": tr.T("cmd.branch.short"),
|
||||
"org": tr.T("cmd.org.short"),
|
||||
"user": tr.T("cmd.user.short"),
|
||||
"search": tr.T("cmd.search.short"),
|
||||
"ci": tr.T("cmd.ci.short"),
|
||||
"compare": "Compare branches, tags, or commits",
|
||||
"dataset": tr.T("cmd.dataset.short"),
|
||||
"webhook": tr.T("cmd.webhook.short"),
|
||||
"wiki": "Wiki page management",
|
||||
"health": "Project health data collection",
|
||||
"ignore": tr.T("cmd.ignore.short"),
|
||||
"repo-mirror": "Repository mirror operations",
|
||||
"workflow": "AI agent workflow analysis",
|
||||
}
|
||||
|
||||
for name, shortcuts := range groups {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ func TestRegisterAll(t *testing.T) {
|
|||
"repo", "issue", "label", "license", "pr", "profile", "release", "branch",
|
||||
"org", "user", "search", "ci", "workflow",
|
||||
"compare", "member", "milestone", "pipeline", "webhook",
|
||||
"dataset", "health", "ignore", "wiki",
|
||||
"dataset", "health", "ignore", "wiki", "repo-mirror",
|
||||
}
|
||||
|
||||
groupSet := map[string]bool{}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
package repomirror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
|
||||
)
|
||||
|
||||
// Shortcuts returns mirror repository operations.
|
||||
func Shortcuts() []*common.Shortcut {
|
||||
return []*common.Shortcut{
|
||||
{
|
||||
Name: "sync",
|
||||
Description: "Synchronize a mirror repository",
|
||||
Flags: []common.Flag{
|
||||
{Name: "id", Short: "i", Usage: "Repository/project ID", Required: true},
|
||||
{Name: "dry-run", Usage: "Preview the request without syncing the mirror", Bool: true, Default: "false"},
|
||||
},
|
||||
Run: runSync,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runSync(ctx *common.RuntimeContext) error {
|
||||
id, err := ctx.RequireArg("id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := strconv.Atoi(id); err != nil {
|
||||
return fmt.Errorf("invalid --id value %q: %w", id, err)
|
||||
}
|
||||
path := fmt.Sprintf("/repositories/%s/sync_mirror", url.PathEscape(id))
|
||||
if ctx.Arg("dry-run") == "true" {
|
||||
return ctx.OutputData(map[string]interface{}{"dry_run": true, "action": "sync_mirror", "method": "POST", "path": path})
|
||||
}
|
||||
env, err := ctx.CallAPI("POST", path, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package repomirror
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gitlink-org/gitlink-cli/internal/client"
|
||||
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
|
||||
)
|
||||
|
||||
func TestMirrorSync(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" || r.URL.Path != "/repositories/42/sync_mirror.json" {
|
||||
t.Fatalf("got %s %s", r.Method, r.URL.Path)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]interface{}{"status": 0})
|
||||
}))
|
||||
defer server.Close()
|
||||
if err := runShortcut(server, map[string]string{"id": "42"}); err != nil {
|
||||
t.Fatalf("sync failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMirrorSyncDryRun(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("dry-run should not call API")
|
||||
}))
|
||||
defer server.Close()
|
||||
if err := runShortcut(server, map[string]string{"id": "42", "dry-run": "true"}); err != nil {
|
||||
t.Fatalf("dry-run failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMirrorSyncRejectsInvalidID(t *testing.T) {
|
||||
if err := runShortcut(httptest.NewServer(http.NewServeMux()), map[string]string{"id": "abc"}); err == nil {
|
||||
t.Fatal("expected invalid id error")
|
||||
}
|
||||
}
|
||||
|
||||
func runShortcut(server *httptest.Server, args map[string]string) error {
|
||||
return Shortcuts()[0].Run(&common.RuntimeContext{Client: &client.Client{HTTP: server.Client(), BaseURL: server.URL}, Format: "json", Args: args})
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
name: gitlink-repo-mirror
|
||||
version: 1.0.0
|
||||
description: "GitLink 镜像仓库同步:触发镜像仓库 sync_mirror,支持 dry-run。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["gitlink-cli"]
|
||||
cliHelp: "gitlink-cli repo-mirror --help"
|
||||
---
|
||||
|
||||
# gitlink-repo-mirror
|
||||
|
||||
镜像同步是写操作,先 dry-run 并确认。
|
||||
|
||||
```bash
|
||||
gitlink-cli repo-mirror +sync --id 42 --dry-run
|
||||
gitlink-cli repo-mirror +sync --id 42
|
||||
```
|
||||
|
||||
对应 OpenAPI:`POST /repositories/{id}/sync_mirror`。
|
||||
Loading…
Reference in New Issue