2023-10-14 17:19:12 +00:00
|
|
|
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()
|
2023-10-15 00:09:47 +00:00
|
|
|
githubClient := authenticateGithub("watzon", "password", "")
|
2023-10-14 17:19:12 +00:00
|
|
|
|
|
|
|
// 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!")
|
|
|
|
// }
|
|
|
|
}
|