From cdb42b647efdad8a922c835b850eea2de61676f0 Mon Sep 17 00:00:00 2001 From: BoxBoxJason Date: Thu, 20 Nov 2025 00:11:57 +0100 Subject: [PATCH 1/3] fix: retrieve user if token provided --- internal/mirroring/instance.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/internal/mirroring/instance.go b/internal/mirroring/instance.go index f4b7efe..04820da 100644 --- a/internal/mirroring/instance.go +++ b/internal/mirroring/instance.go @@ -72,12 +72,6 @@ func NewGitlabInstance(initArgs *GitlabInstanceOpts) (*GitlabInstance, error) { return nil, err } - // Get the current user ID - user, _, err := gitlabClient.Users.CurrentUser() - if err != nil { - return nil, fmt.Errorf("failed to get current user: %w", err) - } - gitlabInstance := &GitlabInstance{ Gitlab: gitlabClient, Projects: make(map[string]*gitlab.Project), @@ -85,7 +79,15 @@ func NewGitlabInstance(initArgs *GitlabInstanceOpts) (*GitlabInstance, error) { Role: initArgs.Role, InstanceSize: initArgs.InstanceSize, GitAuth: helpers.BuildHTTPAuth("", initArgs.GitlabToken), - UserID: user.ID, + } + + if initArgs.GitlabToken != "" { + // Get the current user ID + user, _, err := gitlabClient.Users.CurrentUser() + if err != nil { + return nil, fmt.Errorf("failed to get current user: %w", err) + } + gitlabInstance.UserID = user.ID } return gitlabInstance, nil From d52c89a9d8c9ca932b1d163d674ad3b6b18dc7d9 Mon Sep 17 00:00:00 2001 From: BoxBoxJason Date: Wed, 19 Nov 2025 23:39:01 +0100 Subject: [PATCH 2/3] feat: proper error codes management Closes #14 --- .gitignore | 5 +- cmd/main.go | 19 ++++- cmd/main_test.go | 2 - go.mod | 20 ++--- go.sum | 143 ++++++++++++++++++++++++++++++++ internal/mirroring/main.go | 9 +- internal/mirroring/main_test.go | 23 +++++ pkg/helpers/errtypes.go | 52 ++++++++++++ pkg/helpers/errtypes_test.go | 84 +++++++++++++++++++ 9 files changed, 336 insertions(+), 21 deletions(-) create mode 100644 go.sum create mode 100644 pkg/helpers/errtypes.go create mode 100644 pkg/helpers/errtypes_test.go diff --git a/.gitignore b/.gitignore index 3c2c614..0fbf9d7 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,6 @@ Desktop.ini *.pub mirroring.json -go.sum republisher.sh # Reports files @@ -65,4 +64,6 @@ coverage.out .scannerwork/ *-report* .sonarlint/ -dependency-check/ \ No newline at end of file +dependency-check/ + +.copilot/ diff --git a/cmd/main.go b/cmd/main.go index d656eaa..2a16e2f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -8,6 +8,7 @@ import ( "gitlab-sync/internal/mirroring" "gitlab-sync/internal/utils" + "gitlab-sync/pkg/helpers" "github.com/spf13/cobra" "go.uber.org/zap" @@ -65,9 +66,23 @@ func main() { mirroringErrors := mirroring.MirrorGitlabs(&args) if mirroringErrors != nil { - zap.L().Error("Error during mirroring process", zap.Errors("errors", mirroringErrors)) + // Determine exit code based on error severity + hasBlocking := false + for _, err := range mirroringErrors { + if helpers.SeverityOf(err) == helpers.SeverityBlocking { + hasBlocking = true + break + } + } + if hasBlocking { + zap.L().Error("Blocking errors occurred during mirroring process", zap.Errors("errors", mirroringErrors)) + os.Exit(1) + } else { + zap.L().Warn("Non-blocking errors occurred during mirroring process", zap.Errors("errors", mirroringErrors)) + os.Exit(2) + } } - zap.L().Info("Mirroring completed") + zap.L().Info("Mirroring completed successfully") }, } diff --git a/cmd/main_test.go b/cmd/main_test.go index 461747e..f055fd4 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -249,7 +249,6 @@ func TestPromptForMandatoryInputEmptyPrompt(t *testing.T) { if err == nil { t.Fatalf("Expected subprocess to exit with fatal error, but it succeeded, output: %q", output) } - // Optionally, check output contains INPUT_REQUIRED message or similar, if desired. } // TestPromptForMandatoryInputPromptDisabled tests that if prompting is disabled and no defaultValue is provided, @@ -267,5 +266,4 @@ func TestPromptForMandatoryInputPromptDisabled(t *testing.T) { if err == nil { t.Fatalf("Expected subprocess to fail due to prompting disabled, but it succeeded, output: %q", output) } - // Optionally, further validate that output contains "Prompting is disabled" if needed. } diff --git a/go.mod b/go.mod index 927d602..202d950 100644 --- a/go.mod +++ b/go.mod @@ -7,8 +7,8 @@ require ( github.com/go-git/go-git/v5 v5.16.3 github.com/hashicorp/go-retryablehttp v0.7.8 github.com/spf13/cobra v1.10.1 - gitlab.com/gitlab-org/api/client-go v0.157.1 - go.uber.org/zap v1.27.0 + gitlab.com/gitlab-org/api/client-go v0.160.1 + go.uber.org/zap v1.27.1 ) require ( @@ -16,7 +16,7 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.3.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect - github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect @@ -29,15 +29,15 @@ require ( github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/pjbgf/sha1cd v0.5.0 // indirect github.com/sergi/go-diff v1.4.0 // indirect - github.com/skeema/knownhosts v1.3.1 // indirect + github.com/skeema/knownhosts v1.3.2 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.42.0 // indirect - golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect - golang.org/x/net v0.44.0 // indirect - golang.org/x/oauth2 v0.31.0 // indirect - golang.org/x/sys v0.36.0 // indirect - golang.org/x/time v0.13.0 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/oauth2 v0.33.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/time v0.14.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c044e72 --- /dev/null +++ b/go.sum @@ -0,0 +1,143 @@ +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= +github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= +github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= +github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8= +github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= +github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ= +github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= +github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg= +github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +gitlab.com/gitlab-org/api/client-go v0.160.1 h1:7kEgo1yQ3ZMRps/2JbXzqbRb4Rs8n2ECkAv+6MadJw8= +gitlab.com/gitlab-org/api/client-go v0.160.1/go.mod h1:YqKcnxyV9OPAL5U99mpwBVEgBPz1PK/3qwqq/3h6bao= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0= +golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= +golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/mirroring/main.go b/internal/mirroring/main.go index 7805705..e79d847 100644 --- a/internal/mirroring/main.go +++ b/internal/mirroring/main.go @@ -33,7 +33,7 @@ func MirrorGitlabs(gitlabMirrorArgs *utils.ParserArgs) []error { InstanceSize: sourceGitlabSize, }) if err != nil { - return []error{err} + return []error{helpers.NewBlocking(err)} } // Create destination GitLab instance @@ -49,14 +49,13 @@ func MirrorGitlabs(gitlabMirrorArgs *utils.ParserArgs) []error { InstanceSize: destinationGitlabSize, }) if err != nil { - return []error{err} + return []error{helpers.NewBlocking(err)} } pullMirrorAvailable, err := destinationGitlabInstance.IsPullMirrorAvailable(gitlabMirrorArgs.ForcePremium, gitlabMirrorArgs.ForceNonPremium) if err != nil { // Could not obtain a result from the destination GitLab instance, so we cannot proceed with the mirroring process. - // TODO: have a non zero exit code in this case - return []error{err} + return []error{helpers.NewBlocking(err)} } else if pullMirrorAvailable { // Proceed with the pull mirroring process zap.L().Info("GitLab instance is compatible with the pull mirroring process", zap.String(ROLE, destinationGitlabInstance.Role), zap.String(INSTANCE_SIZE, destinationGitlabInstance.InstanceSize)) @@ -161,7 +160,7 @@ func (destinationGitlabInstance *GitlabInstance) DryRun(sourceGitlabInstance *Gi if copyOptions.MirrorReleases { if err := destinationGitlabInstance.DryRunReleases(sourceGitlabInstance, sourceProject, copyOptions); err != nil { zap.L().Error("Failed to dry run releases", zap.Error(err)) - return []error{err} + return []error{helpers.NewNonBlocking(err)} } } } diff --git a/internal/mirroring/main_test.go b/internal/mirroring/main_test.go index 00a3d99..914fea9 100644 --- a/internal/mirroring/main_test.go +++ b/internal/mirroring/main_test.go @@ -6,6 +6,7 @@ import ( "testing" "gitlab-sync/internal/utils" + "gitlab-sync/pkg/helpers" ) func TestProcessFilters(t *testing.T) { @@ -267,3 +268,25 @@ func TestIsPullMirrorAvailable(t *testing.T) { }) } } + +func TestMirrorGitlabsErrorWrapping(t *testing.T) { + // Test that blocking errors are wrapped correctly + args := &utils.ParserArgs{ + SourceGitlabURL: "invalid-url", + SourceGitlabToken: "token", + DestinationGitlabURL: "https://example.com", + DestinationGitlabToken: "token", + MirrorMapping: &utils.MirrorMapping{}, + Retry: 1, + } + + errors := MirrorGitlabs(args) + if len(errors) == 0 { + t.Fatal("Expected errors, got none") + } + + // Check that the first error is blocking + if severity := helpers.SeverityOf(errors[0]); severity != helpers.SeverityBlocking { + t.Errorf("Expected blocking error, got %v", severity) + } +} diff --git a/pkg/helpers/errtypes.go b/pkg/helpers/errtypes.go new file mode 100644 index 0000000..357db40 --- /dev/null +++ b/pkg/helpers/errtypes.go @@ -0,0 +1,52 @@ +package helpers + +import "errors" + +// Severity represents the severity level of an error. +type Severity int + +const ( + // SeverityNone indicates no error. + SeverityNone Severity = iota + // SeverityNonBlocking indicates a non-blocking error. + SeverityNonBlocking + // SeverityBlocking indicates a blocking error. + SeverityBlocking +) + +// MirrorError wraps an error with a severity level. +type MirrorError struct { + Err error + Severity Severity +} + +// Error implements the error interface. +func (e MirrorError) Error() string { + return e.Err.Error() +} + +// Unwrap returns the underlying error. +func (e MirrorError) Unwrap() error { + return e.Err +} + +// NewBlocking creates a new blocking error. +func NewBlocking(err error) MirrorError { + return MirrorError{Err: err, Severity: SeverityBlocking} +} + +// NewNonBlocking creates a new non-blocking error. +func NewNonBlocking(err error) MirrorError { + return MirrorError{Err: err, Severity: SeverityNonBlocking} +} + +// SeverityOf returns the severity of the error. +// If the error is a MirrorError, it returns its severity. +// Otherwise, it defaults to SeverityBlocking. +func SeverityOf(err error) Severity { + var mirrorErr MirrorError + if errors.As(err, &mirrorErr) { + return mirrorErr.Severity + } + return SeverityBlocking +} diff --git a/pkg/helpers/errtypes_test.go b/pkg/helpers/errtypes_test.go new file mode 100644 index 0000000..1910390 --- /dev/null +++ b/pkg/helpers/errtypes_test.go @@ -0,0 +1,84 @@ +package helpers + +import ( + "errors" + "testing" +) + +const ( + testErrorMsg = "test error" +) + +func TestNewBlocking(t *testing.T) { + err := errors.New(testErrorMsg) + mirrorErr := NewBlocking(err) + if mirrorErr.Err != err { + t.Errorf("Expected Err to be %v, got %v", err, mirrorErr.Err) + } + if mirrorErr.Severity != SeverityBlocking { + t.Errorf("Expected Severity to be %v, got %v", SeverityBlocking, mirrorErr.Severity) + } +} + +func TestNewNonBlocking(t *testing.T) { + err := errors.New(testErrorMsg) + mirrorErr := NewNonBlocking(err) + if mirrorErr.Err != err { + t.Errorf("Expected Err to be %v, got %v", err, mirrorErr.Err) + } + if mirrorErr.Severity != SeverityNonBlocking { + t.Errorf("Expected Severity to be %v, got %v", SeverityNonBlocking, mirrorErr.Severity) + } +} + +func TestMirrorErrorError(t *testing.T) { + err := errors.New(testErrorMsg) + mirrorErr := MirrorError{Err: err, Severity: SeverityBlocking} + if mirrorErr.Error() != testErrorMsg { + t.Errorf("Expected Error() to return '%s', got '%s'", testErrorMsg, mirrorErr.Error()) + } +} + +func TestMirrorErrorUnwrap(t *testing.T) { + err := errors.New(testErrorMsg) + mirrorErr := MirrorError{Err: err, Severity: SeverityBlocking} + if mirrorErr.Unwrap() != err { + t.Errorf("Expected Unwrap() to return %v, got %v", err, mirrorErr.Unwrap()) + } +} + +func TestSeverityOf(t *testing.T) { + tests := []struct { + name string + err error + expected Severity + }{ + { + name: "blocking error", + err: NewBlocking(errors.New("blocking")), + expected: SeverityBlocking, + }, + { + name: "non-blocking error", + err: NewNonBlocking(errors.New("non-blocking")), + expected: SeverityNonBlocking, + }, + { + name: "plain error defaults to blocking", + err: errors.New("plain error"), + expected: SeverityBlocking, + }, + { + name: "nil error defaults to blocking", + err: nil, + expected: SeverityBlocking, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := SeverityOf(tt.err); got != tt.expected { + t.Errorf("SeverityOf() = %v, want %v", got, tt.expected) + } + }) + } +} From 5ad51614b272c27538cd304211f6dc56c3243c17 Mon Sep 17 00:00:00 2001 From: BoxBoxJason Date: Thu, 20 Nov 2025 00:13:34 +0100 Subject: [PATCH 3/3] ci: remove dependabot --- .github/dependabot.yml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 6299bdd..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,13 +0,0 @@ -version: 2 -updates: - # Configuration for Golang - - package-ecosystem: "gomod" - directory: "/" - schedule: - interval: "weekly" - - # Configuration for Docker - - package-ecosystem: "docker" - directory: "/" - schedule: - interval: "weekly"