1
0
mirror of https://github.com/Luzifer/github2gitea.git synced 2024-09-19 08:23:00 +00:00

Allow specifying multiple actions at once

in order to speed up the execution and to lower the need of Github
tokens. Instead of running the tool multiple times it can now be run
once while specifying more than one mapping using a mapping file.

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2019-08-04 14:34:54 +02:00
parent 69b41f5035
commit 494dbf16dd
Signed by: luzifer
GPG Key ID: DC2729FDD34BE99E
6 changed files with 119 additions and 18 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.env .env
github2gitea github2gitea
mapping.yaml

View File

@ -23,7 +23,7 @@ type createMigrationRequest struct {
Wiki bool `json:"wiki"` Wiki bool `json:"wiki"`
} }
func createMigrationRequestFromGithubRepo(gr *github.Repository) createMigrationRequest { func createMigrationRequestFromGithubRepo(gr *github.Repository, repoMapping *mapping) createMigrationRequest {
cmr := createMigrationRequest{ cmr := createMigrationRequest{
CloneAddr: strFromPtr(gr.CloneURL), CloneAddr: strFromPtr(gr.CloneURL),
Description: strFromPtr(gr.Description), Description: strFromPtr(gr.Description),
@ -32,7 +32,7 @@ func createMigrationRequestFromGithubRepo(gr *github.Repository) createMigration
Private: boolFromPtr(gr.Private), Private: boolFromPtr(gr.Private),
PullRequests: boolFromPtr(gr.HasIssues), PullRequests: boolFromPtr(gr.HasIssues),
RepoName: strFromPtr(gr.Name), RepoName: strFromPtr(gr.Name),
UID: cfg.TargetUser, UID: repoMapping.TargetUser,
Wiki: boolFromPtr(gr.HasWiki), Wiki: boolFromPtr(gr.HasWiki),
} }

1
go.mod
View File

@ -12,4 +12,5 @@ require (
github.com/pkg/errors v0.8.1 github.com/pkg/errors v0.8.1
github.com/sirupsen/logrus v1.4.2 github.com/sirupsen/logrus v1.4.2
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
gopkg.in/yaml.v2 v2.2.2
) )

57
main.go
View File

@ -8,7 +8,6 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" "os"
"regexp"
"strings" "strings"
"github.com/Luzifer/rconfig/v2" "github.com/Luzifer/rconfig/v2"
@ -25,16 +24,19 @@ var (
GiteaURL string `flag:"gitea-url" default:"" description:"URL of the Gitea instance" validate:"nonzero"` GiteaURL string `flag:"gitea-url" default:"" description:"URL of the Gitea instance" validate:"nonzero"`
GithubToken string `flag:"github-token" default:"" description:"Github access token" validate:"nonzero"` GithubToken string `flag:"github-token" default:"" description:"Github access token" validate:"nonzero"`
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"` LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
MappingFile string `flag:"mapping-file" default:"" description:"File containing several mappings to execute in one run"`
MigrateArchived bool `flag:"migrate-archived" default:"false" description:"Create migrations for archived repos"` MigrateArchived bool `flag:"migrate-archived" default:"false" description:"Create migrations for archived repos"`
MigrateForks bool `flag:"migrate-forks" default:"false" description:"Create migrations for forked repos"` MigrateForks bool `flag:"migrate-forks" default:"false" description:"Create migrations for forked repos"`
MigratePrivate bool `flag:"migrate-private" default:"true" description:"Migrate private repos (the given Github Token will be entered as sync credential!)"` MigratePrivate bool `flag:"migrate-private" default:"true" description:"Migrate private repos (the given Github Token will be entered as sync credential!)"`
NoMirror bool `flag:"no-mirror" default:"false" description:"Do not enable mirroring but instad do a one-time clone"` NoMirror bool `flag:"no-mirror" default:"false" description:"Do not enable mirroring but instad do a one-time clone"`
SourceExpression string `flag:"source-expression" default:"" description:"Regular expression to match the full name of the source repo (i.e. '^Luzifer/.*$')" validate:"nonzero"` SourceExpression string `flag:"source-expression" default:"" description:"Regular expression to match the full name of the source repo (i.e. '^Luzifer/.*$')"`
TargetUser int64 `flag:"target-user" default:"0" description:"ID of the User / Organization in Gitea to assign the repo to" validate:"nonzero"` TargetUser int64 `flag:"target-user" default:"0" description:"ID of the User / Organization in Gitea to assign the repo to"`
TargetUserName string `flag:"target-user-name" default:"" description:"Username of the given ID (to check whether repo already exists)" validate:"nonzero"` TargetUserName string `flag:"target-user-name" default:"" description:"Username of the given ID (to check whether repo already exists)"`
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"` VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
}{} }{}
mappings *mapFile
version = "dev" version = "dev"
) )
@ -57,11 +59,33 @@ func init() {
} }
func main() { func main() {
mappings = newMapFile()
switch {
case cfg.MappingFile != "":
m, err := loadMapFile(cfg.MappingFile)
if err != nil {
log.WithError(err).Fatal("Unable to load mappings file")
}
mappings = m
case cfg.SourceExpression != "" && cfg.TargetUserName != "" && cfg.TargetUser > 0:
mappings.Mappings = []mapping{{
SourceExpression: cfg.SourceExpression,
TargetUser: cfg.TargetUser,
TargetUserName: cfg.TargetUserName,
}}
default:
log.Fatal("No mappings are defined")
}
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"dry-run": cfg.DryRun, "dry-run": cfg.DryRun,
"source": cfg.SourceExpression, "map_definitions": len(mappings.Mappings),
"target-user": cfg.TargetUserName, "version": version,
"version": version,
}).Info("create-gitea-migration started") }).Info("create-gitea-migration started")
log.Info("Collecting source repos...") log.Info("Collecting source repos...")
@ -92,8 +116,6 @@ func fetchGithubRepos() ([]*github.Repository, error) {
ListOptions: github.ListOptions{PerPage: 100}, ListOptions: github.ListOptions{PerPage: 100},
} }
sourceExpr := regexp.MustCompile(cfg.SourceExpression)
// get all pages of results // get all pages of results
var allRepos []*github.Repository var allRepos []*github.Repository
for { for {
@ -103,8 +125,8 @@ func fetchGithubRepos() ([]*github.Repository, error) {
} }
for _, r := range repos { for _, r := range repos {
if !sourceExpr.MatchString(*r.FullName) { if !mappings.MappingAvailable(*r.FullName) {
log.WithField("repo", *r.FullName).Debug("Skip: Name does not match") log.WithField("repo", *r.FullName).Debug("Skip: No mapping matches repo name")
continue continue
} }
@ -132,12 +154,15 @@ func fetchGithubRepos() ([]*github.Repository, error) {
} }
func giteaCreateMigration(gr *github.Repository) error { func giteaCreateMigration(gr *github.Repository) error {
repoMapping := mappings.GetMapping(*gr.FullName)
logger := log.WithFields(log.Fields{ logger := log.WithFields(log.Fields{
"repo": strFromPtr(gr.Name), "private": boolFromPtr(gr.Private),
"private": boolFromPtr(gr.Private), "source_repo": strFromPtr(gr.FullName),
"target_repo": strings.Join([]string{repoMapping.TargetUserName, strFromPtr(gr.Name)}, "/"),
}) })
req, _ := http.NewRequest(http.MethodGet, giteaURL(strings.Join([]string{"api/v1/repos", cfg.TargetUserName, strFromPtr(gr.Name)}, "/")), nil) req, _ := http.NewRequest(http.MethodGet, giteaURL(strings.Join([]string{"api/v1/repos", repoMapping.TargetUserName, strFromPtr(gr.Name)}, "/")), nil)
req.Header.Set("Authorization", "token "+cfg.GiteaToken) req.Header.Set("Authorization", "token "+cfg.GiteaToken)
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
@ -151,7 +176,7 @@ func giteaCreateMigration(gr *github.Repository) error {
return nil return nil
} }
cmr := createMigrationRequestFromGithubRepo(gr) cmr := createMigrationRequestFromGithubRepo(gr, repoMapping)
body := new(bytes.Buffer) body := new(bytes.Buffer)
if err := json.NewEncoder(body).Encode(cmr); err != nil { if err := json.NewEncoder(body).Encode(cmr); err != nil {

58
mapfile.go Normal file
View File

@ -0,0 +1,58 @@
package main
import (
"os"
"regexp"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
)
type mapFile struct {
Mappings []mapping `yaml:"mappings"`
}
type mapping struct {
SourceExpression string `yaml:"source_expression"`
TargetUser int64 `yaml:"target_user"`
TargetUserName string `yaml:"target_user_name"`
}
func loadMapFile(fileName string) (*mapFile, error) {
if _, err := os.Stat(fileName); err != nil {
return nil, errors.Wrap(err, "Mapping file not available")
}
f, err := os.Open(fileName)
if err != nil {
return nil, errors.Wrap(err, "Unable to open mapping file")
}
defer f.Close()
var out = &mapFile{}
return out, errors.Wrap(yaml.NewDecoder(f).Decode(out), "Unable to decode mapping file")
}
func newMapFile() *mapFile {
return &mapFile{}
}
func (m mapFile) GetMapping(repoName string) *mapping {
for _, me := range m.Mappings {
if regexp.MustCompile(me.SourceExpression).MatchString(repoName) {
return &me
}
}
return nil
}
func (m mapFile) MappingAvailable(repoName string) bool {
for _, me := range m.Mappings {
if regexp.MustCompile(me.SourceExpression).MatchString(repoName) {
return true
}
}
return false
}

16
mapping_sample.yaml Normal file
View File

@ -0,0 +1,16 @@
---
mappings:
- source_expression: '^Luzifer/'
target_user: 1
target_user_name: luzifer
- source_expression: '^luzifer-ansible/'
target_user: 3
target_user_name: luzifer-ansible
- source_expression: '^luzifer-docker/'
target_user: 2
target_user_name: luzifer-docker
...