Skip to content

Commit 47ffbe5

Browse files
committed
Refactor SteamCMD update and validation methods to use a unified runSteamCMD function for improved output handling and logging
1 parent f75acf1 commit 47ffbe5

File tree

1 file changed

+78
-11
lines changed

1 file changed

+78
-11
lines changed

internal/steamcmd/client.go

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package steamcmd
22

33
import (
4+
"bufio"
5+
"bytes"
46
"context"
57
"fmt"
8+
"io"
69
"os"
710
"os/exec"
811
"path/filepath"
912
"strings"
13+
"sync"
1014

1115
"k8s.io/klog/v2"
1216
)
@@ -103,10 +107,7 @@ func (c *Client) ApplyUpdate(ctx context.Context) error {
103107
return fmt.Errorf("failed to create update script: %w", err)
104108
}
105109

106-
cmd := exec.CommandContext(ctx, c.steamCMDPath+"/steamcmd.sh", "+runscript", scriptPath)
107-
output, err := cmd.CombinedOutput()
108-
109-
klog.V(2).Infof("SteamCMD update output: %s", string(output))
110+
output, err := c.runSteamCMD(ctx, scriptPath, "update")
110111

111112
// Check for 0x6 error state and attempt recovery
112113
if c.hasState0x6Error(output) {
@@ -117,9 +118,7 @@ func (c *Client) ApplyUpdate(ctx context.Context) error {
117118

118119
// Retry update after clearing steamapps
119120
klog.Info("Retrying update after clearing steamapps...")
120-
cmd = exec.CommandContext(ctx, c.steamCMDPath+"/steamcmd.sh", "+runscript", scriptPath)
121-
output, err = cmd.CombinedOutput()
122-
klog.V(2).Infof("SteamCMD retry output: %s", string(output))
121+
output, err = c.runSteamCMD(ctx, scriptPath, "update-retry")
123122

124123
if err != nil {
125124
return fmt.Errorf("steamcmd update failed after 0x6 recovery: %w, output: %s", err, string(output))
@@ -144,10 +143,7 @@ func (c *Client) ValidateUpdate(ctx context.Context) error {
144143
return fmt.Errorf("failed to create validate script: %w", err)
145144
}
146145

147-
cmd := exec.CommandContext(ctx, c.steamCMDPath+"/steamcmd.sh", "+runscript", scriptPath)
148-
output, err := cmd.CombinedOutput()
149-
150-
klog.V(2).Infof("SteamCMD validation output: %s", string(output))
146+
output, err := c.runSteamCMD(ctx, scriptPath, "validate")
151147

152148
if err != nil {
153149
return fmt.Errorf("validation failed: %w, output: %s", err, string(output))
@@ -320,6 +316,77 @@ quit
320316
return "", fmt.Errorf("buildid not found in app_info output")
321317
}
322318

319+
// runSteamCMD executes steamcmd scripts while streaming progress output.
320+
func (c *Client) runSteamCMD(ctx context.Context, scriptPath, stage string) ([]byte, error) {
321+
cmd := exec.CommandContext(ctx, c.steamCMDPath+"/steamcmd.sh", "+runscript", scriptPath)
322+
323+
stdout, err := cmd.StdoutPipe()
324+
if err != nil {
325+
return nil, fmt.Errorf("failed to attach steamcmd stdout: %w", err)
326+
}
327+
328+
stderr, err := cmd.StderrPipe()
329+
if err != nil {
330+
return nil, fmt.Errorf("failed to attach steamcmd stderr: %w", err)
331+
}
332+
333+
if err := cmd.Start(); err != nil {
334+
return nil, fmt.Errorf("failed to start steamcmd: %w", err)
335+
}
336+
337+
var combined bytes.Buffer
338+
var combinedMu sync.Mutex
339+
var wg sync.WaitGroup
340+
logStream := func(r io.Reader, stream string) {
341+
defer wg.Done()
342+
scanner := bufio.NewScanner(r)
343+
for scanner.Scan() {
344+
line := scanner.Text()
345+
trimmed := strings.TrimSpace(line)
346+
combinedMu.Lock()
347+
combined.WriteString(line)
348+
combined.WriteByte('\n')
349+
combinedMu.Unlock()
350+
351+
if trimmed == "" {
352+
continue
353+
}
354+
355+
if c.shouldLogProgress(trimmed) {
356+
klog.Infof("[%s:%s] %s", stage, stream, trimmed)
357+
} else {
358+
klog.V(4).Infof("[%s:%s] %s", stage, stream, trimmed)
359+
}
360+
}
361+
362+
if err := scanner.Err(); err != nil {
363+
klog.Warningf("[%s:%s] error while reading steamcmd output: %v", stage, stream, err)
364+
}
365+
}
366+
367+
wg.Add(2)
368+
go logStream(stdout, "stdout")
369+
go logStream(stderr, "stderr")
370+
371+
cmdErr := cmd.Wait()
372+
wg.Wait()
373+
374+
return combined.Bytes(), cmdErr
375+
}
376+
377+
// shouldLogProgress decides whether a steamcmd line should be surfaced at info level.
378+
func (c *Client) shouldLogProgress(line string) bool {
379+
lower := strings.ToLower(line)
380+
return strings.Contains(lower, "update state") ||
381+
strings.Contains(lower, "progress") ||
382+
strings.Contains(lower, "downloading") ||
383+
strings.Contains(lower, "install") ||
384+
strings.Contains(lower, "validat") ||
385+
strings.Contains(lower, "success") ||
386+
strings.Contains(lower, "error") ||
387+
strings.Contains(lower, "app_update")
388+
}
389+
323390
// parseUpdateStatus parses SteamCMD output to determine if an update is needed
324391
// DEPRECATED: This method triggers actual downloads. Use getInstalledBuildID/getLatestBuildID instead.
325392
func (c *Client) parseUpdateStatus(output []byte) bool {

0 commit comments

Comments
 (0)