Skip to content

Commit 4d5429e

Browse files
feat: ACI-4175 Wrap xcrun commands (#177)
1 parent 83d58c1 commit 4d5429e

File tree

4 files changed

+84
-7
lines changed

4 files changed

+84
-7
lines changed

cmd/xcode/activate_xcode.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,20 @@ const (
2626

2727
ErrFmtCreateXcodeConfig = "failed to create Xcode config: %w"
2828

29-
cliBasename = "bitrise-build-cache-cli"
30-
wrapperScriptContent = `#!/bin/bash
29+
cliBasename = "bitrise-build-cache-cli"
30+
xcodebuildWrapperScriptContent = `#!/bin/bash
3131
set -euxo pipefail
3232
%s/bitrise-build-cache-cli xcelerate xcodebuild "$@"
33+
`
34+
xcrunWrapperScriptContent = `#!/bin/bash
35+
set -euxo pipefail
36+
37+
if [ "${1-}" = "xcodebuild" ]; then
38+
shift
39+
%s/bitrise-build-cache-cli xcelerate xcodebuild "$@"
40+
else
41+
%s "$@"
42+
fi
3343
`
3444
)
3545

@@ -99,6 +109,13 @@ func init() {
99109
activateXcodeParams.XcodePathOverride,
100110
`Override the xcodebuild path. By default it will use the $(which xcodebuild) command to determine the path, and if that fails, it will use the default path: /usr/bin/xcodebuild.
101111
112+
Useful if there are multiple Xcode versions installed and you want to use a specific one.`,
113+
)
114+
activateXcodeCmd.Flags().StringVar(&activateXcodeParams.XcrunPathOverride,
115+
"xcrun-path",
116+
activateXcodeParams.XcrunPathOverride,
117+
`Override the xcrun path. By default it will use the $(which xcrun) command to determine the path, and if that fails, it will use the default path: /usr/bin/xcrun.
118+
102119
Useful if there are multiple Xcode versions installed and you want to use a specific one.`,
103120
)
104121

@@ -146,7 +163,7 @@ func ActivateXcodeCommandFn(
146163
return fmt.Errorf("failed to copy xcelerate cli to ~/.bitrise-xcelerate/bin: %w", err)
147164
}
148165

149-
if err := addXcelerateCommandToPathWithScriptWrapper(ctx, osProxy, commandFunc, logger, envs); err != nil {
166+
if err := addXcelerateCommandToPathWithScriptWrapper(ctx, config, osProxy, commandFunc, logger, envs); err != nil {
150167
return fmt.Errorf("failed to add xcelerate command: %w", err)
151168
}
152169

@@ -174,6 +191,14 @@ func overrideActivateXcodeParamsFromExistingConfig(
174191
activateXcodeParams.XcodePathOverride,
175192
existingConfig.OriginalXcodebuildPath,
176193
)
194+
if strings.Contains(existingConfig.OriginalXcrunPath, xcelerate.PathFor(osProxy, xcelerate.BinDir)) {
195+
logger.Warnf("Removing xcelerate wrapper as original xcrun path...")
196+
existingConfig.OriginalXcrunPath = ""
197+
}
198+
activateXcodeParams.XcrunPathOverride = cmp.Or(
199+
activateXcodeParams.XcrunPathOverride,
200+
existingConfig.OriginalXcrunPath,
201+
)
177202
} else if isXcelerateInPath(osProxy, envs) {
178203
logger.Warnf("It seems that the xcelerate config file is missing, but xcelerate is already in the PATH. \n" +
179204
"This will lead to unexpected behavior when determining the xcodebuild path. \n" +
@@ -263,6 +288,7 @@ func makeSureCLIIsNotRunning(ctx context.Context, target string, logger log.Logg
263288
// addXcelerateCommandToPathWithScriptWrapper creates a script that wraps the CLI and adds it to the PATH
264289
func addXcelerateCommandToPathWithScriptWrapper(
265290
ctx context.Context,
291+
config xcelerate.Config,
266292
osProxy utils.OsProxy,
267293
commandFunc utils.CommandFunc,
268294
logger log.Logger,
@@ -281,10 +307,16 @@ func addXcelerateCommandToPathWithScriptWrapper(
281307
// create a script that wraps the CLI to preserve any arguments and environment variables
282308
scriptPath := filepath.Join(binPath, "xcodebuild")
283309
logger.Debugf("Creating xcodebuild wrapper script: %s", scriptPath)
284-
if err := osProxy.WriteFile(scriptPath, []byte(fmt.Sprintf(wrapperScriptContent, binPath)), 0o755); err != nil {
310+
if err := osProxy.WriteFile(scriptPath, []byte(fmt.Sprintf(xcodebuildWrapperScriptContent, binPath)), 0o755); err != nil {
285311
return fmt.Errorf("failed to create xcodebuild wrapper script: %w", err)
286312
}
287313

314+
scriptPath = filepath.Join(binPath, "xcrun")
315+
logger.Debugf("Creating xcrun wrapper script: %s", scriptPath)
316+
if err := osProxy.WriteFile(scriptPath, []byte(fmt.Sprintf(xcrunWrapperScriptContent, binPath, config.OriginalXcrunPath)), 0o755); err != nil {
317+
return fmt.Errorf("failed to create xcrun wrapper script: %w", err)
318+
}
319+
288320
pathContent := fmt.Sprintf("export PATH=%s:$PATH", binPath)
289321

290322
addPathToEnvman(ctx, commandFunc, binPath, envs, logger)

cmd/xcode/activate_xcode_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func TestActivateXcode_activateXcodeCmdFn(t *testing.T) {
5151
BuildCacheEnabled: true,
5252
DebugLogging: true,
5353
XcodePathOverride: "/xxx/xcodebuild",
54+
XcrunPathOverride: "/xxx/xcrun",
5455
ProxySocketPathOverride: "/xxx/xcelerate.sock",
5556
},
5657
envs,
@@ -73,6 +74,7 @@ func TestActivateXcode_activateXcodeCmdFn(t *testing.T) {
7374
require.True(t, config.BuildCacheEnabled)
7475
require.True(t, config.DebugLogging)
7576
require.Equal(t, "/xxx/xcodebuild", config.OriginalXcodebuildPath)
77+
require.Equal(t, "/xxx/xcrun", config.OriginalXcrunPath)
7678
require.Equal(t, "/xxx/xcelerate.sock", config.ProxySocketPath)
7779
require.Equal(t, "token", config.AuthConfig.AuthToken)
7880
require.Equal(t, "abc123", config.AuthConfig.WorkspaceID)
@@ -103,6 +105,7 @@ func TestActivateXcode_activateXcodeCmdFn(t *testing.T) {
103105
require.True(t, config.BuildCacheEnabled)
104106
require.True(t, config.DebugLogging)
105107
require.Equal(t, "/xxx/xcodebuild", config.OriginalXcodebuildPath)
108+
require.Equal(t, "/xxx/xcrun", config.OriginalXcrunPath)
106109
require.Equal(t, "/xxx/xcelerate.sock", config.ProxySocketPath)
107110
require.Equal(t, "token", config.AuthConfig.AuthToken)
108111
require.Equal(t, "abc123", config.AuthConfig.WorkspaceID)

internal/config/xcelerate/config.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
const (
1717
DefaultXcodePath = "/usr/bin/xcodebuild"
18+
DefaultXcrunPath = "/usr/bin/xcrun"
1819
xcelerateConfigFileName = "config.json"
1920

2021
ErrFmtCreateConfigFile = `failed to create xcelerate config file: %w`
@@ -29,6 +30,7 @@ type Params struct {
2930
DebugLogging bool
3031
Silent bool
3132
XcodePathOverride string
33+
XcrunPathOverride string
3234
ProxySocketPathOverride string
3335
PushEnabled bool
3436
XcodebuildTimestampsEnabled bool
@@ -40,6 +42,7 @@ type Config struct {
4042
CLIVersion string `json:"cliVersion"`
4143
WrapperVersion string `json:"wrapperVersion"`
4244
OriginalXcodebuildPath string `json:"originalXcodebuildPath"`
45+
OriginalXcrunPath string `json:"originalXcrunPath"`
4346
BuildCacheEnabled bool `json:"buildCacheEnabled"`
4447
BuildCacheEndpoint string `json:"buildCacheEndpoint"`
4548
PushEnabled bool `json:"pushEnabled"`
@@ -108,6 +111,18 @@ func NewConfig(ctx context.Context,
108111
}
109112
logger.Infof("Using xcodebuild path: %s. You can always override this by supplying --xcode-path.", xcodePath)
110113

114+
xcrunPath := params.XcrunPathOverride
115+
if xcrunPath == "" {
116+
logger.Debugf("No xcrun path override specified, determining original xcrun path...")
117+
originalXcrunPath, err := getOriginalXcrunPath(ctx, logger, cmdFunc)
118+
if err != nil {
119+
logger.Warnf("Failed to determine xcrun path: %s. Using default: %s", err, DefaultXcrunPath)
120+
originalXcrunPath = DefaultXcrunPath
121+
}
122+
xcrunPath = originalXcrunPath
123+
}
124+
logger.Infof("Using xcrun path: %s. You can always override this by supplying --xcrun-path.", xcrunPath)
125+
111126
proxySocketPath := params.ProxySocketPathOverride
112127
if proxySocketPath == "" {
113128
proxySocketPath = envs["BITRISE_XCELERATE_PROXY_SOCKET_PATH"]
@@ -139,6 +154,7 @@ func NewConfig(ctx context.Context,
139154
WrapperVersion: envs["BITRISE_XCELERATE_WRAPPER_VERSION"],
140155
CLIVersion: envs["BITRISE_BUILD_CACHE_CLI_VERSION"],
141156
OriginalXcodebuildPath: xcodePath,
157+
OriginalXcrunPath: xcrunPath,
142158
BuildCacheEnabled: params.BuildCacheEnabled,
143159
BuildCacheEndpoint: params.BuildCacheEndpoint,
144160
PushEnabled: params.PushEnabled,
@@ -166,6 +182,23 @@ func getOriginalXcodebuildPath(ctx context.Context, logger log.Logger, cmdFunc u
166182
return trimmed, nil
167183
}
168184

185+
func getOriginalXcrunPath(ctx context.Context, logger log.Logger, cmdFunc utils.CommandFunc) (string, error) {
186+
logger.Debugf("Determining original xcrun path...")
187+
cmd := cmdFunc(ctx, "which", "xcrun")
188+
output, err := cmd.CombinedOutput()
189+
if err != nil {
190+
return "", fmt.Errorf("failed to get xcrun output: %w", err)
191+
}
192+
trimmed := strings.TrimSpace(string(output))
193+
if len(trimmed) == 0 {
194+
logger.Warnf("No xcrun path found, using default: %s", DefaultXcrunPath)
195+
196+
return DefaultXcrunPath, nil
197+
}
198+
199+
return trimmed, nil
200+
}
201+
169202
func (config Config) Save(logger log.Logger, os utils.OsProxy, encoderFactory utils.EncoderFactory) error {
170203
xcelerateFolder := DirPath(os)
171204

internal/config/xcelerate/config_test.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,9 @@ func TestConfig_NewConfig(t *testing.T) {
212212
}, envs, osProxyMock, func(_ context.Context, command string, args ...string) utils.Command {
213213
assert.Equal(t, "which", command)
214214
require.Len(t, args, 1)
215-
assert.Equal(t, "xcodebuild", args[0])
215+
if args[0] != "xcodebuild" && args[0] != "xcrun" {
216+
t.Fatalf("expected 'xcodebuild' or 'xcrun' as arg, got: %s", args[0])
217+
}
216218

217219
return cmdMock
218220
})
@@ -224,6 +226,7 @@ func TestConfig_NewConfig(t *testing.T) {
224226
WrapperVersion: "wrapper-version-1.0.0",
225227
CLIVersion: "cli-version-1.0.0",
226228
OriginalXcodebuildPath: "/usr/bin/xcodebuild2",
229+
OriginalXcrunPath: "/usr/bin/xcodebuild2",
227230
BuildCacheEndpoint: "grpcs://bitrise-accelerate.services.bitrise.io",
228231
BuildCacheEnabled: true,
229232
DebugLogging: true,
@@ -233,7 +236,7 @@ func TestConfig_NewConfig(t *testing.T) {
233236
WorkspaceID: "workspace-id",
234237
},
235238
}
236-
require.Len(t, cmdMock.CombinedOutputCalls(), 1)
239+
require.Len(t, cmdMock.CombinedOutputCalls(), 2)
237240
assert.Equal(t, expected, actual)
238241
})
239242

@@ -275,6 +278,7 @@ func TestConfig_NewConfig(t *testing.T) {
275278
WrapperVersion: "wrapper-version-1.0.0",
276279
CLIVersion: "cli-version-1.0.0",
277280
OriginalXcodebuildPath: "/usr/bin/xcodebuild2",
281+
OriginalXcrunPath: "/usr/bin/xcodebuild2",
278282
BuildCacheEndpoint: "grpcs://bitrise-accelerate.services.bitrise.io",
279283
BuildCacheEnabled: true,
280284
DebugLogging: false,
@@ -285,7 +289,7 @@ func TestConfig_NewConfig(t *testing.T) {
285289
WorkspaceID: "workspace-id",
286290
},
287291
}
288-
require.Len(t, cmdMock.CombinedOutputCalls(), 1)
292+
require.Len(t, cmdMock.CombinedOutputCalls(), 2)
289293
assert.Equal(t, expected, actual)
290294
})
291295

@@ -312,6 +316,7 @@ func TestConfig_NewConfig(t *testing.T) {
312316
DebugLogging: true,
313317
BuildCacheEndpoint: "grpcs://bitrise-accelerate.services.bitrise.io",
314318
XcodePathOverride: "/usr/bin/xcodebuild-override",
319+
XcrunPathOverride: "/usr/bin/xcrun-override",
315320
ProxySocketPathOverride: "/tmp/xcelerate-proxy.sock",
316321
}, envs, osProxyMock, func(_ context.Context, _ string, _ ...string) utils.Command {
317322
return cmdMock
@@ -324,6 +329,7 @@ func TestConfig_NewConfig(t *testing.T) {
324329
CLIVersion: "",
325330
BuildCacheEndpoint: "grpcs://bitrise-accelerate.services.bitrise.io",
326331
OriginalXcodebuildPath: "/usr/bin/xcodebuild-override",
332+
OriginalXcrunPath: "/usr/bin/xcrun-override",
327333
ProxySocketPath: "/tmp/xcelerate-proxy.sock",
328334
BuildCacheEnabled: true,
329335
DebugLogging: true,
@@ -369,6 +375,7 @@ func TestConfig_NewConfig(t *testing.T) {
369375
CLIVersion: "",
370376
BuildCacheEndpoint: "grpcs://bitrise-accelerate.services.bitrise.io",
371377
OriginalXcodebuildPath: xcelerate.DefaultXcodePath,
378+
OriginalXcrunPath: xcelerate.DefaultXcrunPath,
372379
BuildCacheEnabled: true,
373380
DebugLogging: true,
374381
AuthConfig: common.CacheAuthConfig{
@@ -413,6 +420,7 @@ func TestConfig_NewConfig(t *testing.T) {
413420
CLIVersion: "",
414421
BuildCacheEndpoint: "grpc://localhost:6666",
415422
OriginalXcodebuildPath: xcelerate.DefaultXcodePath,
423+
OriginalXcrunPath: xcelerate.DefaultXcrunPath,
416424
ProxySocketPath: "my-temp-dir/xcelerate-proxy.sock",
417425
BuildCacheEnabled: true,
418426
DebugLogging: true,
@@ -459,6 +467,7 @@ func TestConfig_NewConfig(t *testing.T) {
459467
CLIVersion: "",
460468
BuildCacheEndpoint: "grpc://localhost:6666",
461469
OriginalXcodebuildPath: xcelerate.DefaultXcodePath,
470+
OriginalXcrunPath: xcelerate.DefaultXcrunPath,
462471
ProxySocketPath: "my-temp-dir/xcelerate-proxy.sock",
463472
BuildCacheEnabled: true,
464473
DebugLogging: true,

0 commit comments

Comments
 (0)