diff --git a/build.gradle b/build.gradle index 95ab468..09be664 100644 --- a/build.gradle +++ b/build.gradle @@ -51,7 +51,8 @@ cveHandler { dependencies { api 'net.wooga.gradle:macos-security:[1.0.1,2[' api 'net.wooga.gradle:xcodebuild:[1,2[' - api 'net.wooga.gradle:fastlane:[1.0.1,2[' + api 'net.wooga.gradle:fastlane:[1.2,2[' + implementation "com.googlecode.plist:dd-plist:1.23" integrationTestImplementation'com.wooga.spock.extensions:spock-macos-keychain-extension:[1,2[' implementation 'com.wooga.gradle:gradle-commons:[1,2[' testImplementation 'com.wooga.gradle:gradle-commons-test:[1,2[' diff --git a/src/integrationTest/groovy/wooga/gradle/build/unity/ios/IOSBuildPluginIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/build/unity/ios/IOSBuildPluginIntegrationSpec.groovy index de09eaa..0fb2107 100644 --- a/src/integrationTest/groovy/wooga/gradle/build/unity/ios/IOSBuildPluginIntegrationSpec.groovy +++ b/src/integrationTest/groovy/wooga/gradle/build/unity/ios/IOSBuildPluginIntegrationSpec.groovy @@ -351,6 +351,8 @@ class IOSBuildPluginIntegrationSpec extends IOSBuildIntegrationSpec { SighRenew | "provisioningName" | "provisioningNameValue" | "String" | ConventionSource.extension(extensionName, property) | _ | _ SighRenew | "adhoc" | true | "Boolean" | ConventionSource.extension(extensionName, property) | _ | _ SighRenew | "fileName" | "signing.mobileprovision" | "String" | _ | _ | _ + SighRenew | "readOnly" | true | "Boolean" | _ | _ | _ + SighRenew | "skipInstall" | true | "Boolean" | _ | _ | _ PilotUpload | "devPortalTeamId" | "test.teamId" | "String" | ConventionSource.extension(extensionName, "teamId") | _ | _ PilotUpload | "appIdentifier" | "test.appIdentifier" | "String" | ConventionSource.extension(extensionName, property) | _ | _ @@ -366,213 +368,220 @@ class IOSBuildPluginIntegrationSpec extends IOSBuildIntegrationSpec { runPropertyQuery(get, set).matches(rawValue) where: - property | invocation | rawValue | type | location - "keychainPassword" | _ | "testPassword1" | _ | PropertyLocation.environment - "keychainPassword" | _ | "testPassword2" | _ | PropertyLocation.property - "keychainPassword" | assignment | "testPassword3" | "String" | PropertyLocation.script - "keychainPassword" | assignment | "testPassword4" | "Provider" | PropertyLocation.script - "keychainPassword" | providerSet | "testPassword5" | "String" | PropertyLocation.script - "keychainPassword" | providerSet | "testPassword6" | "Provider" | PropertyLocation.script - "keychainPassword" | setter | "testPassword7" | "String" | PropertyLocation.script - "keychainPassword" | setter | "testPassword8" | "Provider" | PropertyLocation.script - "keychainPassword" | _ | null | _ | PropertyLocation.none - - "signingIdentities" | _ | TestValue.set("ID1,ID2").expect(["ID1", "ID2"]) | _ | PropertyLocation.environment - "signingIdentities" | _ | TestValue.set("ID1,ID2").expect(["ID1", "ID2"]) | _ | PropertyLocation.property - "signingIdentities" | assignment | ["ID1", "ID2"] | "List" | PropertyLocation.script - "signingIdentities" | assignment | ["ID1", "ID2"] | "Provider>" | PropertyLocation.script - "signingIdentities" | providerSet | ["ID1", "ID2"] | "List" | PropertyLocation.script - "signingIdentities" | providerSet | ["ID1", "ID2"] | "Provider>" | PropertyLocation.script - "signingIdentities" | setter | ["ID1", "ID2"] | "List" | PropertyLocation.script - "signingIdentities" | setter | ["ID1", "ID2"] | "Provider>" | PropertyLocation.script - "signingIdentities" | customSetter("setSigningIdentity") | TestValue.set(wrapValueBasedOnType("code sign: ID3", "String")).expect(["code sign: ID3"]) | "Provider>" | PropertyLocation.script - "signingIdentities" | customSetter("setSigningIdentity") | TestValue.set(wrapValueBasedOnType("code sign: ID3", "String")).expect(["code sign: ID3"]) | "String>" | PropertyLocation.script - "signingIdentities" | _ | [] | _ | PropertyLocation.none - - "codeSigningIdentityFile" | _ | osPath("/path/to/p12") | _ | PropertyLocation.environment - "codeSigningIdentityFile" | _ | osPath("/path/to/p12") | _ | PropertyLocation.property - "codeSigningIdentityFile" | assignment | osPath("/path/to/p12") | "File" | PropertyLocation.script - "codeSigningIdentityFile" | assignment | osPath("/path/to/p12") | "Provider" | PropertyLocation.script - "codeSigningIdentityFile" | providerSet | osPath("/path/to/p12") | "File" | PropertyLocation.script - "codeSigningIdentityFile" | providerSet | osPath("/path/to/p12") | "Provider" | PropertyLocation.script - "codeSigningIdentityFile" | setter | osPath("/path/to/p12") | "File" | PropertyLocation.script - "codeSigningIdentityFile" | setter | osPath("/path/to/p12") | "Provider" | PropertyLocation.script - "codeSigningIdentityFile" | _ | null | _ | PropertyLocation.none - - "codeSigningIdentityFilePassphrase" | _ | "testPassphrase1" | _ | PropertyLocation.environment - "codeSigningIdentityFilePassphrase" | _ | "testPassphrase2" | _ | PropertyLocation.property - "codeSigningIdentityFilePassphrase" | assignment | "testPassphrase3" | "String" | PropertyLocation.script - "codeSigningIdentityFilePassphrase" | assignment | "testPassphrase4" | "Provider" | PropertyLocation.script - "codeSigningIdentityFilePassphrase" | providerSet | "testPassphrase5" | "String" | PropertyLocation.script - "codeSigningIdentityFilePassphrase" | providerSet | "testPassphrase6" | "Provider" | PropertyLocation.script - "codeSigningIdentityFilePassphrase" | setter | "testPassphrase7" | "String" | PropertyLocation.script - "codeSigningIdentityFilePassphrase" | setter | "testPassphrase8" | "Provider" | PropertyLocation.script - "codeSigningIdentityFilePassphrase" | _ | null | _ | PropertyLocation.none - - "appIdentifier" | _ | "com.test.app.1" | _ | PropertyLocation.environment - "appIdentifier" | _ | "com.test.app.2" | _ | PropertyLocation.property - "appIdentifier" | assignment | "com.test.app.3" | "String" | PropertyLocation.script - "appIdentifier" | assignment | "com.test.app.4" | "Provider" | PropertyLocation.script - "appIdentifier" | providerSet | "com.test.app.5" | "String" | PropertyLocation.script - "appIdentifier" | providerSet | "com.test.app.6" | "Provider" | PropertyLocation.script - "appIdentifier" | setter | "com.test.app.7" | "String" | PropertyLocation.script - "appIdentifier" | setter | "com.test.app.8" | "Provider" | PropertyLocation.script - "appIdentifier" | _ | null | _ | PropertyLocation.none - - "provisioningProfileAppId" | _ | "com.test.app.1.*" | _ | PropertyLocation.environment - "provisioningProfileAppId" | _ | "com.test.app.2.*" | _ | PropertyLocation.property - "provisioningProfileAppId" | assignment | "com.test.app.3.*" | "String" | PropertyLocation.script - "provisioningProfileAppId" | assignment | "com.test.app.4.*" | "Provider" | PropertyLocation.script - "provisioningProfileAppId" | providerSet | "com.test.app.5.*" | "String" | PropertyLocation.script - "provisioningProfileAppId" | providerSet | "com.test.app.6.*" | "Provider" | PropertyLocation.script - "provisioningProfileAppId" | setter | "com.test.app.7.*" | "String" | PropertyLocation.script - "provisioningProfileAppId" | setter | "com.test.app.8.*" | "Provider" | PropertyLocation.script - "provisioningProfileAppId" | _ | null | _ | PropertyLocation.none - - "teamId" | _ | "team1" | _ | PropertyLocation.environment - "teamId" | _ | "team2" | _ | PropertyLocation.property - "teamId" | assignment | "team3" | "String" | PropertyLocation.script - "teamId" | assignment | "team4" | "Provider" | PropertyLocation.script - "teamId" | providerSet | "team5" | "String" | PropertyLocation.script - "teamId" | providerSet | "team6" | "Provider" | PropertyLocation.script - "teamId" | setter | "team7" | "String" | PropertyLocation.script - "teamId" | setter | "team8" | "Provider" | PropertyLocation.script - "teamId" | _ | null | _ | PropertyLocation.none - - "scheme" | _ | "value1" | _ | PropertyLocation.environment - "scheme" | _ | "value2" | _ | PropertyLocation.property - "scheme" | assignment | "value3" | "String" | PropertyLocation.script - "scheme" | assignment | "value4" | "Provider" | PropertyLocation.script - "scheme" | providerSet | "value5" | "String" | PropertyLocation.script - "scheme" | providerSet | "value6" | "Provider" | PropertyLocation.script - "scheme" | setter | "value7" | "String" | PropertyLocation.script - "scheme" | setter | "value8" | "Provider" | PropertyLocation.script - "scheme" | _ | null | _ | PropertyLocation.none - - "configuration" | _ | "value1" | _ | PropertyLocation.environment - "configuration" | _ | "value2" | _ | PropertyLocation.property - "configuration" | assignment | "value3" | "String" | PropertyLocation.script - "configuration" | assignment | "value4" | "Provider" | PropertyLocation.script - "configuration" | providerSet | "value5" | "String" | PropertyLocation.script - "configuration" | providerSet | "value6" | "Provider" | PropertyLocation.script - "configuration" | setter | "value7" | "String" | PropertyLocation.script - "configuration" | setter | "value8" | "Provider" | PropertyLocation.script - "configuration" | _ | null | _ | PropertyLocation.none - - "provisioningName" | _ | "value1" | _ | PropertyLocation.environment - "provisioningName" | _ | "value2" | _ | PropertyLocation.property - "provisioningName" | assignment | "value3" | "String" | PropertyLocation.script - "provisioningName" | assignment | "value4" | "Provider" | PropertyLocation.script - "provisioningName" | providerSet | "value5" | "String" | PropertyLocation.script - "provisioningName" | providerSet | "value6" | "Provider" | PropertyLocation.script - "provisioningName" | setter | "value7" | "String" | PropertyLocation.script - "provisioningName" | setter | "value8" | "Provider" | PropertyLocation.script - "provisioningName" | _ | null | _ | PropertyLocation.none - - "adhoc" | _ | true | _ | PropertyLocation.environment - "adhoc" | _ | true | _ | PropertyLocation.property - "adhoc" | assignment | true | "Boolean" | PropertyLocation.script - "adhoc" | assignment | true | "Provider" | PropertyLocation.script - "adhoc" | providerSet | true | "Boolean" | PropertyLocation.script - "adhoc" | providerSet | true | "Provider" | PropertyLocation.script - "adhoc" | setter | true | "Boolean" | PropertyLocation.script - "adhoc" | setter | true | "Provider" | PropertyLocation.script - "adhoc" | _ | false | _ | PropertyLocation.none - - "publishToTestFlight" | _ | true | _ | PropertyLocation.environment - "publishToTestFlight" | _ | true | _ | PropertyLocation.property - "publishToTestFlight" | assignment | true | "Boolean" | PropertyLocation.script - "publishToTestFlight" | assignment | true | "Provider" | PropertyLocation.script - "publishToTestFlight" | providerSet | true | "Boolean" | PropertyLocation.script - "publishToTestFlight" | providerSet | true | "Provider" | PropertyLocation.script - "publishToTestFlight" | setter | true | "Boolean" | PropertyLocation.script - "publishToTestFlight" | setter | true | "Provider" | PropertyLocation.script - "publishToTestFlight" | _ | false | _ | PropertyLocation.none - - "exportOptionsPlist" | _ | osPath("/path/to/exportOptions.plist") | _ | PropertyLocation.environment - "exportOptionsPlist" | _ | osPath("/path/to/exportOptions.plist") | _ | PropertyLocation.property - "exportOptionsPlist" | assignment | osPath("/path/to/exportOptions.plist") | "File" | PropertyLocation.script - "exportOptionsPlist" | assignment | osPath("/path/to/exportOptions.plist") | "Provider" | PropertyLocation.script - "exportOptionsPlist" | providerSet | osPath("/path/to/exportOptions.plist") | "File" | PropertyLocation.script - "exportOptionsPlist" | providerSet | osPath("/path/to/exportOptions.plist") | "Provider" | PropertyLocation.script - "exportOptionsPlist" | setter | osPath("/path/to/exportOptions.plist") | "File" | PropertyLocation.script - "exportOptionsPlist" | setter | osPath("/path/to/exportOptions.plist") | "Provider" | PropertyLocation.script - "exportOptionsPlist" | _ | projectFile("exportOptions.plist") | _ | PropertyLocation.none - - "preferWorkspace" | _ | false | _ | PropertyLocation.environment - "preferWorkspace" | _ | false | _ | PropertyLocation.property - "preferWorkspace" | assignment | false | "Boolean" | PropertyLocation.script - "preferWorkspace" | assignment | false | "Provider" | PropertyLocation.script - "preferWorkspace" | providerSet | false | "Boolean" | PropertyLocation.script - "preferWorkspace" | providerSet | false | "Provider" | PropertyLocation.script - "preferWorkspace" | setter | false | "Boolean" | PropertyLocation.script - "preferWorkspace" | setter | false | "Provider" | PropertyLocation.script - "preferWorkspace" | _ | true | _ | PropertyLocation.none - - "xcodeProjectDirectory" | _ | osPath("/path/to/project") | _ | PropertyLocation.environment - "xcodeProjectDirectory" | _ | osPath("/path/to/project2") | _ | PropertyLocation.property - "xcodeProjectDirectory" | assignment | osPath("/path/to/project3") | "File" | PropertyLocation.script - "xcodeProjectDirectory" | assignment | osPath("/path/to/project4") | "Provider" | PropertyLocation.script - "xcodeProjectDirectory" | providerSet | osPath("/path/to/project5") | "File" | PropertyLocation.script - "xcodeProjectDirectory" | providerSet | osPath("/path/to/project6") | "Provider" | PropertyLocation.script - "xcodeProjectDirectory" | setter | osPath("/path/to/project7") | "File" | PropertyLocation.script - "xcodeProjectDirectory" | setter | osPath("/path/to/project8") | "Provider" | PropertyLocation.script - "xcodeProjectDirectory" | _ | projectFile("") | _ | PropertyLocation.none - - "xcodeProjectPath" | _ | osPath("/path/to/project") | _ | PropertyLocation.environment - "xcodeProjectPath" | _ | osPath("/path/to/project2") | _ | PropertyLocation.property - "xcodeProjectPath" | assignment | osPath("/path/to/project3") | "File" | PropertyLocation.script - "xcodeProjectPath" | assignment | osPath("/path/to/project4") | "Provider" | PropertyLocation.script - "xcodeProjectPath" | providerSet | osPath("/path/to/project5") | "File" | PropertyLocation.script - "xcodeProjectPath" | providerSet | osPath("/path/to/project6") | "Provider" | PropertyLocation.script - "xcodeProjectPath" | setter | osPath("/path/to/project7") | "File" | PropertyLocation.script - "xcodeProjectPath" | setter | osPath("/path/to/project8") | "Provider" | PropertyLocation.script - "xcodeProjectPath" | _ | projectFile("Unity-iPhone.xcodeproj") | _ | PropertyLocation.none - - "xcodeWorkspacePath" | _ | osPath("/path/to/project") | _ | PropertyLocation.environment - "xcodeWorkspacePath" | _ | osPath("/path/to/project2") | _ | PropertyLocation.property - "xcodeWorkspacePath" | assignment | osPath("/path/to/project3") | "File" | PropertyLocation.script - "xcodeWorkspacePath" | assignment | osPath("/path/to/project4") | "Provider" | PropertyLocation.script - "xcodeWorkspacePath" | providerSet | osPath("/path/to/project5") | "File" | PropertyLocation.script - "xcodeWorkspacePath" | providerSet | osPath("/path/to/project6") | "Provider" | PropertyLocation.script - "xcodeWorkspacePath" | setter | osPath("/path/to/project7") | "File" | PropertyLocation.script - "xcodeWorkspacePath" | setter | osPath("/path/to/project8") | "Provider" | PropertyLocation.script - "xcodeWorkspacePath" | _ | projectFile("Unity-iPhone.xcworkspace") | _ | PropertyLocation.none - - "projectBaseName" | _ | "value1" | _ | PropertyLocation.environment - "projectBaseName" | _ | "value2" | _ | PropertyLocation.property - "projectBaseName" | assignment | "value3" | "String" | PropertyLocation.script - "projectBaseName" | assignment | "value4" | "Provider" | PropertyLocation.script - "projectBaseName" | providerSet | "value5" | "String" | PropertyLocation.script - "projectBaseName" | providerSet | "value6" | "Provider" | PropertyLocation.script - "projectBaseName" | setter | "value7" | "String" | PropertyLocation.script - "projectBaseName" | setter | "value8" | "Provider" | PropertyLocation.script - "projectBaseName" | _ | "Unity-iPhone" | _ | PropertyLocation.none - - "cocoapods.executableName" | _ | "value1" | _ | PropertyLocation.environment - "cocoapods.executableName" | _ | "value2" | _ | PropertyLocation.property - "cocoapods.executableName" | assignment | "value3" | "String" | PropertyLocation.script - "cocoapods.executableName" | assignment | "value4" | "Provider" | PropertyLocation.script - "cocoapods.executableName" | providerSet | "value5" | "String" | PropertyLocation.script - "cocoapods.executableName" | providerSet | "value6" | "Provider" | PropertyLocation.script - "cocoapods.executableName" | setter | "value7" | "String" | PropertyLocation.script - "cocoapods.executableName" | setter | "value8" | "Provider" | PropertyLocation.script - "cocoapods.executableName" | _ | "pod" | _ | PropertyLocation.none - - "cocoapods.executableDirectory" | _ | osPath("/path/to/project") | _ | PropertyLocation.environment - "cocoapods.executableDirectory" | _ | osPath("/path/to/project2") | _ | PropertyLocation.property - "cocoapods.executableDirectory" | assignment | osPath("/path/to/project3") | "File" | PropertyLocation.script - "cocoapods.executableDirectory" | assignment | osPath("/path/to/project4") | "Provider" | PropertyLocation.script - "cocoapods.executableDirectory" | providerSet | osPath("/path/to/project5") | "File" | PropertyLocation.script - "cocoapods.executableDirectory" | providerSet | osPath("/path/to/project6") | "Provider" | PropertyLocation.script - "cocoapods.executableDirectory" | setter | osPath("/path/to/project7") | "File" | PropertyLocation.script - "cocoapods.executableDirectory" | setter | osPath("/path/to/project8") | "Provider" | PropertyLocation.script - "cocoapods.executableDirectory" | _ | null | _ | PropertyLocation.none - - "projectPath" | _ | projectFile("Unity-iPhone.xcodeproj") | "File" | PropertyLocation.none - "xcodeProjectFileName" | _ | "Unity-iPhone.xcodeproj" | "String" | PropertyLocation.none - "xcodeWorkspaceFileName" | _ | "Unity-iPhone.xcworkspace" | "String" | PropertyLocation.none - "preferredProjectFileName" | _ | "Unity-iPhone.xcworkspace" | "String" | PropertyLocation.none + property | invocation | rawValue | type | location + "keychainPassword" | _ | "testPassword1" | _ | PropertyLocation.environment + "keychainPassword" | _ | "testPassword2" | _ | PropertyLocation.property + "keychainPassword" | assignment | "testPassword3" | "String" | PropertyLocation.script + "keychainPassword" | assignment | "testPassword4" | "Provider" | PropertyLocation.script + "keychainPassword" | providerSet | "testPassword5" | "String" | PropertyLocation.script + "keychainPassword" | providerSet | "testPassword6" | "Provider" | PropertyLocation.script + "keychainPassword" | setter | "testPassword7" | "String" | PropertyLocation.script + "keychainPassword" | setter | "testPassword8" | "Provider" | PropertyLocation.script + "keychainPassword" | _ | null | _ | PropertyLocation.none + + "signingIdentities" | _ | TestValue.set("ID1,ID2").expect(["ID1", "ID2"]) | _ | PropertyLocation.environment + "signingIdentities" | _ | TestValue.set("ID1,ID2").expect(["ID1", "ID2"]) | _ | PropertyLocation.property + "signingIdentities" | assignment | ["ID1", "ID2"] | "List" | PropertyLocation.script + "signingIdentities" | assignment | ["ID1", "ID2"] | "Provider>" | PropertyLocation.script + "signingIdentities" | providerSet | ["ID1", "ID2"] | "List" | PropertyLocation.script + "signingIdentities" | providerSet | ["ID1", "ID2"] | "Provider>" | PropertyLocation.script + "signingIdentities" | setter | ["ID1", "ID2"] | "List" | PropertyLocation.script + "signingIdentities" | setter | ["ID1", "ID2"] | "Provider>" | PropertyLocation.script + "signingIdentities" | customSetter("setSigningIdentity") | TestValue.set(wrapValueBasedOnType("code sign: ID3", "String")).expect(["code sign: ID3"]) | "Provider>" | PropertyLocation.script + "signingIdentities" | customSetter("setSigningIdentity") | TestValue.set(wrapValueBasedOnType("code sign: ID3", "String")).expect(["code sign: ID3"]) | "String>" | PropertyLocation.script + "signingIdentities" | _ | [] | _ | PropertyLocation.none + + "codeSigningIdentityFile" | _ | osPath("/path/to/p12") | _ | PropertyLocation.environment + "codeSigningIdentityFile" | _ | osPath("/path/to/p12") | _ | PropertyLocation.property + "codeSigningIdentityFile" | assignment | osPath("/path/to/p12") | "File" | PropertyLocation.script + "codeSigningIdentityFile" | assignment | osPath("/path/to/p12") | "Provider" | PropertyLocation.script + "codeSigningIdentityFile" | providerSet | osPath("/path/to/p12") | "File" | PropertyLocation.script + "codeSigningIdentityFile" | providerSet | osPath("/path/to/p12") | "Provider" | PropertyLocation.script + "codeSigningIdentityFile" | setter | osPath("/path/to/p12") | "File" | PropertyLocation.script + "codeSigningIdentityFile" | setter | osPath("/path/to/p12") | "Provider" | PropertyLocation.script + "codeSigningIdentityFile" | _ | null | _ | PropertyLocation.none + + "codeSigningIdentityFilePassphrase" | _ | "testPassphrase1" | _ | PropertyLocation.environment + "codeSigningIdentityFilePassphrase" | _ | "testPassphrase2" | _ | PropertyLocation.property + "codeSigningIdentityFilePassphrase" | assignment | "testPassphrase3" | "String" | PropertyLocation.script + "codeSigningIdentityFilePassphrase" | assignment | "testPassphrase4" | "Provider" | PropertyLocation.script + "codeSigningIdentityFilePassphrase" | providerSet | "testPassphrase5" | "String" | PropertyLocation.script + "codeSigningIdentityFilePassphrase" | providerSet | "testPassphrase6" | "Provider" | PropertyLocation.script + "codeSigningIdentityFilePassphrase" | setter | "testPassphrase7" | "String" | PropertyLocation.script + "codeSigningIdentityFilePassphrase" | setter | "testPassphrase8" | "Provider" | PropertyLocation.script + "codeSigningIdentityFilePassphrase" | _ | null | _ | PropertyLocation.none + + "appIdentifier" | _ | "com.test.app.1" | _ | PropertyLocation.environment + "appIdentifier" | _ | "com.test.app.2" | _ | PropertyLocation.property + "appIdentifier" | assignment | "com.test.app.3" | "String" | PropertyLocation.script + "appIdentifier" | assignment | "com.test.app.4" | "Provider" | PropertyLocation.script + "appIdentifier" | providerSet | "com.test.app.5" | "String" | PropertyLocation.script + "appIdentifier" | providerSet | "com.test.app.6" | "Provider" | PropertyLocation.script + "appIdentifier" | setter | "com.test.app.7" | "String" | PropertyLocation.script + "appIdentifier" | setter | "com.test.app.8" | "Provider" | PropertyLocation.script + "appIdentifier" | _ | null | _ | PropertyLocation.none + + "provisioningProfileAppId" | _ | "com.test.app.1.*" | _ | PropertyLocation.environment + "provisioningProfileAppId" | _ | "com.test.app.2.*" | _ | PropertyLocation.property + "provisioningProfileAppId" | assignment | "com.test.app.3.*" | "String" | PropertyLocation.script + "provisioningProfileAppId" | assignment | "com.test.app.4.*" | "Provider" | PropertyLocation.script + "provisioningProfileAppId" | providerSet | "com.test.app.5.*" | "String" | PropertyLocation.script + "provisioningProfileAppId" | providerSet | "com.test.app.6.*" | "Provider" | PropertyLocation.script + "provisioningProfileAppId" | setter | "com.test.app.7.*" | "String" | PropertyLocation.script + "provisioningProfileAppId" | setter | "com.test.app.8.*" | "Provider" | PropertyLocation.script + "provisioningProfileAppId" | _ | null | _ | PropertyLocation.none + + "teamId" | _ | "team1" | _ | PropertyLocation.environment + "teamId" | _ | "team2" | _ | PropertyLocation.property + "teamId" | assignment | "team3" | "String" | PropertyLocation.script + "teamId" | assignment | "team4" | "Provider" | PropertyLocation.script + "teamId" | providerSet | "team5" | "String" | PropertyLocation.script + "teamId" | providerSet | "team6" | "Provider" | PropertyLocation.script + "teamId" | setter | "team7" | "String" | PropertyLocation.script + "teamId" | setter | "team8" | "Provider" | PropertyLocation.script + "teamId" | _ | null | _ | PropertyLocation.none + + "scheme" | _ | "value1" | _ | PropertyLocation.environment + "scheme" | _ | "value2" | _ | PropertyLocation.property + "scheme" | assignment | "value3" | "String" | PropertyLocation.script + "scheme" | assignment | "value4" | "Provider" | PropertyLocation.script + "scheme" | providerSet | "value5" | "String" | PropertyLocation.script + "scheme" | providerSet | "value6" | "Provider" | PropertyLocation.script + "scheme" | setter | "value7" | "String" | PropertyLocation.script + "scheme" | setter | "value8" | "Provider" | PropertyLocation.script + "scheme" | _ | null | _ | PropertyLocation.none + + "configuration" | _ | "value1" | _ | PropertyLocation.environment + "configuration" | _ | "value2" | _ | PropertyLocation.property + "configuration" | assignment | "value3" | "String" | PropertyLocation.script + "configuration" | assignment | "value4" | "Provider" | PropertyLocation.script + "configuration" | providerSet | "value5" | "String" | PropertyLocation.script + "configuration" | providerSet | "value6" | "Provider" | PropertyLocation.script + "configuration" | setter | "value7" | "String" | PropertyLocation.script + "configuration" | setter | "value8" | "Provider" | PropertyLocation.script + "configuration" | _ | null | _ | PropertyLocation.none + + "provisioningName" | _ | "value1" | _ | PropertyLocation.environment + "provisioningName" | _ | "value2" | _ | PropertyLocation.property + "provisioningName" | assignment | "value3" | "String" | PropertyLocation.script + "provisioningName" | assignment | "value4" | "Provider" | PropertyLocation.script + "provisioningName" | providerSet | "value5" | "String" | PropertyLocation.script + "provisioningName" | providerSet | "value6" | "Provider" | PropertyLocation.script + "provisioningName" | setter | "value7" | "String" | PropertyLocation.script + "provisioningName" | setter | "value8" | "Provider" | PropertyLocation.script + "provisioningName" | _ | null | _ | PropertyLocation.none + + "provisioningProfiles" | assignment | ["net.test.foo": "Provisoning Name Foo"] | "Map" | PropertyLocation.script + "provisioningProfiles" | providerSet | ["net.test.foo": "Provisoning Name Foo"] | "Map" | PropertyLocation.script + "provisioningProfiles" | providerSet | ["net.test.foo": "Provisoning Name Foo"] | "Provider>" | PropertyLocation.script + "provisioningProfiles" | setter | ["net.test.foo": "Provisoning Name Foo"] | "Map" | PropertyLocation.script + "provisioningProfiles" | setter | ["net.test.foo": "Provisoning Name Foo"] | "Provider>" | PropertyLocation.script + "provisioningProfiles" | _ | [:] | _ | PropertyLocation.none + + "adhoc" | _ | true | _ | PropertyLocation.environment + "adhoc" | _ | true | _ | PropertyLocation.property + "adhoc" | assignment | true | "Boolean" | PropertyLocation.script + "adhoc" | assignment | true | "Provider" | PropertyLocation.script + "adhoc" | providerSet | true | "Boolean" | PropertyLocation.script + "adhoc" | providerSet | true | "Provider" | PropertyLocation.script + "adhoc" | setter | true | "Boolean" | PropertyLocation.script + "adhoc" | setter | true | "Provider" | PropertyLocation.script + "adhoc" | _ | false | _ | PropertyLocation.none + + "publishToTestFlight" | _ | true | _ | PropertyLocation.environment + "publishToTestFlight" | _ | true | _ | PropertyLocation.property + "publishToTestFlight" | assignment | true | "Boolean" | PropertyLocation.script + "publishToTestFlight" | assignment | true | "Provider" | PropertyLocation.script + "publishToTestFlight" | providerSet | true | "Boolean" | PropertyLocation.script + "publishToTestFlight" | providerSet | true | "Provider" | PropertyLocation.script + "publishToTestFlight" | setter | true | "Boolean" | PropertyLocation.script + "publishToTestFlight" | setter | true | "Provider" | PropertyLocation.script + "publishToTestFlight" | _ | false | _ | PropertyLocation.none + + "exportOptionsPlist" | _ | osPath("/path/to/exportOptions.plist") | _ | PropertyLocation.environment + "exportOptionsPlist" | _ | osPath("/path/to/exportOptions.plist") | _ | PropertyLocation.property + "exportOptionsPlist" | assignment | osPath("/path/to/exportOptions.plist") | "File" | PropertyLocation.script + "exportOptionsPlist" | assignment | osPath("/path/to/exportOptions.plist") | "Provider" | PropertyLocation.script + "exportOptionsPlist" | providerSet | osPath("/path/to/exportOptions.plist") | "File" | PropertyLocation.script + "exportOptionsPlist" | providerSet | osPath("/path/to/exportOptions.plist") | "Provider" | PropertyLocation.script + "exportOptionsPlist" | setter | osPath("/path/to/exportOptions.plist") | "File" | PropertyLocation.script + "exportOptionsPlist" | setter | osPath("/path/to/exportOptions.plist") | "Provider" | PropertyLocation.script + "exportOptionsPlist" | _ | projectFile("exportOptions.plist") | _ | PropertyLocation.none + + "preferWorkspace" | _ | false | _ | PropertyLocation.environment + "preferWorkspace" | _ | false | _ | PropertyLocation.property + "preferWorkspace" | assignment | false | "Boolean" | PropertyLocation.script + "preferWorkspace" | assignment | false | "Provider" | PropertyLocation.script + "preferWorkspace" | providerSet | false | "Boolean" | PropertyLocation.script + "preferWorkspace" | providerSet | false | "Provider" | PropertyLocation.script + "preferWorkspace" | setter | false | "Boolean" | PropertyLocation.script + "preferWorkspace" | setter | false | "Provider" | PropertyLocation.script + "preferWorkspace" | _ | true | _ | PropertyLocation.none + + "xcodeProjectDirectory" | _ | osPath("/path/to/project") | _ | PropertyLocation.environment + "xcodeProjectDirectory" | _ | osPath("/path/to/project2") | _ | PropertyLocation.property + "xcodeProjectDirectory" | assignment | osPath("/path/to/project3") | "File" | PropertyLocation.script + "xcodeProjectDirectory" | assignment | osPath("/path/to/project4") | "Provider" | PropertyLocation.script + "xcodeProjectDirectory" | providerSet | osPath("/path/to/project5") | "File" | PropertyLocation.script + "xcodeProjectDirectory" | providerSet | osPath("/path/to/project6") | "Provider" | PropertyLocation.script + "xcodeProjectDirectory" | setter | osPath("/path/to/project7") | "File" | PropertyLocation.script + "xcodeProjectDirectory" | setter | osPath("/path/to/project8") | "Provider" | PropertyLocation.script + "xcodeProjectDirectory" | _ | projectFile("") | _ | PropertyLocation.none + + "xcodeProjectPath" | _ | osPath("/path/to/project") | _ | PropertyLocation.environment + "xcodeProjectPath" | _ | osPath("/path/to/project2") | _ | PropertyLocation.property + "xcodeProjectPath" | assignment | osPath("/path/to/project3") | "File" | PropertyLocation.script + "xcodeProjectPath" | assignment | osPath("/path/to/project4") | "Provider" | PropertyLocation.script + "xcodeProjectPath" | providerSet | osPath("/path/to/project5") | "File" | PropertyLocation.script + "xcodeProjectPath" | providerSet | osPath("/path/to/project6") | "Provider" | PropertyLocation.script + "xcodeProjectPath" | setter | osPath("/path/to/project7") | "File" | PropertyLocation.script + "xcodeProjectPath" | setter | osPath("/path/to/project8") | "Provider" | PropertyLocation.script + "xcodeProjectPath" | _ | projectFile("Unity-iPhone.xcodeproj") | _ | PropertyLocation.none + + "xcodeWorkspacePath" | _ | osPath("/path/to/project") | _ | PropertyLocation.environment + "xcodeWorkspacePath" | _ | osPath("/path/to/project2") | _ | PropertyLocation.property + "xcodeWorkspacePath" | assignment | osPath("/path/to/project3") | "File" | PropertyLocation.script + "xcodeWorkspacePath" | assignment | osPath("/path/to/project4") | "Provider" | PropertyLocation.script + "xcodeWorkspacePath" | providerSet | osPath("/path/to/project5") | "File" | PropertyLocation.script + "xcodeWorkspacePath" | providerSet | osPath("/path/to/project6") | "Provider" | PropertyLocation.script + "xcodeWorkspacePath" | setter | osPath("/path/to/project7") | "File" | PropertyLocation.script + "xcodeWorkspacePath" | setter | osPath("/path/to/project8") | "Provider" | PropertyLocation.script + "xcodeWorkspacePath" | _ | projectFile("Unity-iPhone.xcworkspace") | _ | PropertyLocation.none + + "projectBaseName" | _ | "value1" | _ | PropertyLocation.environment + "projectBaseName" | _ | "value2" | _ | PropertyLocation.property + "projectBaseName" | assignment | "value3" | "String" | PropertyLocation.script + "projectBaseName" | assignment | "value4" | "Provider" | PropertyLocation.script + "projectBaseName" | providerSet | "value5" | "String" | PropertyLocation.script + "projectBaseName" | providerSet | "value6" | "Provider" | PropertyLocation.script + "projectBaseName" | setter | "value7" | "String" | PropertyLocation.script + "projectBaseName" | setter | "value8" | "Provider" | PropertyLocation.script + "projectBaseName" | _ | "Unity-iPhone" | _ | PropertyLocation.none + + "cocoapods.executableName" | _ | "value1" | _ | PropertyLocation.environment + "cocoapods.executableName" | _ | "value2" | _ | PropertyLocation.property + "cocoapods.executableName" | assignment | "value3" | "String" | PropertyLocation.script + "cocoapods.executableName" | assignment | "value4" | "Provider" | PropertyLocation.script + "cocoapods.executableName" | providerSet | "value5" | "String" | PropertyLocation.script + "cocoapods.executableName" | providerSet | "value6" | "Provider" | PropertyLocation.script + "cocoapods.executableName" | setter | "value7" | "String" | PropertyLocation.script + "cocoapods.executableName" | setter | "value8" | "Provider" | PropertyLocation.script + "cocoapods.executableName" | _ | "pod" | _ | PropertyLocation.none + + "cocoapods.executableDirectory" | _ | osPath("/path/to/project") | _ | PropertyLocation.environment + "cocoapods.executableDirectory" | _ | osPath("/path/to/project2") | _ | PropertyLocation.property + "cocoapods.executableDirectory" | assignment | osPath("/path/to/project3") | "File" | PropertyLocation.script + "cocoapods.executableDirectory" | assignment | osPath("/path/to/project4") | "Provider" | PropertyLocation.script + "cocoapods.executableDirectory" | providerSet | osPath("/path/to/project5") | "File" | PropertyLocation.script + "cocoapods.executableDirectory" | providerSet | osPath("/path/to/project6") | "Provider" | PropertyLocation.script + "cocoapods.executableDirectory" | setter | osPath("/path/to/project7") | "File" | PropertyLocation.script + "cocoapods.executableDirectory" | setter | osPath("/path/to/project8") | "Provider" | PropertyLocation.script + "cocoapods.executableDirectory" | _ | null | _ | PropertyLocation.none + + "projectPath" | _ | projectFile("Unity-iPhone.xcodeproj") | "File" | PropertyLocation.none + "xcodeProjectFileName" | _ | "Unity-iPhone.xcodeproj" | "String" | PropertyLocation.none + "xcodeWorkspaceFileName" | _ | "Unity-iPhone.xcworkspace" | "String" | PropertyLocation.none + "preferredProjectFileName" | _ | "Unity-iPhone.xcworkspace" | "String" | PropertyLocation.none set = new PropertySetterWriter(extensionName, property) diff --git a/src/integrationTest/groovy/wooga/gradle/build/unity/ios/tasks/IOSBuildTaskIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/build/unity/ios/tasks/IOSBuildTaskIntegrationSpec.groovy new file mode 100644 index 0000000..8c65ee5 --- /dev/null +++ b/src/integrationTest/groovy/wooga/gradle/build/unity/ios/tasks/IOSBuildTaskIntegrationSpec.groovy @@ -0,0 +1,13 @@ +package wooga.gradle.build.unity.ios.tasks + +import com.wooga.gradle.test.TaskIntegrationSpec +import org.gradle.api.Task +import wooga.gradle.build.unity.ios.IOSBuildIntegrationSpec + +abstract class IOSBuildTaskIntegrationSpec extends IOSBuildIntegrationSpec implements TaskIntegrationSpec { + def setup() { + buildFile << """ + task ${subjectUnderTestName}(type: ${subjectUnderTestTypeName}) + """.stripIndent() + } +} diff --git a/src/integrationTest/groovy/wooga/gradle/build/unity/ios/tasks/ImportCodeSigningIdentitiesIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/build/unity/ios/tasks/ImportCodeSigningIdentitiesIntegrationSpec.groovy index 69cdba7..0a427fc 100644 --- a/src/integrationTest/groovy/wooga/gradle/build/unity/ios/tasks/ImportCodeSigningIdentitiesIntegrationSpec.groovy +++ b/src/integrationTest/groovy/wooga/gradle/build/unity/ios/tasks/ImportCodeSigningIdentitiesIntegrationSpec.groovy @@ -12,21 +12,15 @@ import static com.wooga.gradle.test.PropertyUtils.toProviderSet import static com.wooga.gradle.test.PropertyUtils.toSetter @Requires({ os.macOs }) -class ImportCodeSigningIdentitiesIntegrationSpec extends IOSBuildIntegrationSpec { - - String subjectUnderTestName = "importSigningIdentity" - String subjectUnderTestTypeName = ImportCodeSigningIdentities.class.name +class ImportCodeSigningIdentitiesIntegrationSpec extends IOSBuildTaskIntegrationSpec { @Keychain(unlockKeychain = true) MacOsKeychain buildKeychain def setup() { - buildFile << """ - task ${subjectUnderTestName}(type: ${subjectUnderTestTypeName}) { - //inputKeychain = file('${buildKeychain.location}') + appendToSubjectTask(""" keychain = file('${buildKeychain.location}') - } - """.stripIndent() + """.stripIndent()) } @Unroll("import #taskStatus when #reason") diff --git a/src/integrationTest/groovy/wooga/gradle/build/unity/ios/tasks/InstallProvisioningProfilesIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/build/unity/ios/tasks/InstallProvisioningProfilesIntegrationSpec.groovy new file mode 100644 index 0000000..cd05fe8 --- /dev/null +++ b/src/integrationTest/groovy/wooga/gradle/build/unity/ios/tasks/InstallProvisioningProfilesIntegrationSpec.groovy @@ -0,0 +1,123 @@ +package wooga.gradle.build.unity.ios.tasks + +import com.wooga.gradle.PlatformUtils +import com.wooga.gradle.test.PropertyQueryTaskWriter +import com.wooga.gradle.test.ios.MobileProvisionMock +import spock.lang.Requires +import spock.lang.Unroll + +import static com.wooga.gradle.test.PropertyUtils.toProviderSet +import static com.wooga.gradle.test.PropertyUtils.toSetter + +class InstallProvisioningProfilesIntegrationSpec extends IOSBuildTaskIntegrationSpec { + + @Requires({ PlatformUtils.mac }) + @Unroll("can set property #property with #method and type #type") + def "can set property"() { + given: "a task to read back the value" + def query = new PropertyQueryTaskWriter("${subjectUnderTestName}.${property}") + query.write(buildFile) + + and: "a set property" + appendToSubjectTask("${method}($value)") + + when: + def result = runTasksSuccessfully(query.taskName) + + then: + query.matches(result, expectedValue) + + where: + property | method | rawValue | returnValue | type + "outputDirectory" | property | "/path/to/outputDir" | _ | "File" + "outputDirectory" | property | "/path/to/outputDir" | _ | "Provider" + "outputDirectory" | toProviderSet(property) | "/path/to/outputDir" | _ | "File" + "outputDirectory" | toProviderSet(property) | "/path/to/outputDir" | _ | "Provider" + "outputDirectory" | toSetter(property) | "/path/to/outputDir" | _ | "File" + "outputDirectory" | toSetter(property) | "/path/to/outputDir" | _ | "Provider" + + "logFile" | property | "/path/to/log" | _ | "File" + "logFile" | property | "/path/to/log" | _ | "Provider" + "logFile" | toProviderSet(property) | "/path/to/log" | _ | "File" + "logFile" | toProviderSet(property) | "/path/to/log" | _ | "Provider" + "logFile" | toSetter(property) | "/path/to/log" | _ | "File" + "logFile" | toSetter(property) | "/path/to/log" | _ | "Provider" + + "logToStdout" | toProviderSet(property) | true | _ | "Boolean" + "logToStdout" | toProviderSet(property) | true | _ | "Provider" + "logToStdout" | toSetter(property) | true | _ | "Boolean" + "logToStdout" | toSetter(property) | true | _ | "Provider" + value = wrapValueBasedOnType(rawValue, type, wrapValueFallback) + expectedValue = returnValue == _ ? rawValue : returnValue + } + + def "installs provided provisioning profiles to the output directory"() { + given: "a mock mobile provisioning file with a known uuid" + def files = uuids.collect { UUID id -> + def mock = MobileProvisionMock.createMock({ + it.uuid = id + }) + def installedProfile = new File(projectDir, "build/profiles/${id}.mobileprovision") + new Tuple2(mock, installedProfile) + } + + and: "a future provisioning profile location" + assert !files.any { it.second.exists() } + + and: + appendToSubjectTask(""" + provisioningProfiles.from(${wrapValueBasedOnType(files.collect { it.first }, "List")}) + outputDirectory = ${wrapValueBasedOnType(new File(projectDir, "build/profiles"), "File")} + """.stripIndent()) + + when: + runTasksSuccessfully(subjectUnderTestName) + + then: + files.every { it.second.exists() } + files.every { + def mockBytes = it.first.bytes + def profileBytes = it.second.bytes + mockBytes == profileBytes + } + + where: + uuids = [UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID()] + } + + def "writes log to logfile and stdout when configured"() { + given: "a mock mobile provisioning file with a known uuid" + def files = uuids.collect { UUID id -> + def mock = MobileProvisionMock.createMock({ + it.uuid = id + }) + def installedProfile = new File(projectDir, "build/profiles/${id}.mobileprovision") + new Tuple2(mock, installedProfile) + } + + and: "a future logfile" + def logFile = new File(projectDir, logFilePath) + assert !logFile.exists() + + and: + appendToSubjectTask(""" + provisioningProfiles.from(${wrapValueBasedOnType(files.collect { it.first }, "List")}) + outputDirectory = ${wrapValueBasedOnType(new File(projectDir, "build/profiles"), "File")} + logFile = ${wrapValueBasedOnType(logFile, "File")} + logToStdout = ${wrapValueBasedOnType(logToStdout, "Boolean")} + """.stripIndent()) + + when: + def result = runTasksSuccessfully(subjectUnderTestName) + + then: + logFile.exists() + logFile.text.contains("Install Profiles: ${uuids.size()}") + result.standardOutput.contains(logFile.text) == logToStdout + + where: + uuids = [UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID()] + logFilePath = osPath("build/logs/custom.log") + logToStdout << [true, false] + } +} 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 b17e34a..84a6cda 100644 --- a/src/main/groovy/wooga/gradle/build/unity/ios/IOSBuildPlugin.groovy +++ b/src/main/groovy/wooga/gradle/build/unity/ios/IOSBuildPlugin.groovy @@ -31,6 +31,7 @@ import org.gradle.api.tasks.Sync import org.gradle.api.tasks.TaskProvider import wooga.gradle.build.unity.ios.internal.DefaultIOSBuildPluginExtension import wooga.gradle.build.unity.ios.tasks.ImportCodeSigningIdentities +import wooga.gradle.build.unity.ios.tasks.InstallProvisionProfiles import wooga.gradle.build.unity.ios.tasks.PodInstallTask import wooga.gradle.fastlane.FastlanePlugin import wooga.gradle.fastlane.FastlanePluginExtension @@ -110,6 +111,25 @@ class IOSBuildPlugin implements Plugin { extension.keychainPassword.convention(IOSBuildPluginConventions.keychainPassword.getStringValueProvider(project)) extension.publishToTestFlight.convention(IOSBuildPluginConventions.publishToTestFlight.getBooleanValueProvider(project)) extension.scheme.convention(IOSBuildPluginConventions.scheme.getStringValueProvider(project)) + extension.provisioningProfiles.convention(extension.exportOptions.map({ + def profiles = it.getProvisioningProfiles() + def appIdentifier = extension.appIdentifier.getOrElse("") + def provisioningProfileAppId = extension.provisioningProfileAppId.getOrElse("") + if (appIdentifier != provisioningProfileAppId) { + if(provisioningProfileAppId.endsWith(".*")) { + String wildCardPrefix = provisioningProfileAppId.substring(0, provisioningProfileAppId.length() -2) + profiles = profiles.collectEntries { appId, name -> + if (appId.startsWith(wildCardPrefix)) { + return [provisioningProfileAppId, name] + } + [appId, name] + } + } else { + LOG.warn("property 'provisioningProfileAppId' has a different value than 'appIdentifier' but is not a wildcard Id. Potential miss-configuration") + } + } + profiles + }).orElse([:])) //register some defaults project.tasks.withType(XcodeArchive.class, new Action() { @@ -169,6 +189,8 @@ class IOSBuildPlugin implements Plugin { fastlaneExtension.password.getOrNull() })) + task.readOnly.convention(true) + task.skipInstall.convention(true) task.teamId.convention(extension.getTeamId()) task.appIdentifier.convention(extension.getAppIdentifier()) task.destinationDir.convention(project.layout.dir(project.provider({ task.getTemporaryDir() }))) @@ -207,6 +229,17 @@ class IOSBuildPlugin implements Plugin { } }) + project.tasks.withType(InstallProvisionProfiles.class, new Action() { + @Override + void execute(InstallProvisionProfiles task) { + task.logFile.convention(project.layout.buildDirectory.file("logs/${task.name}.log")) + task.logToStdout.convention(project.provider {project.logger.isInfoEnabled()}) + task.outputDirectory.convention(project.layout.dir(project.provider { + new File("${System.getProperty("user.home")}/Library/MobileDevice/Provisioning\\ Profiles/") + })) + } + }) + project.tasks.withType(PodInstallTask.class, new Action() { @Override void execute(PodInstallTask task) { @@ -294,29 +327,15 @@ class IOSBuildPlugin implements Plugin { }) def importProvisioningProfiles = tasks.register("importProvisioningProfiles", SighRenewBatch) { - it.profiles.set(extension.exportOptions.map({ - def profiles = it.getProvisioningProfiles() - def appIdentifier = extension.appIdentifier.get() - def provisioningProfileAppId = extension.provisioningProfileAppId.get() - if (appIdentifier != provisioningProfileAppId) { - if(provisioningProfileAppId.endsWith(".*")) { - String wildCardPrefix = provisioningProfileAppId.substring(0, provisioningProfileAppId.length() -2) - profiles = profiles.collectEntries { appId, name -> - if (appId.startsWith(wildCardPrefix)) { - return [provisioningProfileAppId, name] - } - [appId, name] - } - } else { - logger.warn("property 'provisioningProfileAppId' has a different value than 'appIdentifier' but is not a wildcard Id. Potential miss-configuration") - } - } - profiles - })) + it.profiles.set(extension.provisioningProfiles) it.dependsOn addKeychain, buildKeychain, unlockKeychain it.finalizedBy removeKeychain, lockKeychain } + def installProvisioningProfiles = tasks.register("installProvisioningProfiles", InstallProvisionProfiles) { + it.provisioningProfiles.from(importProvisioningProfiles.get().getOutputs()) + } + TaskProvider podInstall = tasks.register("podInstall", PodInstallTask) { it.projectDirectory.set(extension.xcodeProjectDirectory) it.xcodeWorkspaceFileName.set(extension.xcodeWorkspaceFileName) @@ -324,7 +343,7 @@ class IOSBuildPlugin implements Plugin { } def xcodeArchive = tasks.register("xcodeArchive", XcodeArchive) { - it.dependsOn addKeychain, unlockKeychain, importProvisioningProfiles, podInstall, buildKeychain + it.dependsOn addKeychain, unlockKeychain, installProvisioningProfiles, podInstall, buildKeychain it.projectPath.set(extension.projectPath) it.buildKeychain.set(buildKeychain.flatMap({ it.keychain })) } diff --git a/src/main/groovy/wooga/gradle/build/unity/ios/IOSBuildPluginExtension.groovy b/src/main/groovy/wooga/gradle/build/unity/ios/IOSBuildPluginExtension.groovy index 18c92a7..63b9c2b 100644 --- a/src/main/groovy/wooga/gradle/build/unity/ios/IOSBuildPluginExtension.groovy +++ b/src/main/groovy/wooga/gradle/build/unity/ios/IOSBuildPluginExtension.groovy @@ -27,6 +27,7 @@ import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFile import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input @@ -266,6 +267,21 @@ trait IOSBuildPluginExtension extends BaseSpec { }) } + private final MapProperty provisioningProfiles = objects.mapProperty(String, String) + + @Input + MapProperty getProvisioningProfiles() { + provisioningProfiles + } + + void setProvisioningProfiles(Map value) { + provisioningProfiles.set(value) + } + + void setProvisioningProfiles(Provider> value) { + provisioningProfiles.set(value) + } + private final List> exportOptionsActions = [] void exportOptions(Action action) { diff --git a/src/main/groovy/wooga/gradle/build/unity/ios/MobileProvisioning.groovy b/src/main/groovy/wooga/gradle/build/unity/ios/MobileProvisioning.groovy new file mode 100644 index 0000000..50c2dda --- /dev/null +++ b/src/main/groovy/wooga/gradle/build/unity/ios/MobileProvisioning.groovy @@ -0,0 +1,67 @@ +package wooga.gradle.build.unity.ios + +trait MobileProvisioning { + abstract String getName() + + abstract String getAppIdName() + + abstract String getUuid() + + abstract String getTeamName() + + abstract List getTeamIdentifier() + + abstract Date getExpirationDate() + + abstract Date getCreationDate() + + abstract Boolean isExpired() + + abstract Integer getTimeToLive() + + abstract Boolean isXcodeManaged() + + abstract Integer getVersion() + + abstract Map getEntitlements() + + boolean equals(MobileProvisioning o) { + if (this.is(o)) return true + + if (!(o instanceof MobileProvisioning)) { + return false + } + + MobileProvisioning that = (MobileProvisioning) o + + if (appIdName != that.appIdName) return false + if (creationDate != that.creationDate) return false + if (entitlements != that.entitlements) return false + if (expirationDate != that.expirationDate) return false + if (name != that.name) return false + if (teamIdentifier != that.teamIdentifier) return false + if (teamName != that.teamName) return false + if (timeToLive != that.timeToLive) return false + if (uuid != that.uuid) return false + if (version != that.version) return false + if (xcodeManaged != that.xcodeManaged) return false + + return true + } + + int hashCode() { + int result + result = (uuid != null ? uuid.hashCode() : 0) + result = 31 * result + (appIdName != null ? appIdName.hashCode() : 0) + result = 31 * result + (name != null ? name.hashCode() : 0) + result = 31 * result + (teamIdentifier != null ? teamIdentifier.hashCode() : 0) + result = 31 * result + (teamName != null ? teamName.hashCode() : 0) + result = 31 * result + (timeToLive != null ? timeToLive.hashCode() : 0) + result = 31 * result + (version != null ? version.hashCode() : 0) + result = 31 * result + (creationDate != null ? creationDate.hashCode() : 0) + result = 31 * result + (expirationDate != null ? expirationDate.hashCode() : 0) + result = 31 * result + (xcodeManaged != null ? xcodeManaged.hashCode() : 0) + result = 31 * result + (entitlements != null ? entitlements.hashCode() : 0) + return result + } +} diff --git a/src/main/groovy/wooga/gradle/build/unity/ios/internal/DefaultMobileProvisioning.groovy b/src/main/groovy/wooga/gradle/build/unity/ios/internal/DefaultMobileProvisioning.groovy new file mode 100644 index 0000000..fedc2a8 --- /dev/null +++ b/src/main/groovy/wooga/gradle/build/unity/ios/internal/DefaultMobileProvisioning.groovy @@ -0,0 +1,95 @@ +package wooga.gradle.build.unity.ios.internal + +import com.dd.plist.NSDictionary +import com.dd.plist.XMLPropertyListParser +import wooga.gradle.build.unity.ios.MobileProvisioning + +class DefaultMobileProvisioning implements MobileProvisioning, GroovyInterceptable { + private static final String UUID_KEY = "UUID" + private static final String APP_ID_NAME_KEY = "AppIDName" + private static final String NAME_KEY = "Name" + private static final String TEAM_IDENTIFIER_KEY = "TeamIdentifier" + private static final String TEAM_NAME_KEY = "TeamName" + private static final String TIME_TO_LIVE_KEY = "TimeToLive" + private static final String VERSION_KEY = "Version" + private static final String CREATION_DATE_KEY= "CreationDate" + private static final String EXPIRATION_DATE_KEY= "ExpirationDate" + private static final String IS_XCODE_MANAGED_KEY= "IsXcodeManaged" + private static final String DEVELOPER_CERTIFICATES_KEY= "DeveloperCertificates" + private static final String DER_ENCODED_PROFILE_KEY = "DER-Encoded-Profile" + private static final String ENTITLEMENTS_KEY = "Entitlements" + + private Map plist = new HashMap() + + DefaultMobileProvisioning(Map data) { + plist = data + } + + private static int PLIST_START_OFFSET = 0x3E + + String getName() { + plist[NAME_KEY] + } + + String getAppIdName() { + plist[APP_ID_NAME_KEY] + } + + String getUuid() { + plist[UUID_KEY] + } + + String getTeamName() { + plist[TEAM_NAME_KEY] + } + + List getTeamIdentifier() { + plist[TEAM_IDENTIFIER_KEY] as List + } + + Date getExpirationDate() { + (Date) plist[EXPIRATION_DATE_KEY] + } + + Date getCreationDate() { + (Date) plist[CREATION_DATE_KEY] + } + + Boolean isExpired() { + expirationDate < new Date() + } + + Integer getTimeToLive() { + Integer.parseInt(plist[TIME_TO_LIVE_KEY].toString()) + } + + Boolean isXcodeManaged() { + Boolean.parseBoolean(plist[IS_XCODE_MANAGED_KEY].toString()) + } + + Integer getVersion() { + Integer.parseInt(plist[VERSION_KEY].toString()) + } + + Map getEntitlements() { + (Map) plist[ENTITLEMENTS_KEY] + } + + static DefaultMobileProvisioning open(InputStream data) { + if (data.skip(PLIST_START_OFFSET) == PLIST_START_OFFSET) { + def lines = data.readLines() + def plistEndIndex = lines.findIndexOf { it.startsWith("") } + lines[plistEndIndex] = "" + def bytes = lines.subList(0, plistEndIndex + 1).join("\n").bytes + + NSDictionary rootDict = (NSDictionary) XMLPropertyListParser.parse(bytes) + return new DefaultMobileProvisioning((Map) rootDict.toJavaObject()) + } else { + throw new IllegalArgumentException("Provided file is to short to be a provisioning profile") + } + } + + static DefaultMobileProvisioning open(File plistFile) { + open(plistFile.newInputStream()) + } +} diff --git a/src/main/groovy/wooga/gradle/build/unity/ios/tasks/InstallProvisionProfiles.groovy b/src/main/groovy/wooga/gradle/build/unity/ios/tasks/InstallProvisionProfiles.groovy new file mode 100644 index 0000000..89ee445 --- /dev/null +++ b/src/main/groovy/wooga/gradle/build/unity/ios/tasks/InstallProvisionProfiles.groovy @@ -0,0 +1,103 @@ +package wooga.gradle.build.unity.ios.tasks + +import com.wooga.gradle.BaseSpec +import com.wooga.gradle.io.LogFileSpec +import com.wooga.gradle.io.ProcessOutputSpec +import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.Directory +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileCollection +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputFiles +import org.gradle.api.tasks.TaskAction +import wooga.gradle.build.unity.ios.MobileProvisioning +import wooga.gradle.build.unity.ios.internal.DefaultMobileProvisioning +import java.io.PrintWriter +import java.text.SimpleDateFormat + +class InstallProvisionProfiles extends DefaultTask implements BaseSpec, LogFileSpec, ProcessOutputSpec { + + private final ConfigurableFileCollection provisioningProfiles = objects.fileCollection() + + @InputFiles + ConfigurableFileCollection getProvisioningProfiles() { + provisioningProfiles + } + + private final DirectoryProperty outputDirectory = objects.directoryProperty() + + @Internal + DirectoryProperty getOutputDirectory() { + outputDirectory + } + + void setOutputDirectory(Provider value) { + outputDirectory.set(value) + } + + void setOutputDirectory(File value) { + outputDirectory.set(value) + } + + @Internal + Provider> getProfiles() { + provisioningProfiles.getElements().map { + it.collect { + DefaultMobileProvisioning.open(it.asFile) + } + } as Provider> + } + + @OutputFiles + protected FileCollection getOutputFiles() { + def files = objects.fileCollection() + files.setFrom(provisioningProfiles.collect { + outputDirectory.file("${readUUIDFromMobileProvision(it)}.mobileprovision") + }) + files + } + + static String readUUIDFromMobileProvision(File input) { + DefaultMobileProvisioning.open(input).uuid + } + + @TaskAction + protected void install() { + def logFile = logFile.asFile.getOrNull() + if(logFile) { + logFile.parentFile.mkdirs() + } + def output = new PrintWriter(getOutputStream(logFile), true) + output.println("Install Profiles: ${provisioningProfiles.size()}") + provisioningProfiles.each { + def profile = DefaultMobileProvisioning.open(it) + SimpleDateFormat outputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + + output.println("""\ + Install mobile provisioning profile: + ---------------------------------------------------------------- + Name: ${profile.name} + Uuid: ${profile.uuid} + Team: ${profile.teamName}(${profile.teamIdentifier}) + Creation date: ${outputFormat.format(profile.creationDate)} + Expiration date: ${outputFormat.format(profile.expirationDate)} + Is xcode managed: ${profile.isXcodeManaged()} + Entitlements: ${profile.entitlements} + ---------------------------------------------------------------- + """.stripIndent().trim()) + + if (profile.expired) { + logger.warn("profile ${profile.name}(${profile.uuid}) is expired") + output.println("profile ${profile.name}(${profile.uuid}) is expired") + } + + def out = outputDirectory.file("${readUUIDFromMobileProvision(it)}.mobileprovision").get().asFile + output.println("to: '${out.absolutePath}'\n") + out.bytes = it.bytes + } + output.close() + } +} diff --git a/src/test/groovy/com/wooga/gradle/test/ios/MobileProvisionMock.groovy b/src/test/groovy/com/wooga/gradle/test/ios/MobileProvisionMock.groovy new file mode 100644 index 0000000..bad82d2 --- /dev/null +++ b/src/test/groovy/com/wooga/gradle/test/ios/MobileProvisionMock.groovy @@ -0,0 +1,113 @@ +package com.wooga.gradle.test.ios + +import com.dd.plist.NSDate +import com.dd.plist.NSDictionary +import com.dd.plist.XMLPropertyListParser +import groovy.transform.stc.ClosureParams +import groovy.transform.stc.FromString +import wooga.gradle.build.unity.ios.MobileProvisioning + +class MobileProvisionMock implements MobileProvisioning { + + String uuid = UUID.randomUUID() + String appIdName = "Mock AppId" + String name = "Mock Profile" + List teamIdentifier = ["randomId"] + String teamName = "Mock Team" + Integer timeToLive = 265 + Integer version = 1 + + private Date creationDate + + void setCreationDate(Date creationDate) { + this.creationDate = normalizeDate(creationDate) + } + + Date getCreationDate() { + return creationDate + } + + private Date expirationDate + + void setExpirationDate(Date expirationDate) { + this.expirationDate = normalizeDate(expirationDate) + } + + Date getExpirationDate() { + return expirationDate + } + + Boolean xcodeManaged = false + Map entitlements = [:] + + MobileProvisionMock() { + setCreationDate(new Date()) + setExpirationDate(new Date(creationDate.time + 100000)) + } + + MobileProvisionMock(String uuid, String name) { + super() + this.uuid = uuid + this.name = name + } + + @Override + Boolean isExpired() { + expirationDate < new Date() + } + + @Override + Boolean isXcodeManaged() { + xcodeManaged + } + + private Date normalizeDate(Date input) { + def nsDateString = NSDate.fromJavaObject(input).toXMLPropertyList() + ((NSDate) XMLPropertyListParser.parse(nsDateString.bytes)).toJavaObject(Date) + } + + byte[] asBytes() { + def s = new ByteArrayOutputStream() + Random rd = new Random() + byte[] intro = new byte[0x3E] + rd.nextBytes(intro) + //fill first bytes with random bytes + s.write(intro, 0, intro.length) + + //write the provisioning profile plist content + def plistRaw = [ + "UUID" : uuid, + "AppIDName" : appIdName, + "Name" : name, + "TeamIdentifier" : teamIdentifier, + "TeamName" : teamName, + "TimeToLive" : timeToLive, + "Version" : version, + "CreationDate" : creationDate, + "ExpirationDate" : expirationDate, + "IsXcodeManaged" : xcodeManaged, + "DeveloperCertificates": [], + "DER-Encoded-Profile" : "", + "Entitlements" : entitlements, + ] + s.write(NSDictionary.fromJavaObject(plistRaw).toXMLPropertyList().bytes) + + //write other random data as stand in for certificates + byte[] outro = new byte[200] + rd.nextBytes(intro) + //fill first bytes with random bytes + s.write(outro, 0, intro.length) + s.toByteArray() + } + + static File createMock(@ClosureParams(value = FromString.class, options = "com.wooga.gradle.test.ios.MobileProvisionMock") Closure configuration = null) { + def mock = new MobileProvisionMock() + if (configuration) { + configuration.setDelegate(mock) + configuration.call(mock) + } + def file = File.createTempFile("mock", ".mobileprovisioning") + file.bytes = mock.asBytes() + file + } +} diff --git a/src/test/groovy/wooga/gradle/build/unity/ios/internal/MobileProvisioningSpec.groovy b/src/test/groovy/wooga/gradle/build/unity/ios/internal/MobileProvisioningSpec.groovy new file mode 100644 index 0000000..d6185fe --- /dev/null +++ b/src/test/groovy/wooga/gradle/build/unity/ios/internal/MobileProvisioningSpec.groovy @@ -0,0 +1,39 @@ +package wooga.gradle.build.unity.ios.internal + +import com.wooga.gradle.test.ios.MobileProvisionMock +import spock.lang.Shared +import spock.lang.Specification + +class MobileProvisioningSpec extends Specification { + + @Shared + MobileProvisionMock profile + + def setup() { + profile = new MobileProvisionMock() + } + + def "basic mock which can act as a standin for a mobile provision file"() { + given: "some known profile values" + profile.uuid = UUID.randomUUID() + profile.name = "some name" + + + and: "a profile file" + def f = File.createTempFile("test", ".mobileprovisioning") + f.bytes = profile.asBytes() + + when: + def subject = DefaultMobileProvisioning.open(f) + + then: + subject.uuid == profile.uuid + subject.name == profile.name + subject.teamName == profile.teamName + subject.teamIdentifier == profile.teamIdentifier + subject.expirationDate.time == profile.expirationDate.time + subject.creationDate.time == profile.creationDate.time + subject.timeToLive == profile.timeToLive + subject.xcodeManaged == profile.xcodeManaged + } +}