Skip to content

Commit 844bd95

Browse files
feat: ACI-4180 Add to GITHUB_PATH, add --timestamps option (#174)
1 parent e48593b commit 844bd95

File tree

5 files changed

+125
-21
lines changed

5 files changed

+125
-21
lines changed

cmd/xcode/activate_xcode.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package xcode
22

33
import (
4+
"bufio"
45
"cmp"
56
"context"
67
"fmt"
@@ -105,6 +106,10 @@ Useful if there are multiple Xcode versions installed and you want to use a spec
105106
"silent",
106107
activateXcodeParams.Silent,
107108
"Removes all stdout/err logging from the wrapper and proxy. Only xcodebuild logs will be logged.")
109+
activateXcodeCmd.Flags().BoolVar(&activateXcodeParams.XcodebuildTimestampsEnabled,
110+
"timestamps",
111+
activateXcodeParams.XcodebuildTimestampsEnabled,
112+
"Enable xcodebuild timestamps. This will add timestamps to the xcodebuild output.")
108113
}
109114

110115
func ActivateXcodeCommandFn(
@@ -283,6 +288,9 @@ func addXcelerateCommandToPathWithScriptWrapper(
283288
pathContent := fmt.Sprintf("export PATH=%s:$PATH", binPath)
284289

285290
addPathToEnvman(ctx, commandFunc, binPath, envs, logger)
291+
if err = addPathToGithubPathFile(osProxy, binPath, envs, logger); err != nil {
292+
logger.Errorf("failed to add path to github path file: %s", err)
293+
}
286294

287295
logger.Debugf("Adding xcelerate command to PATH in ~/.bashrc: %s", binPath)
288296
err = utils.AddContentOrCreateFile(logger,
@@ -307,6 +315,30 @@ func addXcelerateCommandToPathWithScriptWrapper(
307315
return nil
308316
}
309317

318+
func addPathToGithubPathFile(osProxy utils.OsProxy, binPath string, envs map[string]string, logger log.Logger) error {
319+
filePath := envs["GITHUB_PATH"]
320+
if filePath == "" {
321+
return nil
322+
}
323+
324+
logger.Infof("Adding path to %s", filePath)
325+
f, err := osProxy.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o644)
326+
if err != nil {
327+
return fmt.Errorf("failed to open GITHUB_PATH file: %w", err)
328+
}
329+
defer f.Close()
330+
331+
writer := bufio.NewWriter(f)
332+
if _, err := writer.WriteString(binPath); err != nil {
333+
return fmt.Errorf("failed to write to GITHUB_PATH file: %w", err)
334+
}
335+
if err := writer.Flush(); err != nil {
336+
return fmt.Errorf("failed to flush GITHUB_PATH file: %w", err)
337+
}
338+
339+
return nil
340+
}
341+
310342
func addPathToEnvman(
311343
ctx context.Context,
312344
commandFunc utils.CommandFunc,

cmd/xcode/activate_xcode_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ func TestActivateXcode_activateXcodeCmdFn(t *testing.T) {
2323
envs := map[string]string{
2424
"BITRISE_BUILD_CACHE_AUTH_TOKEN": "token",
2525
"BITRISE_BUILD_CACHE_WORKSPACE_ID": "abc123",
26+
"GITHUB_PATH": filepath.Join(home, ".github_path"),
2627
}
2728

2829
t.Run("success", func(t *testing.T) {
@@ -61,6 +62,7 @@ func TestActivateXcode_activateXcodeCmdFn(t *testing.T) {
6162
// make sure files were created
6263
assert.FileExists(t, filepath.Join(home, ".bashrc"))
6364
assert.FileExists(t, filepath.Join(home, ".zshrc"))
65+
assert.FileExists(t, filepath.Join(home, ".github_path"))
6466
assert.FileExists(t, xcelerate.PathFor(osProxy, filepath.Join(xcelerate.BinDir, "bitrise-build-cache-cli")))
6567
assert.FileExists(t, xcelerate.PathFor(osProxy, filepath.Join(xcelerate.BinDir, "xcodebuild")))
6668

internal/config/xcelerate/config.go

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ const (
2424
)
2525

2626
type Params struct {
27-
BuildCacheEnabled bool
28-
BuildCacheEndpoint string
29-
DebugLogging bool
30-
Silent bool
31-
XcodePathOverride string
32-
ProxySocketPathOverride string
33-
PushEnabled bool
27+
BuildCacheEnabled bool
28+
BuildCacheEndpoint string
29+
DebugLogging bool
30+
Silent bool
31+
XcodePathOverride string
32+
ProxySocketPathOverride string
33+
PushEnabled bool
34+
XcodebuildTimestampsEnabled bool
3435
}
3536

3637
type Config struct {
@@ -44,6 +45,7 @@ type Config struct {
4445
PushEnabled bool `json:"pushEnabled"`
4546
DebugLogging bool `json:"debugLogging,omitempty"`
4647
Silent bool `json:"silent,omitempty"`
48+
XcodebuildTimestamps bool `json:"xcodebuildTimestamps,omitempty"`
4749
AuthConfig common.CacheAuthConfig `json:"authConfig,omitempty"`
4850
}
4951

@@ -67,13 +69,14 @@ func ReadConfig(osProxy utils.OsProxy, decoderFactory utils.DecoderFactory) (Con
6769

6870
func DefaultParams() Params {
6971
return Params{
70-
BuildCacheEnabled: true,
71-
BuildCacheEndpoint: "",
72-
Silent: false,
73-
DebugLogging: false,
74-
XcodePathOverride: "",
75-
ProxySocketPathOverride: "",
76-
PushEnabled: true,
72+
BuildCacheEnabled: true,
73+
BuildCacheEndpoint: "",
74+
Silent: false,
75+
DebugLogging: false,
76+
XcodePathOverride: "",
77+
ProxySocketPathOverride: "",
78+
PushEnabled: true,
79+
XcodebuildTimestampsEnabled: false,
7780
}
7881
}
7982

@@ -125,6 +128,10 @@ func NewConfig(ctx context.Context,
125128
logger.Warnf("Both debug and silent logging specified, silent will take precedence.")
126129
params.DebugLogging = false
127130
}
131+
if params.XcodebuildTimestampsEnabled && params.Silent {
132+
logger.Warnf("Both timestamps and silent logging specified, silent will take precedence.")
133+
params.XcodebuildTimestampsEnabled = false
134+
}
128135

129136
return Config{
130137
ProxyVersion: envs["BITRISE_XCELERATE_PROXY_VERSION"],
@@ -137,6 +144,7 @@ func NewConfig(ctx context.Context,
137144
PushEnabled: params.PushEnabled,
138145
DebugLogging: params.DebugLogging,
139146
Silent: params.Silent,
147+
XcodebuildTimestamps: params.XcodebuildTimestampsEnabled,
140148
AuthConfig: authConfig,
141149
}, nil
142150
}

internal/config/xcelerate/config_test.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ func TestConfig_Save(t *testing.T) {
6868
OriginalXcodebuildPath: "/usr/bin/xcodebuild",
6969
BuildCacheEnabled: true,
7070
DebugLogging: true,
71+
XcodebuildTimestamps: true,
7172
}
7273
err := config.Save(mockLogger, mockOsProxy, mockEncoderFactory)
7374

@@ -205,8 +206,9 @@ func TestConfig_NewConfig(t *testing.T) {
205206
}
206207

207208
actual, err := xcelerate.NewConfig(context.Background(), mockLogger, xcelerate.Params{
208-
BuildCacheEnabled: true,
209-
DebugLogging: true,
209+
BuildCacheEnabled: true,
210+
DebugLogging: true,
211+
XcodebuildTimestampsEnabled: true,
210212
}, envs, osProxyMock, func(_ context.Context, command string, args ...string) utils.Command {
211213
assert.Equal(t, "which", command)
212214
require.Len(t, args, 1)
@@ -225,6 +227,7 @@ func TestConfig_NewConfig(t *testing.T) {
225227
BuildCacheEndpoint: "grpcs://bitrise-accelerate.services.bitrise.io",
226228
BuildCacheEnabled: true,
227229
DebugLogging: true,
230+
XcodebuildTimestamps: true,
228231
AuthConfig: common.CacheAuthConfig{
229232
AuthToken: "auth-token",
230233
WorkspaceID: "workspace-id",
@@ -257,9 +260,10 @@ func TestConfig_NewConfig(t *testing.T) {
257260
}
258261

259262
actual, err := xcelerate.NewConfig(context.Background(), mockLogger, xcelerate.Params{
260-
BuildCacheEnabled: true,
261-
DebugLogging: true,
262-
Silent: true,
263+
BuildCacheEnabled: true,
264+
DebugLogging: true,
265+
Silent: true,
266+
XcodebuildTimestampsEnabled: true,
263267
}, envs, osProxyMock, func(_ context.Context, command string, args ...string) utils.Command {
264268
return cmdMock
265269
})
@@ -274,6 +278,7 @@ func TestConfig_NewConfig(t *testing.T) {
274278
BuildCacheEndpoint: "grpcs://bitrise-accelerate.services.bitrise.io",
275279
BuildCacheEnabled: true,
276280
DebugLogging: false,
281+
XcodebuildTimestamps: false,
277282
Silent: true,
278283
AuthConfig: common.CacheAuthConfig{
279284
AuthToken: "auth-token",

internal/xcelerate/xcodeargs/runner.go

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package xcodeargs
22

33
import (
4+
"bufio"
45
"context"
56
"errors"
67
"fmt"
8+
"io"
79
"os"
810
"os/exec"
911
"os/signal"
1012
"regexp"
1113
"strings"
14+
"sync"
1215
"syscall"
1316
"time"
1417

@@ -58,14 +61,15 @@ func (runner *DefaultRunner) Run(ctx context.Context, args []string) RunStats {
5861
runner.logger.TInfof("Running xcodebuild command: %s", strings.Join(append([]string{xcodePath}, args...), " "))
5962

6063
innerCmd := exec.CommandContext(ctx, xcodePath, args...)
61-
innerCmd.Stdout = os.Stdout
62-
innerCmd.Stderr = os.Stderr
6364
innerCmd.Stdin = os.Stdin
65+
var wg sync.WaitGroup
66+
runner.setupOutputPipes(ctx, innerCmd, &wg)
6467

6568
interruptCtx, cancel := context.WithCancel(ctx)
6669
runner.handleInterrupt(interruptCtx, innerCmd)
6770

6871
err := innerCmd.Run()
72+
wg.Wait()
6973

7074
cancel()
7175
duration := time.Since(runStats.StartTime)
@@ -88,6 +92,31 @@ func (runner *DefaultRunner) Run(ctx context.Context, args []string) RunStats {
8892
return runStats
8993
}
9094

95+
func (runner *DefaultRunner) setupOutputPipes(ctx context.Context, cmd *exec.Cmd, wg *sync.WaitGroup) {
96+
stdOutReader, err := cmd.StdoutPipe()
97+
if err != nil {
98+
runner.logger.Errorf("Failed to get stdout pipe: %v", err)
99+
cmd.Stdout = os.Stdout
100+
} else {
101+
wg.Add(1)
102+
go func() {
103+
runner.streamOutput(ctx, stdOutReader, os.Stdout)
104+
wg.Done()
105+
}()
106+
}
107+
stdErrReader, err := cmd.StderrPipe()
108+
if err != nil {
109+
runner.logger.Errorf("Failed to get stderr pipe: %v", err)
110+
cmd.Stderr = os.Stderr
111+
} else {
112+
wg.Add(1)
113+
go func() {
114+
runner.streamOutput(ctx, stdErrReader, os.Stderr)
115+
wg.Done()
116+
}()
117+
}
118+
}
119+
91120
func (runner *DefaultRunner) handleInterrupt(ctx context.Context, cmd *exec.Cmd) {
92121
sig := make(chan os.Signal, 1)
93122
signal.Notify(sig, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
@@ -157,3 +186,31 @@ func (runner *DefaultRunner) determineXcodeVersionAndBuildNumber(ctx context.Con
157186

158187
return nil
159188
}
189+
190+
func (runner *DefaultRunner) streamOutput(ctx context.Context, reader io.ReadCloser, writer io.Writer) {
191+
scanner := bufio.NewScanner(reader)
192+
const maxTokenSize = 10 * 1024 * 1024
193+
buf := make([]byte, 0, 64*1024)
194+
scanner.Buffer(buf, maxTokenSize)
195+
for scanner.Scan() {
196+
select {
197+
case <-ctx.Done():
198+
return
199+
default:
200+
}
201+
202+
line := scanner.Text()
203+
204+
if runner.config.XcodebuildTimestamps && !runner.config.Silent {
205+
timestamp := time.Now().Format("15:04:05")
206+
line = fmt.Sprintf("[%s] %s", timestamp, line)
207+
}
208+
209+
//nolint: errcheck
210+
fmt.Fprintf(writer, "%s\n", line)
211+
}
212+
213+
if err := scanner.Err(); err != nil {
214+
runner.logger.Errorf("Failed to scan stdout: %v", err)
215+
}
216+
}

0 commit comments

Comments
 (0)