Skip to content

Commit ef037f1

Browse files
committed
refactor: use existing modules
get rid of custom logic modules for logger and concurrency management. Instead use zap for logging and let go manage concurrency limit on its own. Move files in "internal" because project is expanding Update go version
1 parent fbf34c3 commit ef037f1

File tree

16 files changed

+155
-431
lines changed

16 files changed

+155
-431
lines changed

.github/workflows/go.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
- uses: actions/checkout@v4
1616
- uses: actions/setup-go@v5
1717
with:
18-
go-version: '1.23.7'
18+
go-version: '1.24.2'
1919
- run: go mod tidy
2020
- name: golangci-lint
2121
uses: golangci/golangci-lint-action@v7
@@ -32,7 +32,7 @@ jobs:
3232
- uses: actions/checkout@v4
3333
- uses: actions/setup-go@v5
3434
with:
35-
go-version: '1.23.7'
35+
go-version: '1.24.2'
3636
- run: go mod tidy
3737
- name: Run Gosec Security Scanner
3838
uses: securego/gosec@master
@@ -47,7 +47,7 @@ jobs:
4747
- uses: actions/checkout@v4
4848
- uses: actions/setup-go@v5
4949
with:
50-
go-version: '1.23.7'
50+
go-version: '1.24.2'
5151
- run: go mod tidy
5252
- name: go-test
5353
run: |

cmd/main.go

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import (
88
"strings"
99
"time"
1010

11-
"gitlab-sync/mirroring"
12-
"gitlab-sync/utils"
11+
"gitlab-sync/internal/mirroring"
12+
"gitlab-sync/internal/utils"
1313

1414
"github.com/spf13/cobra"
15+
"go.uber.org/zap"
16+
"go.uber.org/zap/zapcore"
1517
)
1618

