Skip to content

Commit fe6b456

Browse files
committed
refactor: refine business logic separation
rename functions / move them around to increase maintainability start making some methods public add some unit tests
1 parent a2a7aed commit fe6b456

File tree

12 files changed

+258
-112
lines changed

12 files changed

+258
-112
lines changed

internal/mirroring/get.go

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"gitlab-sync/internal/utils"
66
"gitlab-sync/pkg/helpers"
77
"path/filepath"
8-
"strings"
98
"sync"
109

1110
"github.com/Masterminds/semver/v3"
@@ -62,30 +61,6 @@ func (g *GitlabInstance) getParentNamespaceID(projectOrGroupPath string) (int, e
6261
return parentGroupID, err
6362
}
6463

65-
// checkPathMatchesFilters checks if the resources matches the filters
66-
// - either is in the projects map
67-
// - or path starts with any of the groups in the groups map
68-
//
69-
// In the case of a match with a group, it returns the group path
70-
func checkPathMatchesFilters(resourcePath string, projectFilters *map[string]struct{}, groupFilters *map[string]struct{}) (string, bool) {
71-
zap.L().Debug("Checking if path matches filters", zap.String("path", resourcePath))
72-
if projectFilters != nil {
73-
if _, ok := (*projectFilters)[resourcePath]; ok {
74-
zap.L().Debug("Resource path matches project filter", zap.String("project", resourcePath))
75-
return "", true
76-
}
77-
}
78-
if groupFilters != nil {
79-
for groupPath := range *groupFilters {
80-
if strings.HasPrefix(resourcePath, groupPath) {
81-
zap.L().Debug("Resource path matches group filter", zap.String("resource", resourcePath), zap.String("group", groupPath))
82-
return groupPath, true
83-
}
84-
}
85-
}
86-
return "", false
87-
}
88-
8964
// IsVersionGreaterThanThreshold checks if the GitLab instance version is below the defined threshold.
9065
// It retrieves the metadata from the GitLab instance and compares the version
9166
// with the INSTANCE_SEMVER_THRESHOLD.

