2025-08-24 12:07:27 -07:00

379 lines
10 KiB
Go

package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
gitlab "gitlab.com/gitlab-org/api/client-go"
)
type Snippet struct {
Title string `json:"title"`
FileName string `json:"file_name"`
Content string `json:"content"`
Description string `json:"description,omitempty"`
Visibility string `json:"visibility"`
}
type Repository struct {
Name string `json:"name"`
InitializeWithReadme bool `json:"initialize_with_readme"`
Snippets []Snippet `json:"snippets,omitempty"`
}
type Group struct {
Name string `json:"name"`
Path string `json:"path"`
Description string `json:"description"`
Repositories []Repository `json:"repositories,omitempty"`
Subgroups []Group `json:"subgroups,omitempty"`
}
type User struct {
Username string `json:"username"`
Email string `json:"email"`
Password string `json:"password"`
Name string `json:"name"`
Repositories []Repository `json:"repositories,omitempty"`
}
type RootUser struct {
Repositories []Repository `json:"repositories"`
}
type SeedData struct {
Groups []Group `json:"groups"`
Users []User `json:"users"`
RootUser RootUser `json:"root_user"`
RootSnippets []Snippet `json:"root_snippets"`
}
type GitLabSeeder struct {
client *gitlab.Client
seedData *SeedData
baseURL string
}
func NewGitLabSeeder(token, baseURL string) (*GitLabSeeder, error) {
client, err := gitlab.NewClient(token, gitlab.WithBaseURL(baseURL))
if err != nil {
return nil, fmt.Errorf("failed to create GitLab client: %w", err)
}
return &GitLabSeeder{
client: client,
baseURL: baseURL,
}, nil
}
func (g *GitLabSeeder) LoadSeedData(configPath string) error {
data, err := os.ReadFile(configPath)
if err != nil {
return fmt.Errorf("failed to read seed config: %w", err)
}
g.seedData = &SeedData{}
if err := json.Unmarshal(data, g.seedData); err != nil {
return fmt.Errorf("failed to parse seed config: %w", err)
}
return nil
}
func (g *GitLabSeeder) CreateGroups() error {
log.Println("Creating groups...")
for _, group := range g.seedData.Groups {
if err := g.createGroup(&group, nil); err != nil {
return fmt.Errorf("failed to create group %s: %w", group.Name, err)
}
}
return nil
}
func (g *GitLabSeeder) createGroup(group *Group, parentID *int) error {
log.Printf("Creating group: %s", group.Name)
createOptions := &gitlab.CreateGroupOptions{
Name: &group.Name,
Path: &group.Path,
Description: &group.Description,
}
if parentID != nil {
createOptions.ParentID = parentID
}
createdGroup, _, err := g.client.Groups.CreateGroup(createOptions)
if err != nil {
return fmt.Errorf("failed to create group: %w", err)
}
log.Printf("Created group: %s (ID: %d)", createdGroup.Name, createdGroup.ID)
// Create repositories in this group
for _, repo := range group.Repositories {
if err := g.createRepository(&repo, &createdGroup.ID, group.Path); err != nil {
return fmt.Errorf("failed to create repository %s in group %s: %w", repo.Name, group.Name, err)
}
}
// Create subgroups recursively
for _, subgroup := range group.Subgroups {
if err := g.createGroup(&subgroup, &createdGroup.ID); err != nil {
return fmt.Errorf("failed to create subgroup %s: %w", subgroup.Name, err)
}
}
return nil
}
func (g *GitLabSeeder) createRepository(repo *Repository, namespaceID *int, groupPath string) error {
log.Printf("Creating repository: %s", repo.Name)
createOptions := &gitlab.CreateProjectOptions{
Name: &repo.Name,
InitializeWithReadme: gitlab.Ptr(repo.InitializeWithReadme),
}
if namespaceID != nil {
createOptions.NamespaceID = namespaceID
}
project, _, err := g.client.Projects.CreateProject(createOptions)
if err != nil {
return fmt.Errorf("failed to create repository: %w", err)
}
log.Printf("Created repository: %s (ID: %d)", project.Name, project.ID)
// Create snippets for this repository
for _, snippet := range repo.Snippets {
if err := g.createProjectSnippet(&snippet, project.ID, groupPath, repo.Name); err != nil {
return fmt.Errorf("failed to create snippet for repository %s: %w", repo.Name, err)
}
}
return nil
}
func (g *GitLabSeeder) createProjectSnippet(snippet *Snippet, projectID int, groupPath, repoName string) error {
log.Printf("Creating project snippet: %s for project %d", snippet.Title, projectID)
visibility := gitlab.PublicVisibility
switch snippet.Visibility {
case "private":
visibility = gitlab.PrivateVisibility
case "internal":
visibility = gitlab.InternalVisibility
}
createOptions := &gitlab.CreateProjectSnippetOptions{
Title: &snippet.Title,
FileName: &snippet.FileName,
Content: &snippet.Content,
Visibility: &visibility,
Description: &snippet.Description,
}
_, _, err := g.client.ProjectSnippets.CreateSnippet(projectID, createOptions)
if err != nil {
return fmt.Errorf("failed to create project snippet: %w", err)
}
log.Printf("Created project snippet: %s", snippet.Title)
return nil
}
func (g *GitLabSeeder) CreateUsers() error {
log.Println("Creating users...")
for _, user := range g.seedData.Users {
if err := g.createUser(&user); err != nil {
return fmt.Errorf("failed to create user %s: %w", user.Username, err)
}
}
return nil
}
func (g *GitLabSeeder) createUser(user *User) error {
log.Printf("Creating user: %s", user.Username)
createOptions := &gitlab.CreateUserOptions{
Username: &user.Username,
Email: &user.Email,
Password: &user.Password,
Name: &user.Name,
}
createdUser, _, err := g.client.Users.CreateUser(createOptions)
if err != nil {
return fmt.Errorf("failed to create user: %w", err)
}
log.Printf("Created user: %s (ID: %d)", createdUser.Username, createdUser.ID)
// Create repositories for this user
for _, repo := range user.Repositories {
if err := g.createUserRepository(&repo, createdUser.ID, user.Username); err != nil {
return fmt.Errorf("failed to create repository %s for user %s: %w", repo.Name, user.Username, err)
}
}
return nil
}
func (g *GitLabSeeder) createUserRepository(repo *Repository, userID int, username string) error {
log.Printf("Creating user repository: %s for user %s", repo.Name, username)
// Create project for user using the correct API format
createOptions := &gitlab.CreateProjectOptions{
Name: &repo.Name,
InitializeWithReadme: gitlab.Ptr(repo.InitializeWithReadme),
}
// We need to get the user's namespace first
user, _, err := g.client.Users.GetUser(userID, gitlab.GetUsersOptions{})
if err != nil {
return fmt.Errorf("failed to get user: %w", err)
}
// Find the user's personal namespace
namespaces, _, err := g.client.Namespaces.ListNamespaces(&gitlab.ListNamespacesOptions{})
if err != nil {
return fmt.Errorf("failed to list namespaces: %w", err)
}
var userNamespaceID *int
for _, ns := range namespaces {
if ns.Kind == "user" && ns.Path == user.Username {
userNamespaceID = &ns.ID
break
}
}
if userNamespaceID == nil {
return fmt.Errorf("could not find user namespace for user %s", username)
}
createOptions.NamespaceID = userNamespaceID
project, _, err := g.client.Projects.CreateProject(createOptions)
if err != nil {
return fmt.Errorf("failed to create user repository: %w", err)
}
log.Printf("Created user repository: %s (ID: %d)", project.Name, project.ID)
// Create snippets for this repository
for _, snippet := range repo.Snippets {
if err := g.createProjectSnippet(&snippet, project.ID, username, repo.Name); err != nil {
return fmt.Errorf("failed to create snippet for user repository %s: %w", repo.Name, err)
}
}
return nil
}
func (g *GitLabSeeder) CreateRootUserRepositories() error {
log.Println("Creating root user repositories...")
for _, repo := range g.seedData.RootUser.Repositories {
if err := g.createRepository(&repo, nil, "root"); err != nil {
return fmt.Errorf("failed to create root repository %s: %w", repo.Name, err)
}
}
return nil
}
func (g *GitLabSeeder) CreateRootSnippets() error {
log.Println("Creating root-level snippets...")
for _, snippet := range g.seedData.RootSnippets {
if err := g.createRootSnippet(&snippet); err != nil {
return fmt.Errorf("failed to create root snippet %s: %w", snippet.Title, err)
}
}
return nil
}
func (g *GitLabSeeder) createRootSnippet(snippet *Snippet) error {
log.Printf("Creating root snippet: %s", snippet.Title)
visibility := gitlab.PublicVisibility
switch snippet.Visibility {
case "private":
visibility = gitlab.PrivateVisibility
case "internal":
visibility = gitlab.InternalVisibility
}
createOptions := &gitlab.CreateSnippetOptions{
Title: &snippet.Title,
FileName: &snippet.FileName,
Content: &snippet.Content,
Visibility: &visibility,
Description: &snippet.Description,
}
_, _, err := g.client.Snippets.CreateSnippet(createOptions)
if err != nil {
return fmt.Errorf("failed to create root snippet: %w", err)
}
log.Printf("Created root snippet: %s", snippet.Title)
return nil
}
func (g *GitLabSeeder) SeedAll() error {
log.Println("Starting GitLab seeding process...")
if err := g.CreateGroups(); err != nil {
return err
}
if err := g.CreateUsers(); err != nil {
return err
}
if err := g.CreateRootUserRepositories(); err != nil {
return err
}
if err := g.CreateRootSnippets(); err != nil {
return err
}
log.Println("GitLab seeding completed successfully!")
return nil
}
func main() {
var (
token = flag.String("token", "", "GitLab API token")
baseURL = flag.String("base-url", "http://gitlab.example.com", "GitLab base URL")
configPath = flag.String("config", "configs/seed-data.json", "Path to seed data configuration file")
)
flag.Parse()
if *token == "" {
log.Fatal("Token is required")
}
seeder, err := NewGitLabSeeder(*token, *baseURL)
if err != nil {
log.Fatalf("Failed to create seeder: %v", err)
}
if err := seeder.LoadSeedData(*configPath); err != nil {
log.Fatalf("Failed to load seed data: %v", err)
}
if err := seeder.SeedAll(); err != nil {
log.Fatalf("Failed to seed GitLab: %v", err)
}
}