Skip to content

Commit a7c4625

Browse files
authored
refactor(app): improve command execution with proper goroutine synchronization
refactor(app): improve command execution with proper goroutine synchronization
1 parent 2b9c72c commit a7c4625

File tree

2 files changed

+61
-48
lines changed

2 files changed

+61
-48
lines changed

files.go.bak

Lines changed: 0 additions & 32 deletions
This file was deleted.

pkg/app/app.go

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ package app
33

44
import (
55
"bufio"
6+
"context"
67
"fmt"
8+
"io"
79
"log/slog"
810
"os/exec"
11+
"sync"
912

1013
"github.com/sgaunet/gitlab-backup2s3/pkg/logger"
1114
)
@@ -55,37 +58,79 @@ func (a *App) Run() error {
5558
// execCommand executes a command.
5659
// It wraps all errors from external packages.
5760
func (a *App) execCommand(cmdToExec []string) error {
58-
cmd := exec.Command(cmdToExec[0], cmdToExec[1:]...) // #nosec G204
61+
cmd := exec.CommandContext(context.Background(), cmdToExec[0], cmdToExec[1:]...) // #nosec G204
62+
5963
stderr, err := cmd.StderrPipe()
6064
if err != nil {
6165
return fmt.Errorf("error creating stderr pipe: %w", err)
6266
}
67+
6368
stdout, err := cmd.StdoutPipe()
6469
if err != nil {
6570
return fmt.Errorf("error creating stdout pipe: %w", err)
6671
}
72+
6773
err = cmd.Start()
6874
if err != nil {
6975
return fmt.Errorf("error starting command: %w", err)
7076
}
7177

72-
go func() {
73-
scanner := bufio.NewScanner(stderr)
74-
for scanner.Scan() {
75-
m := scanner.Text()
76-
a.logger.Error(m)
77-
}
78-
}()
79-
go func() {
80-
scanner := bufio.NewScanner(stdout)
81-
for scanner.Scan() {
82-
m := scanner.Text()
83-
a.logger.Info(m)
84-
}
85-
}()
86-
err = cmd.Wait()
78+
return a.handleCommandOutput(cmd, stderr, stdout)
79+
}
80+
81+
// handleCommandOutput handles the output streams from the command.
82+
func (a *App) handleCommandOutput(cmd *exec.Cmd, stderr, stdout io.ReadCloser) error {
83+
var stderrErr, stdoutErr error
84+
var wg sync.WaitGroup
85+
86+
// Add wait group counter for both stdout and stderr goroutines
87+
const numGoroutines = 2
88+
wg.Add(numGoroutines)
89+
90+
go a.processStderr(stderr, &wg, &stderrErr)
91+
go a.processStdout(stdout, &wg, &stdoutErr)
92+
93+
err := cmd.Wait()
8794
if err != nil {
8895
return fmt.Errorf("error waiting for command: %w", err)
8996
}
97+
98+
wg.Wait()
99+
100+
if stderrErr != nil {
101+
return stderrErr
102+
}
103+
if stdoutErr != nil {
104+
return stdoutErr
105+
}
106+
90107
return nil
91108
}
109+
110+
// processStderr processes the stderr stream.
111+
func (a *App) processStderr(stderr io.ReadCloser, wg *sync.WaitGroup, stderrErr *error) {
112+
defer wg.Done()
113+
scanner := bufio.NewScanner(stderr)
114+
for scanner.Scan() {
115+
m := scanner.Text()
116+
a.logger.Error(m)
117+
}
118+
scannerErr := scanner.Err()
119+
if scannerErr != nil {
120+
*stderrErr = fmt.Errorf("stderr scanner error: %w", scannerErr)
121+
}
122+
}
123+
124+
// processStdout processes the stdout stream.
125+
func (a *App) processStdout(stdout io.ReadCloser, wg *sync.WaitGroup, stdoutErr *error) {
126+
defer wg.Done()
127+
scanner := bufio.NewScanner(stdout)
128+
for scanner.Scan() {
129+
m := scanner.Text()
130+
a.logger.Info(m)
131+
}
132+
scannerErr := scanner.Err()
133+
if scannerErr != nil {
134+
*stdoutErr = fmt.Errorf("stdout scanner error: %w", scannerErr)
135+
}
136+
}

0 commit comments

Comments
 (0)