Skip to content

Commit 2fdb093

Browse files
authored
[STEP-2079] Add xcbeautify log formatter (#373)
- Added xcbeautify log formatter option. - Fixed findIDEDistrubutionLogsPath lookup, broken due to different quoting in latest Xcodes. - Removed extraneous %!(EXTRA ) from plsit content printing. - Some logging improvements. - Removed errorFormatter and xcodebuild errorfinder in the step as package version is available.
1 parent 360f0ce commit 2fdb093

32 files changed

+864
-1196
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ Build a development IPA with custom xcconfig file path:
128128
| `xcconfig_content` | Build settings to override the project's build settings, using xcodebuild's `-xcconfig` option. You can't define `-xcconfig` option in `Additional options for the xcodebuild command` if this input is set. If empty, no setting is changed. When set it can be either: 1. Existing `.xcconfig` file path. Example: `./ios-sample/ios-sample/Configurations/Dev.xcconfig` 2. The contents of a newly created temporary `.xcconfig` file. (This is the default.) Build settings must be separated by newline character (`\n`). Example: ``` COMPILER_INDEX_STORE_ENABLE = NO ONLY_ACTIVE_ARCH[config=Debug][sdk=*][arch=*] = YES ``` | | `COMPILER_INDEX_STORE_ENABLE = NO` |
129129
| `perform_clean_action` | If this input is set, `clean` xcodebuild action will be performed besides the `archive` action. | required | `no` |
130130
| `xcodebuild_options` | Additional options to be added to the executed xcodebuild command. Prefer using `Build settings (xcconfig)` input for specifying `-xcconfig` option. You can't use both. `-destination` is set automatically, unless specified explicitely. | | |
131-
| `log_formatter` | Defines how `xcodebuild` command's log is formatted. Available options: - `xcpretty`: The xcodebuild command's output will be prettified by xcpretty. - `xcodebuild`: Only the last 20 lines of raw xcodebuild output will be visible in the build log. The raw xcodebuild log will be exported in both cases. | required | `xcpretty` |
131+
| `log_formatter` | Defines how `xcodebuild` command's log is formatted. Available options: - `xcbeautify`: The xcodebuild command's output will be beautified by xcbeautify. - `xcodebuild`: Only the last 20 lines of raw xcodebuild output will be visible in the build log. - `xcpretty`: The xcodebuild command's output will be prettified by xcpretty. The raw xcodebuild log will be exported in both cases. | required | `xcpretty` |
132132
| `automatic_code_signing` | This input determines which Bitrise Apple service connection should be used for automatic code signing. Available values: - `off`: Do not do any auto code signing. - `api-key`: [Bitrise Apple Service connection with API Key](https://devcenter.bitrise.io/getting-started/connecting-to-services/setting-up-connection-to-an-apple-service-with-api-key/). - `apple-id`: [Bitrise Apple Service connection with Apple ID](https://devcenter.bitrise.io/getting-started/connecting-to-services/connecting-to-an-apple-service-with-apple-id/). | required | `off` |
133133
| `register_test_devices` | If this input is set, the Step will register the known test devices on Bitrise from team members with the Apple Developer Portal. Note that setting this to yes may cause devices to be registered against your limited quantity of test devices in the Apple Developer Portal, which can only be removed once annually during your renewal window. | required | `no` |
134134
| `test_device_list_path` | If this input is set, the Step will register the listed devices from this file with the Apple Developer Portal. The format of the file is a comma separated list of the identifiers. For example: `00000000–0000000000000001,00000000–0000000000000002,00000000–0000000000000003` And in the above example the registered devices appear with the name of `Device 1`, `Device 2` and `Device 3` in the Apple Developer Portal. Note that setting this will have a higher priority than the Bitrise provided devices list. | | |

e2e/bitrise.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ workflows:
308308
- CODE_SIGNING_METHOD: api-key
309309
- MIN_DAYS_PROFILE_VALID: 0
310310
- IPA_EXPORT_METHOD: app-store
311-
- LOG_FORMATTER: xcpretty
311+
- LOG_FORMATTER: xcbeautify
312312
after_run:
313313
- _run
314314
- _check_outputs
@@ -335,7 +335,7 @@ workflows:
335335
- TEAM_ID: 72SA8V3WYL
336336
- MIN_DAYS_PROFILE_VALID: 0
337337
- IPA_EXPORT_METHOD: app-store
338-
- LOG_FORMATTER: xcpretty
338+
- LOG_FORMATTER: xcbeautify
339339
after_run:
340340
- _run
341341
- _check_outputs

formatted_error_test.go

-70
This file was deleted.

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
github.com/bitrise-io/go-steputils/v2 v2.0.0-alpha.23
99
github.com/bitrise-io/go-utils v1.0.12
1010
github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.23
11-
github.com/bitrise-io/go-xcode v1.1.2
11+
github.com/bitrise-io/go-xcode v1.2.0
1212
github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.54
1313
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
1414
github.com/stretchr/testify v1.10.0

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ github.com/bitrise-io/go-utils v1.0.12 h1:iJV1ZpyvSA0NCte/N6x+aIQ9TrNr5sIBlcJBf0
1111
github.com/bitrise-io/go-utils v1.0.12/go.mod h1:ZY1DI+fEpZuFpO9szgDeICM4QbqoWVt0RSY3tRI1heY=
1212
github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.23 h1:Dfh4nyZPuEtilBisidejqxBrkx9cWvbOUrpq8VEION0=
1313
github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.23/go.mod h1:3XUplo0dOWc3DqT2XA2SeHToDSg7+j1y1HTHibT2H68=
14-
github.com/bitrise-io/go-xcode v1.1.2 h1:7tit56lNPPzH714pBt083z/iZh7P44vgVsNrEdvkYG4=
15-
github.com/bitrise-io/go-xcode v1.1.2/go.mod h1:9OwsvrhZ4A2JxHVoEY7CPcABAKA+OE7FQqFfBfvbFuY=
14+
github.com/bitrise-io/go-xcode v1.2.0 h1:rbyeM7y1CcGD+udmODEPcVN6ek4HjuP9DQ/FHNY3JPE=
15+
github.com/bitrise-io/go-xcode v1.2.0/go.mod h1:9OwsvrhZ4A2JxHVoEY7CPcABAKA+OE7FQqFfBfvbFuY=
1616
github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.54 h1:xTIh8AbSVWfSpkhyHj0uOO0I9BPjz5yJcIIPVy3TbGg=
1717
github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.54/go.mod h1:T4rhWQljdgH5As4Dq/RQWuazdScY0YB7uZAMuBUnxeY=
1818
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=

main.go

+44-24
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
package main
22

33
import (
4-
"errors"
54
"fmt"
65
"os"
76

7+
"github.com/bitrise-io/go-steputils/v2/ruby"
88
"github.com/bitrise-io/go-steputils/v2/stepconf"
99
"github.com/bitrise-io/go-utils/v2/command"
1010
"github.com/bitrise-io/go-utils/v2/env"
11+
"github.com/bitrise-io/go-utils/v2/errorutil"
1112
"github.com/bitrise-io/go-utils/v2/fileutil"
1213
"github.com/bitrise-io/go-utils/v2/log"
1314
"github.com/bitrise-io/go-utils/v2/pathutil"
15+
"github.com/bitrise-io/go-xcode/v2/xcodecommand"
1416
"github.com/bitrise-steplib/steps-xcode-archive/step"
1517
)
1618

@@ -20,66 +22,84 @@ func main() {
2022

2123
func run() int {
2224
logger := log.NewLogger()
23-
archiver := createXcodebuildArchiver(logger)
24-
25-
config, err := archiver.ProcessInputs()
25+
configParser := createConfigParser(logger)
26+
config, err := configParser.ProcessInputs()
2627
if err != nil {
27-
logger.Errorf(formattedError(fmt.Errorf("Failed to process Step inputs: %w", err)))
28+
logger.Errorf("%s", errorutil.FormattedError(fmt.Errorf("Failed to process Step inputs: %w", err)))
2829
return 1
2930
}
3031

31-
dependenciesOpts := step.EnsureDependenciesOpts{
32-
XCPretty: config.LogFormatter == "xcpretty",
33-
}
34-
if err := archiver.EnsureDependencies(dependenciesOpts); err != nil {
35-
var xcprettyInstallErr step.XCPrettyInstallError
36-
if errors.As(err, &xcprettyInstallErr) {
37-
logger.Warnf("Installing xcpretty failed: %s", err)
38-
logger.Warnf("Switching to xcodebuild for log formatter")
39-
config.LogFormatter = "xcodebuild"
40-
} else {
41-
logger.Errorf(formattedError(fmt.Errorf("Failed to install Step dependencies: %w", err)))
42-
return 1
43-
}
32+
archiver, err := createXcodebuildArchiver(logger, config.LogFormatter)
33+
if err != nil {
34+
logger.Errorf("%s", errorutil.FormattedError(fmt.Errorf("Failed to process Step inputs: %w", err)))
35+
return 1
4436
}
4537

38+
archiver.EnsureDependencies()
39+
4640
exitCode := 0
4741
runOpts := createRunOptions(config)
4842
result, err := archiver.Run(runOpts)
4943
if err != nil {
50-
logger.Errorf(formattedError(fmt.Errorf("Failed to execute Step main logic: %w", err)))
44+
logger.Errorf("%s", errorutil.FormattedError(fmt.Errorf("Failed to execute Step main logic: %w", err)))
5145
exitCode = 1
5246
// don't return as step outputs needs to be exported even in case of failure (for example the xcodebuild logs)
5347
}
5448

5549
exportOpts := createExportOptions(config, result)
5650
if err := archiver.ExportOutput(exportOpts); err != nil {
57-
logger.Errorf(formattedError(fmt.Errorf("Failed to export Step outputs: %w", err)))
51+
logger.Errorf("%s", errorutil.FormattedError(fmt.Errorf("Failed to export Step outputs: %w", err)))
5852
return 1
5953
}
6054

6155
return exitCode
6256
}
6357

64-
func createXcodebuildArchiver(logger log.Logger) step.XcodebuildArchiver {
65-
xcodeVersionProvider := step.NewXcodebuildXcodeVersionProvider()
58+
func createConfigParser(logger log.Logger) step.XcodebuildArchiveConfigParser {
6659
envRepository := env.NewRepository()
6760
inputParser := stepconf.NewInputParser(envRepository)
61+
xcodeVersionProvider := step.NewXcodebuildXcodeVersionProvider()
62+
fileManager := fileutil.NewFileManager()
63+
cmdFactory := command.NewFactory(envRepository)
64+
65+
return step.NewXcodeArchiveConfigParser(inputParser, xcodeVersionProvider, fileManager, cmdFactory, logger)
66+
}
67+
68+
func createXcodebuildArchiver(logger log.Logger, logFormatter string) (step.XcodebuildArchiver, error) {
69+
envRepository := env.NewRepository()
6870
pathProvider := pathutil.NewPathProvider()
6971
pathChecker := pathutil.NewPathChecker()
7072
pathModifier := pathutil.NewPathModifier()
7173
fileManager := fileutil.NewFileManager()
7274
cmdFactory := command.NewFactory(envRepository)
7375

74-
return step.NewXcodebuildArchiver(xcodeVersionProvider, inputParser, pathProvider, pathChecker, pathModifier, fileManager, logger, cmdFactory)
76+
xcodeCommandRunner := xcodecommand.Runner(nil)
77+
switch logFormatter {
78+
case step.XcodebuildTool:
79+
xcodeCommandRunner = xcodecommand.NewRawCommandRunner(logger, cmdFactory)
80+
case step.XcbeautifyTool:
81+
xcodeCommandRunner = xcodecommand.NewXcbeautifyRunner(logger, cmdFactory)
82+
case step.XcprettyTool:
83+
commandLocator := env.NewCommandLocator()
84+
rubyComamndFactory, err := ruby.NewCommandFactory(cmdFactory, commandLocator)
85+
if err != nil {
86+
return step.XcodebuildArchiver{}, fmt.Errorf("failed to install xcpretty: %s", err)
87+
}
88+
rubyEnv := ruby.NewEnvironment(rubyComamndFactory, commandLocator, logger)
89+
90+
xcodeCommandRunner = xcodecommand.NewXcprettyCommandRunner(logger, cmdFactory, pathChecker, fileManager, rubyComamndFactory, rubyEnv)
91+
default:
92+
panic(fmt.Sprintf("Unknown log formatter: %s", logFormatter))
93+
}
94+
95+
return step.NewXcodebuildArchiver(xcodeCommandRunner, logFormatter, pathProvider, pathChecker, pathModifier, fileManager, cmdFactory, logger), nil
7596
}
7697

7798
func createRunOptions(config step.Config) step.RunOpts {
7899
return step.RunOpts{
79100
ProjectPath: config.ProjectPath,
80101
Scheme: config.Scheme,
81102
Configuration: config.Configuration,
82-
LogFormatter: config.LogFormatter,
83103
XcodeMajorVersion: config.XcodeMajorVersion,
84104
ArtifactName: config.ArtifactName,
85105

step.yml

+4-3
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,15 @@ inputs:
175175
Defines how `xcodebuild` command's log is formatted.
176176
177177
Available options:
178-
179-
- `xcpretty`: The xcodebuild command's output will be prettified by xcpretty.
178+
- `xcbeautify`: The xcodebuild command's output will be beautified by xcbeautify.
180179
- `xcodebuild`: Only the last 20 lines of raw xcodebuild output will be visible in the build log.
180+
- `xcpretty`: The xcodebuild command's output will be prettified by xcpretty.
181181
182182
The raw xcodebuild log will be exported in both cases.
183183
value_options:
184-
- xcpretty
184+
- xcbeautify
185185
- xcodebuild
186+
- xcpretty
186187
is_required: true
187188

188189
# Automatic code signing

step/archive.go

+9-32
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,33 @@
11
package step
22

33
import (
4-
"bytes"
54
"fmt"
65
"os"
76
"strings"
8-
"time"
97

10-
"github.com/bitrise-io/go-utils/progress"
118
"github.com/bitrise-io/go-utils/v2/log"
9+
"github.com/bitrise-io/go-xcode/v2/xcodecommand"
1210
"github.com/bitrise-io/go-xcode/xcodebuild"
1311
cache "github.com/bitrise-io/go-xcode/xcodecache"
14-
"github.com/bitrise-io/go-xcode/xcpretty"
1512
)
1613

17-
func runArchiveCommandWithRetry(archiveCmd *xcodebuild.CommandBuilder, useXcpretty bool, swiftPackagesPath string, logger log.Logger) (string, error) {
18-
output, err := runArchiveCommand(archiveCmd, useXcpretty, logger)
14+
func runArchiveCommandWithRetry(xcodeCommandRunner xcodecommand.Runner, logFormatter string, archiveCmd *xcodebuild.CommandBuilder, swiftPackagesPath string, logger log.Logger) (string, error) {
15+
output, err := runArchiveCommand(xcodeCommandRunner, logFormatter, archiveCmd, logger)
1916
if err != nil && swiftPackagesPath != "" && strings.Contains(output, cache.SwiftPackagesStateInvalid) {
2017
logger.Warnf("Archive failed, swift packages cache is in an invalid state, error: %s", err)
2118
if err := os.RemoveAll(swiftPackagesPath); err != nil {
2219
return output, fmt.Errorf("failed to remove invalid Swift package caches, error: %s", err)
2320
}
24-
return runArchiveCommand(archiveCmd, useXcpretty, logger)
21+
return runArchiveCommand(xcodeCommandRunner, logFormatter, archiveCmd, logger)
2522
}
2623
return output, err
2724
}
2825

29-
func runArchiveCommand(archiveCmd *xcodebuild.CommandBuilder, useXcpretty bool, logger log.Logger) (string, error) {
30-
if useXcpretty {
31-
xcprettyCmd := xcpretty.New(archiveCmd)
32-
33-
logger.TDonef("$ %s", xcprettyCmd.PrintableCmd())
34-
logger.Println()
35-
36-
out, err := xcprettyCmd.Run()
37-
return out, wrapXcodebuildCommandError(xcprettyCmd, out, err)
26+
func runArchiveCommand(xcodeCommandRunner xcodecommand.Runner, logFormatter string, archiveCmd *xcodebuild.CommandBuilder, logger log.Logger) (string, error) {
27+
output, err := xcodeCommandRunner.Run("", archiveCmd.CommandArgs(), []string{})
28+
if logFormatter == XcodebuildTool || err != nil {
29+
printLastLinesOfXcodebuildLog(logger, string(output.RawOut), err == nil)
3830
}
3931

40-
// Using xcodebuild
41-
logger.TDonef("$ %s", archiveCmd.PrintableCmd())
42-
logger.Println()
43-
44-
var output bytes.Buffer
45-
archiveRootCmd := archiveCmd.Command()
46-
archiveRootCmd.SetStdout(&output)
47-
archiveRootCmd.SetStderr(&output)
48-
49-
var err error
50-
progress.SimpleProgress(".", time.Minute, func() {
51-
err = archiveRootCmd.Run()
52-
})
53-
out := output.String()
54-
55-
return output.String(), wrapXcodebuildCommandError(archiveCmd, out, err)
32+
return string(output.RawOut), err
5633
}

0 commit comments

Comments
 (0)