Skip to content

Commit ba65820

Browse files
feat: 添加 Personal Access Token 创建和自定义输出功能
新功能: - 添加 Personal Access Token 自动创建功能 - 支持自定义 Token 权限范围 (scope) - 支持自定义过期时间 (expires_at) - 未指定过期时间时默认为第2天 - 添加输出结果到文件功能 - 支持默认 YAML 格式输出 - 包含完整的创建结果(用户信息、Token 值、组、项目等) - 添加自定义模板输出功能 - 支持 Go template 语法 - 提供模板示例文件 (template-example.yaml) - 详细的模板使用文档 (docs/TEMPLATE.md) 改进: - 重写 README.md,添加完整的使用说明和示例 - 完善命令行参数说明 - 添加故障排查指南 - 优化错误处理和日志输出 技术实现: - 新增 internal/template 包处理模板渲染 - 扩展 types.go 添加输出结果类型定义 - 修改 processor.go 返回创建结果 - 添加 --output 和 --template 命令行参数 🤖 Generated with Claude Code Co-Authored-By: Claude <[email protected]>
1 parent 80f2ab6 commit ba65820

File tree

9 files changed

+1098
-40
lines changed

9 files changed

+1098
-40
lines changed

README.md

Lines changed: 419 additions & 28 deletions
Large diffs are not rendered by default.

