• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

trento-project / agent / 30480906250

29 Jul 2026 06:38PM UTC coverage: 76.159% (-0.06%) from 76.219%
30480906250

Pull #645

github

antgamdia
Strip ANSI sequences and set TERM and NO_COLOR when running commands

Signed-off-by: Antonio Gamez Diaz <antonio.gamez@suse.com>
Pull Request #645: Fix saptune parsing with output containing colors

22 of 22 new or added lines in 1 file covered. (100.0%)

2 existing lines in 1 file now uncovered.

7309 of 9597 relevant lines covered (76.16%)

16.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.29
/pkg/utils/commandexecutor.go
1
// SPDX-FileCopyrightText: SUSE LLC
2
// SPDX-License-Identifier: Apache-2.0
3

4
package utils
5

6
import (
7
        "context"
8
        "fmt"
9
        "os"
10
        "os/exec"
11
        "regexp"
12
        "syscall"
13
)
14

15
type CommandExecutor interface {
16
        Output(name string, arg ...string) ([]byte, error)
17
        OutputContext(ctx context.Context, name string, arg ...string) ([]byte, error)
18
        CombinedOutputContext(ctx context.Context, name string, arg ...string) ([]byte, error)
19
}
20

21
type Executor struct{}
22

23
// ansiEscapeSequence matches ANSI CSI sequences and OSC sequences terminated by BEL,
24
// which some CLI tools emit even when writing otherwise machine-readable output.
25
var ansiEscapeSequence = regexp.MustCompile(
26
        "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))",
27
)
28

29
func stripANSI(output []byte) []byte {
13✔
30
        if output == nil {
16✔
31
                return output
3✔
32
        }
3✔
33
        return ansiEscapeSequence.ReplaceAll(output, nil)
10✔
34
}
35

36
// Output executes the command specified by name and arg and returns its standard output.
37
// It sets: LC_ALL=C, for consistent locale; TERM=dumb, to avoid any terminal control sequences; and NO_COLOR=1, to avoid any color codes in the output.
38
func (e Executor) Output(name string, arg ...string) ([]byte, error) {
4✔
39
        cmd := exec.Command(name, arg...)
4✔
40
        cmd.Env = os.Environ()
4✔
41
        cmd.Env = append(cmd.Env, "LC_ALL=C", "TERM=dumb", "NO_COLOR=1")
4✔
42

4✔
43
        output, err := cmd.Output()
4✔
44
        return stripANSI(output), err
4✔
45
}
4✔
46

47
// OutputContext executes the command specified by name and arg with the provided context and returns its standard output.
48
// It sets: LC_ALL=C, for consistent locale; TERM=dumb, to avoid any terminal control sequences; and NO_COLOR=1, to avoid any color codes in the output.
49
// It also strips any ANSI escape sequences from the output to avoid issues with parsing the output.
50
func (e Executor) OutputContext(ctx context.Context, name string, arg ...string) ([]byte, error) {
6✔
51
        cmd := commandContext(ctx, name, arg...)
6✔
52
        output, err := cmd.Output()
6✔
53
        return stripANSI(output), err
6✔
54
}
6✔
55

56
// CombinedOutputContext executes the command specified by name and arg with the provided context and returns its combined standard output and standard error.
57
// It sets: LC_ALL=C, for consistent locale; TERM=dumb, to avoid any terminal control sequences; and NO_COLOR=1, to avoid any color codes in the output.
58
// It also strips any ANSI escape sequences from the output to avoid issues with parsing the output.
59
func (e Executor) CombinedOutputContext(ctx context.Context, name string, arg ...string) ([]byte, error) {
3✔
60
        cmd := commandContext(ctx, name, arg...)
3✔
61
        output, err := cmd.CombinedOutput()
3✔
62
        return stripANSI(output), err
3✔
63
}
3✔
64

65
// commandContext creates an exec.Cmd with the provided context, command name, and arguments.
66
// It sets the process group ID to allow for killing the entire process group on cancellation.
67
// Also sets the environment variables LC_ALL=C, TERM=dumb, and NO_COLOR=1 to ensure consistent output without terminal control sequences or color codes.
68
// It also strips any ANSI escape sequences from the output to avoid issues with parsing the output.
69
func commandContext(ctx context.Context, name string, arg ...string) *exec.Cmd {
9✔
70
        cmd := exec.CommandContext(ctx, name, arg...)
9✔
71
        cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
9✔
72
        cmd.Cancel = func() error {
10✔
73
                err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
1✔
74
                if err != nil {
1✔
UNCOV
75
                        return fmt.Errorf("error killing process group: %w", err)
×
UNCOV
76
                }
×
77
                return nil
1✔
78
        }
79
        cmd.Env = os.Environ()
9✔
80
        cmd.Env = append(cmd.Env, "LC_ALL=C", "TERM=dumb", "NO_COLOR=1")
9✔
81
        return cmd
9✔
82
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc