Skip to content

Commit 49a5f16

Browse files
committed
feat: add full project resync
added a function to check if a project data does not match and update it if needed created unit tests for all new functions added
1 parent 628d518 commit 49a5f16

File tree

4 files changed

+377
-22
lines changed

4 files changed

+377
-22
lines changed

internal/mirroring/post.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ func (g *GitlabInstance) createProjectFromSource(sourceProject *gitlab.Project,
226226
// The function handles the API calls concurrently using goroutines and a wait group.
227227
// It returns an error if any of the API calls fail.
228228
func (destinationGitlab *GitlabInstance) mirrorReleases(sourceGitlab *GitlabInstance, sourceProject *gitlab.Project, destinationProject *gitlab.Project) []error {
229-
zap.L().Debug("Starting releases mirroring", zap.String(ROLE_SOURCE, sourceProject.HTTPURLToRepo), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
229+
zap.L().Info("Starting releases mirroring", zap.String(ROLE_SOURCE, sourceProject.HTTPURLToRepo), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
230230
// Fetch existing releases from the destination project
231231
existingReleases, _, err := destinationGitlab.Gitlab.Releases.ListReleases(destinationProject.ID, &gitlab.ListReleasesOptions{})
232232
if err != nil {

internal/mirroring/put.go

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func (sourceGitlabInstance *GitlabInstance) copyGroupAvatar(destinationGitlabIns
9494
// The function uses goroutines to perform these tasks concurrently and waits for all of them to finish.
9595
func (destinationGitlabInstance *GitlabInstance) updateProjectFromSource(sourceGitlabInstance *GitlabInstance, sourceProject *gitlab.Project, destinationProject *gitlab.Project, copyOptions *utils.MirroringOptions) []error {
9696
wg := sync.WaitGroup{}
97-
maxErrors := 2
97+
maxErrors := 3
9898
if copyOptions.CI_CD_Catalog {
9999
maxErrors++
100100
}
@@ -106,44 +106,92 @@ func (destinationGitlabInstance *GitlabInstance) updateProjectFromSource(sourceG
106106

107107
go func() {
108108
defer wg.Done()
109+
errorChan <- destinationGitlabInstance.syncProjectAttributes(sourceProject, destinationProject, copyOptions)
110+
}()
109111

110-
zap.L().Debug("Enabling project mirror pull", zap.String(ROLE_SOURCE, sourceProject.HTTPURLToRepo), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
111-
err := destinationGitlabInstance.enableProjectMirrorPull(sourceProject, destinationProject, copyOptions)
112-
if err != nil {
113-
errorChan <- fmt.Errorf("failed to enable project mirror pull for %s: %s", destinationProject.HTTPURLToRepo, err)
114-
}
112+
go func() {
113+
defer wg.Done()
114+
errorChan <- destinationGitlabInstance.enableProjectMirrorPull(sourceProject, destinationProject, copyOptions)
115115
}()
116116

117117
go func() {
118118
defer wg.Done()
119-
zap.L().Debug("Copying project avatar", zap.String(ROLE_SOURCE, sourceProject.HTTPURLToRepo), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
120-
err := sourceGitlabInstance.copyProjectAvatar(destinationGitlabInstance, destinationProject, sourceProject)
121-
if err != nil {
122-
errorChan <- fmt.Errorf("failed to copy project avatar for %s: %s", destinationProject.HTTPURLToRepo, err)
123-
}
119+
errorChan <- sourceGitlabInstance.copyProjectAvatar(destinationGitlabInstance, destinationProject, sourceProject)
124120
}()
125121

126122
if copyOptions.CI_CD_Catalog {
127123
go func() {
128124
defer wg.Done()
129-
err := destinationGitlabInstance.addProjectToCICDCatalog(destinationProject)
130-
if err != nil {
131-
errorChan <- fmt.Errorf("failed to add project %s to CI/CD catalog: %s", destinationProject.HTTPURLToRepo, err)
132-
}
125+
errorChan <- destinationGitlabInstance.addProjectToCICDCatalog(destinationProject)
133126
}()
134127
}
135128

129+
allErrors := []error{}
136130
if copyOptions.MirrorReleases {
137131
go func() {
138132
defer wg.Done()
139-
err := destinationGitlabInstance.mirrorReleases(sourceGitlabInstance, sourceProject, destinationProject)
140-
if err != nil {
141-
errorChan <- fmt.Errorf("failed to copy project %s releases: %s", destinationProject.HTTPURLToRepo, err)
142-
}
133+
allErrors = destinationGitlabInstance.mirrorReleases(sourceGitlabInstance, sourceProject, destinationProject)
143134
}()
144135
}
145136

146137
wg.Wait()
147138
close(errorChan)
148-
return utils.MergeErrors(errorChan)
139+
for err := range errorChan {
140+
if err != nil {
141+
allErrors = append(allErrors, err)
142+
}
143+
}
144+
return allErrors
145+
}
146+
147+
// syncProjectAttributes updates the destination project with settings from the source project.
148+
// It checks if any diverged project data exists and if so, it overwrites it.
149+
func (destinationGitlabInstance *GitlabInstance) syncProjectAttributes(sourceProject *gitlab.Project, destinationProject *gitlab.Project, copyOptions *utils.MirroringOptions) error {
150+
zap.L().Debug("Checking if project requires attributes resync", zap.String(ROLE_SOURCE, sourceProject.HTTPURLToRepo), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
151+
gitlabEditOptions := &gitlab.EditProjectOptions{}
152+
missmatched := false
153+
if sourceProject.Name != destinationProject.Name {
154+
gitlabEditOptions.Name = &sourceProject.Name
155+
missmatched = true
156+
}
157+
if sourceProject.Description != destinationProject.Description {
158+
gitlabEditOptions.Description = &sourceProject.Description
159+
missmatched = true
160+
}
161+
if sourceProject.DefaultBranch != destinationProject.DefaultBranch {
162+
gitlabEditOptions.DefaultBranch = &sourceProject.DefaultBranch
163+
missmatched = true
164+
}
165+
if !utils.StringArraysMatchValues(sourceProject.Topics, destinationProject.Topics) {
166+
gitlabEditOptions.Topics = &sourceProject.Topics
167+
missmatched = true
168+
}
169+
if copyOptions.MirrorTriggerBuilds != destinationProject.MirrorTriggerBuilds {
170+
gitlabEditOptions.MirrorTriggerBuilds = &copyOptions.MirrorTriggerBuilds
171+
missmatched = true
172+
}
173+
if !destinationProject.MirrorOverwritesDivergedBranches {
174+
gitlabEditOptions.MirrorOverwritesDivergedBranches = gitlab.Ptr(true)
175+
missmatched = true
176+
}
177+
if !destinationProject.Mirror {
178+
gitlabEditOptions.Mirror = gitlab.Ptr(true)
179+
missmatched = true
180+
}
181+
if copyOptions.Visibility != string(destinationProject.Visibility) {
182+
visibilityValue := utils.ConvertVisibility(copyOptions.Visibility)
183+
gitlabEditOptions.Visibility = &visibilityValue
184+
missmatched = true
185+
}
186+
187+
if missmatched {
188+
destinationProject, _, err := destinationGitlabInstance.Gitlab.Projects.EditProject(destinationProject.ID, gitlabEditOptions)
189+
if err != nil {
190+
return fmt.Errorf("failed to edit project %s: %s", destinationProject.HTTPURLToRepo, err)
191+
}
192+
zap.L().Debug("Project attributes resync completed", zap.String(ROLE_SOURCE, sourceProject.HTTPURLToRepo), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
193+
} else {
194+
zap.L().Debug("Project attributes are already in sync, skipping", zap.String(ROLE_SOURCE, sourceProject.HTTPURLToRepo), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
195+
}
196+
return nil
149197
}

internal/utils/types.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func (m *MirrorMapping) checkProjects(errChan chan error) {
160160
// Check the visibility
161161
visibilityString := strings.TrimSpace(string(options.Visibility))
162162
if visibilityString != "" && !checkVisibility(visibilityString) {
163-
errChan <- fmt.Errorf("invalid project visibility: %s", string(options.Visibility))
163+
errChan <- fmt.Errorf("invalid project visibility: %s", options.Visibility)
164164
options.Visibility = string(gitlab.PublicVisibility)
165165
}
166166
}
@@ -225,3 +225,32 @@ func checkVisibility(visibility string) bool {
225225
}
226226
return valid
227227
}
228+
229+
func ConvertVisibility(visibility string) gitlab.VisibilityValue {
230+
switch visibility {
231+
case string(gitlab.PublicVisibility):
232+
return gitlab.PublicVisibility
233+
case string(gitlab.InternalVisibility):
234+
return gitlab.InternalVisibility
235+
case string(gitlab.PrivateVisibility):
236+
return gitlab.PrivateVisibility
237+
default:
238+
return gitlab.PublicVisibility
239+
}
240+
}
241+
242+
func StringArraysMatchValues(array1 []string, array2 []string) bool {
243+
if len(array1) != len(array2) {
244+
return false
245+
}
246+
matchMap := make(map[string]struct{}, len(array1))
247+
for _, value := range array1 {
248+
matchMap[value] = struct{}{}
249+
}
250+
for _, value := range array2 {
251+
if _, ok := matchMap[value]; !ok {
252+
return false
253+
}
254+
}
255+
return true
256+
}

0 commit comments

Comments
 (0)