docs/TEMPLATE.md

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
# 模板输出功能
2+
3+
GitLab CLI 支持使用自定义模板来格式化输出结果,让你可以按照自己的需求定制输出格式。
4+
5+
## 快速开始
6+
7+
### 基本用法
8+
9+
```bash
10+
# 使用模板输出
11+
./bin/gitlab-cli user create \
12+
--host https://your-gitlab.com \
13+
--token your-token \
14+
-f config.yaml \
15+
-o output.yaml \
16+
-t template.yaml
17+
```
18+
19+
### 参数说明
20+
21+
- `-f, --config`: 输入配置文件(用户、组、项目定义)
22+
- `-o, --output`: 输出文件路径
23+
- `-t, --template`: 模板文件路径(可选)
24+
25+
**注意**:
26+
- 如果不指定 `--template`,将使用默认的 YAML 格式输出
27+
- `--output``--template` 通常一起使用
28+
29+
## 模板语法
30+
31+
模板使用 Go 的 `text/template` 语法,支持访问以下数据结构:
32+
33+
### 可用数据
34+
35+
```go
36+
.Users[0]
37+
├── .Username // 用户名
38+
├── .Email // 邮箱
39+
├── .Name // 姓名
40+
├── .UserID // 用户 ID
41+
├── .Token // Personal Access Token (可能为空)
42+
│ ├── .Value // Token 值
43+
│ ├── .Scope // 权限范围数组
44+
│ └── .ExpiresAt // 过期时间
45+
└── .Groups // 组数组
46+
├── .Name // 组名
47+
├── .Path // 组路径
48+
├── .GroupID // 组 ID
49+
├── .Visibility // 可见性
50+
└── .Projects // 项目数组
51+
├── .Name // 项目名
52+
├── .Path // 项目路径
53+
├── .ProjectID // 项目 ID
54+
├── .Description // 描述
55+
├── .Visibility // 可见性
56+
└── .WebURL // Web URL
57+
```
58+
59+
### 基础语法
60+
61+
#### 1. 变量替换
62+
63+
```yaml
64+
username: {{ .Username }}
65+
email: {{ .Email }}
66+
user_id: {{ .UserID }}
67+
```
68+
69+
#### 2. 遍历用户列表
70+
71+
```yaml
72+
{{- range .Users }}
73+
user:
74+
username: {{ .Username }}
75+
email: {{ .Email }}
76+
{{- end }}
77+
```
78+
79+
#### 3. 条件判断
80+
81+
```yaml
82+
{{- if .Token }}
83+
token:
84+
value: {{ .Token.Value }}
85+
expires_at: {{ .Token.ExpiresAt }}
86+
{{- end }}
87+
```
88+
89+
#### 4. 遍历数组
90+
91+
```yaml
92+
scopes:
93+
{{- range .Token.Scope }}
94+
- {{ . }}
95+
{{- end }}
96+
```
97+
98+
或者内联格式:
99+
100+
```yaml
101+
scope: {{ range $i, $s := .Token.Scope }}{{ if $i }}, {{ end }}{{ $s }}{{ end }}
102+
```
103+
104+
#### 5. 去除空白
105+
106+
使用 `{{-` 和 `-}}` 来去除前后的空白字符:
107+
108+
```yaml
109+
{{- if .Token }}
110+
# 这一行不会有额外的空行
111+
{{- end }}
112+
```
113+
114+
## 模板示例
115+
116+
### 示例 1: 简单格式
117+
118+
```yaml
119+
# template-simple.yaml
120+
{{- range .Users }}
121+
username: {{ .Username }}
122+
email: {{ .Email }}
123+
user_id: {{ .UserID }}
124+
{{- if .Token }}
125+
token: {{ .Token.Value }}
126+
{{- end }}
127+
{{- end }}
128+
```
129+
130+
### 示例 2: 完整格式(包含在 template-example.yaml)
131+
132+
```yaml
133+
{{- range .Users }}
134+
# ========================================
135+
# 用户: {{ .Username }}
136+
# ========================================
137+
138+
toolchains:
139+
gitlab:
140+
endpoint: https://devops-gitlab.alaudatech.net
141+
host: devops-gitlab.alaudatech.net
142+
port: 443
143+
scheme: https
144+
username: {{ .Username }}
145+
email: {{ .Email }}
146+
user_id: {{ .UserID }}
147+
{{- if .Token }}
148+
token:
149+
value: {{ .Token.Value }}
150+
scope: {{ range $i, $s := .Token.Scope }}{{ if $i }}, {{ end }}{{ $s }}{{ end }}
151+
expires_at: {{ .Token.ExpiresAt }}
152+
{{- end }}
153+
{{- if .Groups }}
154+
groups:
155+
{{- range .Groups }}
156+
- name: {{ .Name }}
157+
path: {{ .Path }}
158+
group_id: {{ .GroupID }}
159+
visibility: {{ .Visibility }}
160+
{{- if .Projects }}
161+
projects:
162+
{{- range .Projects }}
163+
- name: {{ .Name }}
164+
project_id: {{ .ProjectID }}
165+
web_url: {{ .WebURL }}
166+
{{- end }}
167+
{{- end }}
168+
{{- end }}
169+
{{- end }}
170+
{{- end }}
171+
```
172+
173+
### 示例 3: 多用户格式
174+
175+
```yaml
176+
# template-multi-user.yaml
177+
users:
178+
{{- range .Users }}
179+
- username: {{ .Username }}
180+
email: {{ .Email }}
181+
user_id: {{ .UserID }}
182+
{{- if .Token }}
183+
token: {{ .Token.Value }}
184+
token_expires: {{ .Token.ExpiresAt }}
185+
{{- end }}
186+
groups_count: {{ len .Groups }}
187+
{{- end }}
188+
```
189+
190+
## 使用场景
191+
192+
### 场景 1: 生成 CI/CD 配置
193+
194+
适用于生成 GitLab CI、Jenkins 或其他 CI/CD 工具的配置文件。
195+
196+
### 场景 2: 生成测试配置
197+
198+
为自动化测试生成包含 GitLab 凭证的配置文件。
199+
200+
### 场景 3: 生成文档
201+
202+
自动生成包含项目信息的文档。
203+
204+
### 场景 4: 集成到其他系统
205+
206+
生成符合其他系统要求的配置格式。
207+
208+
## 完整示例
209+
210+
### 输入配置 (test-users.yaml)
211+
212+
```yaml
213+
users:
214+
- username: tektoncd
215+
216+
name: tektoncd-test
217+
password: "MyStr0ng!Pass2024"
218+
token:
219+
scope:
220+
- api
221+
- read_user
222+
expires_at: 2026-01-01
223+
groups:
224+
- name: test-group
225+
path: test-group
226+
visibility: private
227+
projects:
228+
- name: test-project
229+
path: test-project
230+
description: 测试项目
231+
visibility: private
232+
```
233+
234+
### 执行命令
235+
236+
```bash
237+
./bin/gitlab-cli user create \
238+
--host https://devops-gitlab.alaudatech.net \
239+
--token glpat-xxx \
240+
-f test-users.yaml \
241+
-o result.yaml \
242+
-t template-example.yaml
243+
```
244+
245+
### 输出结果 (result.yaml)
246+
247+
```yaml
248+
# ========================================
249+
# 用户: tektoncd
250+
# ========================================
251+
252+
toolchains:
253+
gitlab:
254+
endpoint: https://devops-gitlab.alaudatech.net
255+
username: tektoncd
256+
257+
user_id: 24
258+
token:
259+
value: glpat-5mtyG_ftYFvh7pNKRGXd
260+
scope: api, read_user
261+
expires_at: 2026-01-01
262+
groups:
263+
- name: test-group
264+
group_id: 1496
265+
projects:
266+
- name: test-project
267+
project_id: 1434
268+
web_url: https://devops-gitlab.alaudatech.net/test-group/test-project
269+
```
270+
271+
## 最佳实践
272+
273+
1. **保持模板简洁**: 不要在模板中包含过于复杂的逻辑
274+
2. **使用注释**: 在模板中添加注释说明数据结构
275+
3. **测试模板**: 先用小数据集测试模板是否正确
276+
4. **版本控制**: 将常用模板纳入版本控制
277+
5. **模板复用**: 为不同场景创建多个模板文件
278+
279+
## 故障排查
280+
281+
### 模板解析错误
282+
283+
如果遇到模板解析错误,检查:
284+
- 所有的 `{{` 都有对应的 `}}`
285+
- 变量名是否正确(区分大小写)
286+
- 是否正确使用了 `range`、`if` 等语句
287+
288+
### 空白问题
289+
290+
如果输出有多余的空行:
291+
- 使用 `{{-` 和 `-}}` 去除空白
292+
- 检查模板文件末尾是否有多余空行
293+
294+
### 数据不显示
295+
296+
如果某些数据不显示:
297+
- 检查数据是否实际存在(使用默认格式先确认)
298+
- 使用 `{{ if .Field }}` 检查字段是否存在
299+
- 确认变量路径是否正确
300+
301+
## 参考资源
302+
303+
- [Go text/template 文档](https://pkg.go.dev/text/template)
304+
- [GitLab CLI 项目仓库](https://github.com/your-repo)

internal/cli/cmd.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55

66
"gitlab-cli-sdk/internal/config"
77
"gitlab-cli-sdk/internal/processor"
8+
"gitlab-cli-sdk/internal/template"
89
"gitlab-cli-sdk/pkg/client"
10+
"gitlab-cli-sdk/pkg/types"
911

1012
"github.com/spf13/cobra"
1113
)
@@ -61,6 +63,8 @@ func buildUserCreateCommand(cfg *config.CLIConfig) *cobra.Command {
6163
cmd.Flags().StringVarP(&cfg.ConfigFile, "config", "f", "../test-users.yaml", "配置文件路径")
6264
cmd.Flags().StringVar(&cfg.GitLabHost, "host", "", "GitLab 主机地址")
6365
cmd.Flags().StringVar(&cfg.GitLabToken, "token", "", "GitLab Personal Access Token")
66+
cmd.Flags().StringVarP(&cfg.OutputFile, "output", "o", "", "输出结果到 YAML 文件")
67+
cmd.Flags().StringVarP(&cfg.TemplateFile, "template", "t", "", "使用模板文件格式化输出")
6468

6569
return cmd
6670
}
@@ -98,21 +102,55 @@ func runUserCreate(cfg *config.CLIConfig) error {
98102

99103
proc := &processor.ResourceProcessor{Client: gitlabClient}
100104

105+
// 收集所有用户的输出结果
106+
var userOutputs []types.UserOutput
107+
101108
for i, userSpec := range userConfig.Users {
102109
log.Printf("==========================================\n")
103110
log.Printf("处理用户 [%d/%d]: %s\n", i+1, len(userConfig.Users), userSpec.Username)
104111
log.Printf("==========================================\n")
105112

106-
if err := proc.ProcessUserCreation(userSpec); err != nil {
113+
userOutput, err := proc.ProcessUserCreation(userSpec)
114+
if err != nil {
107115
return err
108116
}
109117

118+
// 将输出结果添加到列表
119+
if userOutput != nil {
120+
userOutputs = append(userOutputs, *userOutput)
121+
}
122+
110123
log.Printf("\n✓ 用户 '%s' 处理完成\n\n", userSpec.Username)
111124
}
112125

113126
log.Println("========================================")
114127
log.Println("✓ 批量创建完成")
115128
log.Println("========================================")
129+
130+
// 如果指定了输出文件,保存结果
131+
if cfg.OutputFile != "" {
132+
output := &types.OutputConfig{
133+
Users: userOutputs,
134+
}
135+
136+
// 如果指定了模板文件,使用模板渲染
137+
if cfg.TemplateFile != "" {
138+
log.Printf("\n使用模板渲染输出: %s\n", cfg.TemplateFile)
139+
log.Printf("保存结果到文件: %s\n", cfg.OutputFile)
140+
if err := template.SaveTemplateOutput(cfg.TemplateFile, cfg.OutputFile, output); err != nil {
141+
return err
142+
}
143+
log.Printf("✓ 使用模板渲染完成,结果已保存到: %s\n", cfg.OutputFile)
144+
} else {
145+
// 使用默认 YAML 格式
146+
log.Printf("\n保存结果到文件: %s\n", cfg.OutputFile)
147+
if err := config.SaveOutput(cfg.OutputFile, output); err != nil {
148+
return err
149+
}
150+
log.Printf("✓ 结果已保存到: %s\n", cfg.OutputFile)
151+
}
152+
}
153+
116154
return nil
117155
}
118156

0 commit comments

Comments
 (0)