Skip to content

Commit b4a3713

Browse files
authored
GitLab: Allow cloning starred repositories (#70)
Add support for cloning only starred repositories in GitLab
1 parent edc0842 commit b4a3713

File tree

6 files changed

+100
-55
lines changed

6 files changed

+100
-55
lines changed

README.md

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -104,31 +104,35 @@ Typing ``-help`` will display the command line options that `gitbackup` recogniz
104104
$ gitbackup -help
105105
Usage of ./gitbackup:
106106
-backupdir string
107-
Backup directory
107+
Backup directory
108108
-bare
109-
Clone bare repositories
109+
Clone bare repositories
110110
-githost.url string
111-
DNS of the custom Git host
111+
DNS of the custom Git host
112112
-github.createUserMigration
113-
Download user data
113+
Download user data
114+
-github.createUserMigrationRetry
115+
Retry creating the GitHub user migration if we get an error (default true)
116+
-github.createUserMigrationRetryMax int
117+
Number of retries to attempt for creating GitHub user migration (default 5)
114118
-github.listUserMigrations
115-
List available user migrations
119+
List available user migrations
116120
-github.repoType string
117-
Repo types to backup (all, owner, member, starred) (default "all")
121+
Repo types to backup (all, owner, member, starred) (default "all")
118122
-github.waitForUserMigration
119-
Wait for migration to complete (default true)
123+
Wait for migration to complete (default true)
120124
-gitlab.projectMembershipType string
121-
Project type to clone (all, owner, member) (default "all")
125+
Project type to clone (all, owner, member, starred) (default "all")
122126
-gitlab.projectVisibility string
123-
Visibility level of Projects to clone (internal, public, private) (default "internal")
127+
Visibility level of Projects to clone (internal, public, private) (default "internal")
124128
-ignore-fork
125-
Ignore repositories which are forks
129+
Ignore repositories which are forks
126130
-ignore-private
127-
Ignore private repositories/projects
131+
Ignore private repositories/projects
128132
-service string
129-
Git Hosted Service Name (github/gitlab/bitbucket)
133+
Git Hosted Service Name (github/gitlab/bitbucket)
130134
-use-https-clone
131-
Use HTTPS for cloning instead of SSH
135+
Use HTTPS for cloning instead of SSH
132136
```
133137

134138
#### Backing up your GitHub repositories
@@ -196,6 +200,14 @@ $ GITLAB_TOKEN=secret$token gitbackup \
196200
-gitlab.projectMembershipType member
197201
```
198202

203+
To backup GitLub repositories you have starred:
204+
205+
```lang=bash
206+
$ GITLAB_TOKEN=secret$token gitbackup -service gitlab \
207+
-gitlab.projectMembershipType starred \
208+
-gitlab.projectVisibility public
209+
```
210+
199211
#### GitHub Enterprise or custom GitLab installation
200212

201213
To specify a custom GitHub enterprise or GitLab location, specify the ``service`` as well as the

backup.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ func backUp(backupDir string, repo *Repository, bare bool, wg *sync.WaitGroup) (
6363
}
6464
stdoutStderr, err = cmd.CombinedOutput()
6565
}
66-
6766
return stdoutStderr, err
6867
}
6968

helpers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func getUsername(client interface{}, service string) string {
4444
}
4545

