package main import ( "context" "errors" "fmt" "os" "github.com/cqroot/prompt" "github.com/cqroot/prompt/input" "github.com/cqroot/prompt/multichoose" "github.com/google/go-github/github" ) func CheckErr(err error) { if err != nil { if errors.Is(err, prompt.ErrUserQuit) { fmt.Fprintln(os.Stderr, "Error:", err) os.Exit(1) } else { panic(err) } } } // authenticateGithub authenticates the user with Github using their username // and password. // A Github client is returned. func authenticateGithub(username string, password string, otp string) *github.Client { ctx := context.Background() ts := github.BasicAuthTransport{ Username: username, Password: password, OTP: otp, } client := github.NewClient(ts.Client()) _, _, err := client.Users.Get(ctx, "") if err != nil { fmt.Println("Error", err) } return client } // getGithubUsername prompts the user for their Github username. // The username is returned as a string. func getGithubUsername() string { result, err := prompt.New().Ask("Github username").Input("johndoe69") CheckErr(err) return result } // getGithubPassword prompts the user for their Github password. // The password is returned as a string. func getGithubPassword() string { result, err := prompt.New().Ask("Github password or token"). Input("", input.WithEchoMode(input.EchoPassword)) CheckErr(err) return result } // getGithubOTP prompts the user for their Github OTP. // The OTP is returned as a string. func getGithubOTP() string { result, err := prompt.New().Ask("Github OTP (if you used a token, leave blank)"). Input("", input.WithEchoMode(input.EchoPassword)) CheckErr(err) return result } // getGiteaUsername prompts the user for their Gitea username. // The username is returned as a string. func getGiteaUsername() string { result, err := prompt.New().Ask("Gitea username").Input("johndoe69") CheckErr(err) return result } // getGiteaToken prompts the user for their Gitea application token. // The token is returned as a string. func getGiteaToken() string { result, err := prompt.New().Ask("Gitea application token"). Input("", input.WithEchoMode(input.EchoPassword)) CheckErr(err) return result } // getRepos first prompts users to choose how they want to select repositories. // They will have the option of fetching a list from github and selecting from that, // entering a list of repositories manually, providing a path to a file containing // a list of repositories, or selecting filters which will be used to fetch a list // of repositories from github. // Once the user has selected how they want to select repositories, they will be // prompted to select repositories based on their chosen method. // The selected repositories are returned as a list of strings. func getRepos(client *github.Client) []string { result, err := prompt.New().Ask("How do you want to select repositories?"). Choose([]string{"Fetch from Github", "Enter manually", "File path", "Filters"}) CheckErr(err) switch result { case "Fetch from Github": return getReposFromGithub(client) // case "Enter manually": // return getReposManually() // case "File path": // return getReposFromFile() // case "Filters": // return getReposFromFilters(client) } return nil } // fetchReposForUser fetches a list of all repositories from github for the given user // or organization. Github by default only returns 30 repositories at a time, so this // function will make multiple requests to github to fetch all repositories. // The list of repositories is returned as a list of strings. func fetchReposForUser(client *github.Client, user string, organization bool) []string { var repos []string var page int = 1 var per_page int = 30 for { var repo_list []*github.Repository var err error if organization { repo_list, _, err = client.Repositories.ListByOrg(context.Background(), user, &github.RepositoryListByOrgOptions{ ListOptions: github.ListOptions{ Page: page, PerPage: per_page, }, }) } else { repo_list, _, err = client.Repositories.List(context.Background(), user, &github.RepositoryListOptions{ ListOptions: github.ListOptions{ Page: page, PerPage: per_page, }, }) } if err != nil { fmt.Println("Error", err) } if len(repo_list) == 0 { break } for _, repo := range repo_list { repos = append(repos, fmt.Sprintf("%s/%s", user, repo.GetName())) } page++ } return repos } // getReposFromGithub first asks the user for the name of the organization, // or user, whose repositories they want to migrate. // It then fetches a list of repositories from github, and prompts the user // to select which repositories they want to migrate. // The selected repositories are returned as a list of strings. func getReposFromGithub(client *github.Client) []string { result, err := prompt.New().Ask("Select repositories from:"). Choose([]string{"Organization", "User"}) CheckErr(err) var repo_names []string switch result { case "Organization": result, err := prompt.New().Ask("Organization name").Input("acme") CheckErr(err) repos := fetchReposForUser(client, result, true) repo_names = append(repo_names, repos...) case "User": result, err := prompt.New().Ask("User name").Input("johndoe69") CheckErr(err) repos := fetchReposForUser(client, result, false) repo_names = append(repo_names, repos...) } selected, err := prompt.New().Ask("Select repositories:"). MultiChoose(repo_names, multichoose.WithTheme(ThemeScroll)) CheckErr(err) return selected } // Github to Gitea migration tool. // This tool will migrate selected repositories from Github to Gitea, // based on user input. This process is entirely interactive. // The tool will ask for the following: // - Github username // - Github password/token // - Gitea username // - Gitea application token // - Repositories to migrate, or filters to select repositories to migrate // - Whether repos should be mirrored, or migrated as a one-time copy // - Whether to migrate issues, use the github issue tracker, or neither // - How to set visibility of the migrated repos // - Whether to migrate wiki pages, labels, pull requests, milestones, and releases func main() { fmt.Println("Welcome to the Github to Gitea migration tool!") // githubUsername := getGithubUsername() // githubPassword := getGithubPassword() // githubOTP := getGithubOTP() githubClient := authenticateGithub("watzon", "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", "") // giteaUsername := getGiteaUsername() // giteaToken := getGiteaToken() // giteaClient = authenticateGitea(giteaUsername, giteaToken) repos := getRepos(githubClient) fmt.Println(repos) // should_mirror := getMirror() // should_migrate_issues, use_github_issue_tracker := getIssues() // repos_by_visibility := getVisibility(repos) // should_migrate_wiki := getWiki() // should_migrate_labels := getLabels() // should_migrate_pull_requests := getPullRequests() // should_migrate_milestones := getMilestones() // should_migrate_releases := getReleases() // res, err = execute_migration(&MigrationOptions{ // githubUsername: githubUsername, // githubPassword: githubPassword, // giteaUsername: giteaUsername, // giteaToken: giteaToken, // repos: repos, // should_mirror: should_mirror, // should_migrate_issues: should_migrate_issues, // use_github_issue_tracker: use_github_issue_tracker, // repos_by_visibility: repos_by_visibility, // should_migrate_wiki: should_migrate_wiki, // should_migrate_labels: should_migrate_labels, // should_migrate_pull_requests: should_migrate_pull_requests, // should_migrate_milestones: should_migrate_milestones, // should_migrate_releases: should_migrate_releases, // }) // if err != nil { // fmt.Println("Error", err) // } else { // fmt.Println("Migration completed successfully!") // } }