diff --git a/README.md b/README.md index fd1a550..7f00f68 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ make install # Set environment variables (optional) export GITLAB_URL=https://your-gitlab-instance.com export GITLAB_TOKEN=your-personal-access-token +# Optional: SSH endpoint for clone/push templates +export GITLAB_SSH_ENDPOINT=ssh://git@your-gitlab.com:ssh-port # Create user, groups, and projects ./bin/gitlab-cli user create \ @@ -58,6 +60,7 @@ export GITLAB_TOKEN=your-personal-access-token ./bin/gitlab-cli user create \ --host https://your-gitlab.com \ --token your-token \ + --ssh-endpoint ssh://git@your-gitlab.com:ssh-port \ -f config.yaml \ -o output.yaml \ -t template.yaml @@ -216,6 +219,7 @@ token: ``` Output contains all created resource information: +- GitLab endpoint info: endpoint, scheme, host, port, ssh (endpoint/host/port when provided) - User info: username, email, name, user_id, password - Token info: value, scope, expires_at (if token is configured) - Group info: name, path, group_id, visibility (if groups are configured) @@ -224,6 +228,14 @@ Output contains all created resource information: Output format: ```yaml +endpoint: https://your-gitlab.com +scheme: https +host: your-gitlab.com +port: 443 +ssh: + endpoint: ssh://git@your-gitlab.com:ssh-port + host: your-gitlab.com + port: ssh-port users: - username: tektoncd email: tektoncd001@test.example.com @@ -299,6 +311,12 @@ toolchains: endpoint: {{ $.Endpoint }} host: {{ $.Host }} scheme: {{ $.Scheme }} + {{- if $.SSH }} + ssh: + endpoint: {{ $.SSH.Endpoint }} + host: {{ $.SSH.Host }} + port: {{ $.SSH.Port }} + {{- end }} # User information username: {{ .Username }} email: {{ .Email }} @@ -325,6 +343,7 @@ toolchains: **Template Notes:** - `default: {{ .Username }}` - Specifies the default group, newly created projects will use this username as the namespace by default +- `.SSH` is available when `--ssh-endpoint` or `GITLAB_SSH_ENDPOINT` is provided, useful for clone/push configs Using the template: diff --git a/examples/template-example.yaml b/examples/template-example.yaml index 9ba7605..f75b6aa 100644 --- a/examples/template-example.yaml +++ b/examples/template-example.yaml @@ -1,6 +1,7 @@ # GitLab CLI 输出模板示例 # 支持 Go template 语法,可以访问 OutputConfig 结构中的所有数据 # 数据结构: .Endpoint (完整 URL), .Scheme (http/https), .Host (主机名), .Port (端口号) 是 GitLab 服务器配置 +# Data: .SSH provides SSH endpoint, host and port when available # 数据结构: .Users[0] 包含 Username, Email, Name, UserID, Password, Token, Groups, Projects {{- range .Users }} @@ -17,6 +18,12 @@ toolchains: port: {{ $.Port }} # scheme, http or https scheme: {{ $.Scheme }} + {{- if $.SSH }} + ssh: + endpoint: {{ $.SSH.Endpoint }} + host: {{ $.SSH.Host }} + port: {{ $.SSH.Port }} + {{- end }} # username, the user name of the login account username: {{ .Username }} # email diff --git a/internal/cli/cmd.go b/internal/cli/cmd.go index d0ee716..88892d3 100644 --- a/internal/cli/cmd.go +++ b/internal/cli/cmd.go @@ -3,6 +3,7 @@ package cli import ( "fmt" "log" + "net/url" "strconv" "strings" "time" @@ -71,6 +72,7 @@ func buildUserCreateCommand(cfg *config.CLIConfig) *cobra.Command { cmd.Flags().StringVarP(&cfg.ConfigFile, "config", "f", "../test-users.yaml", "配置文件路径") cmd.Flags().StringVar(&cfg.GitLabHost, "host", "", "GitLab 主机地址") cmd.Flags().StringVar(&cfg.GitLabToken, "token", "", "GitLab Personal Access Token") + cmd.Flags().StringVar(&cfg.GitLabSSHEndpoint, "ssh-endpoint", "", "GitLab SSH endpoint (e.g., ssh://git@host:22)") cmd.Flags().StringVarP(&cfg.OutputFile, "output", "o", "", "输出结果到 YAML 文件") cmd.Flags().StringVarP(&cfg.TemplateFile, "template", "t", "", "使用模板文件格式化输出") @@ -175,11 +177,25 @@ func runUserCreate(cfg *config.CLIConfig) error { // 从 GitLabHost 解析 endpoint、scheme、host 和 port endpoint, scheme, host, port := parseGitLabHostURL(cfg.GitLabHost) + // Parse SSH endpoint information for template rendering + sshEndpoint, sshHost, sshPort := parseGitLabSSHEndpoint(cfg.GitLabSSHEndpoint) + + // sshConfig holds parsed SSH endpoint details when available + var sshConfig *types.SSHConfig + if sshEndpoint != "" { + sshConfig = &types.SSHConfig{ + Endpoint: sshEndpoint, + Host: sshHost, + Port: sshPort, + } + } + output := &types.OutputConfig{ Endpoint: endpoint, Scheme: scheme, Host: host, Port: port, + SSH: sshConfig, Users: userOutputs, } @@ -495,6 +511,51 @@ func initializeClient(cfg *config.CLIConfig) (*client.GitLabClient, error) { return gitlabClient, nil } +// parseGitLabSSHEndpoint extracts endpoint, host, and port from the SSH URL string +func parseGitLabSSHEndpoint(rawEndpoint string) (endpoint, host string, port int) { + // trimmedEndpoint stores the SSH endpoint without trailing slash or spaces + trimmedEndpoint := strings.TrimSpace(rawEndpoint) + if trimmedEndpoint == "" { + return "", "", 0 + } + + // normalizedEndpoint ensures the SSH URL includes a scheme for parsing + normalizedEndpoint := strings.TrimSuffix(trimmedEndpoint, "/") + if !strings.Contains(normalizedEndpoint, "://") { + normalizedEndpoint = "ssh://" + normalizedEndpoint + } + + // parsedURL holds the parsed SSH URL result + parsedURL, err := url.Parse(normalizedEndpoint) + if err != nil { + return normalizedEndpoint, "", 0 + } + + // hostName captures the hostname portion of the SSH URL + hostName := parsedURL.Hostname() + // portNumber defaults to standard SSH port + portNumber := 22 + if parsedURL.Port() != "" { + if parsedPort, err := strconv.Atoi(parsedURL.Port()); err == nil { + portNumber = parsedPort + } else { + portNumber = 0 + } + } + + // endpointBuilder rebuilds the endpoint including user info when available + endpointBuilder := parsedURL.Scheme + "://" + if parsedURL.User != nil { + endpointBuilder += parsedURL.User.String() + "@" + } + endpointBuilder += hostName + if portNumber > 0 { + endpointBuilder += ":" + strconv.Itoa(portNumber) + } + + return endpointBuilder, hostName, portNumber +} + // parseGitLabHostURL 从 GitLab Host URL 解析出 endpoint、scheme 和 host func parseGitLabHostURL(gitlabHost string) (endpoint, scheme, host string, port int) { // 默认值 diff --git a/internal/config/config.go b/internal/config/config.go index c79760c..22ad6ba 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -10,12 +10,13 @@ import ( // CLIConfig 封装CLI配置参数 type CLIConfig struct { - ConfigFile string - GitLabHost string - GitLabToken string - OutputFile string // 输出文件路径 - TemplateFile string // 模板文件路径 - DaysOld int // 只删除创建日期超过指定天数的用户(cleanup 命令使用) + ConfigFile string + GitLabHost string + GitLabToken string + OutputFile string // 输出文件路径 + TemplateFile string // 模板文件路径 + DaysOld int // 只删除创建日期超过指定天数的用户(cleanup 命令使用) + GitLabSSHEndpoint string // GitLab SSH endpoint (e.g., ssh://git@host:22) } // LoadGitLabCredentials 从环境变量或命令行参数加载 GitLab 凭证 @@ -32,6 +33,10 @@ func LoadGitLabCredentials(cfg *CLIConfig) error { return fmt.Errorf("GitLab token is required (use --token or GITLAB_TOKEN env)") } } + // Populate SSH endpoint from environment when flag is not provided + if cfg.GitLabSSHEndpoint == "" { + cfg.GitLabSSHEndpoint = os.Getenv("GITLAB_SSH_ENDPOINT") + } return nil } diff --git a/pkg/types/types.go b/pkg/types/types.go index bc318a5..7e56f1d 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -45,13 +45,21 @@ type ProjectSpec struct { // 输出结果类型 // ======================================== +// SSHConfig captures parsed SSH endpoint information +type SSHConfig struct { + Endpoint string `yaml:"endpoint"` // Endpoint is the normalized SSH endpoint string + Host string `yaml:"host"` // Host is the SSH hostname extracted from the endpoint + Port int `yaml:"port"` // Port is the SSH port number +} + // OutputConfig 输出配置结构 type OutputConfig struct { - Endpoint string `yaml:"endpoint"` - Scheme string `yaml:"scheme"` - Host string `yaml:"host"` - Port int `yaml:"port"` - Users []UserOutput `yaml:"users"` + Endpoint string `yaml:"endpoint"` // Endpoint is the normalized HTTP endpoint of GitLab + Scheme string `yaml:"scheme"` // Scheme is the HTTP scheme of the GitLab endpoint + Host string `yaml:"host"` // Host is the hostname of the GitLab endpoint + Port int `yaml:"port"` // Port is the HTTP port of the GitLab endpoint + SSH *SSHConfig `yaml:"ssh,omitempty"` // SSH holds parsed SSH endpoint details when available + Users []UserOutput `yaml:"users"` // Users carries all generated user outputs } // UserOutput 用户输出结果 diff --git a/template-example.yaml b/template-example.yaml index 5d17c7a..1db3966 100644 --- a/template-example.yaml +++ b/template-example.yaml @@ -1,6 +1,7 @@ # GitLab CLI 输出模板示例 # 支持 Go template 语法,可以访问 OutputConfig 结构中的所有数据 # 数据结构: .Endpoint (完整 URL), .Scheme (http/https), .Host (主机名) 是 GitLab 服务器配置 +# Data: .SSH provides SSH endpoint, host and port when available # 数据结构: .Users[0] 包含 Username, Email, Name, UserID, Token, Groups {{- range .Users }} @@ -15,6 +16,12 @@ toolchains: host: {{ $.Host }} # scheme, http or https scheme: {{ $.Scheme }} + {{- if $.SSH }} + ssh: + endpoint: {{ $.SSH.Endpoint }} + host: {{ $.SSH.Host }} + port: {{ $.SSH.Port }} + {{- end }} # username, the user name of the login account username: {{ .Username }} # email