mirror of
https://github.com/go-i2p/gitlab-to-gitea.git
synced 2025-07-01 09:06:22 -04:00
215 lines
7.2 KiB
Go
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))
|
|
}
|
|
}
|
|
}
|
|
}
|