Files
gitlab-to-gitea/migration/repositories.go
2025-04-21 00:16:37 -04:00

215 lines
7.2 KiB
Go

// repositories.go
// Package migration handles the migration of data from GitLab to Gitea
package migration
import (
"fmt"
"github.com/xanzy/go-gitlab"
"github.com/go-i2p/gitlab-to-gitea/utils"
)
// repositoryMigrateRequest represents the data needed to migrate a repository to Gitea
type repositoryMigrateRequest struct {
AuthPassword string `json:"auth_password"`
AuthUsername string `json:"auth_username"`
CloneAddr string `json:"clone_addr"`
Description string `json:"description"`
Mirror bool `json:"mirror"`
Private bool `json:"private"`
RepoName string `json:"repo_name"`
UID int `json:"uid"`
}
// ImportProject imports a GitLab project to Gitea
// ImportProject imports a GitLab project to Gitea
func (m *Manager) ImportProject(project *gitlab.Project) error {
cleanName := utils.CleanName(project.Name)
utils.PrintInfo(fmt.Sprintf("Importing project %s from owner %s", cleanName, project.Namespace.Name))
// Get the owner information first, so we use the correct name format
ownerInfo, err := m.getOwner(project)
if err != nil {
return fmt.Errorf("failed to get project owner: %w", err)
}
// Get the correct owner username from the result
owner, ok := ownerInfo["username"].(string)
if !ok || owner == "" {
return fmt.Errorf("failed to get valid username for project owner")
}
utils.PrintInfo(fmt.Sprintf("Using owner %s for project %s", owner, cleanName))
// Check if repository already exists
if exists, err := m.repoExists(owner, cleanName); err != nil {
return fmt.Errorf("failed to check if repository exists: %w", err)
} else if exists {
utils.PrintWarning(fmt.Sprintf("Project %s already exists in Gitea, skipping repository creation!", cleanName))
} else {
// Prepare clone URL
cloneURL := project.HTTPURLToRepo
if m.config.GitLabAdminUser == "" && m.config.GitLabAdminPass == "" {
cloneURL = project.SSHURLToRepo
}
// Determine visibility
private := project.Visibility == "private" || project.Visibility == "internal"
// Create migration request
migrateReq := repositoryMigrateRequest{
AuthPassword: m.config.GitLabAdminPass,
AuthUsername: m.config.GitLabAdminUser,
CloneAddr: cloneURL,
Description: project.Description,
Mirror: false,
Private: private,
RepoName: cleanName,
UID: int(ownerInfo["id"].(float64)),
}
// Call Gitea API to migrate repository
var result map[string]interface{}
err = m.giteaClient.Post("/repos/migrate", migrateReq, &result)
if err != nil {
return fmt.Errorf("failed to migrate repository %s: %w", cleanName, err)
}
utils.PrintInfo(fmt.Sprintf("Project %s imported!", cleanName))
}
// Process collaborators
collaborators, err := m.gitlabClient.GetProjectMembers(project.ID)
if err != nil {
utils.PrintWarning(fmt.Sprintf("Error fetching collaborators for project %s: %v", project.Name, err))
} else {
utils.PrintInfo(fmt.Sprintf("Found %d collaborators for project %s", len(collaborators), cleanName))
if err := m.importProjectCollaborators(collaborators, project); err != nil {
utils.PrintWarning(fmt.Sprintf("Error importing collaborators: %v", err))
}
}
// Process labels
labels, err := m.gitlabClient.GetProjectLabels(project.ID)
if err != nil {
utils.PrintWarning(fmt.Sprintf("Error fetching labels for project %s: %v", project.Name, err))
} else {
utils.PrintInfo(fmt.Sprintf("Found %d labels for project %s", len(labels), cleanName))
if err := m.importProjectLabels(labels, owner, cleanName); err != nil {
utils.PrintWarning(fmt.Sprintf("Error importing labels: %v", err))
}
}
// Process milestones
milestones, err := m.gitlabClient.GetProjectMilestones(project.ID)
if err != nil {
utils.PrintWarning(fmt.Sprintf("Error fetching milestones for project %s: %v", project.Name, err))
} else {
utils.PrintInfo(fmt.Sprintf("Found %d milestones for project %s", len(milestones), cleanName))
if err := m.importProjectMilestones(milestones, owner, cleanName); err != nil {
utils.PrintWarning(fmt.Sprintf("Error importing milestones: %v", err))
}
}
// Process issues
issues, err := m.gitlabClient.GetProjectIssues(project.ID)
if err != nil {
utils.PrintWarning(fmt.Sprintf("Error fetching issues for project %s: %v", project.Name, err))
} else {
utils.PrintInfo(fmt.Sprintf("Found %d issues for project %s", len(issues), cleanName))
// Ensure all mentioned users exist in Gitea
m.ensureMentionedUsersExist(issues)
if err := m.importProjectIssues(issues, owner, cleanName, project.ID); err != nil {
utils.PrintWarning(fmt.Sprintf("Error importing issues: %v", err))
}
}
return nil
}
// getOwner retrieves the user or organization info for a project
func (m *Manager) getOwner(project *gitlab.Project) (map[string]interface{}, error) {
namespacePath := utils.NormalizeUsername(project.Namespace.Path)
// Try to get as a user first
var result map[string]interface{}
err := m.giteaClient.Get("/users/"+namespacePath, &result)
if err == nil && result != nil {
// Verify required fields exist
if username, ok := result["username"].(string); ok && username != "" {
return result, nil
}
}
// Try to get as an organization
orgName := utils.CleanName(project.Namespace.Name)
err = m.giteaClient.Get("/orgs/"+orgName, &result)
if err == nil && result != nil {
// Verify required fields exist
if username, ok := result["username"].(string); ok && username != "" {
return result, nil
}
}
// Create a placeholder user instead of failing
utils.PrintWarning(fmt.Sprintf("Could not find owner for project %s, creating placeholder user", project.Name))
if err := m.ImportPlaceholderUser(namespacePath); err != nil {
return nil, fmt.Errorf("failed to create placeholder user: %w", err)
}
// Try to get the newly created user
err = m.giteaClient.Get("/users/"+namespacePath, &result)
if err == nil && result != nil {
return result, nil
}
return nil, fmt.Errorf("failed to find or create owner for project: %s", project.Path)
}
// repoExists checks if a repository exists in Gitea
func (m *Manager) repoExists(owner, repo string) (bool, error) {
var repository map[string]interface{}
err := m.giteaClient.Get(fmt.Sprintf("/repos/%s/%s", owner, repo), &repository)
if err != nil {
if isNotFoundError(err) {
return false, nil
}
return false, fmt.Errorf("error checking if repository exists: %w", err)
}
return true, nil
}
// ensureMentionedUsersExist makes sure all users mentioned in issues exist in Gitea
func (m *Manager) ensureMentionedUsersExist(issues []*gitlab.Issue) {
mentionedUsers := make(map[string]struct{})
// Extract mentions from issues
for _, issue := range issues {
if issue.Description != "" {
for _, mention := range utils.ExtractUserMentions(issue.Description) {
mentionedUsers[mention] = struct{}{}
}
}
}
// Create placeholder users for any missing mentioned users
for username := range mentionedUsers {
exists, err := m.userExists(utils.NormalizeUsername(username))
if err != nil {
utils.PrintWarning(fmt.Sprintf("Error checking if user %s exists: %v", username, err))
continue
}
if !exists {
if err := m.ImportPlaceholderUser(username); err != nil {
utils.PrintWarning(fmt.Sprintf("Failed to create placeholder user %s: %v", username, err))
}
}
}
}