Skip to content

Commit 6aad7ce

Browse files
committed
feat: add releases mirroring display in dry-run
also add unit tests for new / modified functions
1 parent 89dddd3 commit 6aad7ce

File tree

2 files changed

+149
-7
lines changed

2 files changed

+149
-7
lines changed

internal/mirroring/main.go

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"gitlab-sync/internal/utils"
99

10+
gitlab "gitlab.com/gitlab-org/api/client-go"
1011
"go.uber.org/zap"
1112
)
1213

@@ -70,7 +71,7 @@ func MirrorGitlabs(gitlabMirrorArgs *utils.ParserArgs) []error {
7071

7172
// In case of dry run, simply print the groups and projects that would be created or updated
7273
if gitlabMirrorArgs.DryRun {
73-
DryRun(sourceGitlabInstance, gitlabMirrorArgs)
74+
destinationGitlabInstance.DryRun(sourceGitlabInstance, gitlabMirrorArgs.MirrorMapping)
7475
return nil
7576
}
7677

@@ -128,28 +129,55 @@ func processFilters(filters *utils.MirrorMapping) (map[string]struct{}, map[stri
128129
}
129130

130131
// DryRun prints the groups and projects that would be created or updated in dry run mode.
131-
func DryRun(sourceGitlabInstance *GitlabInstance, gitlabMirrorArgs *utils.ParserArgs) {
132+
func (destinationGitlabInstance *GitlabInstance) DryRun(sourceGitlabInstance *GitlabInstance, mirrorMapping *utils.MirrorMapping) []error {
132133
zap.L().Info("Dry run mode enabled, will not create groups or projects")
133134
zap.L().Info("Groups that will be created (or updated if they already exist):")
134-
for sourceGroupPath, copyOptions := range gitlabMirrorArgs.MirrorMapping.Groups {
135+
for sourceGroupPath, copyOptions := range mirrorMapping.Groups {
135136
if sourceGroup, ok := sourceGitlabInstance.Groups[sourceGroupPath]; ok {
136137
fmt.Printf(" - %s (source gitlab) -> %s (destination gitlab)\n", sourceGroup.WebURL, copyOptions.DestinationPath)
137138
}
138139
}
139140
zap.L().Info("Projects that will be created (or updated if they already exist):")
140-
for sourceProjectPath, copyOptions := range gitlabMirrorArgs.MirrorMapping.Projects {
141+
for sourceProjectPath, copyOptions := range mirrorMapping.Projects {
141142
if sourceProject, ok := sourceGitlabInstance.Projects[sourceProjectPath]; ok {
142143
fmt.Printf(" - %s (source gitlab) -> %s (destination gitlab)\n", sourceProject.WebURL, copyOptions.DestinationPath)
144+
145+
if copyOptions.MirrorReleases {
146+
if err := destinationGitlabInstance.DryRunReleases(sourceGitlabInstance, sourceProject, copyOptions); err != nil {
147+
zap.L().Error("Failed to dry run releases", zap.Error(err))
148+
return []error{err}
149+
}
150+
}
143151
}
152+
153+
}
154+
zap.L().Info("Dry run completed")
155+
return nil
156+
}
157+
158+
// DryRunReleases prints the releases that would be created in dry run mode.
159+
// It fetches the releases from the source project and prints them.
160+
// It does not create any releases in the destination project.
161+
func (destinationGitlabInstance *GitlabInstance) DryRunReleases(sourceGitlabInstance *GitlabInstance, sourceProject *gitlab.Project, copyOptions *utils.MirroringOptions) error {
162+
// Fetch releases from the source project
163+
sourceReleases, _, err := sourceGitlabInstance.Gitlab.Releases.ListReleases(sourceProject.ID, &gitlab.ListReleasesOptions{})
164+
if err != nil {
165+
return fmt.Errorf("failed to fetch releases for source project %s: %s", sourceProject.HTTPURLToRepo, err)
144166
}
167+
// Print the releases that will be created in the destination project
168+
for _, release := range sourceReleases {
169+
fmt.Printf(" - Release %s will be created in %s (if it does not already exist)\n", release.Name, destinationGitlabInstance.Gitlab.BaseURL().String()+copyOptions.DestinationPath)
170+
}
171+
return nil
145172
}
146173

147-
func (destinationGitlab *GitlabInstance) CheckDestinationInstance() error {
174+
// CheckDestinationInstance checks the destination GitLab instance for version and license compatibility.
175+
func (g *GitlabInstance) CheckDestinationInstance() error {
148176
zap.L().Info("Checking destination GitLab instance")
149-
if err := destinationGitlab.CheckVersion(); err != nil {
177+
if err := g.CheckVersion(); err != nil {
150178
return fmt.Errorf("destination GitLab instance version check failed: %w", err)
151179
}
152-
if err := destinationGitlab.CheckVersion(); err != nil {
180+
if err := g.CheckLicense(); err != nil {
153181
return fmt.Errorf("destination GitLab instance version check failed: %w", err)
154182
}
155183
return nil

internal/mirroring/main_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package mirroring
22

33
import (
4+
"net/http"
45
"reflect"
56
"testing"
67

@@ -130,3 +131,116 @@ func TestProcessFilters(t *testing.T) {
130131
})
131132
}
132133
}
134+
135+
func TestDryRun(t *testing.T) {
136+
tests := []struct {
137+
name string
138+
sourceSize string
139+
}{
140+
{
141+
name: "Dry Run Source Small",
142+
sourceSize: INSTANCE_SIZE_SMALL,
143+
},
144+
{
145+
name: "Dry Run Source Big",
146+
sourceSize: INSTANCE_SIZE_BIG,
147+
},
148+
}
149+
for _, tt := range tests {
150+
t.Run(tt.name, func(t *testing.T) {
151+
t.Parallel()
152+
_, sourceGitlabInstance := setupTestServer(t, ROLE_SOURCE, tt.sourceSize)
153+
_, destinationGitlabInstance := setupTestServer(t, ROLE_DESTINATION, INSTANCE_SIZE_SMALL)
154+
gitlabMirrorArgs := &utils.MirrorMapping{
155+
Projects: map[string]*utils.MirroringOptions{
156+
TEST_PROJECT.PathWithNamespace: {
157+
DestinationPath: TEST_PROJECT.PathWithNamespace,
158+
MirrorReleases: true,
159+
},
160+
},
161+
Groups: map[string]*utils.MirroringOptions{
162+
TEST_GROUP_2.FullPath: {
163+
DestinationPath: TEST_GROUP_2.FullPath,
164+
MirrorReleases: true,
165+
},
166+
},
167+
}
168+
sourceGitlabInstance.addProject(TEST_PROJECT)
169+
sourceGitlabInstance.addGroup(TEST_GROUP_2)
170+
sourceGitlabInstance.addGroup(TEST_GROUP_2)
171+
172+
destinationGitlabInstance.DryRun(sourceGitlabInstance, gitlabMirrorArgs)
173+
})
174+
}
175+
}
176+
177+
func TestCheckDestinationInstance(t *testing.T) {
178+
tests := []struct {
179+
name string
180+
licensePlan string
181+
version string
182+
expectedError bool
183+
}{
184+
{
185+
name: "Premium license, good version",
186+
licensePlan: PREMIUM_PLAN,
187+
version: "18.0.0",
188+
expectedError: false,
189+
},
190+
{
191+
name: "Ultimate license, good version",
192+
licensePlan: ULTIMATE_PLAN,
193+
version: "18.0.0",
194+
expectedError: false,
195+
},
196+
{
197+
name: "Free license, good version",
198+
licensePlan: "free",
199+
version: "18.0.0",
200+
expectedError: true,
201+
},
202+
{
203+
name: "Premium license, bad version",
204+
licensePlan: PREMIUM_PLAN,
205+
version: "17.0.0",
206+
expectedError: true,
207+
},
208+
{
209+
name: "Ultimate license, bad version",
210+
licensePlan: ULTIMATE_PLAN,
211+
version: "17.0.0",
212+
expectedError: true,
213+
},
214+
{
215+
name: "Bad license, good version",
216+
licensePlan: "bad_license",
217+
version: "18.0.0",
218+
expectedError: true,
219+
},
220+
{
221+
name: "Bad license, bad version",
222+
licensePlan: "bad_license",
223+
version: "17.0.0",
224+
expectedError: true,
225+
},
226+
}
227+
for _, tt := range tests {
228+
t.Run(tt.name, func(t *testing.T) {
229+
t.Parallel()
230+
mux, gitlabInstance := setupEmptyTestServer(t, ROLE_DESTINATION, INSTANCE_SIZE_SMALL)
231+
mux.HandleFunc("/api/v4/license", func(w http.ResponseWriter, r *http.Request) {
232+
w.WriteHeader(200)
233+
w.Write([]byte(`{"plan": "` + tt.licensePlan + `", "expired": false}`))
234+
})
235+
mux.HandleFunc("/api/v4/metadata", func(w http.ResponseWriter, r *http.Request) {
236+
w.WriteHeader(200)
237+
w.Write([]byte(`{"version": "` + tt.version + `"}`))
238+
})
239+
240+
err := gitlabInstance.CheckDestinationInstance()
241+
if (err != nil) != tt.expectedError {
242+
t.Errorf("CheckDestinationInstance() error = %v, expectedError %v", err, tt.expectedError)
243+
}
244+
})
245+
}
246+
}

0 commit comments

Comments
 (0)