1719
var (
@@ -29,34 +31,27 @@ func main() {
2931
Short: "Copy and enable mirroring of gitlab projects and groups",
3032
Long: "Fully customizable gitlab repositories and groups mirroring between two (or one) gitlab instances.",
3133
Run: func(cmd *cobra.Command, cmdArgs []string) {
32-
// Set the concurrency limit
33-
if args.Concurrency == -1 {
34-
args.Concurrency = 10000
35-
} else if args.Concurrency == 0 {
36-
log.Fatal("concurrency limit must be -1 (no limit) or strictly greater than 0")
37-
}
38-
utils.ConcurrencyManager.SetLimit(args.Concurrency)
34+
// Set up the logger
35+
setupZapLogger(args.Verbose)
36+
zap.L().Debug("Verbose mode enabled")
37+
zap.L().Debug("Parsing command line arguments")
3938

4039
// Obtain the retry count
4140
if args.Retry == -1 {
4241
args.Retry = 10000
4342
} else if args.Retry == 0 {
44-
log.Fatal("retry count must be -1 (no limit) or strictly greater than 0")
43+
zap.L().Fatal("retry count must be -1 (no limit) or strictly greater than 0")
4544
}
4645

4746
// Set the timeout for GitLab API requests
4847
if timeout == -1 {
4948
args.Timeout = time.Duration(10000 * time.Second)
5049
} else if timeout == 0 {
51-
log.Fatal("timeout must be -1 (no limit) or strictly greater than 0")
50+
zap.L().Fatal("timeout must be -1 (no limit) or strictly greater than 0")
5251
} else {
5352
args.Timeout = time.Duration(timeout) * time.Second
5453
}
5554

56-
utils.SetVerbose(args.Verbose)
57-
utils.LogVerbose("Verbose mode enabled")
58-
utils.LogVerbose("Parsing command line arguments")
59-
6055
// Check if the source GitLab URL is provided
6156
args.SourceGitlabURL = promptForMandatoryInput(args.SourceGitlabURL, "Input Source GitLab URL (MANDATORY)", "Source GitLab URL is mandatory", "Source GitLab URL", args.NoPrompt, false)
6257

@@ -68,14 +63,14 @@ func main() {
6863

6964
// Check if the Mirror Mapping file path is provided
7065
mirrorMappingPath = promptForMandatoryInput(mirrorMappingPath, "Input Mirror Mapping file path (MANDATORY)", "Mirror Mapping file path is mandatory", "Mirror Mapping file path set", args.NoPrompt, false)
71-
utils.LogVerbose("Mirror Mapping file resolved path: " + filepath.Clean(mirrorMappingPath))
66+
zap.L().Debug("Mirror Mapping file resolved path: " + filepath.Clean(mirrorMappingPath))
7267

73-
utils.LogVerbose("Parsing mirror mapping file")
68+
zap.L().Debug("Parsing mirror mapping file")
7469
mapping, err := utils.OpenMirrorMapping(mirrorMappingPath)
7570
if err != nil {
76-
log.Fatalf("Error opening mirror mapping file: %s", err)
71+
zap.L().Sugar().Fatalf("Error opening mirror mapping file: %s", err)
7772
}
78-
utils.LogVerbose("Mirror mapping file parsed successfully")
73+
zap.L().Debug("Mirror mapping file parsed successfully")
7974
args.MirrorMapping = mapping
8075

8176
err = mirroring.MirrorGitlabs(&args)
@@ -96,7 +91,6 @@ func main() {
9691
rootCmd.Flags().StringVar(&mirrorMappingPath, "mirror-mapping", os.Getenv("MIRROR_MAPPING"), "Path to the mirror mapping file")
9792
rootCmd.Flags().BoolVar(&args.DryRun, "dry-run", false, "Perform a dry run without making any changes")
9893
rootCmd.Flags().IntVarP(&timeout, "timeout", "t", 30, "Timeout in seconds for GitLab API requests")
99-
rootCmd.Flags().IntVarP(&args.Concurrency, "concurrency", "c", 10, "Max number of concurrent requests")
10094
rootCmd.Flags().IntVarP(&args.Retry, "retry", "r", 3, "Number of retries for failed requests")
10195

10296
if err := rootCmd.Execute(); err != nil {
@@ -118,16 +112,37 @@ func promptForMandatoryInput(defaultValue string, prompt string, errorMsg string
118112
if !promptsDisabled {
119113
input = strings.TrimSpace(promptForInput(prompt))
120114
if input == "" {
121-
log.Fatal(errorMsg)
115+
zap.L().Fatal(errorMsg)
122116
}
123117
if !hideOutput {
124-
utils.LogVerbose(loggerMsg + ": " + input)
118+
zap.L().Debug(loggerMsg + ": " + input)
125119
} else {
126-
utils.LogVerbose(loggerMsg)
120+
zap.L().Debug(loggerMsg)
127121
}
128122
} else {
129-
log.Fatalf("Prompting is disabled, %s", errorMsg)
123+
zap.L().Sugar().Fatal("Prompting is disabled, %s", errorMsg)
130124
}
131125
}
132126
return input
133127
}
128+
129+
func setupZapLogger(verbose bool) {
130+
// Set up the logger configuration
131+
config := zap.NewProductionConfig()
132+
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
133+
config.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
134+
if verbose {
135+
config.Level.SetLevel(zapcore.DebugLevel)
136+
} else {
137+
config.Level.SetLevel(zapcore.InfoLevel)
138+
}
139+
140+
// Create the logger
141+
logger, err := config.Build()
142+
if err != nil {
143+
zap.L().Fatal("Failed to create logger: " + err.Error())
144+
}
145+
146+
// Set the global logger
147+
zap.ReplaceGlobals(logger)
148+
}

go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module gitlab-sync
22

3-
go 1.23.7
3+
go 1.24.2
44

55
require (
66
github.com/spf13/cobra v1.9.1
@@ -13,6 +13,8 @@ require (
1313
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
1414
github.com/inconshreveable/mousetrap v1.1.0 // indirect
1515
github.com/spf13/pflag v1.0.6 // indirect
16+
go.uber.org/multierr v1.11.0 // indirect
17+
go.uber.org/zap v1.27.0 // indirect
1618
golang.org/x/oauth2 v0.25.0 // indirect
1719
golang.org/x/time v0.10.0 // indirect
1820
)
Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,38 @@ package mirroring
22

33
import (
44
"fmt"
5-
"log"
65
"path/filepath"
76
"strings"
87
"sync"
98

10-
"gitlab-sync/utils"
9+
"gitlab-sync/internal/utils"
1110

1211
gitlab "gitlab.com/gitlab-org/api/client-go"
12+
"go.uber.org/zap"
1313
)
1414

1515
func (g *GitlabInstance) fetchProjects(projectFilters *map[string]bool, groupFilters *map[string]bool, mirrorMapping *utils.MirrorMapping, isSource bool) error {
1616
sourceString := "source"
1717
if !isSource {
1818
sourceString = "destination"
1919
}
20-
utils.LogVerbosef("Fetching all projects from %s GitLab instance", sourceString)
20+
zap.L().Sugar().Debugf("Fetching all projects from %s GitLab instance", sourceString)
2121
projects, _, err := g.Gitlab.Projects.ListProjects(nil)
2222
if err != nil {
2323
return err
2424
}
2525

26-
utils.LogVerbosef("Processing %d projects from %s GitLab instance", len(projects), sourceString)
26+
zap.L().Sugar().Debugf("Processing %d projects from %s GitLab instance", len(projects), sourceString)
2727

2828
// Create a wait group to wait for all goroutines to finish
2929
var wg sync.WaitGroup
3030

31-
// Create a channel to limit the number of concurrent goroutines
32-
concurrencyLimit := 10
33-
sem := make(chan struct{}, concurrencyLimit)
34-
3531
for _, project := range projects {
3632
wg.Add(1)
3733
// Acquire a token from the semaphore
38-
sem <- struct{}{}
3934

4035
go func(project *gitlab.Project) {
4136
defer wg.Done()
42-
// Release the token back to the semaphore
43-
defer func() { <-sem }()
4437

4538
// Check if the project matches the filters:
4639
// - either is in the projects map
@@ -57,13 +50,13 @@ func (g *GitlabInstance) fetchProjects(projectFilters *map[string]bool, groupFil
5750
// Retrieve the corresponding group creation options from the mirror mapping
5851
groupCreationOptions, ok := mirrorMapping.Groups[group]
5952
if !ok {
60-
log.Fatalf("Group %s not found in mirror mapping (internal error, please review script)", group)
53+
zap.L().Sugar().Errorf("Group %s not found in mirror mapping (internal error, please review script)", group)
6154
}
6255

6356
// Calculate the relative path between the project and the group
6457
relativePath, err := filepath.Rel(group, project.PathWithNamespace)
6558
if err != nil {
66-
log.Fatalf("Failed to calculate relative path for project %s: %s", project.PathWithNamespace, err)
59+
zap.L().Sugar().Errorf("Failed to calculate relative path for project %s: %s", project.PathWithNamespace, err)
6760
}
6861

6962
// Add the project to the mirror mapping
@@ -85,7 +78,7 @@ func (g *GitlabInstance) fetchProjects(projectFilters *map[string]bool, groupFil
8578

8679
wg.Wait()
8780

88-
utils.LogVerbosef("Found %d projects to mirror in the %s GitLab instance", len(g.Projects), sourceString)
81+
zap.L().Sugar().Debugf("Found %d projects to mirror in the %s GitLab instance", len(g.Projects), sourceString)
8982

9083
return nil
9184
}
@@ -95,30 +88,22 @@ func (g *GitlabInstance) fetchGroups(groupFilters *map[string]bool, mirrorMappin
9588
if !isSource {
9689
sourceString = "destination"
9790
}
98-
utils.LogVerbosef("Fetching all groups from %s GitLab instance", sourceString)
91+
zap.L().Sugar().Debugf("Fetching all groups from %s GitLab instance", sourceString)
9992
groups, _, err := g.Gitlab.Groups.ListGroups(nil)
10093
if err != nil {
10194
return err
10295
}
10396

104-
utils.LogVerbosef("Processing %d groups from %s GitLab instance", len(groups), sourceString)
97+
zap.L().Sugar().Debugf("Processing %d groups from %s GitLab instance", len(groups), sourceString)
10598

10699
// Create a wait group to wait for all goroutines to finish
107100
var wg sync.WaitGroup
108101

109-
// Create a channel to limit the number of concurrent goroutines
110-
concurrencyLimit := 10
111-
sem := make(chan struct{}, concurrencyLimit)
112-
113102
for _, group := range groups {
114103
wg.Add(1)
115-
// Acquire a token from the semaphore
116-
sem <- struct{}{}
117104

118105
go func(group *gitlab.Group) {
119106
defer wg.Done()
120-
// Release the token back to the semaphore
121-
defer func() { <-sem }()
122107

123108
// Check if the group matches the filters:
124109
// - either is in the groups map
@@ -136,13 +121,13 @@ func (g *GitlabInstance) fetchGroups(groupFilters *map[string]bool, mirrorMappin
136121
// Retrieve the corresponding group creation options from the mirror mapping
137122
groupCreationOptions, ok := mirrorMapping.Groups[groupPath]
138123
if !ok {
139-
log.Fatalf("Group %s not found in mirror mapping (internal error, please review script)", groupPath)
124+
zap.L().Sugar().Fatalf("Group %s not found in mirror mapping (internal error, please review script)", groupPath)
140125
}
141126

142127
// Calculate the relative path between the group and the groupPath
143128
relativePath, err := filepath.Rel(groupPath, group.FullPath)
144129
if err != nil {
145-
log.Fatalf("Failed to calculate relative path for group %s: %s", group.FullPath, err)
130+
zap.L().Sugar().Fatalf("Failed to calculate relative path for group %s: %s", group.FullPath, err)
146131
}
147132

148133
// Add the group to the mirror mapping
@@ -164,7 +149,7 @@ func (g *GitlabInstance) fetchGroups(groupFilters *map[string]bool, mirrorMappin
164149

165150
wg.Wait()
166151

167-
utils.LogVerbosef("Found %d matching groups in %s GitLab instance", len(g.Groups), sourceString)
152+
zap.L().Sugar().Debugf("Found %d matching groups in %s GitLab instance", len(g.Groups), sourceString)
168153

169154
return nil
170155
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"sync"
66
"time"
77

8-
"gitlab-sync/utils"
8+
"gitlab-sync/internal/utils"
99

1010
gitlab "gitlab.com/gitlab-org/api/client-go"
1111
)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"path/filepath"
77
"sync"
88

9-
"gitlab-sync/utils"
9+
"gitlab-sync/internal/utils"
1010
)
1111

1212
func MirrorGitlabs(gitlabMirrorArgs *utils.ParserArgs) error {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"reflect"
55
"testing"
66

7-
"gitlab-sync/utils"
7+
"gitlab-sync/internal/utils"
88
)
99

1010
func TestProcessFilters(t *testing.T) {

0 commit comments

Comments
 (0)