mirror of
https://github.com/cooperspencer/gickup
synced 2025-04-20 05:37:55 +02:00
641 lines
16 KiB
Go
641 lines
16 KiB
Go
package gitlab
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/cooperspencer/gickup/logger"
|
|
"github.com/cooperspencer/gickup/types"
|
|
"github.com/rs/zerolog"
|
|
"github.com/xanzy/go-gitlab"
|
|
)
|
|
|
|
var (
|
|
sub zerolog.Logger
|
|
)
|
|
|
|
// Backup TODO.
|
|
func Backup(r types.Repo, d types.GenRepo, dry bool) bool {
|
|
var gitlabclient *gitlab.Client
|
|
token := d.GetToken()
|
|
var err error
|
|
if d.URL == "" {
|
|
d.URL = "https://gitlab.com"
|
|
gitlabclient, err = gitlab.NewClient(token)
|
|
} else {
|
|
gitlabclient, err = gitlab.NewClient(token, gitlab.WithBaseURL(d.URL))
|
|
}
|
|
sub = logger.CreateSubLogger("stage", "gitlab", "url", d.URL)
|
|
|
|
if err != nil {
|
|
sub.Error().
|
|
Msg(err.Error())
|
|
return false
|
|
}
|
|
|
|
sub.Info().
|
|
Msgf("mirroring %s to %s", types.Blue(r.Name), d.URL)
|
|
|
|
True := true
|
|
|
|
opt := gitlab.ListProjectsOptions{
|
|
Search: &r.Name,
|
|
Owned: &True,
|
|
}
|
|
|
|
projects, _, err := gitlabclient.Projects.ListProjects(&opt)
|
|
if err != nil {
|
|
sub.Error().Msg(err.Error())
|
|
return false
|
|
}
|
|
|
|
found := false
|
|
for _, p := range projects {
|
|
if p.Name == r.Name {
|
|
found = true
|
|
}
|
|
}
|
|
|
|
if dry || found {
|
|
return true
|
|
}
|
|
|
|
if r.Token != "" {
|
|
splittedurl := strings.Split(r.URL, "//")
|
|
|
|
r.URL = fmt.Sprintf("%s//%s:%s@%s",
|
|
splittedurl[0], r.Owner, r.Token, splittedurl[1])
|
|
}
|
|
|
|
var visibility gitlab.VisibilityValue
|
|
|
|
if r.Private {
|
|
visibility = gitlab.PrivateVisibility
|
|
} else {
|
|
visibility = gitlab.PublicVisibility
|
|
}
|
|
|
|
opts := &gitlab.CreateProjectOptions{
|
|
Mirror: &True,
|
|
ImportURL: &r.URL,
|
|
Name: &r.Name,
|
|
Description: &r.Description,
|
|
Visibility: gitlab.Ptr(visibility),
|
|
}
|
|
|
|
_, _, err = gitlabclient.Projects.CreateProject(opts)
|
|
if err != nil {
|
|
sub.Error().
|
|
Msg(err.Error())
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// Get TODO.
|
|
func Get(conf *types.Conf) ([]types.Repo, bool) {
|
|
ran := false
|
|
repos := []types.Repo{}
|
|
inSlice := map[string]bool{}
|
|
for _, repo := range conf.Source.Gitlab {
|
|
if repo.URL == "" {
|
|
repo.URL = "https://gitlab.com"
|
|
}
|
|
err := repo.Filter.ParseDuration()
|
|
sub = logger.CreateSubLogger("stage", "gitlab", "url", repo.URL)
|
|
if err != nil {
|
|
sub.Warn().
|
|
Msg(err.Error())
|
|
}
|
|
ran = true
|
|
|
|
token := repo.GetToken()
|
|
client, err := gitlab.NewClient(token, gitlab.WithBaseURL(repo.URL))
|
|
if err != nil {
|
|
sub.Error().
|
|
Msg(err.Error())
|
|
continue
|
|
}
|
|
|
|
userNotInConfig := false
|
|
|
|
if repo.User == "" {
|
|
user, _, err := client.Users.CurrentUser()
|
|
if err != nil {
|
|
sub.Error().
|
|
Msg(err.Error())
|
|
continue
|
|
}
|
|
repo.User = user.Username
|
|
userNotInConfig = true
|
|
}
|
|
|
|
sub.Info().
|
|
Msgf("grabbing repositories from %s", repo.User)
|
|
gitlabrepos := []*gitlab.Project{}
|
|
gitlabgrouprepos := map[string][]*gitlab.Project{}
|
|
|
|
opt := &gitlab.ListProjectsOptions{Membership: gitlab.Ptr(true)}
|
|
users, _, err := client.Users.ListUsers(&gitlab.ListUsersOptions{Username: &repo.User})
|
|
if err != nil {
|
|
sub.Error().
|
|
Msg(err.Error())
|
|
continue
|
|
}
|
|
if len(users) == 0 {
|
|
sub.Error().Msgf("couldn't find user %s", repo.User)
|
|
break
|
|
}
|
|
|
|
opt.PerPage = 50
|
|
for _, user := range users {
|
|
if user.Username == repo.User {
|
|
i := 1
|
|
for {
|
|
opt.Page = i
|
|
projects := []*gitlab.Project{}
|
|
if userNotInConfig {
|
|
projects, _, err = client.Projects.ListProjects(opt)
|
|
} else {
|
|
projects, _, err = client.Projects.ListUserProjects(user.ID, opt)
|
|
}
|
|
if err != nil {
|
|
sub.Error().
|
|
Msg(err.Error())
|
|
}
|
|
if len(projects) == 0 {
|
|
break
|
|
}
|
|
gitlabrepos = append(gitlabrepos, projects...)
|
|
i++
|
|
}
|
|
}
|
|
}
|
|
|
|
if repo.Starred {
|
|
for _, user := range users {
|
|
if user.Username == repo.User {
|
|
i := 1
|
|
for {
|
|
opt.Page = i
|
|
projects, _, err := client.Projects.ListUserStarredProjects(user.ID, opt)
|
|
if err != nil {
|
|
sub.Error().
|
|
Msg(err.Error())
|
|
}
|
|
if len(projects) == 0 {
|
|
break
|
|
}
|
|
gitlabrepos = append(gitlabrepos, projects...)
|
|
i++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
include := types.GetMap(repo.Include)
|
|
includeorgs := types.GetMap(repo.IncludeOrgs)
|
|
exclude := types.GetMap(repo.Exclude)
|
|
excludeorgs := types.GetMap(repo.ExcludeOrgs)
|
|
languages := types.GetMap(repo.Filter.Languages)
|
|
|
|
for _, org := range repo.IncludeOrgs {
|
|
group, _, err := client.Groups.GetGroup(org, &gitlab.GetGroupOptions{})
|
|
if err != nil {
|
|
sub.Error().
|
|
Msg(err.Error())
|
|
continue
|
|
}
|
|
subgroups, _, err := client.Groups.ListSubGroups(group.ID, &gitlab.ListSubGroupsOptions{})
|
|
for _, sub := range subgroups {
|
|
includeorgs[sub.FullPath] = true
|
|
}
|
|
}
|
|
|
|
for _, org := range repo.ExcludeOrgs {
|
|
group, _, err := client.Groups.GetGroup(org, &gitlab.GetGroupOptions{})
|
|
if err != nil {
|
|
sub.Error().
|
|
Msg(err.Error())
|
|
continue
|
|
}
|
|
subgroups, _, err := client.Groups.ListSubGroups(group.ID, &gitlab.ListSubGroupsOptions{})
|
|
for _, sub := range subgroups {
|
|
excludeorgs[sub.FullPath] = true
|
|
}
|
|
}
|
|
|
|
for _, r := range gitlabrepos {
|
|
if !inSlice[r.PathWithNamespace] {
|
|
sub.Debug().Str("repo-type", "user").Msg(r.WebURL)
|
|
if repo.Filter.ExcludeForks {
|
|
if r.ForkedFromProject != nil {
|
|
continue
|
|
}
|
|
}
|
|
if repo.Filter.ExcludeArchived {
|
|
if r.Archived {
|
|
continue
|
|
}
|
|
}
|
|
|
|
if len(repo.Filter.Languages) > 0 {
|
|
langs, _, err := client.Projects.GetProjectLanguages(r.ID)
|
|
if err != nil {
|
|
sub.Error().
|
|
Msg(err.Error())
|
|
continue
|
|
} else {
|
|
language := ""
|
|
percentage := float32(0)
|
|
|
|
for lang, percent := range *langs {
|
|
if percent > percentage {
|
|
language = lang
|
|
}
|
|
}
|
|
if !languages[strings.ToLower(language)] {
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
if r.StarCount < repo.Filter.Stars {
|
|
continue
|
|
}
|
|
if time.Since(*r.LastActivityAt) > repo.Filter.LastActivityDuration && repo.Filter.LastActivityDuration != 0 {
|
|
continue
|
|
}
|
|
if include[r.Name] {
|
|
if r.RepositoryAccessLevel != gitlab.DisabledAccessControl {
|
|
repos = append(repos, types.Repo{
|
|
Name: r.Path,
|
|
URL: r.HTTPURLToRepo,
|
|
SSHURL: r.SSHURLToRepo,
|
|
Token: token,
|
|
Defaultbranch: r.DefaultBranch,
|
|
Origin: repo,
|
|
Owner: r.Namespace.FullPath,
|
|
Hoster: types.GetHost(repo.URL),
|
|
Description: r.Description,
|
|
Private: r.Visibility == gitlab.PrivateVisibility,
|
|
Issues: GetIssues(r, client, repo),
|
|
})
|
|
}
|
|
|
|
if r.WikiEnabled && repo.Wiki {
|
|
if activeWiki(r, client, repo) {
|
|
httpURLToRepo := types.DotGitRx.ReplaceAllString(r.HTTPURLToRepo, ".wiki.git")
|
|
sshURLToRepo := types.DotGitRx.ReplaceAllString(r.SSHURLToRepo, ".wiki.git")
|
|
repos = append(repos, types.Repo{
|
|
Name: r.Path + ".wiki",
|
|
URL: httpURLToRepo,
|
|
SSHURL: sshURLToRepo,
|
|
Token: token,
|
|
Defaultbranch: r.DefaultBranch,
|
|
Origin: repo,
|
|
Owner: r.Namespace.FullPath,
|
|
Hoster: types.GetHost(repo.URL),
|
|
Description: r.Description,
|
|
Private: r.Visibility == gitlab.PrivateVisibility,
|
|
})
|
|
}
|
|
}
|
|
|
|
continue
|
|
}
|
|
if exclude[r.Name] {
|
|
continue
|
|
}
|
|
if len(include) == 0 {
|
|
if r.RepositoryAccessLevel != gitlab.DisabledAccessControl {
|
|
repos = append(repos, types.Repo{
|
|
Name: r.Path,
|
|
URL: r.HTTPURLToRepo,
|
|
SSHURL: r.SSHURLToRepo,
|
|
Token: token,
|
|
Defaultbranch: r.DefaultBranch,
|
|
Origin: repo,
|
|
Owner: r.Namespace.FullPath,
|
|
Hoster: types.GetHost(repo.URL),
|
|
Description: r.Description,
|
|
Private: r.Visibility == gitlab.PrivateVisibility,
|
|
Issues: GetIssues(r, client, repo),
|
|
})
|
|
}
|
|
|
|
if r.WikiEnabled && repo.Wiki {
|
|
if activeWiki(r, client, repo) {
|
|
httpURLToRepo := types.DotGitRx.ReplaceAllString(r.HTTPURLToRepo, ".wiki.git")
|
|
sshURLToRepo := types.DotGitRx.ReplaceAllString(r.SSHURLToRepo, ".wiki.git")
|
|
repos = append(repos, types.Repo{
|
|
Name: r.Path + ".wiki",
|
|
URL: httpURLToRepo,
|
|
SSHURL: sshURLToRepo,
|
|
Token: token,
|
|
Defaultbranch: r.DefaultBranch,
|
|
Origin: repo,
|
|
Owner: r.Namespace.FullPath,
|
|
Hoster: types.GetHost(repo.URL),
|
|
Description: r.Description,
|
|
Private: r.Visibility == gitlab.PrivateVisibility,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
inSlice[r.PathWithNamespace] = true
|
|
}
|
|
}
|
|
|
|
if token != "" {
|
|
groups := []*gitlab.Group{}
|
|
i := 1
|
|
for {
|
|
g, _, err := client.Groups.ListGroups(&gitlab.ListGroupsOptions{
|
|
ListOptions: gitlab.ListOptions{
|
|
Page: i,
|
|
PerPage: 50,
|
|
},
|
|
})
|
|
if err != nil {
|
|
sub.Error().Msg(err.Error())
|
|
}
|
|
|
|
if len(g) == 0 {
|
|
break
|
|
}
|
|
|
|
groups = append(groups, g...)
|
|
i++
|
|
}
|
|
|
|
gopt := &gitlab.ListGroupProjectsOptions{}
|
|
for _, group := range groups {
|
|
i = 1
|
|
gopt.PerPage = 50
|
|
gopt.Page = i
|
|
for {
|
|
projects, _, err := client.Groups.ListGroupProjects(group.ID, gopt)
|
|
if err != nil {
|
|
sub.Error().
|
|
Msg(err.Error())
|
|
}
|
|
if len(projects) == 0 {
|
|
break
|
|
}
|
|
splfullpath := strings.Split(group.FullPath, "/")
|
|
fullpath := path.Join(splfullpath...)
|
|
if _, ok := gitlabgrouprepos[fullpath]; !ok {
|
|
gitlabgrouprepos[fullpath] = []*gitlab.Project{}
|
|
}
|
|
|
|
gitlabgrouprepos[fullpath] = append(gitlabgrouprepos[fullpath], projects...)
|
|
i++
|
|
gopt.Page = i
|
|
}
|
|
}
|
|
for k, gr := range gitlabgrouprepos {
|
|
for _, r := range gr {
|
|
if !inSlice[r.PathWithNamespace] {
|
|
sub.Debug().Str("repo-type", "group").Msg(r.WebURL)
|
|
if repo.Filter.ExcludeForks {
|
|
if r.ForkedFromProject != nil {
|
|
continue
|
|
}
|
|
}
|
|
if repo.Filter.ExcludeArchived {
|
|
if r.Archived {
|
|
continue
|
|
}
|
|
}
|
|
|
|
if len(repo.Filter.Languages) > 0 {
|
|
langs, _, err := client.Projects.GetProjectLanguages(r.ID)
|
|
if err != nil {
|
|
sub.Error().
|
|
Msg(err.Error())
|
|
continue
|
|
} else {
|
|
language := ""
|
|
percentage := float32(0)
|
|
|
|
for lang, percent := range *langs {
|
|
if percent > percentage {
|
|
language = lang
|
|
}
|
|
}
|
|
if !languages[strings.ToLower(language)] {
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
if r.StarCount < repo.Filter.Stars {
|
|
continue
|
|
}
|
|
if time.Since(*r.LastActivityAt) > repo.Filter.LastActivityDuration && repo.Filter.LastActivityDuration != 0 {
|
|
continue
|
|
}
|
|
|
|
if include[r.Name] {
|
|
if r.RepositoryAccessLevel != gitlab.DisabledAccessControl {
|
|
repos = append(repos, types.Repo{
|
|
Name: r.Path,
|
|
URL: r.HTTPURLToRepo,
|
|
SSHURL: r.SSHURLToRepo,
|
|
Token: token,
|
|
Defaultbranch: r.DefaultBranch,
|
|
Origin: repo,
|
|
Owner: k,
|
|
Hoster: types.GetHost(repo.URL),
|
|
Description: r.Description,
|
|
Private: r.Visibility == gitlab.PrivateVisibility,
|
|
Issues: GetIssues(r, client, repo),
|
|
})
|
|
}
|
|
|
|
if r.WikiEnabled && repo.Wiki {
|
|
if activeWiki(r, client, repo) {
|
|
httpURLToRepo := types.DotGitRx.ReplaceAllString(r.HTTPURLToRepo, ".wiki.git")
|
|
sshURLToRepo := types.DotGitRx.ReplaceAllString(r.SSHURLToRepo, ".wiki.git")
|
|
repos = append(repos, types.Repo{
|
|
Name: r.Path + ".wiki",
|
|
URL: httpURLToRepo,
|
|
SSHURL: sshURLToRepo,
|
|
Token: token,
|
|
Defaultbranch: r.DefaultBranch,
|
|
Origin: repo,
|
|
Owner: k,
|
|
Hoster: types.GetHost(repo.URL),
|
|
Description: r.Description,
|
|
Private: r.Visibility == gitlab.PrivateVisibility,
|
|
})
|
|
}
|
|
}
|
|
|
|
continue
|
|
}
|
|
if exclude[r.Name] {
|
|
continue
|
|
}
|
|
if excludeorgs[r.Namespace.FullPath] {
|
|
continue
|
|
}
|
|
|
|
if len(include) == 0 {
|
|
if len(includeorgs) == 0 || includeorgs[r.Namespace.FullPath] {
|
|
if r.RepositoryAccessLevel != gitlab.DisabledAccessControl {
|
|
repos = append(repos, types.Repo{
|
|
Name: r.Path,
|
|
URL: r.HTTPURLToRepo,
|
|
SSHURL: r.SSHURLToRepo,
|
|
Token: token,
|
|
Defaultbranch: r.DefaultBranch,
|
|
Origin: repo,
|
|
Owner: k,
|
|
Hoster: types.GetHost(repo.URL),
|
|
Description: r.Description,
|
|
Private: r.Visibility == gitlab.PrivateVisibility,
|
|
Issues: GetIssues(r, client, repo),
|
|
})
|
|
}
|
|
|
|
if r.WikiEnabled && repo.Wiki {
|
|
if activeWiki(r, client, repo) {
|
|
httpURLToRepo := types.DotGitRx.ReplaceAllString(r.HTTPURLToRepo, ".wiki.git")
|
|
sshURLToRepo := types.DotGitRx.ReplaceAllString(r.SSHURLToRepo, ".wiki.git")
|
|
repos = append(repos, types.Repo{
|
|
Name: r.Path + ".wiki",
|
|
URL: httpURLToRepo,
|
|
SSHURL: sshURLToRepo,
|
|
Token: token,
|
|
Defaultbranch: r.DefaultBranch,
|
|
Origin: repo,
|
|
Owner: k,
|
|
Hoster: types.GetHost(repo.URL),
|
|
Description: r.Description,
|
|
Private: r.Visibility == gitlab.PrivateVisibility,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
inSlice[r.PathWithNamespace] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return repos, ran
|
|
}
|
|
|
|
func activeWiki(r *gitlab.Project, client *gitlab.Client, repo types.GenRepo) bool {
|
|
wikilistoptions := &gitlab.ListWikisOptions{
|
|
WithContent: gitlab.Ptr(true),
|
|
}
|
|
|
|
wikis, _, err := client.Wikis.ListWikis(r.ID, wikilistoptions)
|
|
if err != nil {
|
|
sub.Warn().
|
|
Msg(err.Error())
|
|
}
|
|
|
|
return len(wikis) > 0
|
|
}
|
|
|
|
// GetIssues get issues
|
|
func GetIssues(repo *gitlab.Project, client *gitlab.Client, conf types.GenRepo) map[string]interface{} {
|
|
issues := map[string]interface{}{}
|
|
if conf.Issues {
|
|
listOptions := &gitlab.ListProjectIssuesOptions{ListOptions: gitlab.ListOptions{PerPage: 100}}
|
|
errorcount := 0
|
|
for {
|
|
i, response, err := client.Issues.ListProjectIssues(repo.ID, listOptions)
|
|
if err != nil {
|
|
if response.StatusCode == http.StatusForbidden {
|
|
sub.Error().Err(err).Str("repo", repo.Name).Msg("can't fetch issues")
|
|
return issues
|
|
}
|
|
if errorcount < 5 {
|
|
sub.Warn().Err(err).Str("repo", repo.Name).Msg("can't fetch issues")
|
|
time.Sleep(5 * time.Second)
|
|
errorcount++
|
|
} else {
|
|
return issues
|
|
}
|
|
} else {
|
|
if len(i) > 0 {
|
|
for _, issue := range i {
|
|
issues[strconv.Itoa(issue.IID)] = issue
|
|
}
|
|
} else {
|
|
break
|
|
}
|
|
listOptions.Page++
|
|
}
|
|
}
|
|
}
|
|
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 {
|
|
return "", err
|
|
}
|
|
|
|
user, _, err := client.Users.CurrentUser()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
me := user
|
|
|
|
if destination.User == "" {
|
|
destination.User = me.Username
|
|
}
|
|
|
|
repos, _, err := client.Projects.ListProjects(&gitlab.ListProjectsOptions{Search: &repo.Name})
|
|
|
|
if err != nil {
|
|
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 {
|
|
return "", err
|
|
}
|
|
|
|
return r.HTTPURLToRepo, nil
|
|
}
|