diff --git a/README.md b/README.md index 763bf1e0..a0b49fe6 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ Build a development IPA with custom xcconfig file path: | --- | --- | --- | --- | | `project_path` | Xcode Project (`.xcodeproj`) or Workspace (`.xcworkspace`) path. The input value sets xcodebuild's `-project` or `-workspace` option. | required | `$BITRISE_PROJECT_PATH` | | `scheme` | Xcode Scheme name. The input value sets xcodebuild's `-scheme` option. | required | `$BITRISE_SCHEME` | +| `platform` | Platform to archive the product for. If set to `detect`, the step will try to detect the platform from the Xcode project settings. Its value sets xcodebuild's `-destination` option. Example: `-destination generic/platform=iOS Simulator`. | required | `detect` | | `distribution_method` | Describes how Xcode should export the archive. The input value sets the method in the export options plist content. Note: In Xcode 15.3, distribution methods have been renamed. The values of this input reflect the old names. When running with Xcode 15.3 and later, the new names are passed to `xcodebuild`: - `debugging`, when `development` is selected - `app-store-connect`, when `app-store` is selected - `release-testing`, when `ad-hoc` is selected - `enterprise` is unchanged | 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, 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` | diff --git a/e2e/bitrise.yml b/e2e/bitrise.yml index 931e5869..dddc890c 100644 --- a/e2e/bitrise.yml +++ b/e2e/bitrise.yml @@ -58,6 +58,7 @@ workflows: - TEST_APP_COMMIT: "" - BITRISE_PROJECT_PATH: Fruta.xcodeproj - BITRISE_SCHEME: Fruta iOS + - DESTINATION: iOS - CODE_SIGNING_METHOD: api-key - API_KEY_PATH: $BITFALL_APPSTORECONNECT_API_KEY_URL - API_KEY_ID: $BITFALL_APPSTORECONNECT_API_KEY_ID @@ -188,6 +189,7 @@ workflows: - TEST_APP_BRANCH: new-certificates - BITRISE_PROJECT_PATH: ios-simple-objc/ios-simple-objc.xcodeproj - BITRISE_SCHEME: ios-simple-objc + - DESTINATION: detect - CODE_SIGNING_METHOD: api-key - XCCONFIG_CONTENT: | COMPILER_INDEX_STORE_ENABLE = NO @@ -221,6 +223,7 @@ workflows: - TEST_APP_BRANCH: master - BITRISE_PROJECT_PATH: Fruta.xcodeproj - BITRISE_SCHEME: Fruta iOS + - DESTINATION: detect - CODE_SIGNING_METHOD: api-key - MIN_DAYS_PROFILE_VALID: 0 - IPA_EXPORT_METHOD: ad-hoc @@ -247,6 +250,7 @@ workflows: - TEST_APP_BRANCH: manual-signing - BITRISE_PROJECT_PATH: Fruta.xcodeproj - BITRISE_SCHEME: Fruta iOS + - DESTINATION: detect - CODE_SIGNING_METHOD: api-key - MIN_DAYS_PROFILE_VALID: 0 - IPA_EXPORT_METHOD: development @@ -272,6 +276,7 @@ workflows: - TEST_APP_BRANCH: new-certificates - BITRISE_PROJECT_PATH: ios-simple-objc/ios-simple-objc.xcodeproj - BITRISE_SCHEME: ios-simple-objc + - DESTINATION: iOS - CODE_SIGNING_METHOD: apple-id - TEAM_ID: 72SA8V3WYL - MIN_DAYS_PROFILE_VALID: 0 @@ -289,6 +294,7 @@ workflows: - TEST_APP_BRANCH: entitlements - BITRISE_PROJECT_PATH: code-sign-test.xcodeproj - BITRISE_SCHEME: code-sign-test + - DESTINATION: detect - CODE_SIGNING_METHOD: api-key - MIN_DAYS_PROFILE_VALID: 0 - IPA_EXPORT_METHOD: app-store @@ -305,6 +311,7 @@ workflows: - TEST_APP_BRANCH: automatic - BITRISE_PROJECT_PATH: code-sign-test.xcodeproj - BITRISE_SCHEME: code-sign-test + - DESTINATION: detect - CODE_SIGNING_METHOD: api-key - MIN_DAYS_PROFILE_VALID: 0 - IPA_EXPORT_METHOD: app-store @@ -331,6 +338,7 @@ workflows: - TEST_APP_BRANCH: automatic - BITRISE_PROJECT_PATH: code-sign-test.xcodeproj - BITRISE_SCHEME: code-sign-test + - DESTINATION: detect - CODE_SIGNING_METHOD: apple-id - TEAM_ID: 72SA8V3WYL - MIN_DAYS_PROFILE_VALID: 0 @@ -348,6 +356,7 @@ workflows: - TEST_APP_COMMIT: "" - BITRISE_PROJECT_PATH: code-sign-test.xcodeproj - BITRISE_SCHEME: code-sign-test-Prod + - DESTINATION: detect - CODE_SIGNING_METHOD: api-key - MIN_DAYS_PROFILE_VALID: 0 - TEAM_ID: 72SA8V3WYL @@ -366,6 +375,7 @@ workflows: - TEST_APP_COMMIT: "" - BITRISE_PROJECT_PATH: ios-simple-objc/ios-simple-objc.xcworkspace - BITRISE_SCHEME: ios-simple-objc + - DESTINATION: detect - CODE_SIGNING_METHOD: api-key - MIN_DAYS_PROFILE_VALID: 0 - TEAM_ID: 72SA8V3WYL @@ -385,6 +395,7 @@ workflows: - TEST_APP_COMMIT: "" - BITRISE_PROJECT_PATH: ios-simple-objc/ios-simple-objc.xcodeproj - BITRISE_SCHEME: ios-simple-objc + - DESTINATION: detect - CODE_SIGNING_METHOD: api-key - MIN_DAYS_PROFILE_VALID: 0 - TEAM_ID: 72SA8V3WYL @@ -407,6 +418,7 @@ workflows: - TEST_APP_BRANCH: master - BITRISE_PROJECT_PATH: code-sign-test.xcodeproj - BITRISE_SCHEME: code-sign-test + - DESTINATION: iOS - CODE_SIGNING_METHOD: api-key - MIN_DAYS_PROFILE_VALID: 112 - TEAM_ID: 72SA8V3WYL @@ -424,6 +436,7 @@ workflows: - TEST_APP_BRANCH: master - BITRISE_PROJECT_PATH: sample-apps-ios-workspace-swift.xcworkspace - BITRISE_SCHEME: sample-apps-ios-workspace-swift + - DESTINATION: iOS - CODE_SIGNING_METHOD: api-key - MIN_DAYS_PROFILE_VALID: 0 - TEAM_ID: 72SA8V3WYL @@ -442,6 +455,7 @@ workflows: - TEST_APP_COMMIT: "" - BITRISE_PROJECT_PATH: Catalyst Sample.xcodeproj - BITRISE_SCHEME: Catalyst Sample + - DESTINATION: detect - CODE_SIGNING_METHOD: api-key - MIN_DAYS_PROFILE_VALID: 0 - FORCE_CODE_SIGN_IDENTITY: "Apple Development: Tooling Bot Bitrise" @@ -484,6 +498,7 @@ workflows: inputs: - project_path: ./_tmp/$BITRISE_PROJECT_PATH - scheme: $BITRISE_SCHEME + - platform: $DESTINATION - automatic_code_signing: $CODE_SIGNING_METHOD - min_profile_validity: $MIN_DAYS_PROFILE_VALID - certificate_url_list: $BITFALL_APPLE_APPLE_CERTIFICATE_URL_LIST|$BITFALL_APPLE_IOS_CERTIFICATE_URL_LIST diff --git a/main.go b/main.go index f8dc2952..36749d0f 100644 --- a/main.go +++ b/main.go @@ -99,11 +99,12 @@ func createXcodebuildArchiver(logger log.Logger, logFormatter string) (step.Xcod func createRunOptions(config step.Config) step.RunOpts { return step.RunOpts{ - ProjectPath: config.ProjectPath, - Scheme: config.Scheme, - Configuration: config.Configuration, - XcodeMajorVersion: config.XcodeMajorVersion, - ArtifactName: config.ArtifactName, + ProjectPath: config.ProjectPath, + Scheme: config.Scheme, + DestinationPlatform: config.DestinationPlatform, + Configuration: config.Configuration, + XcodeMajorVersion: config.XcodeMajorVersion, + ArtifactName: config.ArtifactName, CodesignManager: config.CodesignManager, diff --git a/step.yml b/step.yml index c4a076b6..91d73000 100644 --- a/step.yml +++ b/step.yml @@ -90,6 +90,24 @@ inputs: The input value sets xcodebuild's `-scheme` option. is_required: true +- platform: detect + opts: + title: Platform + summary: Platform to archive the product for. + description: |- + Platform to archive the product for. + If set to `detect`, the step will try to detect the platform from the Xcode project settings. + + Its value sets xcodebuild's `-destination` option. + Example: `-destination generic/platform=iOS Simulator`. + value_options: + - detect + - iOS + - watchOS + - tvOS + - visionOS + is_required: true + - distribution_method: development opts: title: Distribution method diff --git a/step/platform.go b/step/platform.go index 1bb397b9..d664ae68 100644 --- a/step/platform.go +++ b/step/platform.go @@ -15,13 +15,31 @@ import ( type Platform string const ( - iOS Platform = "iOS" - osX Platform = "OS X" - tvOS Platform = "tvOS" - watchOS Platform = "watchOS" - visionOS Platform = "visionOS" + detectPlatform Platform = "detect" + iOS Platform = "iOS" + osX Platform = "OS X" + tvOS Platform = "tvOS" + watchOS Platform = "watchOS" + visionOS Platform = "visionOS" ) +func parsePlatform(platform string) (Platform, error) { + switch strings.ToLower(platform) { + case "detect": + return detectPlatform, nil + case "ios": + return iOS, nil + case "tvos": + return tvOS, nil + case "watchos": + return watchOS, nil + case "visionos": + return visionOS, nil + default: + return "", fmt.Errorf("unknown platform: %s", platform) + } +} + func OpenArchivableProject(pth, schemeName, configurationName string) (*xcodeproj.XcodeProj, *xcscheme.Scheme, string, error) { scheme, schemeContainerDir, err := schemeint.Scheme(pth, schemeName) if err != nil { diff --git a/step/step.go b/step/step.go index 47ba1986..84605697 100644 --- a/step/step.go +++ b/step/step.go @@ -76,6 +76,7 @@ type Inputs struct { ProjectPath string `env:"project_path,file"` Scheme string `env:"scheme,required"` ExportMethod string `env:"distribution_method,opt[app-store,ad-hoc,enterprise,development]"` + Platform string `env:"platform,opt[detect,iOS,watchOS,tvOS,visionOS]"` // xcodebuild configuration Configuration string `env:"configuration"` @@ -130,6 +131,7 @@ type Inputs struct { // Config ... type Config struct { Inputs + DestinationPlatform Platform XcodeMajorVersion int XcodebuildAdditionalOptions []string CodesignManager *codesign.Manager // nil if automatic code signing is "off" @@ -199,6 +201,10 @@ func (s XcodebuildArchiveConfigParser) ProcessInputs() (Config, error) { } var err error + if config.DestinationPlatform, err = parsePlatform(config.Platform); err != nil { + return Config{}, fmt.Errorf("issue with input Platform: %w", err) + } + 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) @@ -316,11 +322,12 @@ func (s *XcodebuildArchiver) EnsureDependencies() { // RunOpts ... type RunOpts struct { // Shared - ProjectPath string - Scheme string - Configuration string - XcodeMajorVersion int - ArtifactName string + ProjectPath string + Scheme string + DestinationPlatform Platform + Configuration string + XcodeMajorVersion int + ArtifactName string // Code signing, nil if automatic code signing is "off" CodesignManager *codesign.Manager @@ -426,12 +433,13 @@ func (s XcodebuildArchiver) Run(opts RunOpts) (RunResult, error) { s.logger.Println() archiveOpts := xcodeArchiveOpts{ - ProjectPath: opts.ProjectPath, - Scheme: opts.Scheme, - Configuration: opts.Configuration, - XcodeMajorVersion: opts.XcodeMajorVersion, - ArtifactName: opts.ArtifactName, - XcodeAuthOptions: authOptions, + ProjectPath: opts.ProjectPath, + Scheme: opts.Scheme, + DestinationPlatform: opts.DestinationPlatform, + Configuration: opts.Configuration, + XcodeMajorVersion: opts.XcodeMajorVersion, + ArtifactName: opts.ArtifactName, + XcodeAuthOptions: authOptions, PerformCleanAction: opts.PerformCleanAction, XcconfigContent: opts.XcconfigContent, @@ -784,12 +792,13 @@ func (s XcodebuildArchiveConfigParser) createCodesignManager(config Config) (cod } type xcodeArchiveOpts struct { - ProjectPath string - Scheme string - Configuration string - XcodeMajorVersion int - ArtifactName string - XcodeAuthOptions *xcodebuild.AuthenticationParams + ProjectPath string + Scheme string + DestinationPlatform Platform + Configuration string + XcodeMajorVersion int + ArtifactName string + XcodeAuthOptions *xcodebuild.AuthenticationParams PerformCleanAction bool XcconfigContent string @@ -816,9 +825,14 @@ func (s XcodebuildArchiver) xcodeArchive(opts xcodeArchiveOpts) (xcodeArchiveRes s.logger.TInfof("Reading xcode project") - platform, err := BuildableTargetPlatform(xcodeProj, scheme, configuration, opts.AdditionalOptions, XcodeBuild{}, s.logger) - if err != nil { - return out, fmt.Errorf("failed to read project platform: %s: %s", opts.ProjectPath, err) + if opts.DestinationPlatform == detectPlatform { + s.logger.TInfof("Platform is set to 'automatic', detecting platform from the project.") + s.logger.TWarnf("Define the platform step input manually to avoid this phase in the future.") + platform, err := BuildableTargetPlatform(xcodeProj, scheme, configuration, opts.AdditionalOptions, XcodeBuild{}, s.logger) + if err != nil { + return out, fmt.Errorf("failed to read project platform: %s: %s", opts.ProjectPath, err) + } + opts.DestinationPlatform = platform } s.logger.TInfof("Reading main target") @@ -869,7 +883,7 @@ and use 'Export iOS and tvOS Xcode archive' step to export an App Clip.`, opts.S archiveCmd.SetAuthentication(*opts.XcodeAuthOptions) } - additionalOptions := generateAdditionalOptions(string(platform), opts.AdditionalOptions) + additionalOptions := generateAdditionalOptions(string(opts.DestinationPlatform), opts.AdditionalOptions) archiveCmd.SetCustomOptions(additionalOptions) var swiftPackagesPath string