4646
func validGitlabProjectMembership(membership string) bool {
47-
validMemberships := []string{"all", "owner", "member"}
47+
validMemberships := []string{"all", "owner", "member", "starred"}
4848
for _, m := range validMemberships {
4949
if membership == m {
5050
return true

main.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,16 @@ func main() {
5858
githubWaitForMigrationComplete := flag.Bool("github.waitForUserMigration", true, "Wait for migration to complete")
5959

6060
// Gitlab specific flags
61-
gitlabRepoVisibility := flag.String("gitlab.projectVisibility", "internal", "Visibility level of Projects to clone (internal, public, private)")
62-
gitlabProjectMembership := flag.String("gitlab.projectMembershipType", "all", "Project type to clone (all, owner, member)")
61+
gitlabProjectVisibility := flag.String("gitlab.projectVisibility", "internal", "Visibility level of Projects to clone (internal, public, private)")
62+
gitlabProjectMembershipType := flag.String("gitlab.projectMembershipType", "all", "Project type to clone (all, owner, member, starred)")
6363

6464
flag.Parse()
6565

6666
if _, ok := knownServices[*service]; !ok {
6767
log.Fatal("Please specify the git service type: github, gitlab, bitbucket")
6868
}
6969

70-
if !validGitlabProjectMembership(*gitlabProjectMembership) {
70+
if !validGitlabProjectMembership(*gitlabProjectMembershipType) {
7171
log.Fatal("Please specify a valid gitlab project membership - all/owner/member")
7272
}
7373

@@ -111,7 +111,7 @@ func main() {
111111

112112
} else if *githubCreateUserMigration {
113113

114-
repos, err := getRepositories(client, *service, *githubRepoType, *gitlabRepoVisibility, *gitlabProjectMembership, *ignoreFork)
114+
repos, err := getRepositories(client, *service, *githubRepoType, *gitlabProjectVisibility, *gitlabProjectMembershipType, *ignoreFork)
115115
if err != nil {
116116
log.Fatalf("Error getting list of repositories: %v", err)
117117
}
@@ -161,7 +161,10 @@ func main() {
161161
if len(gitHostUsername) == 0 && !*ignorePrivate && *useHTTPSClone {
162162
log.Fatal("Your Git host's username is needed for backing up private repositories via HTTPS")
163163
}
164-
repos, err := getRepositories(client, *service, *githubRepoType, *gitlabRepoVisibility, *gitlabProjectMembership, *ignoreFork)
164+
repos, err := getRepositories(
165+
client, *service, *githubRepoType,
166+
*gitlabProjectVisibility, *gitlabProjectMembershipType, *ignoreFork,
167+
)
165168
if err != nil {
166169
log.Fatal(err)
167170
} else {

repositories.go

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ type Repository struct {
3737
Private bool
3838
}
3939

40-
func getRepositories(client interface{}, service string, githubRepoType string, gitlabRepoVisibility string, gitlabProjectType string, ignoreFork bool) ([]*Repository, error) {
40+
func getRepositories(client interface{},
41+
service string, githubRepoType string,
42+
gitlabProjectVisibility string, gitlabProjectMembershipType string,
43+
ignoreFork bool,
44+
) ([]*Repository, error) {
4145

4246
if client == nil {
4347
log.Fatalf("Couldn't acquire a client to talk to %s", service)
@@ -104,25 +108,26 @@ func getRepositories(client interface{}, service string, githubRepoType string,
104108

105109
if service == "gitlab" {
106110
var visibility gitlab.VisibilityValue
107-
var options gitlab.ListProjectsOptions
108-
109-
var owned bool
110-
var memberOf bool
111-
112-
if gitlabProjectType == "owner" {
113-
owned = true
114-
}
115-
if gitlabProjectType == "member" {
116-
memberOf = true
111+
var boolTrue bool = true
112+
113+
gitlabListOptions := gitlab.ListProjectsOptions{}
114+
115+
switch gitlabProjectMembershipType {
116+
117+
case "owner":
118+
gitlabListOptions.Owned = &boolTrue
119+
case "member":
120+
gitlabListOptions.Membership = &boolTrue
121+
case "starred":
122+
gitlabListOptions.Starred = &boolTrue
123+
case "all":
124+
gitlabListOptions.Owned = &boolTrue
125+
gitlabListOptions.Membership = &boolTrue
126+
gitlabListOptions.Starred = &boolTrue
117127
}
118128

119-
if gitlabProjectType == "all" {
120-
owned = true
121-
memberOf = true
122-
}
123-
124-
if gitlabRepoVisibility != "all" {
125-
switch gitlabRepoVisibility {
129+
if gitlabProjectVisibility != "all" {
130+
switch gitlabProjectVisibility {
126131
case "public":
127132
visibility = gitlab.PublicVisibility
128133
case "private":
@@ -132,30 +137,27 @@ func getRepositories(client interface{}, service string, githubRepoType string,
132137
case "default":
133138
visibility = gitlab.InternalVisibility
134139
}
135-
options = gitlab.ListProjectsOptions{Visibility: &visibility, Membership: &memberOf, Owned: &owned}
136-
} else {
137-
options = gitlab.ListProjectsOptions{Membership: &memberOf, Owned: &owned}
140+
gitlabListOptions.Visibility = &visibility
138141
}
139142

140143
for {
141-
repos, resp, err := client.(*gitlab.Client).Projects.ListProjects(&options)
142-
if err == nil {
143-
for _, repo := range repos {
144-
namespace := strings.Split(repo.PathWithNamespace, "/")[0]
145-
if useHTTPSClone != nil && *useHTTPSClone {
146-
cloneURL = repo.WebURL
147-
} else {
148-
cloneURL = repo.SSHURLToRepo
149-
}
150-
repositories = append(repositories, &Repository{CloneURL: cloneURL, Name: repo.Name, Namespace: namespace, Private: repo.Public})
151-
}
152-
} else {
144+
repos, resp, err := client.(*gitlab.Client).Projects.ListProjects(&gitlabListOptions)
145+
if err != nil {
153146
return nil, err
154147
}
148+
for _, repo := range repos {
149+
namespace := strings.Split(repo.PathWithNamespace, "/")[0]
150+
if useHTTPSClone != nil && *useHTTPSClone {
151+
cloneURL = repo.WebURL
152+
} else {
153+
cloneURL = repo.SSHURLToRepo
154+
}
155+
repositories = append(repositories, &Repository{CloneURL: cloneURL, Name: repo.Name, Namespace: namespace, Private: repo.Public})
156+
}
155157
if resp.NextPage == 0 {
156158
break
157159
}
158-
options.ListOptions.Page = resp.NextPage
160+
gitlabListOptions.ListOptions.Page = resp.NextPage
159161
}
160162
}
161163

@@ -207,6 +209,5 @@ func getRepositories(client interface{}, service string, githubRepoType string,
207209
}
208210
}
209211
}
210-
211212
return repositories, nil
212213
}

repositories_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,36 @@ func TestGetGitLabRepositories(t *testing.T) {
138138
}
139139
}
140140

141+
func TestGetStarredGitLabRepositories(t *testing.T) {
142+
setup()
143+
defer teardown()
144+
145+
mux.HandleFunc("/api/v4/projects", func(w http.ResponseWriter, r *http.Request) {
146+
log.Printf("%#v\n", r.URL.Query())
147+
if len(r.URL.Query().Get("starred")) != 0 {
148+
fmt.Fprint(w, `[{"path_with_namespace": "test/starred-repo-r1", "id":1, "ssh_url_to_repo": "https://gitlab.com/u/r1", "name": "starred-repo-r1"}]`)
149+
return
150+
}
151+
fmt.Fprintf(w, `[]`)
152+
})
153+
154+
repos, err := getRepositories(GitLabClient, "gitlab", "", "", "starred", false)
155+
if err != nil {
156+
t.Fatalf("%v", err)
157+
}
158+
var expected []*Repository
159+
expected = append(expected, &Repository{Namespace: "test", CloneURL: "https://gitlab.com/u/r1", Name: "starred-repo-r1"})
160+
161+
if !reflect.DeepEqual(repos, expected) {
162+
if len(repos) != len(expected) {
163+
t.Fatalf("Expected: %#v, Got: %v", expected, repos)
164+
}
165+
for i := 0; i < len(expected); i++ {
166+
t.Errorf("Expected %+v, Got %+v", expected[i], repos[i])
167+
}
168+
}
169+
}
170+
141171
func TestGetBitbucketRepositories(t *testing.T) {
142172
setup()
143173
defer teardown()

0 commit comments

Comments
 (0)