diff --git a/build.gradle b/build.gradle index 8889fd2a..92d44577 100644 --- a/build.gradle +++ b/build.gradle @@ -1,25 +1,25 @@ -allprojects { - apply plugin: 'java-gradle-plugin' - apply plugin: 'groovy' +String getGitDescribeVersion() { + ByteArrayOutputStream output = new ByteArrayOutputStream() - def versionNumber = "0.16.0.a603076-SNAPSHOT" + project.exec({ ExecSpec execSpec -> + execSpec.args = ["describe", "--tags", "--always", "--first-parent"] + execSpec.executable = "git" + execSpec.standardOutput = output + }) - if (project.hasProperty("versionNumber")) { - versionNumber = project.versionNumber - } - if (project.hasProperty("versionSuffix")) { - versionNumber += project.versionSuffix - } - if (project.hasProperty("buildNumber")) { - versionNumber += "." + project.buildNumber - } + return output.toString().trim() +} +// +allprojects { + apply plugin: 'java-gradle-plugin' + apply plugin: 'groovy' group = 'org.openbakery' - version = versionNumber + version = getGitDescribeVersion() + "-SNAPSHOT" - sourceCompatibility = "1.6" - targetCompatibility = "1.6" + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 repositories { mavenCentral() diff --git a/example/OSX/ExampleOSX/ExampleOSX.xcodeproj/project.pbxproj b/example/OSX/ExampleOSX/ExampleOSX.xcodeproj/project.pbxproj index 5a9d55a3..0e7cec4c 100644 --- a/example/OSX/ExampleOSX/ExampleOSX.xcodeproj/project.pbxproj +++ b/example/OSX/ExampleOSX/ExampleOSX.xcodeproj/project.pbxproj @@ -187,7 +187,7 @@ TargetAttributes = { A7F1890E1A6FE161002D206F = { CreatedOnToolsVersion = 6.1.1; - DevelopmentTeam = Z7L2YCUH45; + DevelopmentTeam = 2YFT3F89TY; }; A7F189211A6FE161002D206F = { CreatedOnToolsVersion = 6.1.1; @@ -404,6 +404,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "Developer ID Application"; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 2YFT3F89TY; INFOPLIST_FILE = ExampleOSX/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -417,6 +418,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "Developer ID Application"; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 2YFT3F89TY; INFOPLIST_FILE = ExampleOSX/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ea720f98..9d2dc020 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip diff --git a/libxcode/src/main/groovy/org/openbakery/CommandRunner.groovy b/libxcode/src/main/groovy/org/openbakery/CommandRunner.groovy index b52aeeff..2f3dba20 100644 --- a/libxcode/src/main/groovy/org/openbakery/CommandRunner.groovy +++ b/libxcode/src/main/groovy/org/openbakery/CommandRunner.groovy @@ -23,7 +23,7 @@ import org.openbakery.output.OutputAppender import org.slf4j.Logger import org.slf4j.LoggerFactory -class CommandRunner { +class CommandRunner implements Serializable{ private static Logger logger = LoggerFactory.getLogger(CommandRunner.class) @@ -34,10 +34,6 @@ class CommandRunner { Thread readerThread - public CommandRunner() { - - } - void setOutputFile(File outputFile) { if (outputFile.exists()) { diff --git a/libxcode/src/main/groovy/org/openbakery/codesign/ProvisioningProfileReader.groovy b/libxcode/src/main/groovy/org/openbakery/codesign/ProvisioningProfileReader.groovy index 121b6cc8..2dedab82 100644 --- a/libxcode/src/main/groovy/org/openbakery/codesign/ProvisioningProfileReader.groovy +++ b/libxcode/src/main/groovy/org/openbakery/codesign/ProvisioningProfileReader.groovy @@ -149,6 +149,9 @@ class ProvisioningProfileReader { } } + List getPlatforms() { + return config.getProperty("Platform") + } String getUUID() { return config.getString("UUID") diff --git a/libxcode/src/main/groovy/org/openbakery/util/PlistHelper.groovy b/libxcode/src/main/groovy/org/openbakery/util/PlistHelper.groovy index 256e23d1..cec046c9 100644 --- a/libxcode/src/main/groovy/org/openbakery/util/PlistHelper.groovy +++ b/libxcode/src/main/groovy/org/openbakery/util/PlistHelper.groovy @@ -98,10 +98,10 @@ class PlistHelper { void setValueForPlist(File plist, String key, String value) { - commandForPlist(plist, "Set :" + key + " " + value) + if (value != null) + commandForPlist(plist, "Set :" + key + " " + value) } - void commandForPlist(File plist, String command) { if (!plist.exists()) { throw new IllegalStateException("Info Plist does not exist: " + plist.absolutePath); diff --git a/libxcode/src/main/groovy/org/openbakery/xcode/Xcodebuild.groovy b/libxcode/src/main/groovy/org/openbakery/xcode/Xcodebuild.groovy index b84bae48..0989e05c 100644 --- a/libxcode/src/main/groovy/org/openbakery/xcode/Xcodebuild.groovy +++ b/libxcode/src/main/groovy/org/openbakery/xcode/Xcodebuild.groovy @@ -19,6 +19,8 @@ class Xcodebuild { public static final String EXECUTABLE = "xcodebuild" public static final String ACTION_ARCHIVE = "archive" public static final String ACTION_EXPORT_ARCHIVE = "-exportArchive" + public static final String ARGUMENT_CONFIGURATION = "-configuration" + public static final String ARGUMENT_WORKSPACE = "-workspace" public static final String ARGUMENT_SCHEME = "-scheme" public static final String ARGUMENT_ARCHIVE_PATH = "-archivePath" public static final String ARGUMENT_EXPORT_PATH = "-exportPath" @@ -50,26 +52,34 @@ class Xcodebuild { static void packageIpa(CommandRunner commandRunner, File archivePath, File exportPath, - File exportOptionsPlist) { + File exportOptionsPlist, + @Nullable File xcodeApp) { assert archivePath != null && archivePath.exists() assert exportPath != null && exportPath.exists() assert exportOptionsPlist != null && exportOptionsPlist.exists() - commandRunner.run(EXECUTABLE, - ACTION_EXPORT_ARCHIVE, - ARGUMENT_ARCHIVE_PATH, archivePath.absolutePath, - ARGUMENT_EXPORT_PATH, exportPath.absolutePath, - ARGUMENT_EXPORT_OPTIONS_PLIST, exportOptionsPlist.absolutePath) + // Env value + HashMap envMap = new HashMap<>() + if (xcodeApp != null) { + envMap.put(Xcode.ENV_DEVELOPER_DIR, xcodeApp.absolutePath) + } + + List args = [EXECUTABLE, + ACTION_EXPORT_ARCHIVE, + ARGUMENT_ARCHIVE_PATH, archivePath.absolutePath, + ARGUMENT_EXPORT_PATH, exportPath.absolutePath, + ARGUMENT_EXPORT_OPTIONS_PLIST, exportOptionsPlist.absolutePath] + + commandRunner.run(args, envMap) } static void archive(CommandRunner commandRunner, String scheme, File outputPath, - File xcConfig, + String configuration, @Nullable File xcodeApp) { assert scheme != null - assert xcConfig.exists() && !xcConfig.isDirectory() HashMap envMap = new HashMap<>() @@ -80,8 +90,8 @@ class Xcodebuild { List args = [EXECUTABLE, ACTION_ARCHIVE, ARGUMENT_SCHEME, scheme, - ARGUMENT_ARCHIVE_PATH, outputPath.absolutePath, - ARGUMENT_XCCONFIG, xcConfig.absolutePath] + "-configuration", configuration, + ARGUMENT_ARCHIVE_PATH, outputPath.absolutePath] commandRunner.run(args, envMap) } diff --git a/plugin/build.gradle b/plugin/build.gradle index 83cac330..32b9c6d1 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -98,7 +98,7 @@ uploadArchives { mavenDeployer { configuration = configurations.deployerJars - repository(url: publishURL) { + repository(url: mavenLocal().url) { authentication(userName: publishUser, password: publishPassword) } diff --git a/plugin/src/functionalTest/groovy/org/openbakery/FunctionalTestBase.groovy b/plugin/src/functionalTest/groovy/org/openbakery/FunctionalTestBase.groovy new file mode 100644 index 00000000..f2d5862b --- /dev/null +++ b/plugin/src/functionalTest/groovy/org/openbakery/FunctionalTestBase.groovy @@ -0,0 +1,55 @@ +package org.openbakery + +import org.apache.commons.io.FileUtils +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.Specification + +class FunctionalTestBase extends Specification { + + @Rule + final TemporaryFolder testProjectDir = new TemporaryFolder() + + List pluginClasspath + + File buildFile + + void genericSetup() { + extractPluginClassPathResource() + createMockBuildFile() + copyTestProject() + } + + void extractPluginClassPathResource() { + URL pluginClasspathResource = getClass().classLoader + .findResource("plugin-classpath.txt") + + if (pluginClasspathResource == null) { + throw new IllegalStateException("Did not find plugin classpath resource, " + + "run `testClasses` build task.") + } + + pluginClasspath = pluginClasspathResource.readLines() + .collect { new File(it) } + } + + void createMockBuildFile() { + buildFile = testProjectDir.newFile('build.gradle') + buildFile << """ + plugins { + id 'org.openbakery.xcode-plugin' + } + """ + } + + void copyTestProject() { + URL folder = getClass().classLoader + .findResource("TestProject") + + + File file = new File(folder.getFile()) + assert file.exists() + + FileUtils.copyDirectory(file, testProjectDir.root) + } +} diff --git a/plugin/src/functionalTest/groovy/org/openbakery/KeychainCreateTaskFunctionalTest.groovy b/plugin/src/functionalTest/groovy/org/openbakery/KeychainCreateTaskFunctionalTest.groovy index 71b02662..ce4c7f5c 100644 --- a/plugin/src/functionalTest/groovy/org/openbakery/KeychainCreateTaskFunctionalTest.groovy +++ b/plugin/src/functionalTest/groovy/org/openbakery/KeychainCreateTaskFunctionalTest.groovy @@ -3,43 +3,23 @@ package org.openbakery import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome -import org.junit.Rule -import org.junit.rules.TemporaryFolder import org.openbakery.signing.KeychainCreateTask import spock.lang.Shared -import spock.lang.Specification import java.nio.file.Paths -class KeychainCreateTaskFunctionalTest extends Specification { - - @Rule - final TemporaryFolder testProjectDir = new TemporaryFolder() - - List pluginClasspath +class KeychainCreateTaskFunctionalTest extends FunctionalTestBase { @Shared File certificate - File buildFile - def setup() { - def pluginClasspathResource = getClass().classLoader.findResource("plugin-classpath.txt") - if (pluginClasspathResource == null) { - throw new IllegalStateException("Did not find plugin classpath resource, run `testClasses` build task.") - } - - pluginClasspath = pluginClasspathResource.readLines().collect { new File(it) } + extractPluginClassPathResource() + createMockBuildFile() + copyTestProject() certificate = findResource("fake_distribution.p12") assert certificate.exists() - - buildFile = testProjectDir.newFile('build.gradle') - buildFile << """ - plugins { - id 'org.openbakery.xcode-plugin' - } - """ } def "The task list should contain the task"() { diff --git a/plugin/src/functionalTest/groovy/org/openbakery/PackageTaskIosAndTvOSTest.groovy b/plugin/src/functionalTest/groovy/org/openbakery/PackageTaskIosAndTvOSTest.groovy index 83c7d69b..5c8b563c 100644 --- a/plugin/src/functionalTest/groovy/org/openbakery/PackageTaskIosAndTvOSTest.groovy +++ b/plugin/src/functionalTest/groovy/org/openbakery/PackageTaskIosAndTvOSTest.groovy @@ -1,39 +1,15 @@ package org.openbakery import org.gradle.testkit.runner.GradleRunner -import org.junit.Rule -import org.junit.rules.TemporaryFolder import org.openbakery.packaging.PackageTaskIosAndTvOS -import spock.lang.Specification -class PackageTaskIosAndTvOSTest extends Specification { - - @Rule - final TemporaryFolder testProjectDir = new TemporaryFolder() - - List pluginClasspath - - File buildFile +class PackageTaskIosAndTvOSTest extends FunctionalTestBase { def setup() { - buildFile = testProjectDir.newFile('build.gradle') - - def pluginClasspathResource = getClass().classLoader.findResource("plugin-classpath.txt") - if (pluginClasspathResource == null) { - throw new IllegalStateException("Did not find plugin classpath resource, run `testClasses` build task.") - } - - pluginClasspath = pluginClasspathResource.readLines().collect { new File(it) } + genericSetup() } def "The task list should contain the task"() { - given: - buildFile << """ - plugins { - id 'org.openbakery.xcode-plugin' - } - """ - when: def result = GradleRunner.create() .withProjectDir(testProjectDir.root) diff --git a/plugin/src/functionalTest/groovy/org/openbakery/PrepareXcodeArchivingFunctionalTest.groovy b/plugin/src/functionalTest/groovy/org/openbakery/PrepareXcodeArchivingFunctionalTest.groovy deleted file mode 100644 index f1606a6d..00000000 --- a/plugin/src/functionalTest/groovy/org/openbakery/PrepareXcodeArchivingFunctionalTest.groovy +++ /dev/null @@ -1,193 +0,0 @@ -package org.openbakery - -import org.apache.commons.io.FileUtils -import org.gradle.testkit.runner.BuildResult -import org.gradle.testkit.runner.GradleRunner -import org.gradle.testkit.runner.TaskOutcome -import org.junit.Rule -import org.junit.rules.TemporaryFolder -import org.openbakery.util.PathHelper -import spock.lang.Specification - -import java.nio.file.Paths - -class PrepareXcodeArchivingFunctionalTest extends Specification { - - @Rule - final TemporaryFolder testProjectDir = new TemporaryFolder() - - List pluginClasspath - - File buildFile - - - File provisioningFileWildCard - - def setup() { - pluginClasspath = findResource("plugin-classpath.txt") - .readLines() - .collect { new File(it) } - - FileUtils.copyDirectory(findResource("TestProject"), testProjectDir.getRoot()) - - provisioningFileWildCard = findResource("test1.mobileprovision") - } - - def setupBuildFile() { - buildFile = testProjectDir.newFile('build.gradle') - buildFile << """ - plugins { - id 'org.openbakery.xcode-plugin' - } - - xcodebuild { - target = 'TestProject' - scheme = "TestScheme" - signing { - mobileProvisionURI = "${provisioningFileWildCard.toURI().toString()}" - } - } - - """ - } - - def "The task list should contain the task"() { - given: - setupBuildFile() - - when: - def result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments('tasks') - .withPluginClasspath(pluginClasspath) - .build() - - then: - result.output.contains(PrepareXcodeArchivingTask.NAME - + " - " - + PrepareXcodeArchivingTask.DESCRIPTION) - } - - def "The task should complete without error and generate the xcconfig file"() { - setup: - setupBuildFile() - - when: - buildFile << """ - xcodebuild { - infoplist { - bundleIdentifier = "org.openbakery.test.ExampleWidget" - } - } - """ - - final File certificate = findResource("fake_distribution.p12") - assert certificate.exists() - buildFile << """ - xcodebuild { - infoplist { - bundleIdentifier = "org.openbakery.test.ExampleWidget" - } - - signing { - certificateURI = "${certificate.toURI().toString()}" - certificatePassword = "p4ssword" - } - } - """ - - BuildResult result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments(PrepareXcodeArchivingTask.NAME) - .withPluginClasspath(pluginClasspath) - .build() - - then: "The task should complete without error" - - result.task(":" + PrepareXcodeArchivingTask.NAME) - .outcome == TaskOutcome.SUCCESS - - and: "The archive xcconfig provisioningFile1 should be properly generated and populated from configured values" - - File outputFile = new File(testProjectDir.root, "build/" - + PathHelper.FOLDER_ARCHIVE - + "/" + PathHelper.GENERATED_XCARCHIVE_FILE_NAME) - - outputFile.exists() - - String text = outputFile.text - text.contains("PRODUCT_BUNDLE_IDENTIFIER = org.openbakery.test.ExampleWidget") - text.contains("iPhone Distribution: Test Company Name (12345ABCDE)") - text.contains("PROVISIONING_PROFILE = XXXXFFFF-AAAA-BBBB-CCCC-DDDDEEEEFFFF") - text.contains("PROVISIONING_PROFILE_SPECIFIER = ad hoc") - text.contains("DEVELOPMENT_TEAM = XXXYYYZZZZ") - - and: "Should no contain any entitlements information" - !text.contains("CODE_SIGN_ENTITLEMENTS =") - } - - def "If present the entitlements file should be present into the xcconfig file"() { - setup: - setupBuildFile() - - when: - buildFile << """ - xcodebuild { - infoplist { - bundleIdentifier = "org.openbakery.test.ExampleWidget" - } - } - """ - - final File certificate = findResource("fake_distribution.p12") - assert certificate.exists() - - final File entitlementsFile = findResource("fake.entitlements") - assert entitlementsFile.exists() - - buildFile << """ - xcodebuild { - infoplist { - bundleIdentifier = "org.openbakery.test.ExampleWidget" - } - - signing { - certificateURI = "${certificate.toURI().toString()}" - certificatePassword = "p4ssword" - entitlementsFile = "${entitlementsFile.absolutePath}" - } - } - """ - - BuildResult result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments(PrepareXcodeArchivingTask.NAME) - .withPluginClasspath(pluginClasspath) - .build() - - then: "The task should complete without error" - - result.task(":" + PrepareXcodeArchivingTask.NAME) - .outcome == TaskOutcome.SUCCESS - - and: "The archive xcconfig should contains the path to the entitlements provisioningFile1" - - File outputFile = new File(testProjectDir.root, "build/" - + PathHelper.FOLDER_ARCHIVE - + "/" + PathHelper.GENERATED_XCARCHIVE_FILE_NAME) - - outputFile.exists() - - String text = outputFile.text - text.contains("CODE_SIGN_ENTITLEMENTS = ${entitlementsFile.absolutePath}") - } - - private File findResource(String name) { - ClassLoader classLoader = getClass().getClassLoader() - return (File) Optional.ofNullable(classLoader.getResource(name)) - .map { URL url -> url.toURI() } - .map { URI uri -> Paths.get(uri).toFile() } - .filter { File file -> file.exists() } - .orElseThrow { new Exception("Resource $name cannot be found") } - } -} diff --git a/plugin/src/functionalTest/groovy/org/openbakery/carthage/CarthageBootStrapTaskFunctionalTest.groovy b/plugin/src/functionalTest/groovy/org/openbakery/carthage/CarthageBootStrapTaskFunctionalTest.groovy index ec6a21fc..a3e120da 100644 --- a/plugin/src/functionalTest/groovy/org/openbakery/carthage/CarthageBootStrapTaskFunctionalTest.groovy +++ b/plugin/src/functionalTest/groovy/org/openbakery/carthage/CarthageBootStrapTaskFunctionalTest.groovy @@ -3,34 +3,15 @@ package org.openbakery.carthage import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome -import org.junit.Rule -import org.junit.rules.TemporaryFolder -import spock.lang.Specification +import org.openbakery.FunctionalTestBase -class CarthageBootStrapTaskFunctionalTest extends Specification { - @Rule - final TemporaryFolder testProjectDir = new TemporaryFolder() +class CarthageBootStrapTaskFunctionalTest extends FunctionalTestBase { - List pluginClasspath - File buildFile GradleRunner gradleRunner File carthageFolder void setup() { - buildFile = testProjectDir.newFile('build.gradle') - - buildFile << """ - plugins { - id 'org.openbakery.xcode-plugin' - } - """ - - def pluginClasspathResource = getClass().classLoader.findResource("plugin-classpath.txt") - if (pluginClasspathResource == null) { - throw new IllegalStateException("Did not find plugin classpath resource, run `testClasses` build task.") - } - - pluginClasspath = pluginClasspathResource.readLines().collect { new File(it) } + genericSetup() gradleRunner = GradleRunner.create() .withProjectDir(testProjectDir.root) diff --git a/plugin/src/functionalTest/groovy/org/openbakery/signing/ProvisioningInstallTaskFunctionalTest.groovy b/plugin/src/functionalTest/groovy/org/openbakery/signing/ProvisioningInstallTaskFunctionalTest.groovy index 16dce6f6..79621157 100644 --- a/plugin/src/functionalTest/groovy/org/openbakery/signing/ProvisioningInstallTaskFunctionalTest.groovy +++ b/plugin/src/functionalTest/groovy/org/openbakery/signing/ProvisioningInstallTaskFunctionalTest.groovy @@ -2,50 +2,28 @@ package org.openbakery.signing import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome -import org.junit.Rule -import org.junit.rules.TemporaryFolder -import spock.lang.Specification +import org.openbakery.FunctionalTestBase import spock.lang.Unroll import java.nio.file.Paths -class ProvisioningInstallTaskFunctionalTest extends Specification { - @Rule - final TemporaryFolder testProjectDir = new TemporaryFolder() +class ProvisioningInstallTaskFunctionalTest extends FunctionalTestBase { - List pluginClasspath - - File buildFile File provisioningFile1 def setup() { - buildFile = testProjectDir.newFile('build.gradle') - - buildFile << """ - plugins { - id 'org.openbakery.xcode-plugin' - } - """ - + genericSetup() provisioningFile1 = findResource("test1.mobileprovision") assert provisioningFile1.exists() - - def pluginClasspathResource = getClass().classLoader - .findResource("plugin-classpath.txt") - - if (pluginClasspathResource == null) { - throw new IllegalStateException("Did not find plugin classpath resource, run `testClasses` build task.") - } - - pluginClasspath = pluginClasspathResource.readLines().collect { new File(it) } } def "The task list should contain the task"() { when: def result = GradleRunner.create() .withProjectDir(testProjectDir.root) - .withArguments('tasks') + .withArguments('tasks', '--s') .withPluginClasspath(pluginClasspath) + .withDebug(true) .build() then: @@ -102,7 +80,7 @@ class ProvisioningInstallTaskFunctionalTest extends Specification { when: def result = GradleRunner.create() .withProjectDir(testProjectDir.root) - .withArguments(ProvisioningInstallTask.TASK_NAME) + .withArguments(ProvisioningInstallTask.TASK_NAME, "--s") .withPluginClasspath(pluginClasspath) .withDebug(true) .build() @@ -141,10 +119,8 @@ class ProvisioningInstallTaskFunctionalTest extends Specification { where: gradleVersion | _ - "4.4" | _ - "4.5" | _ - "4.6" | _ "4.7" | _ + "4.8" | _ } private File findResource(String name) { diff --git a/plugin/src/functionalTest/resources/test1.mobileprovision b/plugin/src/functionalTest/resources/test1.mobileprovision index d8fded0c..2435aef5 100644 --- a/plugin/src/functionalTest/resources/test1.mobileprovision +++ b/plugin/src/functionalTest/resources/test1.mobileprovision @@ -42,5 +42,9 @@ XXXYYYZZZZ + Platform + + iOS + - \ No newline at end of file + diff --git a/plugin/src/main/groovy/org/openbakery/AbstractXcodeBuildTask.groovy b/plugin/src/main/groovy/org/openbakery/AbstractXcodeBuildTask.groovy index b50c49a7..edd69479 100644 --- a/plugin/src/main/groovy/org/openbakery/AbstractXcodeBuildTask.groovy +++ b/plugin/src/main/groovy/org/openbakery/AbstractXcodeBuildTask.groovy @@ -107,7 +107,7 @@ abstract class AbstractXcodeBuildTask extends AbstractXcodeTask { } String getBundleIdentifier() { - File infoPlist = new File(project.projectDir, getXcodeExtension().infoPlist) + File infoPlist = new File(project.rootProject.rootDir, getXcodeExtension().infoPlist) return plistHelper.getValueFromPlist(infoPlist, "CFBundleIdentifier") } @@ -119,8 +119,6 @@ abstract class AbstractXcodeBuildTask extends AbstractXcodeTask { List provisioningList = getProvisioningUriList() .collect { it -> new File(new URI(it)) } - println "provisioningList : " + provisioningList - return Optional.ofNullable(ProvisioningProfileReader.getProvisionFileForIdentifier(bundleIdentifier, provisioningList, commandRunner, diff --git a/plugin/src/main/groovy/org/openbakery/InfoPlistModifyTask.groovy b/plugin/src/main/groovy/org/openbakery/InfoPlistModifyTask.groovy index 51b972a5..dda4ad00 100644 --- a/plugin/src/main/groovy/org/openbakery/InfoPlistModifyTask.groovy +++ b/plugin/src/main/groovy/org/openbakery/InfoPlistModifyTask.groovy @@ -43,7 +43,7 @@ class InfoPlistModifyTask extends AbstractDistributeTask { throw new IllegalArgumentException("No Info.plist was found! Check you xcode project settings if the specified target has a Info.plist set.") } - infoPlist = new File(project.projectDir, project.xcodebuild.infoPlist) + infoPlist = new File(project.rootProject.projectDir, project.xcodebuild.infoPlist) logger.debug("Try to updating {}", infoPlist) @@ -148,7 +148,7 @@ class InfoPlistModifyTask extends AbstractDistributeTask { setValueForPlist(KeyBundleIdentifier, extension.bundleIdentifier) } else { xcodeExtension.getBuildTargetConfiguration(xcodeExtension.scheme.getOrNull(), - xcodeExtension.configuration) + xcodeExtension.configuration.get()) .map { it -> it.bundleIdentifier } .ifPresent { it -> setValueForPlist(KeyBundleIdentifier, it) } } diff --git a/plugin/src/main/groovy/org/openbakery/PrepareXcodeArchivingTask.groovy b/plugin/src/main/groovy/org/openbakery/PrepareXcodeArchivingTask.groovy index 57364b9c..27da3182 100644 --- a/plugin/src/main/groovy/org/openbakery/PrepareXcodeArchivingTask.groovy +++ b/plugin/src/main/groovy/org/openbakery/PrepareXcodeArchivingTask.groovy @@ -9,9 +9,11 @@ import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.openbakery.codesign.ProvisioningProfileReader +import org.openbakery.extension.TargetConfiguration import org.openbakery.signing.KeychainCreateTask -import org.openbakery.signing.ProvisioningInstallTask +import org.openbakery.util.PathHelper import org.openbakery.util.PlistHelper +import org.openbakery.util.SignatureUtil @CompileStatic class PrepareXcodeArchivingTask extends DefaultTask { @@ -21,7 +23,16 @@ class PrepareXcodeArchivingTask extends DefaultTask { final Provider entitlementsFile = newInputFile() @OutputFile - final Provider outputFile = newOutputFile() + final Property outputFile = newOutputFile() + + @InputDirectory + final Provider projectFile = newInputFile() + + @Input + final ListProperty targetConfigurations = project.objects.listProperty(TargetConfiguration) + + final Provider buildConfiguration = project.objects.property(String) + final Provider target = project.objects.property(String) final ListProperty registeredProvisioningFiles = project.objects.listProperty(File) final Property commandRunnerProperty = project.objects.property(CommandRunner) @@ -37,7 +48,6 @@ class PrepareXcodeArchivingTask extends DefaultTask { public static final String DESCRIPTION = "Prepare the archive configuration file" public static final String NAME = "prepareArchiving" - static final String KEY_BUNDLE_IDENTIFIER = "PRODUCT_BUNDLE_IDENTIFIER" static final String KEY_CODE_SIGN_IDENTITY = "CODE_SIGN_IDENTITY" static final String KEY_CODE_SIGN_ENTITLEMENTS = "CODE_SIGN_ENTITLEMENTS" static final String KEY_DEVELOPMENT_TEAM = "DEVELOPMENT_TEAM" @@ -47,11 +57,7 @@ class PrepareXcodeArchivingTask extends DefaultTask { PrepareXcodeArchivingTask() { super() - dependsOn(XcodePlugin.INFOPLIST_MODIFY_TASK_NAME) - dependsOn(XcodePlugin.XCODE_CONFIG_TASK_NAME) - dependsOn(KeychainCreateTask.TASK_NAME) - dependsOn(ProvisioningInstallTask.TASK_NAME) - + this.dependsOn(KeychainCreateTask.TASK_NAME) this.description = DESCRIPTION this.entitlementsFilePath.set(entitlementsFile.map(new Transformer() { @@ -79,38 +85,175 @@ class PrepareXcodeArchivingTask extends DefaultTask { } })) + this.outputFile.set(project.layout + .buildDirectory + .file(PathHelper.FOLDER_ARCHIVE + "/" + PathHelper.GENERATED_XCARCHIVE_FILE_NAME)) + this.onlyIf { - return certificateFriendlyName.present && - configurationBundleIdentifier.present && - outputFile.present && - provisioningForConfiguration.present +// println "configurationBundleIdentifier :>> " + configurationBundleIdentifier +// println "provisioningForConfiguration : " + provisioningForConfiguration +// println "certificateFriendlyName : " + certificateFriendlyName + return certificateFriendlyName.present +// && +// configurationBundleIdentifier.present && +// provisioningForConfiguration.present + } + } + + private File getPbxProjFile() { + return new File(projectFile.get().asFile, "project.pbxproj") + } + + private Serializable getValueFromPbxProjFile(String value) { + return plistHelperProperty.get() + .getValueFromPlist(toXml(getPbxProjFile()), value) as Serializable + } + + private Serializable getObjectValueFromPbxProjFile(String value) { + return getValueFromPbxProjFile("objects:${value}") + } + + private String findProductId(String rootKey) { + return findProductId(rootKey, target.get()) + } + + private String findProductId(String rootKey, + String targetName) { + return getValueFromPbxProjFile("objects:${rootKey}:targets") + .find { it -> getObjectValueFromPbxProjFile("${it}:productName") == targetName } + } + + private String getBuildConfigurationId(String targetId) { + String configurationId = getObjectValueFromPbxProjFile("$targetId:buildConfigurationList") + List list = getObjectValueFromPbxProjFile("${configurationId}:buildConfigurations") as List + return list.find { + getValueFromPbxProjFile("objects:${it}:name") == buildConfiguration.get() + } + } + + private void setBuildConfigurationBuildSetting(String bcId, + String key, + String value) { + String completeKey = "objects:${bcId}:buildSettings:${key}" + Object property = plistHelperProperty.get().getValueFromPlist(getPbxProjFile(), completeKey) + + if (property == null) { + plistHelperProperty.get() + .addValueForPlist(getPbxProjFile(), completeKey, value) + } else { + + plistHelperProperty.get() + .setValueForPlist(getPbxProjFile(), completeKey, value) } } @TaskAction void generate() { - logger.info("Preparing archiving") + (targetConfigurations.get() as List) + .each { + configureTargetConfiguration(it) + } + } - outputFile.get().asFile.text = "" + private void configureTargetConfiguration(TargetConfiguration tc) { + HashMap map = new HashMap<>() + configureSignature(map, tc) + configureProvisioning(map, tc) + configureEntitlements(map, tc) + configureInfoPlist(map, tc) - append(KEY_CODE_SIGN_IDENTITY, certificateFriendlyName.get()) - append(KEY_BUNDLE_IDENTIFIER, configurationBundleIdentifier.get()) + final String buildConfigurationId = getBuildConfigurationId(findProductId( + getValueFromPbxProjFile("rootObject") as String, + tc.name)) - if (provisioningReader.present) { - ProvisioningProfileReader reader = provisioningReader.get() - append(KEY_DEVELOPMENT_TEAM, reader.getTeamIdentifierPrefix()) - append(KEY_PROVISIONING_PROFILE_ID, reader.getUUID()) - append(KEY_PROVISIONING_PROFILE_SPEC, reader.getName()) + configurePlist(buildConfigurationId, tc) + + map.each { k, v -> + setBuildConfigurationBuildSetting(buildConfigurationId, k, v) + } + } + + private void configureSignature(HashMap map, + TargetConfiguration tc) { + if (tc.certificateFile != null && tc.certificateFile.exists()) { + String friendlyName = SignatureUtil.getCertificateFriendlyName(tc.certificateFile, + tc.certificatePassword) + + map.put(KEY_CODE_SIGN_IDENTITY, friendlyName) + map.put(KEY_CODE_SIGN_IDENTITY + "[sdk=iphoneos*]", friendlyName) + map.put(KEY_CODE_SIGN_IDENTITY + "[sdk=appletvos*]", friendlyName) + } + + map.put("CODE_SIGN_STYLE", "Manual") + } + + private void configureProvisioning(HashMap map, + TargetConfiguration tc) { + if (tc.provisioningFile != null) { + ProvisioningProfileReader reader = new ProvisioningProfileReader(tc.provisioningFile, + commandRunnerProperty.get()) + + map.put(KEY_DEVELOPMENT_TEAM, reader.getTeamIdentifierPrefix()) + map.put(KEY_PROVISIONING_PROFILE_ID, reader.getUUID()) + map.put(KEY_PROVISIONING_PROFILE_SPEC, reader.getName()) + } + } + + private void configureEntitlements(HashMap map, + TargetConfiguration tc) { + + if (tc.entitlementsFile != null) { + map.put(KEY_CODE_SIGN_ENTITLEMENTS, tc.entitlementsFile.absolutePath) } + } - if (entitlementsFilePath.present) { - append(KEY_CODE_SIGN_ENTITLEMENTS, entitlementsFilePath.get()) + private void configureInfoPlist(HashMap map, + TargetConfiguration tc) { + if (tc.bundleIdentifier != null) { + map.put("BUNDLE_ID", tc.bundleIdentifier) } } - private void append(String key, String value) { - outputFile.get() - .asFile - .append(System.getProperty("line.separator") + key + " = " + value) + private File toXml(File source) { + File file = File.createTempFile("project.plist", "") + commandRunnerProperty.get() + .run(["plutil", + "-convert", + "xml1", + source.absolutePath, + "-o", file.absolutePath]) + + return file + } + + private void configurePlist(String buildConfigurationId, + TargetConfiguration tc) { + String plistPath = getValueFromPbxProjFile( + ":objects:${buildConfigurationId}:buildSettings:INFOPLIST_FILE") + + File file = project.rootProject.file(plistPath) + assert file.exists() + + if (tc.version != null) + setValueOrCreate(file, "CFBundleVersion", tc.version) + + if (tc.shortVersion != null) + setValueOrCreate(file, "CFBundleShortVersionString", tc.shortVersion) + } + + private void setValueOrCreate(File file, + String key, + String value) { + + String currentValue = plistHelperProperty.get() + .getValueFromPlist(file, key) + + if (currentValue == null) { + plistHelperProperty.get() + .addValueForPlist(file, key, value) + } else { + plistHelperProperty.get() + .setValueForPlist(file, key, value) + } } } diff --git a/plugin/src/main/groovy/org/openbakery/XcodeBuildPluginExtension.groovy b/plugin/src/main/groovy/org/openbakery/XcodeBuildPluginExtension.groovy index 207649e8..a8887183 100644 --- a/plugin/src/main/groovy/org/openbakery/XcodeBuildPluginExtension.groovy +++ b/plugin/src/main/groovy/org/openbakery/XcodeBuildPluginExtension.groovy @@ -17,6 +17,7 @@ package org.openbakery import org.apache.commons.io.filefilter.SuffixFileFilter import org.apache.commons.lang.StringUtils +import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.Transformer import org.gradle.api.file.Directory @@ -24,6 +25,7 @@ import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.util.ConfigureUtil import org.openbakery.extension.Signing +import org.openbakery.extension.TargetConfiguration import org.openbakery.util.PathHelper import org.openbakery.util.PlistHelper import org.openbakery.util.VariableResolver @@ -35,6 +37,8 @@ class XcodeBuildPluginExtension { final Property bitcode = project.objects.property(Boolean) final Property version = project.objects.property(String) + final Property configuration = project.objects.property(String) + final Property target = project.objects.property(String) final Property targetType = project.objects.property(Type) final Property scheme = project.objects.property(String) final DirectoryProperty archiveDirectory = project.layout.directoryProperty() @@ -45,6 +49,7 @@ class XcodeBuildPluginExtension { final DirectoryProperty sharedPrecompsDir = project.layout.directoryProperty() final DirectoryProperty derivedDataPath = project.layout.directoryProperty() final Property xcodeServiceProperty = project.objects.property(XcodeService) + final NamedDomainObjectContainer targetConfigurations final Signing signing @@ -52,12 +57,9 @@ class XcodeBuildPluginExtension { String infoPlist = null - String configuration = 'Debug' boolean simulator = true Type type = Type.iOS - String target - def additionalParameters = null String bundleNameSuffix = null List arch = null @@ -109,8 +111,10 @@ class XcodeBuildPluginExtension { this.symRoot.set(project.layout.buildDirectory.dir("sym")) this.sharedPrecompsDir.set(project.layout.buildDirectory.dir("shared")) this.derivedDataPath.set(project.layout.buildDirectory.dir("derivedData")) - this.targetType.set(Type.iOS) + + targetConfigurations = project.container(TargetConfiguration) + project.extensions.targetConfigurations = targetConfigurations } private void configureServices() { @@ -145,7 +149,7 @@ class XcodeBuildPluginExtension { if (workspace != null) { return workspace } - String[] fileList = project.projectDir.list(new SuffixFileFilter(".xcworkspace")) + String[] fileList = project.rootProject.projectDir.list(new SuffixFileFilter(".xcworkspace")) if (fileList != null && fileList.length) { return fileList[0] } @@ -260,7 +264,7 @@ class XcodeBuildPluginExtension { // should be removed an replaced by the xcodebuildParameters.outputPath File getOutputPath() { - String path = getConfiguration() + String path = configuration.get() if (type != Type.macOS) { path += "-" if (type == Type.iOS) { @@ -289,7 +293,7 @@ class XcodeBuildPluginExtension { projectSettings.each { String key, BuildTargetConfiguration buildConfiguration -> - BuildConfiguration settings = buildConfiguration.buildSettings[configuration]; + BuildConfiguration settings = buildConfiguration.buildSettings[configuration.get()]; if (settings != null && settings.bundleIdentifier.equalsIgnoreCase(bundleIdentifier)) { result = settings return @@ -303,9 +307,9 @@ class XcodeBuildPluginExtension { File getApplicationBundle() { - BuildTargetConfiguration buildConfiguration = projectSettings[target] + BuildTargetConfiguration buildConfiguration = projectSettings[target.get()] if (buildConfiguration != null) { - BuildConfiguration buildSettings = buildConfiguration.buildSettings[configuration]; + BuildConfiguration buildSettings = buildConfiguration.buildSettings[configuration.get()]; if (buildSettings != null && buildSettings.sdkRoot.equalsIgnoreCase("watchos")) { BuildConfiguration parent = getParent(buildSettings) return new File(getOutputPath(), parent.productName + "." + this.productType) @@ -316,9 +320,9 @@ class XcodeBuildPluginExtension { File getBinary() { logger.debug("getBinary") - BuildTargetConfiguration buildConfiguration = projectSettings[target] + BuildTargetConfiguration buildConfiguration = projectSettings[target.get()] if (buildConfiguration != null) { - BuildConfiguration buildSettings = buildConfiguration.buildSettings[configuration]; + BuildConfiguration buildSettings = buildConfiguration.buildSettings[configuration.get()]; logger.debug("buildSettings: {}", buildSettings) if (type == Type.macOS) { return new File(getOutputPath(), buildSettings.productName + ".app/Contents/MacOS/" + buildSettings.productName) @@ -330,17 +334,17 @@ class XcodeBuildPluginExtension { BuildConfiguration getBuildConfiguration() { - BuildTargetConfiguration buildTargetConfiguration = projectSettings[target] + BuildTargetConfiguration buildTargetConfiguration = projectSettings[target.get()] if (buildTargetConfiguration != null) { - return buildTargetConfiguration.buildSettings[configuration]; + return buildTargetConfiguration.buildSettings[configuration.get()]; } - throw new IllegalStateException("No build configuration found for + target '" + parameters.target + "' and configuration '" + configuration + "'") + throw new IllegalStateException("No build configuration found for + target '" + parameters.target.get() + "' and configuration '" + configuration.get() + "'") } BuildConfiguration getBuildConfiguration(String bundleIdentifier) { BuildConfiguration result = null projectSettings.each() { target, buildTargetConfiguration -> - BuildConfiguration settings = buildTargetConfiguration.buildSettings[configuration] + BuildConfiguration settings = buildTargetConfiguration.buildSettings[configuration.get()] if (settings != null) { @@ -383,10 +387,12 @@ class XcodeBuildPluginExtension { this.simulator = simulator.toString().equalsIgnoreCase("true") || simulator.toString().equalsIgnoreCase("yes") } - void setProjectFile(def projectFile) { - if (projectFile instanceof File) { - this.projectFile = projectFile - } + void setProjectFile(File projectFile) { + assert projectFile.exists() + this.projectFile = projectFile + } + + void setProjectFile(String projectFile) { this.projectFile = new File(project.projectDir.absolutePath, projectFile) } @@ -395,12 +401,16 @@ class XcodeBuildPluginExtension { return this.projectFile } - String[] projectFiles = project.projectDir.list(new SuffixFileFilter(".xcodeproj")) + String[] projectFiles = project.rootProject + .projectDir + .list(new SuffixFileFilter(".xcodeproj")) + if (!projectFiles || projectFiles.length < 1) { throw new FileNotFoundException("No Xcode project files were found in ${project.projectDir}") } - return new File(project.projectDir, projectFiles.first()) + return new File(project.rootProject.projectDir, + projectFiles.first()) } // should be remove in the future, so that every task has its own xcode object @@ -416,11 +426,11 @@ class XcodeBuildPluginExtension { XcodebuildParameters getXcodebuildParameters() { def result = new XcodebuildParameters() result.scheme = this.scheme.getOrNull() - result.target = this.target + result.target = this.target.get() result.simulator = this.simulator result.type = this.type result.workspace = getWorkspace() - result.configuration = this.configuration + result.configuration = this.configuration.get() result.dstRoot = this.getDstRoot().asFile.getOrNull() result.objRoot = this.getObjRoot().asFile.getOrNull() result.symRoot = this.getSymRoot().asFile.getOrNull() diff --git a/plugin/src/main/groovy/org/openbakery/XcodePlugin.groovy b/plugin/src/main/groovy/org/openbakery/XcodePlugin.groovy index ce142110..453262ee 100644 --- a/plugin/src/main/groovy/org/openbakery/XcodePlugin.groovy +++ b/plugin/src/main/groovy/org/openbakery/XcodePlugin.groovy @@ -133,7 +133,6 @@ class XcodePlugin implements Plugin { private Signing signingExtension private XcodeBuildPluginExtension xcodeBuildPluginExtension private InfoPlistExtension infoPlistExtension - private CommandRunner commandRunner private PlistHelper plistHelper private Security securityTool @@ -488,26 +487,32 @@ class XcodePlugin implements Plugin { .create(PrepareXcodeArchivingTask.NAME, PrepareXcodeArchivingTask.class) { it.group = XCODE_GROUP_NAME - it.certificateFriendlyName.set(signingExtension.certificateFriendlyName) it.commandRunnerProperty.set(commandRunner) it.configurationBundleIdentifier.set(infoPlistExtension.configurationBundleIdentifier) it.entitlementsFile.set(signingExtension.entitlementsFile) - it.outputFile.set(signingExtension.xcConfigFile) + it.projectFile.set(xcodeBuildPluginExtension.projectFile) + signingExtension.xcConfigFile.set(it.outputFile) it.plistHelperProperty.set(plistHelper) it.registeredProvisioningFiles.set(signingExtension.registeredProvisioningFiles) + + it.buildConfiguration.set(xcodeBuildPluginExtension.configuration) + it.target.set(xcodeBuildPluginExtension.target) + + it.targetConfigurations.set(xcodeBuildPluginExtension.targetConfigurations) } project.getTasks().create(XcodeBuildArchiveTaskIosAndTvOS.NAME, XcodeBuildArchiveTaskIosAndTvOS.class) { it.setGroup(XCODE_GROUP_NAME) it.buildType.set(xcodeBuildPluginExtension.type) + it.buildConfiguration.set(xcodeBuildPluginExtension.configuration) it.commandRunnerProperty.set(commandRunner) it.outputArchiveFile.set(xcodeBuildPluginExtension.schemeArchiveFile) it.scheme.set(xcodeBuildPluginExtension.scheme) it.xcode.set(xcode) it.xcodeVersion.set(xcodeBuildPluginExtension.version) - it.xcConfigFile.set(signingExtension.xcConfigFile) + it.workspace.set(xcodeBuildPluginExtension.workspace) it.xcodeServiceProperty.set(xcodeBuildPluginExtension.xcodeServiceProperty) } @@ -582,6 +587,7 @@ class XcodePlugin implements Plugin { ProvisioningInstallTask) { it.group = XCODE_GROUP_NAME + it.buildType.set(xcodeBuildPluginExtension.targetType) it.commandRunnerProperty.set(commandRunner) it.mobileProvisioningList.set(signingExtension.mobileProvisionList) it.outputDirectory.set(signingExtension.provisioningDestinationRoot) @@ -626,6 +632,7 @@ class XcodePlugin implements Plugin { it.registeredProvisioningFiles.set(signingExtension.registeredProvisioning) it.scheme.set(xcodeBuildPluginExtension.scheme) it.signingMethod.set(signingExtension.signingMethod) + it.xcodeVersion.set(xcodeBuildPluginExtension.version) } } diff --git a/plugin/src/main/groovy/org/openbakery/XcodeProjectFile.groovy b/plugin/src/main/groovy/org/openbakery/XcodeProjectFile.groovy index ce139146..f55cb5a6 100644 --- a/plugin/src/main/groovy/org/openbakery/XcodeProjectFile.groovy +++ b/plugin/src/main/groovy/org/openbakery/XcodeProjectFile.groovy @@ -30,7 +30,6 @@ class XcodeProjectFile { boolean isOSX = false; - XcodeProjectFile(Project project, File projectFile) { this.project = project this.projectFile = projectFile @@ -91,20 +90,24 @@ class XcodeProjectFile { } /* remove this method and update project.xcodebuild settings on the proper places */ + void parse() { this.project.logger.debug("Parse project file: " + projectFile.absolutePath) if (!this.projectFile.exists()) { throw new IllegalArgumentException("Project file does not exist: " + this.projectFile) } - if (project.xcodebuild.target == null) { + if (!project.xcodebuild.target.present) { throw new IllegalArgumentException("'xcodebuild.target' is null"); } - BuildConfiguration settings = getBuildConfiguration(project.xcodebuild.target, project.xcodebuild.configuration) - logger.debug("rootObjectKey {}", rootObjectKey); - verifyTarget(project.xcodebuild.target) + if (!project.xcodebuild.configuration.present) { + throw new IllegalArgumentException("'xcodebuild.configuration' is not defined"); + } + BuildConfiguration settings = getBuildConfiguration(project.xcodebuild.target.get(), project.xcodebuild.configuration.get()) + logger.debug("rootObjectKey {}", rootObjectKey); + verifyTarget(project.xcodebuild.target.get()) if (StringUtils.isEmpty(project.xcodebuild.productName)) { project.xcodebuild.productName = settings.productName @@ -157,7 +160,7 @@ class XcodeProjectFile { } - String deviceFamily = getBuildSetting(buildConfiguration, "TARGETED_DEVICE_FAMILY") + String deviceFamily = getBuildSetting(buildConfiguration, "TARGETED_DEVICE_FAMILY") if (deviceFamily == "1") { buildSettings.devices = Devices.PHONE } else if (deviceFamily == "2") { diff --git a/plugin/src/main/groovy/org/openbakery/archiving/XcodeBuildArchiveTaskIosAndTvOS.groovy b/plugin/src/main/groovy/org/openbakery/archiving/XcodeBuildArchiveTaskIosAndTvOS.groovy index f1ebe023..fd98b3dc 100644 --- a/plugin/src/main/groovy/org/openbakery/archiving/XcodeBuildArchiveTaskIosAndTvOS.groovy +++ b/plugin/src/main/groovy/org/openbakery/archiving/XcodeBuildArchiveTaskIosAndTvOS.groovy @@ -5,12 +5,10 @@ import org.gradle.api.DefaultTask import org.gradle.api.Task import org.gradle.api.Transformer import org.gradle.api.file.Directory -import org.gradle.api.file.RegularFile import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.specs.Spec import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.openbakery.CommandRunner @@ -30,11 +28,12 @@ class XcodeBuildArchiveTaskIosAndTvOS extends DefaultTask { @Input final Provider scheme = project.objects.property(String) + final Provider workspace = project.objects.property(String) + @Input final Provider buildType = project.objects.property(Type) - @InputFile - final Property xcConfigFile = newInputFile() + final Provider buildConfiguration = project.objects.property(String) @OutputDirectory final Provider outputArchiveFile = newOutputDirectory() @@ -63,20 +62,63 @@ class XcodeBuildArchiveTaskIosAndTvOS extends DefaultTask { @TaskAction void archive() { + println("Archive project with configuration: " + + "\n\tScheme : ${scheme.getOrNull()} " + + "\n\tXcode version : ${xcodeVersion.getOrElse("System default")}" + + "\n\tBuild configuration : ${buildConfiguration.getOrNull()}") + + final Process process = configureProcessBuilder().start() + process.waitFor() + + if (process.exitValue() != 0) { + throw new RuntimeException(process.errorStream.getText()) + } + } + + private ProcessBuilder configureProcessBuilder() { assert scheme.present: "No target scheme configured" + assert buildConfiguration.present: "No build configuration configured" assert outputArchiveFile.present: "No output file folder configured" - assert xcConfigFile.present: "No 'xcconfig' file configured" - logger.lifecycle("Archive project with configuration: " + - "\n\tScheme : ${scheme.get()} " + - "\n\tXcode version : ${xcodeVersion.getOrElse("System default")}") + ProcessBuilder builder + + ArrayList args = new ArrayList([ + "xcodebuild", + Xcodebuild.ACTION_ARCHIVE, + Xcodebuild.ARGUMENT_SCHEME, scheme.get(), + Xcodebuild.ARGUMENT_CONFIGURATION, buildConfiguration.get(), + Xcodebuild.ARGUMENT_ARCHIVE_PATH, outputArchiveFile.get().asFile.absolutePath + ]) + + if (workspace.isPresent()) { + args.add(Xcodebuild.ARGUMENT_WORKSPACE) + args.add(workspace.get()) + } + + logger.debug("Running : ", args.join(" ")) + + builder = new ProcessBuilder(args) + + builder.directory(project.rootProject.rootDir) + + if (getXcodeAppForConfiguration().present) { + builder.environment().put(Xcode.ENV_DEVELOPER_DIR, + getXcodeAppForConfiguration().map { it.absolutePath }.get() as String) + } + + + builder.redirectOutput(configureLogOutputFile()) + return builder + } + + private File configureLogOutputFile() { + File file = project.layout + .buildDirectory + .file("archiving_output.log").get().asFile - Xcodebuild.archive(commandRunnerProperty.get(), - scheme.get(), - outputArchiveFile.get().asFile, - xcConfigFile.get().asFile, - getXcodeAppForConfiguration().getOrNull()) + logger.info("Build log located here : ", file.path) + return file } private Provider getXcodeAppForConfiguration() { diff --git a/plugin/src/main/groovy/org/openbakery/carthage/CarthageBootStrapTask.groovy b/plugin/src/main/groovy/org/openbakery/carthage/CarthageBootStrapTask.groovy index 94df3517..6793a7d0 100644 --- a/plugin/src/main/groovy/org/openbakery/carthage/CarthageBootStrapTask.groovy +++ b/plugin/src/main/groovy/org/openbakery/carthage/CarthageBootStrapTask.groovy @@ -94,11 +94,13 @@ class CarthageBootStrapTask extends DefaultTask { @TaskAction void update() { logger.warn('Bootstrap Carthage for platform ' + carthagePlatformName) + println "getDerivedDataFolder().absolutePath : " + getDerivedDataFolder().absolutePath project.exec(new Action() { @Override void execute(ExecSpec execSpec) { execSpec.args = [ACTION_BOOTSTRAP, ARG_CACHE_BUILDS, + "--derived-data", getDerivedDataFolder().absolutePath, "--new-resolver", "--color", "always", ARG_PLATFORM, @@ -111,6 +113,12 @@ class CarthageBootStrapTask extends DefaultTask { }) } + private static File getDerivedDataFolder() { + File result = new File(System.getProperty("java.io.tmpdir"), "carthage-cache") + result.mkdirs() + return result + } + private final Map getEnvValues() { final Map envValues if (requiredXcodeVersion.present) { diff --git a/plugin/src/main/groovy/org/openbakery/coverage/CoverageTask.groovy b/plugin/src/main/groovy/org/openbakery/coverage/CoverageTask.groovy index 4c47646e..5f677228 100644 --- a/plugin/src/main/groovy/org/openbakery/coverage/CoverageTask.groovy +++ b/plugin/src/main/groovy/org/openbakery/coverage/CoverageTask.groovy @@ -94,7 +94,7 @@ class CoverageTask extends AbstractXcodeTask { } def possibleDirectories = [ - "Build/Intermediates/CodeCoverage/" + project.xcodebuild.target + "/Coverage.profdata", + "Build/Intermediates/CodeCoverage/" + project.xcodebuild.target.get() + "/Coverage.profdata", "Build/Intermediates/CodeCoverage/Coverage.profdata" ] diff --git a/plugin/src/main/groovy/org/openbakery/cpd/CpdTask.groovy b/plugin/src/main/groovy/org/openbakery/cpd/CpdTask.groovy index 70e9d78c..2a186fad 100644 --- a/plugin/src/main/groovy/org/openbakery/cpd/CpdTask.groovy +++ b/plugin/src/main/groovy/org/openbakery/cpd/CpdTask.groovy @@ -47,7 +47,7 @@ class CpdTask extends AbstractXcodeTask { "-cp", "\"${cp.join(':')}\"", 'net.sourceforge.pmd.cpd.CPD', "--minimum-tokens", "10", - "--files", "${projectDir.absolutePath}/${xcodebuild.target}", "${projectDir.absolutePath}/${xcodebuild.target}Tests", + "--files", "${projectDir.absolutePath}/${xcodebuild.target.get()}", "${projectDir.absolutePath}/${xcodebuild.target}Tests", "--language", "ObjectiveC", "--encoding", "UTF-8", "--format", "net.sourceforge.pmd.cpd.XMLRenderer" diff --git a/plugin/src/main/groovy/org/openbakery/extension/Signing.groovy b/plugin/src/main/groovy/org/openbakery/extension/Signing.groovy index 6d93319a..336173c8 100644 --- a/plugin/src/main/groovy/org/openbakery/extension/Signing.groovy +++ b/plugin/src/main/groovy/org/openbakery/extension/Signing.groovy @@ -11,7 +11,6 @@ import org.openbakery.CommandRunner import org.openbakery.codesign.CodesignParameters import org.openbakery.signing.ProvisioningFile import org.openbakery.signing.SigningMethod -import org.openbakery.util.PathHelper import javax.inject.Inject @@ -69,10 +68,6 @@ class Signing { + System.currentTimeMillis() + ".keychain")) this.timeout.set(3600) - - this.xcConfigFile.set(project.layout - .buildDirectory - .file(PathHelper.FOLDER_ARCHIVE + "/" + PathHelper.GENERATED_XCARCHIVE_FILE_NAME)) } void setKeychain(Object keychain) { @@ -82,7 +77,7 @@ class Signing { this.keychain.set(project.file(keychain)) } - public void setMethod(String method) { + void setMethod(String method) { signingMethod.set(SigningMethod.fromString(method) .orElseThrow { new IllegalArgumentException("Method : $method is not a valid export method") @@ -145,7 +140,7 @@ class Signing { } @Override - public String toString() { + String toString() { if (this.keychain != null) { return "Signing{" + " identity='" + identity + '\'' + diff --git a/plugin/src/main/groovy/org/openbakery/extension/TargetConfiguration.groovy b/plugin/src/main/groovy/org/openbakery/extension/TargetConfiguration.groovy new file mode 100644 index 00000000..148366b5 --- /dev/null +++ b/plugin/src/main/groovy/org/openbakery/extension/TargetConfiguration.groovy @@ -0,0 +1,25 @@ +package org.openbakery.extension + +import groovy.transform.CompileStatic + +@CompileStatic +class TargetConfiguration implements Serializable { + + public File provisioningFile + public File certificateFile + public String certificatePassword + public String bundleIdentifier + public File entitlementsFile + public String version + public String shortVersion + + private final String name + + TargetConfiguration(String name) { + this.name = name + } + + String getName() { + return name + } +} diff --git a/plugin/src/main/groovy/org/openbakery/packaging/PackageTaskIosAndTvOS.groovy b/plugin/src/main/groovy/org/openbakery/packaging/PackageTaskIosAndTvOS.groovy index 5f13fdc6..320f5fef 100644 --- a/plugin/src/main/groovy/org/openbakery/packaging/PackageTaskIosAndTvOS.groovy +++ b/plugin/src/main/groovy/org/openbakery/packaging/PackageTaskIosAndTvOS.groovy @@ -4,12 +4,14 @@ import groovy.transform.CompileStatic import groovy.transform.TypeCheckingMode import org.gradle.api.DefaultTask import org.gradle.api.Task +import org.gradle.api.Transformer import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.openbakery.CommandRunner import org.openbakery.XcodePlugin +import org.openbakery.XcodeService import org.openbakery.signing.KeychainCreateTask import org.openbakery.signing.ProvisioningFile import org.openbakery.signing.ProvisioningInstallTask @@ -22,6 +24,9 @@ import org.openbakery.xcode.Xcodebuild @CompileStatic class PackageTaskIosAndTvOS extends DefaultTask { + @Input + final Provider xcodeVersion = project.objects.property(String) + @Input public final Property signingMethod = project.objects.property(SigningMethod) @@ -42,6 +47,8 @@ class PackageTaskIosAndTvOS extends DefaultTask { final Provider commandRunner = project.objects.property(CommandRunner) final Provider plistHelper = project.objects.property(PlistHelper) + final Property xcodeServiceProperty = project.objects.property(XcodeService) + public static final String DESCRIPTION = "Package the archive with Xcode-build" public static final String NAME = "packageWithXcodeBuild" @@ -162,6 +169,24 @@ class PackageTaskIosAndTvOS extends DefaultTask { Xcodebuild.packageIpa(commandRunner.get(), getArchiveFile(), getOutputDirectory(), - getExportOptionsPlistFile()) + getExportOptionsPlistFile(), + getXcodeAppForConfiguration().getOrNull()) + } + + private Provider getXcodeAppForConfiguration() { + + Provider xcodeApp + if (xcodeVersion.present) { + xcodeApp = xcodeServiceProperty.map(new Transformer() { + @Override + File transform(XcodeService xcodeService) { + XcodeService.XcodeApp app = xcodeService.getInstallationForVersion(xcodeVersion.get()) + return app.contentDeveloperFile + } + }) + } else { + xcodeApp = project.objects.property(File) + } + return xcodeApp } } diff --git a/plugin/src/main/groovy/org/openbakery/signing/KeychainCreateTask.groovy b/plugin/src/main/groovy/org/openbakery/signing/KeychainCreateTask.groovy index 51d46b5b..1a9c99b3 100644 --- a/plugin/src/main/groovy/org/openbakery/signing/KeychainCreateTask.groovy +++ b/plugin/src/main/groovy/org/openbakery/signing/KeychainCreateTask.groovy @@ -8,15 +8,12 @@ import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.* import org.openbakery.CommandRunner -import org.openbakery.CommandRunnerException import org.openbakery.codesign.Security import org.openbakery.util.FileUtil +import org.openbakery.util.SignatureUtil import org.openbakery.util.SystemUtil import org.openbakery.xcode.Version -import java.util.regex.Matcher -import java.util.regex.Pattern - @CompileStatic class KeychainCreateTask extends Download { @@ -44,8 +41,6 @@ class KeychainCreateTask extends Download { final Property security = project.objects.property(Security) final DirectoryProperty outputDirectory = newOutputDirectory() - private static final Pattern PATTERN = ~/^\s{4}friendlyName:\s(?[^\n]+)/ - static final String TASK_NAME = "keychainCreate" static final String TASK_DESCRIPTION = "Create a keychain that is used for signing the app" static final String KEYCHAIN_DEFAULT_PASSWORD = "This_is_the_default_keychain_password" @@ -110,7 +105,8 @@ class KeychainCreateTask extends Download { } private void parseCertificateFile() { - certificateFriendlyName.set(getSignatureFriendlyName()) + certificateFriendlyName.set(SignatureUtil.getCertificateFriendlyName(temporaryCertificateFile, + certificatePassword.get())) // Delete on exit the downloaded files project.gradle.buildFinished { @@ -196,37 +192,4 @@ class KeychainCreateTask extends Download { logger.info("The temporary keychain has been removed from the search list") } } - - String getSignatureFriendlyName() { - return java.util.Optional.ofNullable(getKeyContent(temporaryCertificateFile) - .split(System.getProperty("line.separator")) - .find { PATTERN.matcher(it).matches() }) - .map { PATTERN.matcher(it) } - .filter { Matcher it -> it.matches() } - .map { Matcher it -> - return it.group("friendlyName") - } - .orElseThrow { - new IllegalArgumentException("Failed to resolve the code signing identity from the certificate ") - } - } - - private String getKeyContent(File file) { - String result - try { - result = commandRunnerProperty.get() - .runWithResult(["openssl", - "pkcs12", - "-nokeys", - "-in", - file.absolutePath, - "-passin", - "pass:" + certificatePassword.get()]) - } catch (CommandRunnerException exception) { - logger.warn(exception.toString()) - result = null - } - - return result - } } diff --git a/plugin/src/main/groovy/org/openbakery/signing/ProvisioningFile.groovy b/plugin/src/main/groovy/org/openbakery/signing/ProvisioningFile.groovy index ee686b80..4619a3b4 100644 --- a/plugin/src/main/groovy/org/openbakery/signing/ProvisioningFile.groovy +++ b/plugin/src/main/groovy/org/openbakery/signing/ProvisioningFile.groovy @@ -1,6 +1,7 @@ package org.openbakery.signing import org.apache.commons.io.FilenameUtils +import org.openbakery.xcode.Type class ProvisioningFile implements Serializable { @@ -10,6 +11,7 @@ class ProvisioningFile implements Serializable { private String teamIdentifier private String teamName private String name + private List platforms public static final String PROVISIONING_NAME_BASE = "gradle-" @@ -18,13 +20,15 @@ class ProvisioningFile implements Serializable { String uuid, String teamIdentifier, String teamName, - String name) { + String name, + List platforms) { this.applicationIdentifier = applicationIdentifier this.file = file this.uuid = uuid this.teamIdentifier = teamIdentifier this.teamName = teamName this.name = name + this.platforms = platforms } String getApplicationIdentifier() { @@ -55,7 +59,15 @@ class ProvisioningFile implements Serializable { return formattedName(uuid, file) } - public static String formattedName(String uuid, File file) { + List getPlatforms() { + return platforms + } + + boolean supportBuildType(Type type) { + return platforms.contains(type.value) + } + + static String formattedName(String uuid, File file) { return PROVISIONING_NAME_BASE + uuid + "." + FilenameUtils.getExtension(file.getName()) } } diff --git a/plugin/src/main/groovy/org/openbakery/signing/ProvisioningInstallTask.groovy b/plugin/src/main/groovy/org/openbakery/signing/ProvisioningInstallTask.groovy index 92d913fb..34991203 100644 --- a/plugin/src/main/groovy/org/openbakery/signing/ProvisioningInstallTask.groovy +++ b/plugin/src/main/groovy/org/openbakery/signing/ProvisioningInstallTask.groovy @@ -15,10 +15,14 @@ import org.gradle.api.tasks.TaskAction import org.openbakery.CommandRunner import org.openbakery.codesign.ProvisioningProfileReader import org.openbakery.util.PlistHelper +import org.openbakery.xcode.Type @CompileStatic class ProvisioningInstallTask extends Download { + @Input + final Provider buildType = project.objects.property(Type) + @Input final Provider> mobileProvisioningList = project.objects.listProperty(String) @@ -43,7 +47,9 @@ class ProvisioningInstallTask extends Download { super() this.description = TASK_DESCRIPTION - this.onlyIf { !mobileProvisioningList.get().empty } + this.onlyIf { + !mobileProvisioningList.get().empty + } } @TaskAction @@ -61,11 +67,6 @@ class ProvisioningInstallTask extends Download { this.acceptAnyCertificate(true) } - void registerProvisioning(ProvisioningFile provisioningFile) { - registeredProvisioning.add(provisioningFile.getFile()) - registeredProvisioningFiles.add(provisioningFile) - } - File registerProvisioningInToUserLibrary(ProvisioningFile provisioningFile) { PROVISIONING_DIR.mkdirs() @@ -84,13 +85,20 @@ class ProvisioningInstallTask extends Download { deleteFilesOnExit(files.collect { it.file }) // Register it - files.each(this.®isterProvisioning) + files.findAll { it.supportBuildType(buildType.get()) } + .each(this.®isterProvisioning) // Register into the user library List registeredFiles = files.collect(this.®isterProvisioningInToUserLibrary) deleteFilesOnExit(registeredFiles) } + void registerProvisioning(ProvisioningFile provisioningFile) { + provisioningFile.platforms + registeredProvisioning.add(provisioningFile.getFile()) + registeredProvisioningFiles.add(provisioningFile) + } + File fileFromPath(String path) { return new File(outputDirectory.get().asFile, FilenameUtils.getName(path)) } @@ -109,7 +117,8 @@ class ProvisioningInstallTask extends Download { reader.getUUID(), reader.getTeamIdentifierPrefix(), reader.getTeamName(), - reader.getName()) + reader.getName(), + reader.getPlatforms()) } private List rename() { diff --git a/plugin/src/main/groovy/org/openbakery/util/SignatureUtil.groovy b/plugin/src/main/groovy/org/openbakery/util/SignatureUtil.groovy new file mode 100644 index 00000000..00f3080f --- /dev/null +++ b/plugin/src/main/groovy/org/openbakery/util/SignatureUtil.groovy @@ -0,0 +1,49 @@ +package org.openbakery.util + +import java.util.regex.Matcher +import java.util.regex.Pattern + +class SignatureUtil { + + private static final Pattern PATTERN = ~/^\s{4}friendlyName:\s(?[^\n]+)/ + + static String getCertificateFriendlyName(File certificateFile, + String certificatePassword) throws RuntimeException { + return Optional.ofNullable(decryptCertificate(certificateFile, certificatePassword) + .split(System.getProperty("line.separator")) + .find { PATTERN.matcher(it).matches() }) + .map { PATTERN.matcher(it) } + .filter { Matcher it -> it.matches() } + .map { Matcher it -> + return it.group("friendlyName") + } + .orElseThrow { + new IllegalArgumentException("Failed to resolve the code signing identity from the certificate ") + } + } + + static String decryptCertificate(File certificateFile, + String certificatePassword) throws RuntimeException { + assert certificateFile.exists(): "The certificate file does not exists" + + ProcessBuilder builder = new ProcessBuilder("openssl", + "pkcs12", + "-nokeys", + "-in", certificateFile.absolutePath, + "-passin", "pass:${certificatePassword}") + + builder.redirectOutput() + + final Process process = builder.start() + process.waitFor() + + String result + if (process.exitValue() == 0) { + result = process.getInputStream().text.trim() + } else { + throw new RuntimeException(process.getErrorStream().text.trim()) + } + + return result + } +} diff --git a/plugin/src/main/groovy/org/openbakery/util/VariableResolver.groovy b/plugin/src/main/groovy/org/openbakery/util/VariableResolver.groovy index 895a0881..5111dbae 100644 --- a/plugin/src/main/groovy/org/openbakery/util/VariableResolver.groovy +++ b/plugin/src/main/groovy/org/openbakery/util/VariableResolver.groovy @@ -57,7 +57,7 @@ class VariableResolver { return [ "PRODUCT_NAME": project.xcodebuild.productName, "SRC_ROOT" : project.projectDir.absolutePath, - "TARGET_NAME" : project.xcodebuild.target + "TARGET_NAME" : project.xcodebuild.target.get() ] } -} \ No newline at end of file +} diff --git a/plugin/src/test/groovy/org/openbakery/PrepareXcodeArchivingTaskTest.groovy b/plugin/src/test/groovy/org/openbakery/PrepareXcodeArchivingTaskTest.groovy deleted file mode 100644 index 8efa86c0..00000000 --- a/plugin/src/test/groovy/org/openbakery/PrepareXcodeArchivingTaskTest.groovy +++ /dev/null @@ -1,96 +0,0 @@ -package org.openbakery - -import org.gradle.api.Project -import org.gradle.testfixtures.ProjectBuilder -import org.junit.Rule -import org.junit.rules.TemporaryFolder -import org.openbakery.codesign.ProvisioningProfileReader -import org.openbakery.extension.Signing -import org.openbakery.util.PlistHelper -import spock.lang.Specification - -import static org.openbakery.PrepareXcodeArchivingTask.* - -class PrepareXcodeArchivingTaskTest extends Specification { - - PrepareXcodeArchivingTask subject - Signing signing - Project project - File outputFile - File entitlementsFile - - CommandRunner commandRunner = Mock(CommandRunner) - ProvisioningProfileReader provisioningProfileReader = Mock(ProvisioningProfileReader) - PlistHelper plistHelper = Mock(PlistHelper) - - private static final String FAKE_TEAM_ID = "Team Identifier" - private static final String FAKE_BUNDLE_IDENTIFIER = "co.test.test" - private static final String FAKE_FRIENDLY_NAME = "Fake Certificate FriendlyName (12345)" - private static final String FAKE_UUID = "FAKE_UUID" - private static final String FAKE_PROV_NAME = "Provisioning Name" - - @Rule - final TemporaryFolder tmpDirectory = new TemporaryFolder() - - def setup() { - this.project = ProjectBuilder.builder().withProjectDir(tmpDirectory.root).build() - this.project.apply plugin: XcodePlugin - - this.entitlementsFile = tmpDirectory.newFile("test.entitlements") - this.outputFile = tmpDirectory.newFile("test.xcconfig") - this.signing = project.extensions.findByType(Signing) - - configureSubject() - - provisioningProfileReader.getTeamIdentifierPrefix() >> FAKE_TEAM_ID - provisioningProfileReader.getUUID() >> FAKE_UUID - provisioningProfileReader.getName() >> FAKE_PROV_NAME - } - - def configureSubject() { - subject = project.tasks.findByName(NAME) as PrepareXcodeArchivingTask - subject.certificateFriendlyName.set(FAKE_FRIENDLY_NAME) - subject.commandRunnerProperty.set(commandRunner) - subject.configurationBundleIdentifier.set(FAKE_BUNDLE_IDENTIFIER) - subject.outputFile.set(outputFile) - subject.plistHelperProperty.set(plistHelper) - subject.provisioningReader.set(provisioningProfileReader) - } - - def "The generation should be executed without exception"() { - when: - subject.generate() - - then: - noExceptionThrown() - - and: "The generate file content should be valid" - String text = outputFile.text - text.contains("${KEY_CODE_SIGN_IDENTITY} = ${FAKE_FRIENDLY_NAME}") - text.contains("${KEY_DEVELOPMENT_TEAM} = ${FAKE_TEAM_ID}") - text.contains("${KEY_PROVISIONING_PROFILE_ID} = ${FAKE_UUID}") - text.contains("${KEY_PROVISIONING_PROFILE_SPEC} = ${FAKE_PROV_NAME}") - - and: "And no entitlements information should be present" - !text.contains("${KEY_CODE_SIGN_ENTITLEMENTS} = ") - } - - def "The generate file should refer the entitlements file is present"() { - when: - subject.entitlementsFile.set(entitlementsFile) - subject.generate() - - then: - noExceptionThrown() - - and: - String text = outputFile.text - text.contains("${KEY_CODE_SIGN_IDENTITY} = ${FAKE_FRIENDLY_NAME}") - text.contains("${KEY_DEVELOPMENT_TEAM} = ${FAKE_TEAM_ID}") - text.contains("${KEY_PROVISIONING_PROFILE_ID} = ${FAKE_UUID}") - text.contains("${KEY_PROVISIONING_PROFILE_SPEC} = ${FAKE_PROV_NAME}") - - and: "No entitlements information should be present" - text.contains("${KEY_CODE_SIGN_ENTITLEMENTS} = ${entitlementsFile.absolutePath}") - } -} diff --git a/plugin/src/test/groovy/org/openbakery/util/SignatureUtilTest.groovy b/plugin/src/test/groovy/org/openbakery/util/SignatureUtilTest.groovy new file mode 100644 index 00000000..80ba6458 --- /dev/null +++ b/plugin/src/test/groovy/org/openbakery/util/SignatureUtilTest.groovy @@ -0,0 +1,79 @@ +package org.openbakery.util + +import org.junit.Test +import spock.lang.Specification + +import java.nio.file.Paths + +class SignatureUtilTest extends Specification { + + private File certificateFile = findResource("fake_distribution.p12") + private File fakeFile = Mock(File) + + @Test + def "Should properly resolve the certificate friendly name"() { + when: + String result = SignatureUtil.getCertificateFriendlyName(certificateFile, "p4ssword") + + then: + noExceptionThrown() + + and: + result == "iPhone Distribution: Test Company Name (12345ABCDE)" + } + + @Test + def "Should fail to resolve the certificate friendly name if invalid password"() { + when: + SignatureUtil.getCertificateFriendlyName(certificateFile, "toto") + + then: + RuntimeException exception = thrown RuntimeException + + and: + exception.message == "Mac verify error: invalid password?" + } + + @Test + def "Should properly resolve the content of the certificate file if exists"() { + when: + String result = SignatureUtil.decryptCertificate(certificateFile, "p4ssword") + + then: + noExceptionThrown() + + and: + result.contains("localKeyID: FE 93 19 AC CC D7 C1 AC 82 97 02 C2 35 97 B6 CE 37 33 CB 4F") + } + + @Test + def "Should fail if the certificate password is invalid"() { + when: + SignatureUtil.decryptCertificate(certificateFile, "wrong") + + then: + RuntimeException exception = thrown RuntimeException + exception.message == "Mac verify error: invalid password?" + } + + @Test + def "Should throw an exception is the certificate file does not exists"() { + setup: + + when: + SignatureUtil.decryptCertificate(fakeFile, "p4ssword") + + then: + AssertionError error = thrown AssertionError + error.message.startsWith("The certificate file does not exists") + } + + private File findResource(String name) { + ClassLoader classLoader = getClass().getClassLoader() + return (File) Optional.ofNullable(classLoader.getResource(name)) + .map { URL url -> url.toURI() } + .map { URI uri -> Paths.get(uri).toFile() } + .filter { File file -> file.exists() } + .orElseThrow { new Exception("Resource $name cannot be found") } + } +}