From d6100891055e13cac9e7a2f1da17d993585aafc3 Mon Sep 17 00:00:00 2001 From: lpusok <7979773+lpusok@users.noreply.github.com> Date: Wed, 25 May 2022 10:20:08 +0200 Subject: [PATCH] Support custom xcconfig file path (#297) Allow passing a file path to an existing xcconfig file, instead of providing the file contents directly. * `xcconfig_content` can now be a path to a xcconfig file, or left empty. This allows using a custom xcconfig file that already exists in the repository. --- README.md | 37 ++++++++- additional_options.go | 21 +++++ additional_options_test.go | 41 ++++++++++ bitrise.yml | 1 + docs/examples.md | 31 ++++++++ go.mod | 2 +- go.sum | 4 +- main.go | 77 ++++++++++--------- step.yml | 37 ++++++--- .../go-xcode/v2/xcconfig/xcconfig.go | 35 +++++++-- vendor/modules.txt | 2 +- 11 files changed, 226 insertions(+), 62 deletions(-) create mode 100644 additional_options.go create mode 100644 additional_options_test.go create mode 100644 docs/examples.md diff --git a/README.md b/README.md index 76c91147..008de906 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ To configure the Step: Under **xcodebuild configuration**: 1. **Build configuration**: Specify Xcode Build Configuration. The Step uses the provided Build Configuration's Build Settings to understand your project's code signing configuration. If not provided, the Archive action's default Build Configuration will be used. -2. **Build settings (xconfig)**: Build settings to override the project's build settings. The build settings must be separated by a newline character (`\n`). +2. **Build settings (xcconfig)**: Build settings to override the project's build settings. Can be the contents, file path or empty. 3. **Perform clean action**: If this input is set, a `clean` xcodebuild action will be performed besides the `archive` action. Under **Xcode build log formatting**: @@ -67,6 +67,38 @@ Add this step directly to your workflow in the [Bitrise Workflow Editor](https:/ You can also run this step directly with [Bitrise CLI](https://github.com/bitrise-io/bitrise). +### Examples + +Build a development IPA: +```yaml +- xcode-archive: + inputs: + - project_path: ./ios-sample/ios-sample.xcodeproj + - scheme: ios-sample + - distribution_method: development +``` + +Build a development IPA with custom xcconfig content: +```yaml +- xcode-archive: + inputs: + - project_path: ./ios-sample/ios-sample.xcodeproj + - scheme: ios-sample + - distribution_method: development + - xcconfig_content: | + CODE_SIGN_IDENTITY = Apple Development +``` + +Build a development IPA with custom xcconfig file path: +```yaml +- xcode-archive: + inputs: + - project_path: ./ios-sample/ios-sample.xcodeproj + - scheme: ios-sample + - distribution_method: development + - xcconfig_content: ./ios-sample/ios-sample/Configurations/Dev.xcconfig +``` + ## ⚙️ Configuration
@@ -78,7 +110,7 @@ You can also run this step directly with [Bitrise CLI](https://github.com/bitris | `scheme` | Xcode Scheme name. The input value sets xcodebuild's `-scheme` option. | required | `$BITRISE_SCHEME` | | `distribution_method` | Describes how Xcode should export the archive. | required | `development` | | `configuration` | Xcode Build Configuration. If not specified, the default Build Configuration will be used. The input value sets xcodebuild's `-configuration` option. | | | -| `xcconfig_content` | Build settings to override the project's build settings. Build settings must be separated by newline character (`\n`). Example: ``` COMPILER_INDEX_STORE_ENABLE = NO ONLY_ACTIVE_ARCH[config=Debug][sdk=*][arch=*] = YES ``` The input value sets xcodebuild's `-xcconfig` option. | | `COMPILER_INDEX_STORE_ENABLE = NO` | +| `xcconfig_content` | Build settings to override the project's build settings, using xcodebuild's `-xcconfig` option. If empty, no setting is changed. This is required when the `-xcconfig` additional option is used. 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` | | `perform_clean_action` | If this input is set, `clean` xcodebuild action will be performed besides the `archive` action. | required | `no` | | `xcodebuild_options` | Additional options to be added to the executed xcodebuild command. | | | | `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` | @@ -89,6 +121,7 @@ You can also run this step directly with [Bitrise CLI](https://github.com/bitris | `passphrase_list` | Passphrases for the provided code signing certificates. Specify as many passphrases as many Code signing certificate URL provided, separated by a pipe (`\|`) character. Certificates without a passphrase: for using a single certificate, leave this step input empty. For multiple certificates, use the separator as if there was a passphrase (examples: `pass\|`, `\|pass\|`, `\|`) | sensitive | `$BITRISE_CERTIFICATE_PASSPHRASE` | | `keychain_path` | Path to the Keychain where the code signing certificates will be installed. | required | `$HOME/Library/Keychains/login.keychain` | | `keychain_password` | Password for the provided Keychain. | required, sensitive | `$BITRISE_KEYCHAIN_PASSWORD` | +| `fallback_provisioning_profile_url_list` | If set, provided provisioning profiles will be used on Automatic code signing error. URL of the provisioning profile to download. Multiple URLs can be specified, separated by a newline or pipe (`\|`) character. You can specify a local path as well, using the `file://` scheme. For example: `file://./BuildAnything.mobileprovision`. Can also provide a local directory that contains files with `.mobileprovision` extension. For example: `./profilesDirectory/` | sensitive | | | `export_development_team` | The Developer Portal team to use for this export Defaults to the team used to build the archive. Defining this is also required when Automatic Code Signing is set to `apple-id` and the connected account belongs to multiple teams. | | | | `compile_bitcode` | For __non-App Store__ exports, should Xcode re-compile the app from bitcode? | required | `yes` | | `upload_bitcode` | For __App Store__ exports, should the package include bitcode? | required | `yes` | diff --git a/additional_options.go b/additional_options.go new file mode 100644 index 00000000..8cdeaea5 --- /dev/null +++ b/additional_options.go @@ -0,0 +1,21 @@ +package main + +import "github.com/bitrise-io/go-utils/sliceutil" + +func generateAdditionalOptions(platform string, customOptions []string) []string { + destination := "generic/platform=" + platform + destinationOptions := []string{"-destination", destination} + + var options []string + if len(customOptions) != 0 { + if !sliceutil.IsStringInSlice("-destination", customOptions) { + options = append(options, destinationOptions...) + } + + options = append(options, customOptions...) + } else { + options = append(options, destinationOptions...) + } + + return options +} diff --git a/additional_options_test.go b/additional_options_test.go new file mode 100644 index 00000000..9c5a2d7f --- /dev/null +++ b/additional_options_test.go @@ -0,0 +1,41 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_generateAdditionalOptions(t *testing.T) { + tests := []struct { + name string + platform string + customOptions []string + want []string + }{ + { + name: "no custom options", + platform: "iOS", + want: []string{"-destination", "generic/platform=iOS"}, + }, + { + name: "custom opts", + platform: "iOS", + customOptions: []string{"-scmProvider", "system"}, + want: []string{"-destination", "generic/platform=iOS", "-scmProvider", "system"}, + }, + { + name: "custom opts with destination", + platform: "iOS", + customOptions: []string{"-scmProvider", "system", "-destination", "generic/platform=iOS"}, + want: []string{"-scmProvider", "system", "-destination", "generic/platform=iOS"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := generateAdditionalOptions(tt.platform, tt.customOptions) + + require.Equal(t, tt.want, got) + }) + } +} diff --git a/bitrise.yml b/bitrise.yml index 29997463..27cc6907 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -47,3 +47,4 @@ workflows: - git::https://github.com/bitrise-steplib/steps-readme-generator.git@main: inputs: - contrib_section: docs/contribution.md + - example_section: docs/examples.md diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 00000000..153fd2fc --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,31 @@ +### Examples + +Build a development IPA: +```yaml +- xcode-archive: + inputs: + - project_path: ./ios-sample/ios-sample.xcodeproj + - scheme: ios-sample + - distribution_method: development +``` + +Build a development IPA with custom xcconfig content: +```yaml +- xcode-archive: + inputs: + - project_path: ./ios-sample/ios-sample.xcodeproj + - scheme: ios-sample + - distribution_method: development + - xcconfig_content: | + CODE_SIGN_IDENTITY = Apple Development +``` + +Build a development IPA with custom xcconfig file path: +```yaml +- xcode-archive: + inputs: + - project_path: ./ios-sample/ios-sample.xcodeproj + - scheme: ios-sample + - distribution_method: development + - xcconfig_content: ./ios-sample/ios-sample/Configurations/Dev.xcconfig +``` \ No newline at end of file diff --git a/go.mod b/go.mod index e3da549b..526ee18c 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/bitrise-io/go-utils v1.0.1 github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.2 github.com/bitrise-io/go-xcode v1.0.6 - github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.14 + github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.15 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/stretchr/testify v1.7.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b diff --git a/go.sum b/go.sum index 62d76607..4421f565 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,8 @@ github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.2 h1:w3fwgLLmxMOpYNa6W5aLtJZE8M8+ github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.2/go.mod h1:sy+Ir1X8P3tAAx/qU/r+hqDjHDcrMjIzDEvId1wqNc4= github.com/bitrise-io/go-xcode v1.0.6 h1:hSKwkDXUn9/gMk6HiJRUvurGWelfQEBWcO7JAvXi+y8= github.com/bitrise-io/go-xcode v1.0.6/go.mod h1:Y0Wu2dXm0MilJ/4D3+gPHaNMlUcP+1DjIPoLPykq7wY= -github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.14 h1:Tfo5QuCZmb/BTC8QWEhPxuP02FzjnjEE19jTzQFag2o= -github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.14/go.mod h1:IhG2l/bM8+809Jlwt4hgRzOkRfPmhEybfWMOJdEGnEU= +github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.15 h1:swynA2yBvKWdMEPDz/GFM55ELekauWIvtDCLWKScVhU= +github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.15/go.mod h1:IhG2l/bM8+809Jlwt4hgRzOkRfPmhEybfWMOJdEGnEU= github.com/bitrise-io/pkcs12 v0.0.0-20211108084543-e52728e011c8 h1:kmvU8AxrNTxXsVPKepBHD8W+eCVmeaKyTkRuUJB2K38= github.com/bitrise-io/pkcs12 v0.0.0-20211108084543-e52728e011c8/go.mod h1:UiXKNs0essbC14a2TvGlnUKo9isP9m4guPrp8KJHJpU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/main.go b/main.go index c0a0ab27..bb04877f 100644 --- a/main.go +++ b/main.go @@ -113,8 +113,9 @@ type Inputs struct { // Config ... type Config struct { Inputs - XcodeMajorVersion int - CodesignManager *codesign.Manager // nil if automatic code signing is "off" + XcodeMajorVersion int + XcodebuildAdditionalOptions []string + CodesignManager *codesign.Manager // nil if automatic code signing is "off" } var envRepository = env.NewRepository() @@ -199,6 +200,7 @@ type XcodeArchiveStep struct { xcodeVersionProvider xcodeVersionProvider stepInputParser stepconf.InputParser pathProvider pathutil.PathProvider + pathChecker pathutil.PathChecker fileManager fileutil.FileManager } @@ -208,6 +210,7 @@ func NewXcodeArchiveStep() XcodeArchiveStep { xcodeVersionProvider: newXcodebuildXcodeVersionProvider(), stepInputParser: stepconf.NewInputParser(env.NewRepository()), pathProvider: pathutil.NewPathProvider(), + pathChecker: pathutil.NewPathChecker(), fileManager: fileutil.NewFileManager(), } } @@ -226,6 +229,20 @@ func (s XcodeArchiveStep) ProcessInputs() (Config, error) { logger.EnableDebugLog(config.VerboseLog) v1log.SetEnableDebugLog(config.VerboseLog) // For compatibility + var err error + config.XcodebuildAdditionalOptions, err = shellquote.Split(inputs.XcodebuildOptions) + if err != nil { + return Config{}, fmt.Errorf("provided XcodebuildOptions (%s) are not valid CLI parameters: %s", inputs.XcodebuildOptions, err) + } + + if strings.TrimSpace(config.XcconfigContent) == "" { + config.XcconfigContent = "" + } + if sliceutil.IsStringInSlice("-xcconfig", config.XcodebuildAdditionalOptions) && + config.XcconfigContent != "" { + return Config{}, fmt.Errorf("`-xcconfig` option found in XcodebuildOptions (`xcodebuild_options`), please clear Build settings (xcconfig) (`xcconfig_content`) input as only one can be set") + } + if config.ExportOptionsPlistContent != "" { var options map[string]interface{} if _, err := plist.Unmarshal([]byte(config.ExportOptionsPlistContent), &options); err != nil { @@ -438,7 +455,7 @@ type xcodeArchiveOpts struct { PerformCleanAction bool XcconfigContent string - CustomOptions []string + AdditionalOptions []string CacheLevel string } @@ -496,12 +513,14 @@ func (s XcodeArchiveStep) xcodeArchive(opts xcodeArchiveOpts) (xcodeArchiveOutpu archiveCmd.SetCustomBuildAction("clean") } - xcconfigWriter := xcconfig.NewWriter(s.pathProvider, s.fileManager) - xcconfigPath, err := xcconfigWriter.Write(opts.XcconfigContent) - if err != nil { - return out, fmt.Errorf("failed to write xcconfig file contents: %w", err) + if opts.XcconfigContent != "" { + xcconfigWriter := xcconfig.NewWriter(s.pathProvider, s.fileManager, s.pathChecker) + xcconfigPath, err := xcconfigWriter.Write(opts.XcconfigContent) + if err != nil { + return out, fmt.Errorf("failed to write xcconfig file contents: %w", err) + } + archiveCmd.SetXCConfigPath(xcconfigPath) } - archiveCmd.SetXCConfigPath(xcconfigPath) tmpDir, err := v1pathutil.NormalizedOSTempDirPath("xcodeArchive") if err != nil { @@ -514,21 +533,8 @@ func (s XcodeArchiveStep) xcodeArchive(opts xcodeArchiveOpts) (xcodeArchiveOutpu archiveCmd.SetAuthentication(*opts.XcodeAuthOptions) } - destination := "generic/platform=" + string(platform) - destinationOptions := []string{"-destination", destination} - - options := []string{} - if len(opts.CustomOptions) != 0 { - if !sliceutil.IsStringInSlice("-destination", opts.CustomOptions) { - options = append(options, destinationOptions...) - } - - options = append(options, opts.CustomOptions...) - } else { - options = append(options, destinationOptions...) - } - - archiveCmd.SetCustomOptions(options) + additionalOptions := generateAdditionalOptions(string(platform), opts.AdditionalOptions) + archiveCmd.SetCustomOptions(additionalOptions) var swiftPackagesPath string if opts.XcodeMajorVersion >= 11 { @@ -776,10 +782,10 @@ type RunOpts struct { CodesignManager *codesign.Manager // Archive - PerformCleanAction bool - XcconfigContent string - XcodebuildOptions string - CacheLevel string + PerformCleanAction bool + XcconfigContent string + XcodebuildAdditionalOptions []string + CacheLevel string // IPA Export CustomExportOptionsPlistContent string @@ -810,17 +816,12 @@ func (s XcodeArchiveStep) Run(opts RunOpts) (RunOut, error) { authOptions *xcodebuild.AuthenticationParams ) - customOptions, err := shellquote.Split(opts.XcodebuildOptions) - if err != nil { - return out, fmt.Errorf("provided XcodebuildOptions (%s) are not valid CLI parameters: %s", opts.XcodebuildOptions, err) - } - logger.Println() if opts.XcodeMajorVersion >= 11 { // Resolve Swift package dependencies, so running -showBuildSettings later is faster later // Specifying a scheme is required for workspaces resolveDepsCmd := xcodebuild.NewResolvePackagesCommandModel(opts.ProjectPath, opts.Scheme, opts.Configuration) - resolveDepsCmd.SetCustomOptions(customOptions) + resolveDepsCmd.SetCustomOptions(opts.XcodebuildAdditionalOptions) if err := resolveDepsCmd.Run(); err != nil { logger.Warnf("%s", err) } @@ -886,7 +887,7 @@ func (s XcodeArchiveStep) Run(opts RunOpts) (RunOut, error) { PerformCleanAction: opts.PerformCleanAction, XcconfigContent: opts.XcconfigContent, - CustomOptions: customOptions, + AdditionalOptions: opts.XcodebuildAdditionalOptions, CacheLevel: opts.CacheLevel, } archiveOut, err := s.xcodeArchive(archiveOpts) @@ -1171,10 +1172,10 @@ func RunStep() error { CodesignManager: config.CodesignManager, - PerformCleanAction: config.PerformCleanAction, - XcconfigContent: config.XcconfigContent, - XcodebuildOptions: config.XcodebuildOptions, - CacheLevel: config.CacheLevel, + PerformCleanAction: config.PerformCleanAction, + XcconfigContent: config.XcconfigContent, + XcodebuildAdditionalOptions: config.XcodebuildAdditionalOptions, + CacheLevel: config.CacheLevel, CustomExportOptionsPlistContent: config.ExportOptionsPlistContent, ExportMethod: config.ExportMethod, diff --git a/step.yml b/step.yml index 63e28374..a29ecbd7 100644 --- a/step.yml +++ b/step.yml @@ -23,7 +23,7 @@ description: |- Under **xcodebuild configuration**: 1. **Build configuration**: Specify Xcode Build Configuration. The Step uses the provided Build Configuration's Build Settings to understand your project's code signing configuration. If not provided, the Archive action's default Build Configuration will be used. - 2. **Build settings (xconfig)**: Build settings to override the project's build settings. The build settings must be separated by a newline character (`\n`). + 2. **Build settings (xcconfig)**: Build settings to override the project's build settings. Can be the contents, file path or empty. 3. **Perform clean action**: If this input is set, a `clean` xcodebuild action will be performed besides the `archive` action. Under **Xcode build log formatting**: @@ -62,7 +62,6 @@ project_type_tags: - flutter type_tags: - build -is_requires_admin_user: false is_always_run: false is_skippable: false deps: @@ -121,20 +120,28 @@ inputs: opts: category: xcodebuild configuration title: Build settings (xcconfig) - summary: Build settings to override the project's build settings. + summary: Build settings to override the project's build settings. Can be the contents, file path or empty. description: |- - Build settings to override the project's build settings. + Build settings to override the project's build settings, using xcodebuild's `-xcconfig` option. - Build settings must be separated by newline character (`\n`). + If empty, no setting is changed. This is required when the `-xcconfig` additional option is used. - Example: + When set it can be either: + 1. Existing `.xcconfig` file path. - ``` - COMPILER_INDEX_STORE_ENABLE = NO - ONLY_ACTIVE_ARCH[config=Debug][sdk=*][arch=*] = YES - ``` + Example: - The input value sets xcodebuild's `-xcconfig` option. + `./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 + ``` - perform_clean_action: "no" opts: @@ -150,7 +157,13 @@ inputs: opts: category: xcodebuild configuration title: Additional options for the xcodebuild command - summary: Additional options to be added to the executed xcodebuild command. + description: Additional options to be added to the executed xcodebuild command. + summary: |- + Additional options to be added to the executed xcodebuild command. + + When setting the `-xcconfig` option, makes sure to clear the "Build settings (xcconfig)" input, as can not specify both. + + `-destination` is set automatically, unless specified explicitely. # xcodebuild log formatting diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/xcconfig/xcconfig.go b/vendor/github.com/bitrise-io/go-xcode/v2/xcconfig/xcconfig.go index 24cc4263..e7ef14e5 100644 --- a/vendor/github.com/bitrise-io/go-xcode/v2/xcconfig/xcconfig.go +++ b/vendor/github.com/bitrise-io/go-xcode/v2/xcconfig/xcconfig.go @@ -2,34 +2,57 @@ package xcconfig import ( "fmt" + "path/filepath" + "strings" + "github.com/bitrise-io/go-utils/v2/fileutil" "github.com/bitrise-io/go-utils/v2/pathutil" - "path/filepath" ) // Writer ... type Writer interface { - Write(content string) (string, error) + Write(input string) (string, error) } type writer struct { pathProvider pathutil.PathProvider fileManager fileutil.FileManager + pathChecker pathutil.PathChecker } // NewWriter ... -func NewWriter(pathProvider pathutil.PathProvider, fileManager fileutil.FileManager) Writer { - return &writer{pathProvider: pathProvider, fileManager: fileManager} +func NewWriter(pathProvider pathutil.PathProvider, fileManager fileutil.FileManager, pathChecker pathutil.PathChecker) Writer { + return &writer{pathProvider: pathProvider, fileManager: fileManager, pathChecker: pathChecker} } -func (w writer) Write(content string) (string, error) { +// Write writes the contents of input into a xcconfig file if +// the provided content is not already a path to xcconfig file. +// If the content is a valid path to xcconfig, it will validate the path, +// and return the path. It returns error if it cannot finalize a xcconfig +// file and/or its path. +func (w writer) Write(input string) (string, error) { + if w.isPath(input) { + pathExists, err := w.pathChecker.IsPathExists(input) + if err != nil { + return "", fmt.Errorf(err.Error()) + } + if !pathExists { + return "", fmt.Errorf("provided xcconfig file path doesn't exist: %s", input) + } + return input, nil + } + dir, err := w.pathProvider.CreateTempDir("") if err != nil { return "", fmt.Errorf("unable to create temp dir for writing XCConfig: %v", err) } xcconfigPath := filepath.Join(dir, "temp.xcconfig") - if err = w.fileManager.Write(xcconfigPath, content, 0644); err != nil { + if err = w.fileManager.Write(xcconfigPath, input, 0644); err != nil { return "", fmt.Errorf("unable to write XCConfig content into file: %v", err) } return xcconfigPath, nil } + +func (w writer) isPath(input string) bool { + return strings.HasSuffix(input, ".xcconfig") +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 6db41716..05e3aa3c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -54,7 +54,7 @@ github.com/bitrise-io/go-xcode/xcodeproject/xcodeproj github.com/bitrise-io/go-xcode/xcodeproject/xcscheme github.com/bitrise-io/go-xcode/xcodeproject/xcworkspace github.com/bitrise-io/go-xcode/xcpretty -# github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.14 +# github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.15 ## explicit github.com/bitrise-io/go-xcode/v2/autocodesign github.com/bitrise-io/go-xcode/v2/autocodesign/certdownloader