internal/mirroring/get_group.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func (g *GitlabInstance) processGroupsSmallInstance(allGroups []*gitlab.Group, g
121121
go func(group *gitlab.Group) {
122122
defer wg.Done()
123123

124-
groupPath, matches := checkPathMatchesFilters(group.FullPath, nil, groupFilters)
124+
groupPath, matches := helpers.MatchPathAgainstFilters(group.FullPath, nil, groupFilters)
125125
if matches {
126126
g.storeGroup(group, groupPath, mirrorMapping)
127127
}

internal/mirroring/get_project.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func (g *GitlabInstance) processProjectsSmallInstance(allProjects []*gitlab.Proj
127127
go func(project *gitlab.Project) {
128128
defer wg.Done()
129129

130-
group, matches := checkPathMatchesFilters(project.PathWithNamespace, projectFilters, groupFilters)
130+
group, matches := helpers.MatchPathAgainstFilters(project.PathWithNamespace, projectFilters, groupFilters)
131131
if matches {
132132
g.storeProject(project, group, mirrorMapping)
133133
}

internal/mirroring/get_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package mirroring
22

33
import (
44
"gitlab-sync/internal/utils"
5+
"gitlab-sync/pkg/helpers"
56
"net/http"
67
"testing"
78
)
@@ -61,7 +62,7 @@ func TestCheckPathMatchesFilters(t *testing.T) {
6162
t.Run(test.name, func(t *testing.T) {
6263
t.Parallel()
6364
// Call the function with the test case parameters
64-
_, got := checkPathMatchesFilters(test.path, &test.projectFilters, &test.groupFilters)
65+
_, got := helpers.MatchPathAgainstFilters(test.path, &test.projectFilters, &test.groupFilters)
6566

6667
// Check if the result matches the expected value
6768
if got != test.expected {

internal/mirroring/main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,6 @@ func (destinationGitlabInstance *GitlabInstance) DryRun(sourceGitlabInstance *Gi
173173

174174
// DryRunReleases prints the releases that would be created in dry run mode.
175175
// It fetches the releases from the source project and prints them.
176-
// It does not create any releases in the destination project.
177176
func (destinationGitlabInstance *GitlabInstance) DryRunReleases(sourceGitlabInstance *GitlabInstance, sourceProject *gitlab.Project, copyOptions *utils.MirroringOptions) error {
178177
// Fetch releases from the source project
179178
sourceReleases, _, err := sourceGitlabInstance.Gitlab.Releases.ListReleases(sourceProject.ID, &gitlab.ListReleasesOptions{})
@@ -187,6 +186,10 @@ func (destinationGitlabInstance *GitlabInstance) DryRunReleases(sourceGitlabInst
187186
return nil
188187
}
189188

189+
// ===========================================================================
190+
// INSTANCE HEALTH MANAGEMENT //
191+
// ===========================================================================
192+
190193
// IsPullMirrorAvailable checks the destination GitLab instance for version and license compatibility.
191194
func (g *GitlabInstance) IsPullMirrorAvailable(forcePremium bool, forceNonPremium bool) (bool, error) {
192195
zap.L().Info("Checking destination GitLab instance")

internal/mirroring/post.go

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -217,77 +217,6 @@ func (g *GitlabInstance) createProjectFromSource(sourceProject *gitlab.Project,
217217
return destinationProject, err
218218
}
219219

220-
// ============================================================ //
221-
// RELEASES CREATION FUNCTIONS //
222-
// ============================================================ //
223-
224-
// mirrorReleases mirrors releases from the source project to the destination project.
225-
// It fetches existing releases from the destination project and creates new releases for those that do not exist.
226-
// The function handles the API calls concurrently using goroutines and a wait group.
227-
// It returns an error if any of the API calls fail.
228-
func (destinationGitlab *GitlabInstance) mirrorReleases(sourceGitlab *GitlabInstance, sourceProject *gitlab.Project, destinationProject *gitlab.Project) []error {
229-
zap.L().Info("Starting releases mirroring", zap.String(ROLE_SOURCE, sourceProject.HTTPURLToRepo), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
230-
// Fetch existing releases from the destination project
231-
existingReleases, _, err := destinationGitlab.Gitlab.Releases.ListReleases(destinationProject.ID, &gitlab.ListReleasesOptions{})
232-
if err != nil {
233-
return []error{fmt.Errorf("failed to fetch existing releases for destination project %s: %s", destinationProject.HTTPURLToRepo, err)}
234-
}
235-
236-
// Create a map of existing release tags for quick lookup
237-
existingReleaseTags := make(map[string]struct{})
238-
for _, release := range existingReleases {
239-
if release != nil {
240-
existingReleaseTags[release.TagName] = struct{}{}
241-
}
242-
}
243-
244-
// Fetch releases from the source project
245-
sourceReleases, _, err := sourceGitlab.Gitlab.Releases.ListReleases(sourceProject.ID, &gitlab.ListReleasesOptions{})
246-
if err != nil {
247-
return []error{fmt.Errorf("failed to fetch releases for source project %s: %s", sourceProject.HTTPURLToRepo, err)}
248-
}
249-
250-
// Create a wait group and an error channel for handling API calls concurrently
251-
var wg sync.WaitGroup
252-
errorChan := make(chan error, len(sourceReleases))
253-
254-
// Iterate over each source release
255-
for _, release := range sourceReleases {
256-
// Check if the release already exists in the destination project
257-
if _, exists := existingReleaseTags[release.TagName]; exists {
258-
zap.L().Debug("Release already exists", zap.String("release", release.TagName), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
259-
continue
260-
}
261-
262-
// Increment the wait group counter
263-
wg.Add(1)
264-
265-
// Define the API call logic for creating a release
266-
go func(releaseToMirror *gitlab.Release) {
267-
defer wg.Done()
268-
zap.L().Debug("Creating release in destination project", zap.String("release", releaseToMirror.TagName), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
269-
270-
// Create the release in the destination project
271-
_, _, err := destinationGitlab.Gitlab.Releases.CreateRelease(destinationProject.ID, &gitlab.CreateReleaseOptions{
272-
Name: &releaseToMirror.Name,
273-
TagName: &releaseToMirror.TagName,
274-
Description: &releaseToMirror.Description,
275-
ReleasedAt: releaseToMirror.ReleasedAt,
276-
})
277-
if err != nil {
278-
errorChan <- fmt.Errorf("failed to create release %s in project %s: %s", releaseToMirror.TagName, destinationProject.HTTPURLToRepo, err)
279-
}
280-
}(release)
281-
}
282-
283-
// Wait for all goroutines to finish
284-
wg.Wait()
285-
close(errorChan)
286-
287-
zap.L().Info("Releases mirroring completed", zap.String(ROLE_SOURCE, sourceProject.HTTPURLToRepo), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
288-
return helpers.MergeErrors(errorChan)
289-
}
290-
291220
// ============================================================ //
292221
// CI/CD CATALOG FUNCTIONS //
293222
// ============================================================ //

internal/mirroring/post_test.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -180,17 +180,6 @@ func TestCopyProjectAvatar(t *testing.T) {
180180
})
181181
}
182182

183-
func TestMirrorReleases(t *testing.T) {
184-
_, sourceGitlabInstance := setupTestServer(t, ROLE_SOURCE, INSTANCE_SIZE_SMALL)
185-
_, destinationGitlabInstance := setupTestServer(t, ROLE_DESTINATION, INSTANCE_SIZE_SMALL)
186-
t.Run("Mirror Releases", func(t *testing.T) {
187-
err := destinationGitlabInstance.mirrorReleases(sourceGitlabInstance, TEST_PROJECT, TEST_PROJECT_2)
188-
if err != nil {
189-
t.Errorf("Unexpected error when mirroring releases: %v", err)
190-
}
191-
})
192-
}
193-
194183
func TestCreateProjects(t *testing.T) {
195184
t.Run("Test Create Projects", func(t *testing.T) {
196185
_, sourceGitlabInstance := setupTestServer(t, ROLE_SOURCE, INSTANCE_SIZE_SMALL)

internal/mirroring/put.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func (destinationGitlabInstance *GitlabInstance) updateProjectFromSource(sourceG
7676
wg.Add(1)
7777
go func(sp *gitlab.Project, dp *gitlab.Project) {
7878
defer wg.Done()
79-
allErrors = destinationGitlabInstance.mirrorReleases(sourceGitlabInstance, sp, dp)
79+
allErrors = destinationGitlabInstance.MirrorReleases(sourceGitlabInstance, sp, dp)
8080
}(srcProj, dstProj)
8181
}
8282

internal/mirroring/releases.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package mirroring
2+
3+
import (
4+
"fmt"
5+
"gitlab-sync/pkg/helpers"
6+
"sync"
7+
8+
gitlab "gitlab.com/gitlab-org/api/client-go"
9+
"go.uber.org/zap"
10+
)
11+
12+
// ===========================================================================
13+
// RELEASES MIRRORING FUNCTIONS //
14+
// ===========================================================================
15+
16+
// ================
17+
// GET
18+
// ================
19+
20+
// FetchProjectReleases retrieves all releases for a project and returns them
21+
func (g *GitlabInstance) FetchProjectReleases(project *gitlab.Project) ([]*gitlab.Release, error) {
22+
zap.L().Debug("Fetching releases for project", zap.String("project", project.PathWithNamespace))
23+
fetchOpts := &gitlab.ListReleasesOptions{
24+
ListOptions: gitlab.ListOptions{
25+
PerPage: 100,
26+
Page: 1,
27+
},
28+
}
29+
30+
var releases = make([]*gitlab.Release, 0)
31+
32+
for {
33+
fetchedReleases, resp, err := g.Gitlab.Releases.ListReleases(project.ID, fetchOpts)
34+
if err != nil {
35+
return nil, err
36+
}
37+
releases = append(releases, fetchedReleases...)
38+
39+
if resp.CurrentPage >= resp.TotalPages {
40+
break
41+
}
42+
fetchOpts.Page = resp.NextPage
43+
}
44+
45+
return releases, nil
46+
}
47+
48+
// FetchProjectReleasesTags retrieves all release tags for a project and returns them as a map
49+
func (g *GitlabInstance) FetchProjectReleasesTags(project *gitlab.Project) (map[string]struct{}, error) {
50+
// Fetch existing releases from the destination project
51+
releases, err := g.FetchProjectReleases(project)
52+
if err != nil {
53+
return nil, err
54+
}
55+
56+
// Create a map of existing release tags for quick lookup
57+
releasesTags := make(map[string]struct{})
58+
for _, release := range releases {
59+
if release != nil {
60+
releasesTags[release.TagName] = struct{}{}
61+
}
62+
}
63+
return releasesTags, nil
64+
}
65+
66+
// ================
67+
// POST
68+
// ================
69+
70+
// MirrorRelease creates a release in the destination project
71+
func (g *GitlabInstance) MirrorRelease(project *gitlab.Project, release *gitlab.Release) error {
72+
zap.L().Debug("Creating release in destination project", zap.String("release", release.TagName), zap.String(ROLE_DESTINATION, project.HTTPURLToRepo))
73+
74+
// Create the release in the destination project
75+
_, _, err := g.Gitlab.Releases.CreateRelease(project.ID, &gitlab.CreateReleaseOptions{
76+
Name: &release.Name,
77+
TagName: &release.TagName,
78+
Description: &release.Description,
79+
ReleasedAt: release.ReleasedAt,
80+
})
81+
return err
82+
}
83+
84+
// ================
85+
// CONTROLLER
86+
// ================
87+
88+
// MirrorReleases mirrors releases from the source project to the destination project.
89+
// It fetches existing releases from the destination project and creates new releases for those that do not exist.
90+
// The function handles the API calls concurrently using goroutines
91+
func (destinationGitlab *GitlabInstance) MirrorReleases(sourceGitlab *GitlabInstance, sourceProject *gitlab.Project, destinationProject *gitlab.Project) []error {
92+
zap.L().Info("Starting releases mirroring", zap.String(ROLE_SOURCE, sourceProject.HTTPURLToRepo), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
93+
// Fetch existing releases from the destination project
94+
existingReleasesTags, err := destinationGitlab.FetchProjectReleasesTags(destinationProject)
95+
if err != nil {
96+
return []error{fmt.Errorf("failed to fetch existing releases for destination project %s: %s", destinationProject.HTTPURLToRepo, err)}
97+
}
98+
99+
// Fetch releases from the source project
100+
sourceReleases, err := sourceGitlab.FetchProjectReleases(sourceProject)
101+
if err != nil {
102+
return []error{fmt.Errorf("failed to fetch releases for source project %s: %s", sourceProject.HTTPURLToRepo, err)}
103+
}
104+
105+
// Create a wait group and an error channel for handling API calls concurrently
106+
var wg sync.WaitGroup
107+
errorChan := make(chan error, len(sourceReleases))
108+
109+
// Iterate over each source release
110+
for _, release := range sourceReleases {
111+
// Check if the release already exists in the destination project
112+
if _, exists := existingReleasesTags[release.TagName]; exists {
113+
zap.L().Debug("Release already exists", zap.String("release", release.TagName), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
114+
continue
115+
}
116+
117+
// Increment the wait group counter
118+
wg.Add(1)
119+
120+
// Define the API call logic for creating a release
121+
go func(releaseToMirror *gitlab.Release) {
122+
defer wg.Done()
123+
err := destinationGitlab.MirrorRelease(destinationProject, releaseToMirror)
124+
if err != nil {
125+
errorChan <- fmt.Errorf("failed to create release %s in project %s: %s", releaseToMirror.TagName, destinationProject.HTTPURLToRepo, err)
126+
}
127+
}(release)
128+
}
129+
130+
// Wait for all goroutines to finish
131+
wg.Wait()
132+
close(errorChan)
133+
134+
zap.L().Info("Releases mirroring completed", zap.String(ROLE_SOURCE, sourceProject.HTTPURLToRepo), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
135+
return helpers.MergeErrors(errorChan)
136+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package mirroring
2+
3+
import (
4+
"testing"
5+
6+
gitlab "gitlab.com/gitlab-org/api/client-go"
7+
)
8+
9+
func TestMirrorReleases(t *testing.T) {
10+
_, sourceGitlabInstance := setupTestServer(t, ROLE_SOURCE, INSTANCE_SIZE_SMALL)
11+
_, destinationGitlabInstance := setupTestServer(t, ROLE_DESTINATION, INSTANCE_SIZE_SMALL)
12+
t.Run("Mirror Releases", func(t *testing.T) {
13+
err := destinationGitlabInstance.MirrorReleases(sourceGitlabInstance, TEST_PROJECT, TEST_PROJECT_2)
14+
if err != nil {
15+
t.Errorf("Unexpected error when mirroring releases: %v", err)
16+
}
17+
})
18+
}
19+
20+
func TestFetchProjectReleases(t *testing.T) {
21+
_, gitlabInstance := setupTestServer(t, ROLE_DESTINATION, INSTANCE_SIZE_SMALL)
22+
t.Run("Fetch Project Releases", func(t *testing.T) {
23+
releases, err := gitlabInstance.FetchProjectReleases(TEST_PROJECT)
24+
if err != nil {
25+
t.Errorf("Unexpected error when fetching project releases: %v", err)
26+
}
27+
if len(releases) == 0 {
28+
t.Error("Expected to fetch at least one release")
29+
}
30+
})
31+
}
32+
33+
func TestFetchProjectReleasesTags(t *testing.T) {
34+
_, gitlabInstance := setupTestServer(t, ROLE_DESTINATION, INSTANCE_SIZE_SMALL)
35+
t.Run("Fetch Project Releases Tags", func(t *testing.T) {
36+
releasesTags, err := gitlabInstance.FetchProjectReleasesTags(TEST_PROJECT)
37+
if err != nil {
38+
t.Errorf("Unexpected error when fetching project releases tags: %v", err)
39+
}
40+
if len(releasesTags) == 0 {
41+
t.Error("Expected to fetch at least one release tag")
42+
}
43+
})
44+
}
45+
46+
func TestMirrorRelease(t *testing.T) {
47+
_, gitlabInstance := setupTestServer(t, ROLE_SOURCE, INSTANCE_SIZE_SMALL)
48+
t.Run("Mirror Release", func(t *testing.T) {
49+
release := &gitlab.Release{
50+
Name: "Test Release",
51+
TagName: "v1.0.0",
52+
Description: "This is a test release",
53+
}
54+
err := gitlabInstance.MirrorRelease(TEST_PROJECT, release)
55+
if err != nil {
56+
t.Errorf("Unexpected error when mirroring release: %v", err)
57+
}
58+
})
59+
}

0 commit comments

Comments
 (0)