feat: 新增 alias/browse/status 三个开发者体验命令
- 新增 cmd/alias/alias.go: 管理命令别名 (+list/+set/+delete) - 新增 cmd/browse/browse.go: 在浏览器中打开 GitLink 页面(跨平台) - 新增 cmd/status/status.go: 显示登录状态和上下文信息 - 修改 cmd/root.go: 注册三个新命令 关联 Issue: #14
This commit is contained in:
parent
6bcd8b65d2
commit
a28a8eae8a
|
|
@ -0,0 +1,119 @@
|
|||
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)
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
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()
|
||||
}
|
||||
|
|
@ -7,7 +7,10 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
aliasCmd "github.com/gitlink-org/gitlink-cli/cmd/alias"
|
||||
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"
|
||||
"github.com/gitlink-org/gitlink-cli/cmd/cmdutil"
|
||||
configCmd "github.com/gitlink-org/gitlink-cli/cmd/config"
|
||||
|
|
@ -57,6 +60,9 @@ func NewRootCmd(opts RootOptions, tr *i18n.Translator) (*cobra.Command, error) {
|
|||
rootCmd.AddCommand(apiCmd.NewAPICmd(tr))
|
||||
rootCmd.AddCommand(configCmd.NewConfigCmd(tr))
|
||||
rootCmd.AddCommand(newVersionCmd(version, tr))
|
||||
rootCmd.AddCommand(aliasCmd.NewAliasCmd())
|
||||
rootCmd.AddCommand(browseCmd.NewBrowseCmd())
|
||||
rootCmd.AddCommand(statusCmd.NewStatusCmd())
|
||||
|
||||
shortcuts.RegisterAll(rootCmd, tr)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
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 / 配置文件"
|
||||
}
|
||||
Loading…
Reference in New Issue