@@ -2,72 +2,193 @@ package mirroring
22
33import (
44 "fmt"
5- "path"
5+ "log"
6+ "path/filepath"
67 "strings"
8+ "sync"
79
810 "github.com/boxboxjason/gitlab-sync/utils"
11+ gitlab "gitlab.com/gitlab-org/api/client-go"
912)
1013
11- func (g * GitlabInstance ) fetchProjects (filters * utils.MirrorMapping ) error {
14+ func (g * GitlabInstance ) fetchProjects (projectFilters * map [string ]bool , groupFilters * map [string ]bool , mirrorMapping * utils.MirrorMapping , isSource bool ) error {
15+ sourceString := "source"
16+ if ! isSource {
17+ sourceString = "destination"
18+ }
19+ utils .LogVerbosef ("Fetching all projects from %s GitLab instance" , sourceString )
1220 projects , _ , err := g .Gitlab .Projects .ListProjects (nil )
1321 if err != nil {
1422 return err
1523 }
1624
25+ utils .LogVerbosef ("Processing %d projects from %s GitLab instance" , len (projects ), sourceString )
26+
27+ // Create a wait group to wait for all goroutines to finish
28+ var wg sync.WaitGroup
29+
30+ // Create a channel to limit the number of concurrent goroutines
31+ concurrencyLimit := 10
32+ sem := make (chan struct {}, concurrencyLimit )
33+
1734 for _ , project := range projects {
18- // Check if the project matches the filters:
19- // - either is in the projects map
20- // - or path starts with any of the groups in the groups map
21- if _ , ok := filters .Projects [project .PathWithNamespace ]; ok {
22- g .addProject (project .PathWithNamespace , project )
23- } else {
24- for group := range filters .Groups {
25- if strings .HasPrefix (project .PathWithNamespace , group ) {
26- g .addProject (project .PathWithNamespace , project )
27- break
35+ wg .Add (1 )
36+ // Acquire a token from the semaphore
37+ sem <- struct {}{}
38+
39+ go func (project * gitlab.Project ) {
40+ defer wg .Done ()
41+ // Release the token back to the semaphore
42+ defer func () { <- sem }()
43+
44+ // Check if the project matches the filters:
45+ // - either is in the projects map
46+ // - or path starts with any of the groups in the groups map
47+ if _ , ok := (* projectFilters )[project .PathWithNamespace ]; ok {
48+ g .addProject (project .PathWithNamespace , project )
49+ } else {
50+ for group := range * groupFilters {
51+ if strings .HasPrefix (project .PathWithNamespace , group ) {
52+ // Add the project to the gitlab instance projects cache
53+ g .addProject (project .PathWithNamespace , project )
54+
55+ if isSource {
56+ // Retrieve the corresponding group creation options from the mirror mapping
57+ groupCreationOptions , ok := mirrorMapping .Groups [group ]
58+ if ! ok {
59+ log .Fatalf ("Group %s not found in mirror mapping (internal error, please review script)" , group )
60+ }
61+
62+ // Calculate the relative path between the project and the group
63+ relativePath , err := filepath .Rel (group , project .PathWithNamespace )
64+ if err != nil {
65+ log .Fatalf ("Failed to calculate relative path for project %s: %s" , project .PathWithNamespace , err )
66+ }
67+
68+ // Add the project to the mirror mapping
69+ mirrorMapping .AddProject (project .PathWithNamespace , & utils.ProjectMirroringOptions {
70+ CI_CD_Catalog : groupCreationOptions .CI_CD_Catalog ,
71+ Issues : groupCreationOptions .Issues ,
72+ DestinationURL : filepath .Join (groupCreationOptions .DestinationURL , relativePath ),
73+ })
74+ }
75+ break
76+ }
2877 }
2978 }
30- }
79+ }( project )
3180 }
3281
33- utils .LogVerbosef ("Found %d projects to mirror in the source GitLab instance" , len (g .Projects ))
82+ wg .Wait ()
83+
84+ utils .LogVerbosef ("Found %d projects to mirror in the %s GitLab instance" , len (g .Projects ), sourceString )
3485
3586 return nil
3687}
3788
38- func (g * GitlabInstance ) fetchGroups (filters * utils.MirrorMapping ) error {
89+ func (g * GitlabInstance ) fetchGroups (groupFilters * map [string ]bool , mirrorMapping * utils.MirrorMapping , isSource bool ) error {
90+ sourceString := "source"
91+ if ! isSource {
92+ sourceString = "destination"
93+ }
94+ utils .LogVerbosef ("Fetching all groups from %s GitLab instance" , sourceString )
3995 groups , _ , err := g .Gitlab .Groups .ListGroups (nil )
4096 if err != nil {
4197 return err
4298 }
4399
100+ utils .LogVerbosef ("Processing %d groups from %s GitLab instance" , len (groups ), sourceString )
101+
102+ // Create a wait group to wait for all goroutines to finish
103+ var wg sync.WaitGroup
104+
105+ // Create a channel to limit the number of concurrent goroutines
106+ concurrencyLimit := 10
107+ sem := make (chan struct {}, concurrencyLimit )
108+
44109 for _ , group := range groups {
45- // Check if the group matches the filters:
46- // - either is in the groups map
47- // - or path starts with any of the groups in the groups map
48- // - or is a subgroup of any of the groups in the groups map
49- if _ , ok := filters .Groups [group .FullPath ]; ok {
50- g .addGroup (group .FullPath , group )
51- } else {
52- for groupPath := range filters .Groups {
53- if strings .HasPrefix (group .FullPath , groupPath ) {
54- g .addGroup (group .FullPath , group )
55- break
110+ wg .Add (1 )
111+ // Acquire a token from the semaphore
112+ sem <- struct {}{}
113+
114+ go func (group * gitlab.Group ) {
115+ defer wg .Done ()
116+ // Release the token back to the semaphore
117+ defer func () { <- sem }()
118+
119+ // Check if the group matches the filters:
120+ // - either is in the groups map
121+ // - or path starts with any of the groups in the groups map
122+ // - or is a subgroup of any of the groups in the groups map
123+ if _ , ok := (* groupFilters )[group .FullPath ]; ok {
124+ g .addGroup (group .FullPath , group )
125+ } else {
126+ for groupPath := range * groupFilters {
127+ if strings .HasPrefix (group .FullPath , groupPath ) {
128+ // Add the group to the gitlab instance groups cache
129+ g .addGroup (group .FullPath , group )
130+
131+ if isSource {
132+ // Retrieve the corresponding group creation options from the mirror mapping
133+ groupCreationOptions , ok := mirrorMapping .Groups [groupPath ]
134+ if ! ok {
135+ log .Fatalf ("Group %s not found in mirror mapping (internal error, please review script)" , groupPath )
136+ }
137+
138+ // Calculate the relative path between the group and the groupPath
139+ relativePath , err := filepath .Rel (groupPath , group .FullPath )
140+ if err != nil {
141+ log .Fatalf ("Failed to calculate relative path for group %s: %s" , group .FullPath , err )
142+ }
143+
144+ // Add the group to the mirror mapping
145+ mirrorMapping .AddGroup (group .FullPath , & utils.GroupMirroringOptions {
146+ CI_CD_Catalog : groupCreationOptions .CI_CD_Catalog ,
147+ Issues : groupCreationOptions .Issues ,
148+ DestinationURL : filepath .Join (groupCreationOptions .DestinationURL , relativePath ),
149+ })
150+ }
151+ break
152+ }
56153 }
57154 }
58- }
155+ }( group )
59156 }
60157
61- utils .LogVerbosef ("Found %d groups to mirror in the source GitLab instance" , len (g .Groups ))
158+ wg .Wait ()
159+
160+ utils .LogVerbosef ("Found %d matching groups in %s GitLab instance" , len (g .Groups ), sourceString )
62161
63162 return nil
64163}
65164
66- func (g * GitlabInstance ) getParentGroupID (projectOrGroupPath string ) (int , error ) {
165+ func fetchAll (gitlabInstance * GitlabInstance , projectFilters map [string ]bool , groupFilters map [string ]bool , mirrorMapping * utils.MirrorMapping , isSource bool ) error {
166+ wg := sync.WaitGroup {}
167+ errCh := make (chan error , 2 )
168+ wg .Add (2 )
169+ go func () {
170+ defer wg .Done ()
171+ if err := gitlabInstance .fetchGroups (& groupFilters , mirrorMapping , isSource ); err != nil {
172+ errCh <- err
173+ }
174+ }()
175+ go func () {
176+ defer wg .Done ()
177+ if err := gitlabInstance .fetchProjects (& projectFilters , & groupFilters , mirrorMapping , isSource ); err != nil {
178+ errCh <- err
179+ }
180+ }()
181+ wg .Wait ()
182+ close (errCh )
183+
184+ return utils .MergeErrors (errCh , 2 )
185+ }
186+
187+ func (g * GitlabInstance ) getParentNamespaceID (projectOrGroupPath string ) (int , error ) {
67188 parentGroupID := - 1
68- parentPath := path .Dir (projectOrGroupPath )
189+ parentPath := filepath .Dir (projectOrGroupPath )
69190 var err error = nil
70- if parentPath != "." {
191+ if parentPath != "." && parentPath != "/" {
71192 // Check if parent path is already in the instance groups cache
72193 if parentGroup , ok := g .Groups [parentPath ]; ok {
73194 parentGroupID = parentGroup .ID
0 commit comments