Signed-off-by: Jonas Franz <info@jonasfranz.software>latest-release
parent
8ae08539ef
commit
382a788a42
@ -0,0 +1,55 @@ |
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. |
||||
|
||||
|
||||
[[projects]] |
||||
branch = "master" |
||||
name = "code.gitea.io/sdk" |
||||
packages = ["gitea"] |
||||
revision = "39c609e903992e25deca0e7aa2c5304fd680530f" |
||||
|
||||
[[projects]] |
||||
name = "github.com/google/go-github" |
||||
packages = ["github"] |
||||
revision = "e48060a28fac52d0f1cb758bc8b87c07bac4a87d" |
||||
version = "v15.0.0" |
||||
|
||||
[[projects]] |
||||
branch = "master" |
||||
name = "github.com/google/go-querystring" |
||||
packages = ["query"] |
||||
revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a" |
||||
|
||||
[[projects]] |
||||
name = "github.com/mattn/go-isatty" |
||||
packages = ["."] |
||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" |
||||
version = "v0.0.3" |
||||
|
||||
[[projects]] |
||||
name = "github.com/urfave/cli" |
||||
packages = ["."] |
||||
revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1" |
||||
version = "v1.20.0" |
||||
|
||||
[[projects]] |
||||
name = "github.com/vbauerster/mpb" |
||||
packages = [ |
||||
".", |
||||
"cwriter", |
||||
"decor" |
||||
] |
||||
revision = "e227edc706423683a577a93c78beb0ce9d211b00" |
||||
version = "v3.1.1" |
||||
|
||||
[[projects]] |
||||
branch = "master" |
||||
name = "golang.org/x/sys" |
||||
packages = ["unix"] |
||||
revision = "01acb38716e021ed1fc03a602bdb5838e1358c5e" |
||||
|
||||
[solve-meta] |
||||
analyzer-name = "dep" |
||||
analyzer-version = 1 |
||||
inputs-digest = "746c57ee0419dfecdb375bb45e31e55a9347b27d051af544d96397418e2d797d" |
||||
solver-name = "gps-cdcl" |
||||
solver-version = 1 |
@ -0,0 +1,46 @@ |
||||
# Gopkg.toml example |
||||
# |
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md |
||||
# for detailed Gopkg.toml documentation. |
||||
# |
||||
# required = ["github.com/user/thing/cmd/thing"] |
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] |
||||
# |
||||
# [[constraint]] |
||||
# name = "github.com/user/project" |
||||
# version = "1.0.0" |
||||
# |
||||
# [[constraint]] |
||||
# name = "github.com/user/project2" |
||||
# branch = "dev" |
||||
# source = "github.com/myfork/project2" |
||||
# |
||||
# [[override]] |
||||
# name = "github.com/x/y" |
||||
# version = "2.4.0" |
||||
# |
||||
# [prune] |
||||
# non-go = false |
||||
# go-tests = true |
||||
# unused-packages = true |
||||
|
||||
|
||||
[prune] |
||||
go-tests = true |
||||
unused-packages = true |
||||
|
||||
[[constraint]] |
||||
branch = "master" |
||||
name = "code.gitea.io/sdk" |
||||
|
||||
[[constraint]] |
||||
name = "github.com/urfave/cli" |
||||
version = "1.20.0" |
||||
|
||||
[[constraint]] |
||||
name = "github.com/google/go-github" |
||||
version = "15.0.0" |
||||
|
||||
[[constraint]] |
||||
name = "github.com/vbauerster/mpb" |
||||
version = "3.1.1" |
@ -0,0 +1,125 @@ |
||||
package cmd |
||||
|
||||
import ( |
||||
"code.gitea.io/sdk/gitea" |
||||
"context" |
||||
"fmt" |
||||
"git.jonasfranz.software/JonasFranzDEV/gitea-github-migrator/migrations" |
||||
"github.com/google/go-github/github" |
||||
"github.com/urfave/cli" |
||||
"golang.org/x/oauth2" |
||||
"strings" |
||||
) |
||||
|
||||
var CmdMigrate = cli.Command{ |
||||
Name: "migrate", |
||||
Usage: "migrates a github to a gitea repository", |
||||
Action: runMigrate, |
||||
Flags: []cli.Flag{ |
||||
cli.StringFlag{ |
||||
Name: "gh-repo", |
||||
Usage: "GitHub Repository", |
||||
Value: "username/reponame", |
||||
EnvVar: "GH_REPOSITORY", |
||||
}, |
||||
cli.IntFlag{ |
||||
Name: "owner", |
||||
Usage: "Owner ID", |
||||
EnvVar: "OWNER_ID", |
||||
Value: 0, |
||||
}, |
||||
cli.StringFlag{ |
||||
Name: "token", |
||||
Usage: "Gitea Token", |
||||
EnvVar: "GITEA_TOKEN", |
||||
}, |
||||
cli.StringFlag{ |
||||
Name: "url", |
||||
Usage: "Gitea URL", |
||||
EnvVar: "GITEA_URL", |
||||
}, |
||||
cli.BoolFlag{ |
||||
Name: "private", |
||||
Usage: "should new repository be private", |
||||
EnvVar: "GITEA_PRIVATE", |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
func runMigrate(ctx *cli.Context) error { |
||||
m := migrations.Migratory{ |
||||
Client: gitea.NewClient(ctx.String("url"), ctx.String("token")), |
||||
Private: ctx.Bool("private"), |
||||
NewOwnerID: ctx.Int("owner"), |
||||
} |
||||
c := context.Background() |
||||
|
||||
ts := oauth2.StaticTokenSource( |
||||
&oauth2.Token{AccessToken: "246947e59029a71cb568e290cc6b28adeda514df"}, |
||||
) |
||||
tc := oauth2.NewClient(c, ts) |
||||
gc := github.NewClient(tc) |
||||
|
||||
username := strings.Split(ctx.String("gh-repo"), "/")[0] |
||||
repo := strings.Split(ctx.String("gh-repo"), "/")[1] |
||||
|
||||
//p := mpb.New()
|
||||
|
||||
fmt.Printf("Fetching repository %s/%s...\n", username, repo) |
||||
gr, _, err := gc.Repositories.Get(c, username, repo) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
fmt.Printf("Migrating repository %s/%s...\n", username, repo) |
||||
if mr, err := m.Repository(gr); err != nil { |
||||
return err |
||||
} else { |
||||
fmt.Printf("Repository migrated to %s/%s\n", mr.Owner.UserName, mr.Name) |
||||
} |
||||
|
||||
fmt.Println("Fetching issues...") |
||||
opt := &github.IssueListByRepoOptions{ |
||||
Sort: "created", |
||||
Direction: "asc", |
||||
State: "all", |
||||
ListOptions: github.ListOptions{ |
||||
PerPage: 100, |
||||
}, |
||||
} |
||||
var allIssues = make([]*github.Issue, 0) |
||||
for { |
||||
issues, resp, err := gc.Issues.ListByRepo(c, username, repo, opt) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
allIssues = append(allIssues, issues...) |
||||
if resp.NextPage == 0 { |
||||
break |
||||
} |
||||
opt.Page = resp.NextPage |
||||
} |
||||
fmt.Println("Migrating issues...") |
||||
//bar := p.AddBar(int64(len(issues)))
|
||||
for _, gi := range allIssues { |
||||
fmt.Printf("Migrating #%d...\n", *gi.Number) |
||||
issue, err := m.Issue(gi) |
||||
if err != nil { |
||||
return fmt.Errorf("migrating issue[id: %d]: %v", *gi.ID, err) |
||||
} |
||||
comments, _, err := gc.Issues.ListComments(c, username, repo, gi.GetNumber(), nil) |
||||
if err != nil { |
||||
return fmt.Errorf("fetching issue[id: %d] comments: %v", *gi.ID, err) |
||||
} |
||||
for _, gc := range comments { |
||||
fmt.Printf("-> %d...", gc.ID) |
||||
if _, err := m.IssueComment(issue, gc); err != nil { |
||||
return fmt.Errorf("migrating issue comment [issue: %d, comment: %d]: %v", *gi.ID, gc.ID, err) |
||||
} |
||||
fmt.Print("Done!\n") |
||||
} |
||||
fmt.Printf("Migrated #%d...\n", *gi.Number) |
||||
//bar.Increment()
|
||||
|
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,22 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"os" |
||||
|
||||
"github.com/urfave/cli" |
||||
|
||||
"git.jonasfranz.software/JonasFranzDEV/gitea-github-migrator/cmd" |
||||
) |
||||
|
||||
func main() { |
||||
app := cli.NewApp() |
||||
app.Name = "gg-migrator" |
||||
app.Usage = "GitHub to Gitea migrator for repositories" |
||||
app.Description = `Migrate your GitHub repositories including issues to Gitea` |
||||
app.Commands = cli.Commands{ |
||||
cmd.CmdMigrate, |
||||
} |
||||
if err := app.Run(os.Args); err != nil { |
||||
panic(err) |
||||
} |
||||
} |
@ -0,0 +1,111 @@ |
||||
package migrations |
||||
|
||||
import ( |
||||
"code.gitea.io/sdk/gitea" |
||||
"fmt" |
||||
"github.com/google/go-github/github" |
||||
) |
||||
|
||||
func (m *Migratory) Issue(gi *github.Issue) (*gitea.Issue, error) { |
||||
if m.migratedMilestones == nil { |
||||
m.migratedMilestones = make(map[int64]int64) |
||||
} |
||||
if m.migratedLabels == nil { |
||||
m.migratedLabels = make(map[int64]int64) |
||||
} |
||||
|
||||
// Migrate milestone if it is not already migrated
|
||||
milestone := int64(0) |
||||
if gi.Milestone != nil { |
||||
// Lookup if milestone is already migrated
|
||||
if migratedMilestone, ok := m.migratedMilestones[*gi.Milestone.ID]; ok { |
||||
milestone = migratedMilestone |
||||
} else if ms, err := m.Milestone(gi.Milestone); err != nil { |
||||
return nil, err |
||||
} else { |
||||
milestone = ms.ID |
||||
} |
||||
} |
||||
// Migrate labels
|
||||
labels, err := m.labels(gi.Labels) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return m.Client.CreateIssue(m.repository.Owner.UserName, m.repository.Name, |
||||
gitea.CreateIssueOption{ |
||||
Title: gi.GetTitle(), |
||||
Body: fmt.Sprintf("Author: @%s Posted at: %s\n\n\n%s", *gi.User.Login, gi.GetCreatedAt().Format("02.01.2006 15:04"), gi.GetBody()), |
||||
Closed: *gi.State == "closed", |
||||
Milestone: milestone, |
||||
Labels: labels, |
||||
}) |
||||
} |
||||
|
||||
func (m *Migratory) labels(gls []github.Label) (results []int64, err error) { |
||||
for _, gl := range gls { |
||||
if migratedLabel, ok := m.migratedLabels[*gl.ID]; ok { |
||||
results = append(results, migratedLabel) |
||||
} else { |
||||
var newLabel *gitea.Label |
||||
if newLabel, err = m.Label(&gl); err != nil { |
||||
return nil, err |
||||
} |
||||
m.migratedLabels[*gl.ID] = newLabel.ID |
||||
results = append(results, newLabel.ID) |
||||
} |
||||
} |
||||
return |
||||
} |
||||
|
||||
func (m *Migratory) Label(gl *github.Label) (*gitea.Label, error) { |
||||
return m.Client.CreateLabel(m.repository.Owner.UserName, m.repository.Name, |
||||
gitea.CreateLabelOption{ |
||||
Name: gl.GetName(), |
||||
Color: fmt.Sprintf("#%s", gl.GetColor()), |
||||
}) |
||||
} |
||||
|
||||
func (m *Migratory) Milestone(gm *github.Milestone) (*gitea.Milestone, error) { |
||||
ms, err := m.Client.CreateMilestone(m.repository.Owner.UserName, m.repository.Name, |
||||
gitea.CreateMilestoneOption{ |
||||
Title: gm.GetTitle(), |
||||
Description: gm.GetDescription(), |
||||
Deadline: gm.DueOn, |
||||
}) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
m.migratedMilestones[*gm.ID] = ms.ID |
||||
if gm.State != nil && *gm.State != "open" { |
||||
return m.Client.EditMilestone(m.repository.Owner.UserName, m.repository.Name, |
||||
ms.ID, gitea.EditMilestoneOption{ |
||||
State: githubStateToGiteaState(gm.State), |
||||
}) |
||||
} |
||||
return ms, err |
||||
} |
||||
|
||||
func githubStateToGiteaState(ghstate *string) *string { |
||||
if ghstate == nil { |
||||
return ghstate |
||||
} |
||||
switch *ghstate { |
||||
case "open": |
||||
case "closed": |
||||
return ghstate |
||||
case "all": |
||||
open := "open" |
||||
return &open |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func (m *Migratory) IssueComment(issue *gitea.Issue, gic *github.IssueComment) (*gitea.Comment, error) { |
||||
return m.Client.CreateIssueComment(m.repository.Owner.UserName, |
||||
m.repository.Name, |
||||
issue.Index, |
||||
gitea.CreateIssueCommentOption{ |
||||
Body: fmt.Sprintf("Author: @%s Posted at: %s\n\n\n%s", *gic.User.Login, gic.GetCreatedAt().Format("02.01.2006 15:04"), gic.GetBody()), |
||||
}) |
||||
} |
@ -0,0 +1,18 @@ |
||||
package migrations |
||||
|
||||
import "code.gitea.io/sdk/gitea" |
||||
|
||||
type Migratory struct { |
||||
Client *gitea.Client |
||||
AuthUsername string |
||||
AuthPassword string |
||||
|
||||
Private bool |
||||
NewOwnerID int |
||||
|
||||
repository *gitea.Repository |
||||
// key: github milestone id | value: gitea milestone id
|
||||
migratedMilestones map[int64]int64 |
||||
// key: github label id | value: gitea label id
|
||||
migratedLabels map[int64]int64 |
||||
} |
@ -0,0 +1,22 @@ |
||||
package migrations |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"code.gitea.io/sdk/gitea" |
||||
"github.com/google/go-github/github" |
||||
) |
||||
|
||||
func (m *Migratory) Repository(gr *github.Repository) (*gitea.Repository, error) { |
||||
var err error |
||||
m.repository, err = m.Client.MigrateRepo(gitea.MigrateRepoOption{ |
||||
Description: *gr.Description, |
||||
AuthPassword: m.AuthPassword, |
||||
AuthUsername: m.AuthUsername, |
||||
CloneAddr: gr.GetCloneURL(), |
||||
RepoName: gr.GetName(), |
||||
UID: m.NewOwnerID, |
||||
Private: m.Private, |
||||
}) |
||||
return m.repository, err |
||||
} |
Loading…
Reference in new issue