From 494dbf16dd12dff240fb516b5b6133ba9ceef154 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Sun, 4 Aug 2019 14:34:54 +0200 Subject: [PATCH] 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 --- .gitignore | 1 + gitea.go | 4 ++-- go.mod | 1 + main.go | 57 +++++++++++++++++++++++++++++++------------- mapfile.go | 58 +++++++++++++++++++++++++++++++++++++++++++++ mapping_sample.yaml | 16 +++++++++++++ 6 files changed, 119 insertions(+), 18 deletions(-) create mode 100644 mapfile.go create mode 100644 mapping_sample.yaml diff --git a/.gitignore b/.gitignore index b4be452..3f20943 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .env github2gitea +mapping.yaml diff --git a/gitea.go b/gitea.go index aa5e243..afa328c 100644 --- a/gitea.go +++ b/gitea.go @@ -23,7 +23,7 @@ type createMigrationRequest struct { Wiki bool `json:"wiki"` } -func createMigrationRequestFromGithubRepo(gr *github.Repository) createMigrationRequest { +func createMigrationRequestFromGithubRepo(gr *github.Repository, repoMapping *mapping) createMigrationRequest { cmr := createMigrationRequest{ CloneAddr: strFromPtr(gr.CloneURL), Description: strFromPtr(gr.Description), @@ -32,7 +32,7 @@ func createMigrationRequestFromGithubRepo(gr *github.Repository) createMigration Private: boolFromPtr(gr.Private), PullRequests: boolFromPtr(gr.HasIssues), RepoName: strFromPtr(gr.Name), - UID: cfg.TargetUser, + UID: repoMapping.TargetUser, Wiki: boolFromPtr(gr.HasWiki), } diff --git a/go.mod b/go.mod index 2aa6cf0..7302c15 100644 --- a/go.mod +++ b/go.mod @@ -12,4 +12,5 @@ require ( github.com/pkg/errors v0.8.1 github.com/sirupsen/logrus v1.4.2 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 + gopkg.in/yaml.v2 v2.2.2 ) diff --git a/main.go b/main.go index 3ff66bc..c6cf2aa 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,6 @@ import ( "io/ioutil" "net/http" "os" - "regexp" "strings" "github.com/Luzifer/rconfig/v2" @@ -25,16 +24,19 @@ var ( 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"` 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"` 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!)"` 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"` - TargetUser int64 `flag:"target-user" default:"0" description:"ID of the User / Organization in Gitea to assign the repo to" validate:"nonzero"` - TargetUserName string `flag:"target-user-name" default:"" description:"Username of the given ID (to check whether repo already exists)" 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"` + 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"` }{} + mappings *mapFile + version = "dev" ) @@ -57,11 +59,33 @@ func init() { } 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{ - "dry-run": cfg.DryRun, - "source": cfg.SourceExpression, - "target-user": cfg.TargetUserName, - "version": version, + "dry-run": cfg.DryRun, + "map_definitions": len(mappings.Mappings), + "version": version, }).Info("create-gitea-migration started") log.Info("Collecting source repos...") @@ -92,8 +116,6 @@ func fetchGithubRepos() ([]*github.Repository, error) { ListOptions: github.ListOptions{PerPage: 100}, } - sourceExpr := regexp.MustCompile(cfg.SourceExpression) - // get all pages of results var allRepos []*github.Repository for { @@ -103,8 +125,8 @@ func fetchGithubRepos() ([]*github.Repository, error) { } for _, r := range repos { - if !sourceExpr.MatchString(*r.FullName) { - log.WithField("repo", *r.FullName).Debug("Skip: Name does not match") + if !mappings.MappingAvailable(*r.FullName) { + log.WithField("repo", *r.FullName).Debug("Skip: No mapping matches repo name") continue } @@ -132,12 +154,15 @@ func fetchGithubRepos() ([]*github.Repository, error) { } func giteaCreateMigration(gr *github.Repository) error { + repoMapping := mappings.GetMapping(*gr.FullName) + 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) resp, err := http.DefaultClient.Do(req) @@ -151,7 +176,7 @@ func giteaCreateMigration(gr *github.Repository) error { return nil } - cmr := createMigrationRequestFromGithubRepo(gr) + cmr := createMigrationRequestFromGithubRepo(gr, repoMapping) body := new(bytes.Buffer) if err := json.NewEncoder(body).Encode(cmr); err != nil { diff --git a/mapfile.go b/mapfile.go new file mode 100644 index 0000000..e982629 --- /dev/null +++ b/mapfile.go @@ -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 +} diff --git a/mapping_sample.yaml b/mapping_sample.yaml new file mode 100644 index 0000000..21154d4 --- /dev/null +++ b/mapping_sample.yaml @@ -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 + +...