From 0a042b66e59702e149f85419b4cd6201e269aaca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enver=20Bi=C5=A1evac?= Date: Wed, 25 Dec 2024 13:22:48 +0000 Subject: [PATCH] feat: [code-2984]: fix datarace on branch and tag counts, improved cmd package with mutex (#3202) * requested changes * fix datarace on beanch and tag counts, improved cmd package with mux --- git/api/branch.go | 24 ++++++++++++++---------- git/api/tag.go | 16 +++++++++------- git/command/command.go | 16 +++++++++++++--- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/git/api/branch.go b/git/api/branch.go index bb8f7af6b..59e7d4dde 100644 --- a/git/api/branch.go +++ b/git/api/branch.go @@ -131,25 +131,29 @@ func (g *Git) GetBranchCount( command.WithFlag("--format=%(refname:short)"), ) - var err error go func() { - defer pipeIn.Close() - err = cmd.Run(ctx, command.WithDir(repoPath), command.WithStdout(pipeIn)) + err := cmd.Run(ctx, command.WithDir(repoPath), command.WithStdout(pipeIn)) + if err != nil { + _ = pipeIn.CloseWithError( + processGitErrorf(err, "failed to trigger branch command"), + ) + return + } + _ = pipeIn.Close() }() - if err != nil { - return 0, processGitErrorf(err, "failed to trigger branch command") - } - return countLines(pipeOut), nil + return countLines(pipeOut) } -func countLines(pipe io.Reader) int { +func countLines(pipe io.Reader) (int, error) { scanner := bufio.NewScanner(pipe) count := 0 for scanner.Scan() { count++ } - - return count + if err := scanner.Err(); err != nil { + return 0, err + } + return count, nil } diff --git a/git/api/tag.go b/git/api/tag.go index c752eb464..9fdfb0227 100644 --- a/git/api/tag.go +++ b/git/api/tag.go @@ -412,14 +412,16 @@ func (g *Git) GetTagCount( cmd := command.New("tag") - var err error go func() { - defer pipeIn.Close() - err = cmd.Run(ctx, command.WithDir(repoPath), command.WithStdout(pipeIn)) + err := cmd.Run(ctx, command.WithDir(repoPath), command.WithStdout(pipeIn)) + if err != nil { + _ = pipeIn.CloseWithError( + processGitErrorf(err, "failed to trigger tag command"), + ) + return + } + _ = pipeIn.Close() }() - if err != nil { - return 0, processGitErrorf(err, "failed to trigger branch command") - } - return countLines(pipeOut), nil + return countLines(pipeOut) } diff --git a/git/command/command.go b/git/command/command.go index 62f3e13c5..80060a900 100644 --- a/git/command/command.go +++ b/git/command/command.go @@ -22,6 +22,7 @@ import ( "io" "os/exec" "regexp" + "sync" ) var ( @@ -65,6 +66,8 @@ type Command struct { // internal counter for GIT_CONFIG_COUNT environment variable. // more info: [link](https://git-scm.com/docs/git-config#Documentation/git-config.txt-GITCONFIGCOUNT) configEnvCounter int + + mux sync.RWMutex } // New creates new command for interacting with the git process. @@ -74,15 +77,16 @@ func New(name string, options ...CmdOptionFunc) *Command { Envs: make(Envs), } - for _, opt := range options { - opt(c) - } + c.Add(options...) return c } // Clone clones the command object. func (c *Command) Clone() *Command { + c.mux.RLock() + defer c.mux.RUnlock() + globals := make([]string, len(c.Globals)) copy(globals, c.Globals) @@ -113,6 +117,9 @@ func (c *Command) Clone() *Command { // Add appends given options to the command. func (c *Command) Add(options ...CmdOptionFunc) *Command { + c.mux.Lock() + defer c.mux.Unlock() + for _, opt := range options { opt(c) } @@ -181,6 +188,9 @@ func (c *Command) Run(ctx context.Context, opts ...RunOptionFunc) (err error) { } func (c *Command) makeArgs() ([]string, error) { + c.mux.RLock() + defer c.mux.RUnlock() + var safeArgs []string // add globals