Skip to content

Commit 34a4db9

Browse files
fix: 修复用户级项目的 namespace ID 获取和清理逻辑
- 添加 GetUserNamespaceID 方法获取用户的 namespace ID - 添加 ListUserProjects 方法列出用户的个人项目 - 修复 createUserProjectsWithOutput 使用正确的 namespace ID - 修复 deleteUserProjects 自动查找并删除所有用户个人项目 - 测试验证:用户级项目创建和删除功能正常工作
1 parent 21f0522 commit 34a4db9

File tree

2 files changed

+77
-27
lines changed

2 files changed

+77
-27
lines changed

internal/processor/processor.go

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -239,12 +239,14 @@ func (p *ResourceProcessor) ensureGroup(username string, groupSpec types.GroupSp
239239
func (p *ResourceProcessor) createUserProjectsWithOutput(username string, projects []types.ProjectSpec, userNameMode string) ([]types.ProjectOutput, error) {
240240
var projectOutputs []types.ProjectOutput
241241

242-
// 获取用户信息以获取用户的 namespace ID
243-
user, err := p.Client.GetUser(username)
244-
if err != nil || user == nil {
245-
return nil, fmt.Errorf("获取用户信息失败: %w", err)
242+
// 获取用户的 namespace ID
243+
namespaceID, err := p.Client.GetUserNamespaceID(username)
244+
if err != nil {
245+
return nil, fmt.Errorf("获取用户 namespace ID 失败: %w", err)
246246
}
247247

248+
log.Printf(" 用户 %s 的 namespace ID: %d\n", username, namespaceID)
249+
248250
for _, projSpec := range projects {
249251
// 确定项目的 nameMode(如果项目没有指定,则继承用户的 nameMode)
250252
projectNameMode := projSpec.NameMode
@@ -284,10 +286,10 @@ func (p *ResourceProcessor) createUserProjectsWithOutput(username string, projec
284286
webURL = existingProj.WebURL
285287
} else {
286288
log.Printf(" 创建用户级项目: %s (path: %s)\n", projSpec.Name, actualProjectPath)
287-
// 用户级项目使用用户的 ID 作为 namespace ID
289+
// 用户级项目使用用户的 namespace ID
288290
project, err := p.Client.CreateProject(
289291
username,
290-
user.ID, // 使用用户 ID 作为 namespace ID
292+
namespaceID, // 使用用户的 namespace ID
291293
projSpec.Name,
292294
actualProjectPath,
293295
projSpec.Description,
@@ -406,8 +408,8 @@ func (p *ResourceProcessor) ProcessUserCleanup(userSpec types.UserSpec) error {
406408

407409
// 1. 删除用户级项目(不属于任何组的项目)
408410
if len(userSpec.Projects) > 0 {
409-
log.Printf(" 删除 %d 个用户级项目...\n", len(userSpec.Projects))
410-
p.deleteUserProjects(userSpec.Username, userSpec.Projects)
411+
log.Printf(" 删除用户级项目...\n")
412+
p.deleteUserProjects(userSpec.Username)
411413
}
412414

413415
// 2. 删除配置文件中定义的组和项目
@@ -438,28 +440,29 @@ func (p *ResourceProcessor) ProcessUserCleanup(userSpec types.UserSpec) error {
438440
}
439441

440442
// deleteUserProjects 删除用户级项目
441-
func (p *ResourceProcessor) deleteUserProjects(username string, projects []types.ProjectSpec) {
442-
for i, projSpec := range projects {
443-
log.Printf(" ------------------------------------------\n")
444-
log.Printf(" 处理用户级项目 [%d/%d]: %s\n", i+1, len(projects), projSpec.Name)
443+
// 注意:此函数会删除用户命名空间下的所有个人项目(不属于任何组的项目)
444+
func (p *ResourceProcessor) deleteUserProjects(username string) {
445+
// 获取用户拥有的所有项目
446+
userProjects, err := p.Client.ListUserProjects(username)
447+
if err != nil {
448+
log.Printf(" ⚠ 获取用户项目列表失败: %v\n", err)
449+
return
450+
}
445451

446-
// 用户级项目的 full path 是 username/project-path
447-
projectPath := projSpec.Path
448-
if projectPath == "" {
449-
projectPath = projSpec.Name
450-
}
451-
fullPath := fmt.Sprintf("%s/%s", username, projectPath)
452-
project, _ := p.Client.GetProject(fullPath)
452+
if len(userProjects) == 0 {
453+
log.Printf(" 用户没有个人项目\n")
454+
return
455+
}
453456

454-
if project != nil {
455-
log.Printf(" 删除项目: %s (ID: %d)\n", projSpec.Name, project.ID)
456-
if err := p.Client.DeleteProject(project.ID); err != nil {
457-
log.Printf(" ⚠ 删除项目失败: %v\n", err)
458-
} else {
459-
log.Printf(" ✓ 项目删除成功\n")
460-
}
457+
log.Printf(" 发现用户有 %d 个个人项目,开始删除...\n", len(userProjects))
458+
for i, project := range userProjects {
459+
log.Printf(" ------------------------------------------\n")
460+
log.Printf(" 处理用户级项目 [%d/%d]: %s\n", i+1, len(userProjects), project.Name)
461+
log.Printf(" 删除项目: %s (ID: %d, Path: %s)\n", project.Name, project.ID, project.PathWithNamespace)
462+
if err := p.Client.DeleteProject(project.ID); err != nil {
463+
log.Printf(" ⚠ 删除项目失败: %v\n", err)
461464
} else {
462-
log.Printf(" ⚠ 项目不存在,跳过: %s\n", fullPath)
465+
log.Printf(" ✓ 项目删除成功\n")
463466
}
464467
}
465468
}

pkg/client/client.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,53 @@ func (c *GitLabClient) GetUser(username string) (*gitlab.User, error) {
5555
return users[0], nil
5656
}
5757

58+
// GetUserNamespaceID 获取用户的 namespace ID
59+
func (c *GitLabClient) GetUserNamespaceID(username string) (int, error) {
60+
// 列出用户的所有 namespace
61+
namespaces, _, err := c.client.Namespaces.ListNamespaces(&gitlab.ListNamespacesOptions{
62+
Search: gitlab.Ptr(username),
63+
}, gitlab.WithSudo(username))
64+
if err != nil {
65+
return 0, err
66+
}
67+
68+
// 查找用户的个人 namespace(kind 为 "user")
69+
for _, ns := range namespaces {
70+
if ns.Kind == "user" && ns.Path == username {
71+
return ns.ID, nil
72+
}
73+
}
74+
75+
return 0, fmt.Errorf("未找到用户 %s 的 namespace", username)
76+
}
77+
78+
// ListUserProjects 列出用户的个人项目(不属于任何组的项目)
79+
func (c *GitLabClient) ListUserProjects(username string) ([]*gitlab.Project, error) {
80+
// 获取用户信息
81+
user, err := c.GetUser(username)
82+
if err != nil || user == nil {
83+
return nil, fmt.Errorf("获取用户信息失败: %w", err)
84+
}
85+
86+
// 列出用户拥有的所有项目,过滤出个人命名空间下的项目
87+
projects, _, err := c.client.Projects.ListUserProjects(user.ID, &gitlab.ListProjectsOptions{
88+
Owned: gitlab.Ptr(true),
89+
})
90+
if err != nil {
91+
return nil, err
92+
}
93+
94+
// 过滤出用户个人命名空间下的项目(namespace.kind == "user")
95+
var userProjects []*gitlab.Project
96+
for _, project := range projects {
97+
if project.Namespace.Kind == "user" {
98+
userProjects = append(userProjects, project)
99+
}
100+
}
101+
102+
return userProjects, nil
103+
}
104+
58105
// CreateUser 创建用户
59106
func (c *GitLabClient) CreateUser(username, email, name, password string) (*gitlab.User, error) {
60107
user, _, err := c.client.Users.CreateUser(&gitlab.CreateUserOptions{

0 commit comments

Comments
 (0)