Skip to content

Commit 88a9997

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 88a9997

File tree

16 files changed

+164
-442
lines changed

16 files changed

+164
-442
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: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ package main
22

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

11-
"gitlab-sync/mirroring"
12-
"gitlab-sync/utils"
10+
"gitlab-sync/internal/mirroring"
11+
"gitlab-sync/internal/utils"
1312

1413
"github.com/spf13/cobra"
14+
"go.uber.org/zap"
15+
"go.uber.org/zap/zapcore"
1516
)
1617

1718
var (
@@ -29,34 +30,27 @@ func main() {
2930
Short: "Copy and enable mirroring of gitlab projects and groups",
3031
Long: "Fully customizable gitlab repositories and groups mirroring between two (or one) gitlab instances.",
3132
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)
33+
// Set up the logger
34+
setupZapLogger(args.Verbose)
35+
zap.L().Debug("Verbose mode enabled")
36+
zap.L().Debug("Parsing command line arguments")
3937

4038
// Obtain the retry count
4139
if args.Retry == -1 {
4240
args.Retry = 10000
4341
} else if args.Retry == 0 {
44-
log.Fatal("retry count must be -1 (no limit) or strictly greater than 0")
42+
zap.L().Fatal("retry count must be -1 (no limit) or strictly greater than 0")
4543
}
4644

4745
// Set the timeout for GitLab API requests
4846
if timeout == -1 {
4947
args.Timeout = time.Duration(10000 * time.Second)
5048
} else if timeout == 0 {
51-
log.Fatal("timeout must be -1 (no limit) or strictly greater than 0")
49+
zap.L().Fatal("timeout must be -1 (no limit) or strictly greater than 0")
5250
} else {
5351
args.Timeout = time.Duration(timeout) * time.Second
5452
}
5553

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

@@ -68,22 +62,21 @@ func main() {
6862

6963
// Check if the Mirror Mapping file path is provided
7064
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))
65+
zap.L().Debug("Mirror Mapping file resolved path: " + filepath.Clean(mirrorMappingPath))
7266

73-
utils.LogVerbose("Parsing mirror mapping file")
67+
zap.L().Debug("Parsing mirror mapping file")
7468
mapping, err := utils.OpenMirrorMapping(mirrorMappingPath)
7569
if err != nil {
76-
log.Fatalf("Error opening mirror mapping file: %s", err)
70+
zap.L().Sugar().Fatalf("Error opening mirror mapping file: %s", err)
7771
}
78-
utils.LogVerbose("Mirror mapping file parsed successfully")
72+
zap.L().Debug("Mirror mapping file parsed successfully")
7973
args.MirrorMapping = mapping
8074

8175
err = mirroring.MirrorGitlabs(&args)
8276
if err != nil {
83-
fmt.Println("Error during mirroring process:")
84-
fmt.Println(err)
77+
zap.L().Error("Error during mirroring process: " + err.Error())
8578
}
86-
log.Println("Mirroring completed")
79+
zap.L().Info("Mirroring completed")
8780
},
8881
}
8982

@@ -96,11 +89,10 @@ func main() {
9689
rootCmd.Flags().StringVar(&mirrorMappingPath, "mirror-mapping", os.Getenv("MIRROR_MAPPING"), "Path to the mirror mapping file")
9790
rootCmd.Flags().BoolVar(&args.DryRun, "dry-run", false, "Perform a dry run without making any changes")
9891
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")
10092
rootCmd.Flags().IntVarP(&args.Retry, "retry", "r", 3, "Number of retries for failed requests")
10193

10294
if err := rootCmd.Execute(); err != nil {
103-
fmt.Println(err)
95+
zap.L().Error(err.Error())
10496
os.Exit(1)
10597
}
10698
}
@@ -118,16 +110,37 @@ func promptForMandatoryInput(defaultValue string, prompt string, errorMsg string
118110
if !promptsDisabled {
119111
input = strings.TrimSpace(promptForInput(prompt))
120112
if input == "" {
121-
log.Fatal(errorMsg)
113+
zap.L().Fatal(errorMsg)
122114
}
123115
if !hideOutput {
124-
utils.LogVerbose(loggerMsg + ": " + input)
116+
zap.L().Debug(loggerMsg + ": " + input)
125117
} else {
126-
utils.LogVerbose(loggerMsg)
118+
zap.L().Debug(loggerMsg)
127119
}
128120
} else {
129-
log.Fatalf("Prompting is disabled, %s", errorMsg)
121+
zap.L().Sugar().Fatal("Prompting is disabled, %s", errorMsg)
130122
}
131123
}
132124
return input
133125
}
126+
127+
func setupZapLogger(verbose bool) {
128+
// Set up the logger configuration
129+
config := zap.NewProductionConfig()
130+
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
131+
config.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
132+
if verbose {
133+
config.Level.SetLevel(zapcore.DebugLevel)
134+
} else {
135+
config.Level.SetLevel(zapcore.InfoLevel)
136+
}
137+
138+
// Create the logger
139+
logger, err := config.Build()
140+
if err != nil {
141+
zap.L().Fatal("Failed to create logger: " + err.Error())
142+
}
143+
144+
// Set the global logger
145+
zap.ReplaceGlobals(logger)
146+
}

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: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ package mirroring
22

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

9-
"gitlab-sync/utils"
8+
"gitlab-sync/internal/utils"
9+
10+
"go.uber.org/zap"
1011
)
1112

1213
func MirrorGitlabs(gitlabMirrorArgs *utils.ParserArgs) error {
@@ -43,14 +44,14 @@ func MirrorGitlabs(gitlabMirrorArgs *utils.ParserArgs) error {
4344

4445
// In case of dry run, simply print the groups and projects that would be created or updated
4546
if gitlabMirrorArgs.DryRun {
46-
log.Println("Dry run mode enabled, will not create groups or projects")
47-
fmt.Println("Groups that will be created (or updated if they already exist):")
47+
zap.L().Info("Dry run mode enabled, will not create groups or projects")
48+
zap.L().Info("Groups that will be created (or updated if they already exist):")
4849
for sourceGroupPath, copyOptions := range gitlabMirrorArgs.MirrorMapping.Groups {
4950
if sourceGitlabInstance.Groups[sourceGroupPath] != nil {
5051
fmt.Printf(" - %s (source gitlab) -> %s (destination gitlab)\n", sourceGroupPath, copyOptions.DestinationPath)
5152
}
5253
}
53-
fmt.Println("Projects that will be created (or updated if they already exist):")
54+
zap.L().Info("Projects that will be created (or updated if they already exist):")
5455
for sourceProjectPath, copyOptions := range gitlabMirrorArgs.MirrorMapping.Projects {
5556
if sourceGitlabInstance.Projects[sourceProjectPath] != nil {
5657
fmt.Printf(" - %s (source gitlab) -> %s (destination gitlab)\n", sourceProjectPath, copyOptions.DestinationPath)
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)