diff --git a/conf.example.yml b/conf.example.yml index adb25d5..a154a79 100644 --- a/conf.example.yml +++ b/conf.example.yml @@ -227,6 +227,8 @@ destination: - token: some-token # token_file: token.txt # alternatively, specify token in a file url: http(s)://url-to-gitlab + mirror: + enabled: true # if set to true, gickup will clone the repository and push it to gitlab itself github: - token: some-token # token_file: token.txt # alternatively, specify token in a file diff --git a/gitlab/gitlab.go b/gitlab/gitlab.go index d31b73e..aec989c 100644 --- a/gitlab/gitlab.go +++ b/gitlab/gitlab.go @@ -524,3 +524,62 @@ func GetIssues(repo *gitlab.Project, client *gitlab.Client, conf types.GenRepo) } return issues } + +// GetOrCreate Get or create a repository +func GetOrCreate(destination types.GenRepo, repo types.Repo) (string, error) { + visibility := gitlab.PublicVisibility + + if repo.Private { + visibility = gitlab.PrivateVisibility + } + + sub = logger.CreateSubLogger("stage", "gitlab", "url", destination.URL) + + token := destination.GetToken() + client, err := gitlab.NewClient(token, gitlab.WithBaseURL(destination.URL)) + if err != nil { + fmt.Println(0) + return "", err + } + + user, _, err := client.Users.CurrentUser() + if err != nil { + fmt.Println(1) + return "", err + } + me := user + + if destination.User == "" { + destination.User = me.Username + } + + repos, _, err := client.Projects.ListProjects(&gitlab.ListProjectsOptions{Search: &repo.Name}) + + if err != nil { + fmt.Println(2) + return "", err + } + + for _, repository := range repos { + if repository.Owner == nil { + continue + } + if repository.Name == repo.Name && repository.Owner.Username == me.Username { + return repository.HTTPURLToRepo, nil + } + } + + opts := gitlab.CreateProjectOptions{ + Name: gitlab.Ptr(repo.Name), + Visibility: gitlab.Ptr(visibility), + Description: gitlab.Ptr(repo.Description), + } + + r, _, err := client.Projects.CreateProject(&opts) + if err != nil { + fmt.Println(3) + return "", err + } + + return r.HTTPURLToRepo, nil +} diff --git a/main.go b/main.go index 64c024e..60c95e5 100644 --- a/main.go +++ b/main.go @@ -260,7 +260,6 @@ func backup(repos []types.Repo, conf *types.Conf) { status = 1 prometheus.RepoSuccess.WithLabelValues(r.Hoster, r.Name, r.Owner, "gitea", d.URL).Set(float64(status)) - prometheus.DestinationBackupsComplete.WithLabelValues("gitea").Inc() } } else { if gitea.Backup(r, d, cli.Dry) { @@ -345,7 +344,6 @@ func backup(repos []types.Repo, conf *types.Conf) { status = 1 prometheus.RepoSuccess.WithLabelValues(r.Hoster, r.Name, r.Owner, "gogs", d.URL).Set(float64(status)) - prometheus.DestinationBackupsComplete.WithLabelValues("gogs").Inc() } } else { if gogs.Backup(r, d, cli.Dry) { @@ -361,11 +359,85 @@ func backup(repos []types.Repo, conf *types.Conf) { for _, d := range conf.Destination.Gitlab { if !strings.HasSuffix(r.Name, ".wiki") { + if d.URL == "" { + d.URL = "https://gitlab.com" + } + repotime := time.Now() status := 0 - if gitlab.Backup(r, d, cli.Dry) { - prometheus.RepoTime.WithLabelValues(r.Hoster, r.Name, r.Owner, "gitlab", d.URL).Set(time.Since(repotime).Seconds()) - status = 1 + if d.Mirror.Enabled { + log.Info(). + Str("stage", "gitlab"). + Str("url", d.URL). + Msgf("mirroring %s to %s", types.Blue(r.Name), d.URL) + + if !cli.Dry { + tempdir, err := os.MkdirTemp(os.TempDir(), fmt.Sprintf("gitlab-%x", repotime)) + if err != nil { + log.Error(). + Str("stage", "tempclone"). + Str("url", r.URL). + Msg(err.Error()) + continue + } + + defer os.RemoveAll(tempdir) + temprepo, err := local.TempClone(r, tempdir) + if err != nil { + if err == git.NoErrAlreadyUpToDate { + log.Info(). + Str("stage", "gitlab"). + Str("url", r.URL). + Msg(err.Error()) + } else { + log.Error(). + Str("stage", "tempclone"). + Str("url", r.URL). + Str("git", "clone"). + Msg(err.Error()) + os.RemoveAll(tempdir) + continue + } + } + + cloneurl, err := gitlab.GetOrCreate(d, r) + if err != nil { + log.Error(). + Str("stage", "gitlab"). + Str("url", r.URL). + Msg(err.Error()) + os.RemoveAll(tempdir) + continue + } + + err = local.CreateRemotePush(temprepo, d, cloneurl, r.Origin.LFS) + if err != nil { + if err == git.NoErrAlreadyUpToDate { + log.Info(). + Str("stage", "gitlab"). + Str("url", r.URL). + Msg(err.Error()) + } else { + log.Error(). + Str("stage", "gitlab"). + Str("url", r.URL). + Str("git", "push"). + Msg(err.Error()) + os.RemoveAll(tempdir) + continue + } + } + + prometheus.RepoTime.WithLabelValues(r.Hoster, r.Name, r.Owner, "gitlab", d.URL).Set(time.Since(repotime).Seconds()) + status = 1 + + prometheus.RepoSuccess.WithLabelValues(r.Hoster, r.Name, r.Owner, "gitlab", d.URL).Set(float64(status)) + } + } else { + if gitlab.Backup(r, d, cli.Dry) { + prometheus.RepoTime.WithLabelValues(r.Hoster, r.Name, r.Owner, "gitlab", d.URL).Set(time.Since(repotime).Seconds()) + status = 1 + } } prometheus.RepoSuccess.WithLabelValues(r.Hoster, r.Name, r.Owner, "gitlab", d.URL).Set(float64(status)) @@ -472,6 +544,7 @@ func backup(repos []types.Repo, conf *types.Conf) { continue } + defer os.RemoveAll(tempdir) temprepo, err := local.TempClone(r, tempdir) if err != nil { if err == git.NoErrAlreadyUpToDate { @@ -550,6 +623,7 @@ func backup(repos []types.Repo, conf *types.Conf) { continue } + defer os.RemoveAll(tempdir) temprepo, err := local.TempClone(r, tempdir) if err != nil { if err == git.NoErrAlreadyUpToDate {