From ababeb3d37deb82cd938be98ce7fb58c8075c1c9 Mon Sep 17 00:00:00 2001 From: Manfred Endres <2523575+Larusso@users.noreply.github.com> Date: Wed, 25 May 2022 14:11:16 +0200 Subject: [PATCH] Improve multiple provisioning profiles support (#162) Description =========== This patch adds a new fastlane task type `SighRenewBatch` which allows to set a map of `bundleId` -> `profileName`. This allows to set the map pulled from the `exportOption.plist` into the `importProvisioningProfiles` task. I'm not 100% happy with the implementation but one could revisit this with another patch to rework the inner workings. At the moment the new task type simply extends `SighRenew` and adds a `profiles` property which gets looped over. The task action of `SighRenew` is called multiple times. This has the desired effect but can cause issues with input caching etc. I'm fine with this from a first version prespective Changes ======= * ![IMPROVE] multiple provisioning profiles support --- build.gradle | 3 +- .../FastlanePluginIntegrationSpec.groovy | 91 +++++++++++-------- .../gradle/fastlane/IntegrationSpec.groovy | 2 +- .../SighRenewBatchIntegrationSpec.groovy | 71 +++++++++++++++ .../tasks/SighRenewIntegrationSpec.groovy | 23 ++--- .../build/unity/ios/IOSBuildPlugin.groovy | 5 +- .../gradle/fastlane/FastlanePlugin.groovy | 3 + .../fastlane/FastlanePluginConventions.groovy | 2 + .../fastlane/models/FastLaneSpec.groovy | 16 ++++ .../fastlane/models/SighRenewSpec.groovy | 10 +- .../tasks/AbstractFastlaneTask.groovy | 4 + .../gradle/fastlane/tasks/SighRenew.groovy | 16 +++- .../fastlane/tasks/SighRenewBatch.groovy | 88 ++++++++++++++++++ 13 files changed, 271 insertions(+), 63 deletions(-) create mode 100644 src/integrationTest/groovy/wooga/gradle/fastlane/tasks/SighRenewBatchIntegrationSpec.groovy create mode 100644 src/main/groovy/wooga/gradle/fastlane/tasks/SighRenewBatch.groovy diff --git a/build.gradle b/build.gradle index 9dda228d..f46600ce 100644 --- a/build.gradle +++ b/build.gradle @@ -71,7 +71,8 @@ repositories { dependencies { implementation "net.wooga.gradle:dotnet-sonarqube:[0.4,0.5[" implementation "net.wooga.gradle:unity:[3,4[" - implementation 'com.wooga.gradle:gradle-commons:[1,2[' + implementation 'com.wooga.gradle:gradle-commons:[1.2.2,2[' + implementation 'com.wooga.gradle:gradle-commons-test:[1.1.1,2[' implementation "org.gradle:gradle-tooling-api:+" api 'net.wooga.gradle:secrets:1.0.0-rc.6' api 'software.amazon.awssdk:secretsmanager:[2.16,3[' diff --git a/src/integrationTest/groovy/wooga/gradle/fastlane/FastlanePluginIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/fastlane/FastlanePluginIntegrationSpec.groovy index f7f35138..2d37c1a7 100644 --- a/src/integrationTest/groovy/wooga/gradle/fastlane/FastlanePluginIntegrationSpec.groovy +++ b/src/integrationTest/groovy/wooga/gradle/fastlane/FastlanePluginIntegrationSpec.groovy @@ -18,7 +18,6 @@ package wooga.gradle.fastlane import com.wooga.gradle.test.PropertyLocation import com.wooga.gradle.test.PropertyQueryTaskWriter -import spock.lang.Requires import spock.lang.Unroll import wooga.gradle.fastlane.tasks.PilotUpload import wooga.gradle.fastlane.tasks.SighRenew @@ -65,37 +64,48 @@ class FastlanePluginIntegrationSpec extends FastlaneIntegrationSpec { query.matches(result, testValue) where: - property | method | rawValue | expectedValue | type | location | additionalInfo - "username" | _ | "someUser1" | _ | _ | PropertyLocation.environment | "" - "username" | _ | "someUser2" | _ | _ | PropertyLocation.property | "" - "username" | _ | "someUser3" | _ | "String" | PropertyLocation.script | "" - "username" | _ | "someUser4" | _ | "Provider" | PropertyLocation.script | "" - "username" | "username.set" | "someUser5" | _ | "String" | PropertyLocation.script | "" - "username" | "username.set" | "someUser6" | _ | "Provider" | PropertyLocation.script | "" - "username" | "username" | "someUser7" | _ | "String" | PropertyLocation.script | "" - "username" | "username" | "someUser8" | _ | "Provider" | PropertyLocation.script | "" - "username" | _ | _ | null | _ | PropertyLocation.none | "" - - - "password" | _ | "somePassword1" | _ | _ | PropertyLocation.environment | "" - "password" | _ | "somePassword2" | _ | _ | PropertyLocation.property | "" - "password" | _ | "somePassword3" | _ | "String" | PropertyLocation.script | "" - "password" | _ | "somePassword4" | _ | "Provider" | PropertyLocation.script | "" - "password" | "password.set" | "somePassword5" | _ | "String" | PropertyLocation.script | "" - "password" | "password.set" | "somePassword6" | _ | "Provider" | PropertyLocation.script | "" - "password" | "password" | "somePassword7" | _ | "String" | PropertyLocation.script | "" - "password" | "password" | "somePassword8" | _ | "Provider" | PropertyLocation.script | "" - "password" | _ | _ | null | _ | PropertyLocation.none | "" - - "apiKeyPath" | _ | osPath("/path/to/key1.json") | _ | _ | PropertyLocation.environment | "" - "apiKeyPath" | _ | osPath("/path/to/key2.json") | _ | _ | PropertyLocation.property | "" - "apiKeyPath" | _ | osPath("/path/to/key3.json") | _ | "File" | PropertyLocation.script | "" - "apiKeyPath" | _ | osPath("/path/to/key4.json") | _ | "Provider" | PropertyLocation.script | "" - "apiKeyPath" | "apiKeyPath.set" | osPath("/path/to/key5.json") | _ | "File" | PropertyLocation.script | "" - "apiKeyPath" | "apiKeyPath.set" | osPath("/path/to/key6.json") | _ | "Provider" | PropertyLocation.script | "" - "apiKeyPath" | "apiKeyPath" | osPath("/path/to/key7.json") | _ | "File" | PropertyLocation.script | "" - "apiKeyPath" | "apiKeyPath" | osPath("/path/to/key8.json") | _ | "Provider" | PropertyLocation.script | "" - "apiKeyPath" | _ | _ | null | _ | PropertyLocation.none | "" + property | method | rawValue | expectedValue | type | location | additionalInfo + "username" | _ | "someUser1" | _ | _ | PropertyLocation.environment | "" + "username" | _ | "someUser2" | _ | _ | PropertyLocation.property | "" + "username" | _ | "someUser3" | _ | "String" | PropertyLocation.script | "" + "username" | _ | "someUser4" | _ | "Provider" | PropertyLocation.script | "" + "username" | "username.set" | "someUser5" | _ | "String" | PropertyLocation.script | "" + "username" | "username.set" | "someUser6" | _ | "Provider" | PropertyLocation.script | "" + "username" | "username" | "someUser7" | _ | "String" | PropertyLocation.script | "" + "username" | "username" | "someUser8" | _ | "Provider" | PropertyLocation.script | "" + "username" | _ | _ | null | _ | PropertyLocation.none | "" + + + "password" | _ | "somePassword1" | _ | _ | PropertyLocation.environment | "" + "password" | _ | "somePassword2" | _ | _ | PropertyLocation.property | "" + "password" | _ | "somePassword3" | _ | "String" | PropertyLocation.script | "" + "password" | _ | "somePassword4" | _ | "Provider" | PropertyLocation.script | "" + "password" | "password.set" | "somePassword5" | _ | "String" | PropertyLocation.script | "" + "password" | "password.set" | "somePassword6" | _ | "Provider" | PropertyLocation.script | "" + "password" | "password" | "somePassword7" | _ | "String" | PropertyLocation.script | "" + "password" | "password" | "somePassword8" | _ | "Provider" | PropertyLocation.script | "" + "password" | _ | _ | null | _ | PropertyLocation.none | "" + + "apiKeyPath" | _ | osPath("/path/to/key1.json") | _ | _ | PropertyLocation.environment | "" + "apiKeyPath" | _ | osPath("/path/to/key2.json") | _ | _ | PropertyLocation.property | "" + "apiKeyPath" | _ | osPath("/path/to/key3.json") | _ | "File" | PropertyLocation.script | "" + "apiKeyPath" | _ | osPath("/path/to/key4.json") | _ | "Provider" | PropertyLocation.script | "" + "apiKeyPath" | "apiKeyPath.set" | osPath("/path/to/key5.json") | _ | "File" | PropertyLocation.script | "" + "apiKeyPath" | "apiKeyPath.set" | osPath("/path/to/key6.json") | _ | "Provider" | PropertyLocation.script | "" + "apiKeyPath" | "apiKeyPath" | osPath("/path/to/key7.json") | _ | "File" | PropertyLocation.script | "" + "apiKeyPath" | "apiKeyPath" | osPath("/path/to/key8.json") | _ | "Provider" | PropertyLocation.script | "" + "apiKeyPath" | _ | _ | null | _ | PropertyLocation.none | "" + + "skip2faUpgrade" | _ | true | _ | _ | PropertyLocation.environment | "" + "skip2faUpgrade" | _ | true | _ | _ | PropertyLocation.property | "" + "skip2faUpgrade" | _ | true | _ | "Boolean" | PropertyLocation.script | "" + "skip2faUpgrade" | _ | true | _ | "Provider" | PropertyLocation.script | "" + "skip2faUpgrade" | "skip2faUpgrade.set" | true | _ | "Boolean" | PropertyLocation.script | "" + "skip2faUpgrade" | "skip2faUpgrade.set" | true | _ | "Provider" | PropertyLocation.script | "" + "skip2faUpgrade" | "skip2faUpgrade" | true | _ | "Boolean" | PropertyLocation.script | "" + "skip2faUpgrade" | "skip2faUpgrade" | true | _ | "Provider" | PropertyLocation.script | "" + "skip2faUpgrade" | _ | _ | false | _ | PropertyLocation.none | "" + extensionName = "fastlane" value = (type != _) ? wrapValueBasedOnType(rawValue, type) : rawValue providedValue = (location == PropertyLocation.script) ? type : value @@ -128,15 +138,18 @@ class FastlanePluginIntegrationSpec extends FastlaneIntegrationSpec { query.matches(result, testValue) where: - property | extensionProperty | tasktype | rawValue | expectedValue | type | useProviderApi - "username" | "username" | SighRenew | "userName1" | _ | "String" | true - "username" | "username" | PilotUpload | "userName2" | _ | "String" | true + property | extensionProperty | tasktype | rawValue | expectedValue | type | useProviderApi + "username" | "username" | SighRenew | "userName1" | _ | "String" | true + "username" | "username" | PilotUpload | "userName2" | _ | "String" | true + + "password" | "password" | SighRenew | "password1" | _ | "String" | true + "password" | "password" | PilotUpload | "password2" | _ | "String" | true - "password" | "password" | SighRenew | "password1" | _ | "String" | true - "password" | "password" | PilotUpload | "password2" | _ | "String" | true + "apiKeyPath" | "apiKeyPath" | SighRenew | osPath("/path/to/key1.json") | _ | "File" | true + "apiKeyPath" | "apiKeyPath" | PilotUpload | osPath("/path/to/key2.json") | _ | "File" | true - "apiKeyPath" | "apiKeyPath" | SighRenew | osPath("/path/to/key1.json") | _ | "File" | true - "apiKeyPath" | "apiKeyPath" | PilotUpload | osPath("/path/to/key2.json") | _ | "File" | true + "skip2faUpgrade" | "skip2faUpgrade" | SighRenew | true | _ | "Boolean" | true + "skip2faUpgrade" | "skip2faUpgrade" | PilotUpload | true | _ | "Boolean" | true extensionName = "fastlane" taskName = "fastlaneTask" diff --git a/src/integrationTest/groovy/wooga/gradle/fastlane/IntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/fastlane/IntegrationSpec.groovy index 75fcb324..4b117419 100644 --- a/src/integrationTest/groovy/wooga/gradle/fastlane/IntegrationSpec.groovy +++ b/src/integrationTest/groovy/wooga/gradle/fastlane/IntegrationSpec.groovy @@ -27,6 +27,6 @@ class IntegrationSpec extends com.wooga.gradle.test.IntegrationSpec { this.gradleVersion = gradleVersion fork = true } - environmentVariables.clear("FASTLANE_USERNAME", "FASTLANE_PASSWORD") + environmentVariables.clear("FASTLANE_USERNAME", "FASTLANE_PASSWORD", "FASTLANE_API_KEY_PATH", "SPACESHIP_SKIP_2FA_UPGRADE", "FASTLANE_SKIP_2FA_UPGRADE") } } diff --git a/src/integrationTest/groovy/wooga/gradle/fastlane/tasks/SighRenewBatchIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/fastlane/tasks/SighRenewBatchIntegrationSpec.groovy new file mode 100644 index 00000000..439f696c --- /dev/null +++ b/src/integrationTest/groovy/wooga/gradle/fastlane/tasks/SighRenewBatchIntegrationSpec.groovy @@ -0,0 +1,71 @@ +package wooga.gradle.fastlane.tasks + +import spock.lang.Requires +import spock.lang.Unroll + +@Requires({ os.macOs }) +class SighRenewBatchIntegrationSpec extends SighRenewIntegrationSpec { + String testTaskName = "sighRenewBatch" + Class taskType = SighRenewBatch + + String workingFastlaneTaskConfig = """ + task("${testTaskName}", type: ${taskType.name}) { + appIdentifier = 'test' + teamId = "fakeTeamId" + destinationDir = file('build') + profiles = ["foo.bar.baz":"fooBar"] + } + """.stripIndent() + + def "imports multiple profiles"() { + given: "set multiple profiles" + buildFile << """ + ${testTaskName} { + profiles = ${wrapValueBasedOnType(profiles, "Map")} + } + """ + + when: + def result = runTasksSuccessfully(testTaskName) + + then: + profiles.each { bundleId, profileName -> + result.standardOutput.contains("import provisioning profile '${profileName}' for bundleIdentifier '${bundleId}' to file '${profileName}.mobileprovision'") + } + + where: + profileNames = ["profile1", "profile2", "profile3"] + bundleIds = ["net.test.app1", "net.test.app2", "net.test.app3"] + profiles = [bundleIds, profileNames].transpose().collectEntries() + } + + @Unroll + def "imports configured profileName/appIdentifier when set"() { + given: "set multiple profiles" + buildFile << """ + ${testTaskName} { + profiles = ${wrapValueBasedOnType(profiles, "Map")} + appIdentifier = ${appIdentifier ? wrapValueBasedOnType(appIdentifier, String) : null} + provisioningName = ${provisioningName ? wrapValueBasedOnType(provisioningName, String) : null} + } + """ + + when: + def result = runTasksSuccessfully(testTaskName) + + then: + result.standardOutput.contains("import ${expectedProfiles.size()} profiles") + profiles.each { bundleId, profileName -> + result.standardOutput.contains("import provisioning profile '${profileName}' for bundleIdentifier '${bundleId}' to file '${profileName}.mobileprovision'") + } + + where: + provisioningName | appIdentifier | profiles || expectedProfiles + null | null | ["foo.bar.baz": "FooBar"] || profiles + "FooBar" | "foo.bar.baz" | ["foo.bar.baz": "FooBar"] || profiles + "FooBar" | null | ["foo.bar.baz": "FooBar"] || profiles + null | "foo.bar.baz" | ["foo.bar.baz": "FooBar"] || profiles + "FooBar2" | "foo.bar.baz" | ["foo.bar.baz": "FooBar"] || ["foo.bar.baz": "FooBar", (appIdentifier): (provisioningName)] + "BazBar" | "baz.foo.bar" | ["foo.bar.baz": "FooBar"] || ["foo.bar.baz": "FooBar", (appIdentifier): (provisioningName)] + } +} diff --git a/src/integrationTest/groovy/wooga/gradle/fastlane/tasks/SighRenewIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/fastlane/tasks/SighRenewIntegrationSpec.groovy index 866e970a..eec106f7 100644 --- a/src/integrationTest/groovy/wooga/gradle/fastlane/tasks/SighRenewIntegrationSpec.groovy +++ b/src/integrationTest/groovy/wooga/gradle/fastlane/tasks/SighRenewIntegrationSpec.groovy @@ -47,7 +47,7 @@ class SighRenewIntegrationSpec extends AbstractFastlaneTaskIntegrationSpec { buildFile << """ task("readValue") { doLast { - println("arguments: " + ${testTaskName}.arguments.get().join(" ")) + println("arguments: " + ${getTestTaskName()}.arguments.get().join(" ")) } } """.stripIndent() @@ -55,7 +55,7 @@ class SighRenewIntegrationSpec extends AbstractFastlaneTaskIntegrationSpec { and: "a set property" if (method != _) { buildFile << """ - ${testTaskName}.${method}($value) + ${getTestTaskName()}.${method}($value) """.stripIndent() } @@ -99,7 +99,7 @@ class SighRenewIntegrationSpec extends AbstractFastlaneTaskIntegrationSpec { buildFile << """ task("readValue") { doLast { - println("arguments: " + ${testTaskName}.environment.get().collect {k,v -> k + '=' + v}.join("\\n")) + println("arguments: " + ${getTestTaskName()}.environment.get().collect {k,v -> k + '=' + v}.join("\\n")) } } """.stripIndent() @@ -107,7 +107,7 @@ class SighRenewIntegrationSpec extends AbstractFastlaneTaskIntegrationSpec { and: "a set property" if (method != _) { buildFile << """ - ${testTaskName}.${method}($value) + ${getTestTaskName()}.${method}($value) """.stripIndent() } @@ -118,8 +118,9 @@ class SighRenewIntegrationSpec extends AbstractFastlaneTaskIntegrationSpec { outputContains(result, expectedEnvironmentPair) where: - property | method | rawValue | type || expectedEnvironmentPair - "password" | "password.set" | "secretValue" | "String" || "FASTLANE_PASSWORD=secretValue" + property | method | rawValue | type || expectedEnvironmentPair + "password" | "password.set" | "secretValue" | "String" || "FASTLANE_PASSWORD=secretValue" + "skip2faUpgrade" | "skip2faUpgrade.set" | true | "BOLEAN" || "SPACESHIP_SKIP_2FA_UPGRADE=1" value = wrapValueBasedOnType(rawValue, type) valueMessage = (rawValue != _) ? "with value ${value}" : "without value" } @@ -130,14 +131,14 @@ class SighRenewIntegrationSpec extends AbstractFastlaneTaskIntegrationSpec { buildFile << """ task("readValue") { doLast { - println("property: " + ${testTaskName}.${property}.get()) + println("property: " + ${getTestTaskName()}.${property}.get()) } } """.stripIndent() and: "a set property" buildFile << """ - ${testTaskName}.${method}($value) + ${getTestTaskName()}.${method}($value) """.stripIndent() // TODO: Refactor @@ -255,12 +256,12 @@ class SighRenewIntegrationSpec extends AbstractFastlaneTaskIntegrationSpec { @Issue("https://github.com/wooga/atlas-build-unity/issues/38") def "task is never up-to-date"() { given: "call import tasks once" - def r = runTasks(testTaskName) + def r = runTasks(getTestTaskName()) when: "no parameter changes" - def result = runTasksSuccessfully(testTaskName) + def result = runTasksSuccessfully(getTestTaskName()) then: - !result.wasUpToDate(testTaskName) + !result.wasUpToDate(getTestTaskName()) } } diff --git a/src/main/groovy/wooga/gradle/build/unity/ios/IOSBuildPlugin.groovy b/src/main/groovy/wooga/gradle/build/unity/ios/IOSBuildPlugin.groovy index b2af9b8e..b1f4c71d 100644 --- a/src/main/groovy/wooga/gradle/build/unity/ios/IOSBuildPlugin.groovy +++ b/src/main/groovy/wooga/gradle/build/unity/ios/IOSBuildPlugin.groovy @@ -28,6 +28,7 @@ import org.gradle.api.publish.plugins.PublishingPlugin import org.gradle.api.tasks.Sync import wooga.gradle.build.unity.ios.internal.DefaultIOSBuildPluginExtension import wooga.gradle.build.unity.ios.tasks.ImportCodeSigningIdentities +import wooga.gradle.fastlane.tasks.SighRenewBatch import wooga.gradle.build.unity.ios.tasks.PodInstallTask import wooga.gradle.fastlane.FastlanePlugin import wooga.gradle.fastlane.FastlanePluginExtension @@ -238,10 +239,10 @@ class IOSBuildPlugin implements Plugin { Runtime.getRuntime().removeShutdownHook(shutdownHook) } - def importProvisioningProfiles = tasks.create("importProvisioningProfiles", SighRenew) { + def importProvisioningProfiles = tasks.create("importProvisioningProfiles", SighRenewBatch) { + it.profiles.set(extension.exportOptions.map({it.getProvisioningProfiles()})) it.dependsOn addKeychain, buildKeychain, unlockKeychain it.finalizedBy removeKeychain, lockKeychain - it.fileName.set("${'signing'}.mobileprovision".toString()) } PodInstallTask podInstall = tasks.create("podInstall", PodInstallTask) { diff --git a/src/main/groovy/wooga/gradle/fastlane/FastlanePlugin.groovy b/src/main/groovy/wooga/gradle/fastlane/FastlanePlugin.groovy index 064c9714..cb7780b8 100644 --- a/src/main/groovy/wooga/gradle/fastlane/FastlanePlugin.groovy +++ b/src/main/groovy/wooga/gradle/fastlane/FastlanePlugin.groovy @@ -26,6 +26,7 @@ import wooga.gradle.fastlane.tasks.PilotUpload import wooga.gradle.fastlane.tasks.SighRenew import static FastlanePluginConventions.* +import static wooga.gradle.fastlane.FastlanePluginConventions.SKIP_2FA_UPGRADE class FastlanePlugin implements Plugin { static final String EXTENSION_NAME = "fastlane" @@ -47,6 +48,7 @@ class FastlanePlugin implements Plugin { extension.username.convention(USERNAME_LOOKUP.getStringValueProvider(project)) extension.password.convention(PASSWORD_LOOKUP.getStringValueProvider(project)) extension.apiKeyPath.convention(API_KEY_PATH_LOOKUP.getFileValueProvider(project)) + extension.skip2faUpgrade.convention(SKIP_2FA_UPGRADE.getBooleanValueProvider(project)) } private static void configureTasks(Project project, extension) { @@ -56,6 +58,7 @@ class FastlanePlugin implements Plugin { void execute(AbstractFastlaneTask task) { task.apiKeyPath.convention(extension.apiKeyPath) task.logToStdout.convention(true) + task.skip2faUpgrade.convention(extension.skip2faUpgrade) } }) diff --git a/src/main/groovy/wooga/gradle/fastlane/FastlanePluginConventions.groovy b/src/main/groovy/wooga/gradle/fastlane/FastlanePluginConventions.groovy index 4d03a3bc..bd2c1923 100644 --- a/src/main/groovy/wooga/gradle/fastlane/FastlanePluginConventions.groovy +++ b/src/main/groovy/wooga/gradle/fastlane/FastlanePluginConventions.groovy @@ -24,4 +24,6 @@ class FastlanePluginConventions { static final PropertyLookup USERNAME_LOOKUP = new PropertyLookup("FASTLANE_USERNAME", "fastlane.username", null) static final PropertyLookup PASSWORD_LOOKUP = new PropertyLookup("FASTLANE_PASSWORD", "fastlane.password", null) static final PropertyLookup API_KEY_PATH_LOOKUP = new PropertyLookup("FASTLANE_API_KEY_PATH", "fastlane.apiKeyPath", null) + static final PropertyLookup SKIP_2FA_UPGRADE = new PropertyLookup(["SPACESHIP_SKIP_2FA_UPGRADE","FASTLANE_SKIP_2FA_UPGRADE"], "fastlane.skip2faUpgrade", null) } + diff --git a/src/main/groovy/wooga/gradle/fastlane/models/FastLaneSpec.groovy b/src/main/groovy/wooga/gradle/fastlane/models/FastLaneSpec.groovy index 7bdcd152..fe4e921d 100644 --- a/src/main/groovy/wooga/gradle/fastlane/models/FastLaneSpec.groovy +++ b/src/main/groovy/wooga/gradle/fastlane/models/FastLaneSpec.groovy @@ -56,4 +56,20 @@ trait FastLaneSpec extends BaseSpec { void setApiKeyPath(Provider value) { apiKeyPath.set(value) } + + private final Property skip2faUpgrade = objects.property(Boolean) + + @Internal + Property getSkip2faUpgrade() { + skip2faUpgrade + } + + void setSkip2faUpgrade(Provider value) { + skip2faUpgrade.set(value) + } + + void setSkip2faUpgrade(Boolean value) { + skip2faUpgrade.set(value) + } + } diff --git a/src/main/groovy/wooga/gradle/fastlane/models/SighRenewSpec.groovy b/src/main/groovy/wooga/gradle/fastlane/models/SighRenewSpec.groovy index a5359807..c6e9db63 100644 --- a/src/main/groovy/wooga/gradle/fastlane/models/SighRenewSpec.groovy +++ b/src/main/groovy/wooga/gradle/fastlane/models/SighRenewSpec.groovy @@ -3,11 +3,13 @@ package wooga.gradle.fastlane.models import com.wooga.gradle.BaseSpec import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileCollection import org.gradle.api.file.RegularFile import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.OutputFiles trait SighRenewSpec extends BaseSpec { @@ -100,12 +102,4 @@ trait SighRenewSpec extends BaseSpec { void setIgnoreProfilesWithDifferentName(Provider value) { ignoreProfilesWithDifferentName.set(value) } - - // TODO: The right way? - private final Provider mobileProvisioningProfile = providers.provider({ destinationDir.file(fileName) }) - - @OutputFile - Provider getMobileProvisioningProfile() { - mobileProvisioningProfile - } } diff --git a/src/main/groovy/wooga/gradle/fastlane/tasks/AbstractFastlaneTask.groovy b/src/main/groovy/wooga/gradle/fastlane/tasks/AbstractFastlaneTask.groovy index 13c2ed26..28946114 100644 --- a/src/main/groovy/wooga/gradle/fastlane/tasks/AbstractFastlaneTask.groovy +++ b/src/main/groovy/wooga/gradle/fastlane/tasks/AbstractFastlaneTask.groovy @@ -43,6 +43,10 @@ abstract class AbstractFastlaneTask extends DefaultTask implements FastLaneTaskS environment['FASTLANE_PASSWORD'] = password.get() } + if (skip2faUpgrade.isPresent() && skip2faUpgrade.get()) { + environment["SPACESHIP_SKIP_2FA_UPGRADE"] = "1" + } + environment as Map })) diff --git a/src/main/groovy/wooga/gradle/fastlane/tasks/SighRenew.groovy b/src/main/groovy/wooga/gradle/fastlane/tasks/SighRenew.groovy index a8f6b969..a69be597 100644 --- a/src/main/groovy/wooga/gradle/fastlane/tasks/SighRenew.groovy +++ b/src/main/groovy/wooga/gradle/fastlane/tasks/SighRenew.groovy @@ -16,8 +16,12 @@ package wooga.gradle.fastlane.tasks - +import org.gradle.api.file.FileCollection +import org.gradle.api.file.RegularFile +import org.gradle.api.provider.Provider import org.gradle.api.specs.Spec +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputFiles import wooga.gradle.fastlane.models.SighRenewSpec class SighRenew extends AbstractFastlaneTask implements SighRenewSpec { @@ -53,5 +57,15 @@ class SighRenew extends AbstractFastlaneTask implements SighRenewSpec { }) } + @Internal + Provider getMobileProvisioningProfile() { + destinationDir.file(fileName) + } + + @OutputFiles + protected FileCollection getOutputFiles() { + project.files(mobileProvisioningProfile) + } + } diff --git a/src/main/groovy/wooga/gradle/fastlane/tasks/SighRenewBatch.groovy b/src/main/groovy/wooga/gradle/fastlane/tasks/SighRenewBatch.groovy new file mode 100644 index 00000000..13ec4b1a --- /dev/null +++ b/src/main/groovy/wooga/gradle/fastlane/tasks/SighRenewBatch.groovy @@ -0,0 +1,88 @@ +package wooga.gradle.fastlane.tasks + +import org.gradle.api.file.FileCollection +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Provider +import org.gradle.api.specs.Spec +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.TaskAction + +/** + * Batch version of {@code SighRenew} to import multiple + * profiles with one call. + * + * @see wooga.gradle.fastlane.tasks.SighRenew + */ +class SighRenewBatch extends SighRenew { + + private final MapProperty profiles = objects.mapProperty(String, String) + + @Input + @Optional + MapProperty getProfiles() { + profiles + } + + void setProfiles(Map value) { + profiles.set(value) + } + + void setProfiles(Provider> value) { + profiles.set(value) + } + + void profiles(Map value) { + profiles.putAll(value) + } + + void profiles(Provider> value) { + profiles.putAll(value) + } + + void profiles(String key, String value) { + profiles.put(key, value) + } + + void profiles(String key, Provider value) { + profiles.put(key, value) + } + + @Override + protected FileCollection getOutputFiles() { + def files = profiles.map({ profiles -> profiles.values().collect({ profileName -> destinationDir.file("${profileName}.mobileprovision").get() }) }) + project.files(files) + } + + + SighRenewBatch() { + fileName.set("some_value") + setOnlyIf(new Spec() { + @Override + boolean isSatisfiedBy(SighRenewBatch task) { + (task.teamId.present || task.teamName.present) && task.profiles.present && !profiles.get().isEmpty() + } + }) + } + + @TaskAction + protected importProfiles() { + def profiles = new HashMap() + profiles.putAll(this.profiles.getOrElse([:])) + if ((appIdentifier.present && !profiles.containsKey(appIdentifier.get())) && (provisioningName.present && !profiles.containsValue(provisioningName.get()))) { + logger.info("task appIdentifier and provisioning name not in profiles") + logger.info("add them to the profiles map") + profiles.put(appIdentifier.get(), provisioningName.get()) + } + + logger.info("import ${profiles.size()} profiles") + profiles.each { appId, name -> + appIdentifier.set(appId) + provisioningName.set(name) + fileName.set("${name}.mobileprovision") + + logger.info("import provisioning profile '${name}' for bundleIdentifier '${appId}' to file '${fileName.get()}'") + exec() + } + } +}