Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #86 from lampietti/xcode9
add xcode9 new signing support [JENKINS-45509]
  • Loading branch information
kazuhidet committed Apr 20, 2018
2 parents 7172a7c + 87c034b commit 5d4f521
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 59 deletions.
41 changes: 41 additions & 0 deletions src/main/java/au/com/rayh/ProvisioningProfile.java
@@ -0,0 +1,41 @@
package au.com.rayh;

import org.kohsuke.stapler.DataBoundConstructor;

public class ProvisioningProfile {

private static final String PLIST_FORMAT = "<key>${APP_ID}</key><string>${UUID}</string>";

private String provisioningProfileAppId;
private String provisioningProfileUUID;

public ProvisioningProfile() {
}

@DataBoundConstructor
public ProvisioningProfile(String provisioningProfileAppId, String provisioningProfileUUID) {
this.provisioningProfileAppId = provisioningProfileAppId;
this.provisioningProfileUUID = provisioningProfileUUID;
}

public String getProvisioningProfileAppId() {
return provisioningProfileAppId;
}

public void setProvisioningProfileAppId(String provisioningProfileAppId) {
this.provisioningProfileAppId = provisioningProfileAppId;
}

public String getProvisioningProfileUUID() {
return provisioningProfileUUID;
}

public void setProvisioningProfileUUID(String provisioningProfileUUID) {
this.provisioningProfileUUID = provisioningProfileUUID;
}

public String toPlist() {
return PLIST_FORMAT.replace("${APP_ID}", provisioningProfileAppId)
.replace("${UUID}", provisioningProfileUUID);
}
}
148 changes: 121 additions & 27 deletions src/main/java/au/com/rayh/XCodeBuilder.java
Expand Up @@ -49,7 +49,6 @@

import javax.inject.Inject;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectStreamException;
import java.text.SimpleDateFormat;
Expand All @@ -67,16 +66,57 @@
public class XCodeBuilder extends Builder implements SimpleBuildStep {

private static final int SIGTERM = 143;
private static final String DEVELOPMENT_ENV = "Development";
private static final String PRODUCTION_ENV = "Production";
private static final String DEV_SIGNING_CERTIFICATE_SELECTOR = "iOS Developer";
private static final String DIST_SIGNING_CERTIFICATE_SELECTOR = "iOS Distribution";

private static final String MANIFEST_PLIST_TEMPLATE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
+ "<plist version=\"1.0\"><dict><key>items</key><array><dict><key>assets</key><array><dict><key>kind</key><string>software-package</string><key>url</key><string>${IPA_URL_BASE}/${IPA_NAME}</string></dict></array>"
+ "<key>metadata</key><dict><key>bundle-identifier</key><string>${BUNDLE_ID}</string><key>bundle-version</key><string>${BUNDLE_VERSION}</string><key>kind</key><string>software</string><key>title</key><string>${APP_NAME}</string></dict></dict></array></dict></plist>";
+ "<plist version=\"1.0\">"
+ "<dict>"
+ " <key>items</key>"
+ " <array>"
+ " <dict>"
+ " <key>assets</key>"
+ " <array>"
+ " <dict>"
+ " <key>kind</key><string>software-package</string>"
+ " <key>url</key><string>${IPA_URL_BASE}/${IPA_NAME}</string>"
+ " </dict>"
+ " </array>"
+ " <key>metadata</key>"
+ " <dict>"
+ " <key>bundle-identifier</key><string>${BUNDLE_ID}</string>"
+ " <key>bundle-version</key><string>${BUNDLE_VERSION}</string>"
+ " <key>kind</key><string>software</string>"
+ " <key>title</key><string>${APP_NAME}</string>"
+ " </dict>"
+ " </dict>"
+ " </array>"
+ "</dict>"
+ "</plist>";

private static final String AUTOMATIC_EXPORT_OPTIONS_PLIST_TEMPLATE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
+ "<plist version=\"1.0\">"
+ "<dict>"
+ " <key>signingStyle</key><string>automatic</string>"
+ " <key>method</key><string>${IPA_EXPORT_METHOD}</string>"
+ " <key>iCloudContainerEnvironment</key><string>${ICLOUD_CONTAINER_ENV}</string>"
+ "</dict>"
+ "</plist>";

private static final String MANUAL_EXPORT_OPTIONS_PLIST_TEMPLATE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
+ "<plist version=\"1.0\">"
+ "<dict>"
+ " <key>signingStyle</key><string>manual</string>"
+ " <key>method</key><string>${IPA_EXPORT_METHOD}</string>"
+ " <key>teamID</key><string>${DEVELOPMENT_TEAM}</string>"
+ " <key>signingCertificate</key><string>${SIGNING_CERTIFICATE}</string>"
+ " <key>provisioningProfiles</key><dict>${PROVISIONING_PROFILES}</dict>"
+ " <key>iCloudContainerEnvironment</key><string>${ICLOUD_CONTAINER_ENV}</string>"
+ "</dict>"
+ "</plist>";

private static final String EXPORT_PLIST_TEMPLATE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
+ "<plist version=\"1.0\"><dict>"
+ "<key>method</key><string>${IPA_EXPORT_METHOD}</string>"
+ "<key>teamID</key><string>${DEVELOPMENT_TEAM}</string>"
+ "</dict></plist>";

/**
* @since 1.0
Expand Down Expand Up @@ -212,6 +252,14 @@ public class XCodeBuilder extends Builder implements SimpleBuildStep {
* @since 1.5
*/
public final String ipaManifestPlistUrl;
/**
* @since 2.1
*/
public final Boolean manualSigning;
/**
* @since 2.1
*/
public ArrayList<ProvisioningProfile> provisioningProfiles = new ArrayList<>();

// Fields in config.jelly must match the parameter names in the "DataBoundConstructor"
@DataBoundConstructor
Expand All @@ -222,7 +270,8 @@ public XCodeBuilder(Boolean buildIpa, Boolean generateArchive, Boolean noConsole
String keychainName, String keychainPath, String keychainPwd, String symRoot, String xcodeWorkspaceFile,
String xcodeSchema, String buildDir, String developmentTeamName, String developmentTeamID, Boolean allowFailingBuildResults,
String ipaName, Boolean provideApplicationVersion, String ipaOutputDirectory, Boolean changeBundleID, String bundleID,
String bundleIDInfoPlistPath, String ipaManifestPlistUrl, Boolean interpretTargetAsRegEx, String ipaExportMethod) {
String bundleIDInfoPlistPath, String ipaManifestPlistUrl, Boolean interpretTargetAsRegEx, String ipaExportMethod,
Boolean manualSigning, ArrayList<ProvisioningProfile> provisioningProfiles) {

this.buildIpa = buildIpa;
this.generateArchive = generateArchive;
Expand Down Expand Up @@ -258,6 +307,26 @@ public XCodeBuilder(Boolean buildIpa, Boolean generateArchive, Boolean noConsole
this.interpretTargetAsRegEx = interpretTargetAsRegEx;
this.ipaManifestPlistUrl = ipaManifestPlistUrl;
this.ipaExportMethod = ipaExportMethod;
this.manualSigning = manualSigning;
this.provisioningProfiles = provisioningProfiles;
}

@Deprecated
public XCodeBuilder(Boolean buildIpa, Boolean generateArchive, Boolean noConsoleLog, String logfileOutputDirectory, Boolean cleanBeforeBuild,
Boolean cleanTestReports, String configuration,
String target, String sdk, String xcodeProjectPath, String xcodeProjectFile, String xcodebuildArguments,
String cfBundleVersionValue, String cfBundleShortVersionStringValue, Boolean unlockKeychain,
String keychainName, String keychainPath, String keychainPwd, String symRoot, String xcodeWorkspaceFile,
String xcodeSchema, String buildDir, String developmentTeamName, String developmentTeamID, Boolean allowFailingBuildResults,
String ipaName, Boolean provideApplicationVersion, String ipaOutputDirectory, Boolean changeBundleID, String bundleID,
String bundleIDInfoPlistPath, String ipaManifestPlistUrl, Boolean interpretTargetAsRegEx, String ipaExportMethod) {
this(buildIpa, generateArchive, noConsoleLog, logfileOutputDirectory, cleanBeforeBuild, cleanTestReports, configuration,
target, sdk, xcodeProjectPath, xcodeProjectFile, xcodebuildArguments,
cfBundleVersionValue, cfBundleShortVersionStringValue, unlockKeychain,
keychainName, keychainPath, keychainPwd, symRoot, xcodeWorkspaceFile,
xcodeSchema, buildDir, developmentTeamName, developmentTeamID, allowFailingBuildResults,
ipaName, provideApplicationVersion, ipaOutputDirectory, changeBundleID, bundleID,
bundleIDInfoPlistPath, ipaManifestPlistUrl, interpretTargetAsRegEx, ipaExportMethod, true, null);
}

@Deprecated
Expand Down Expand Up @@ -540,16 +609,16 @@ private boolean _perform(Run<?,?> build, FilePath projectRoot, Launcher launcher
/*returnCode =*/ launcher.launch().envs(envs).cmds("/usr/bin/security", "find-identity", "-p", "codesigning", "-v").stdout(listener).pwd(projectRoot).join();

Team team = getDevelopmentTeam();
if(team == null)
{
listener.fatalError(Messages.XCodeBuilder_teamNotConfigured());
return false;
}
String developmentTeamID = envs.expand(team.getTeamID());
if (!StringUtils.isEmpty(developmentTeamID)) {
listener.getLogger().println(Messages.XCodeBuilder_DebugInfoCanFindPProfile());
/*returnCode =*/ launcher.launch().envs(envs).cmds("/usr/bin/security", "find-certificate", "-a", "-c", developmentTeamID, "-Z", "|", "grep", "^SHA-1").stdout(listener).pwd(projectRoot).join();
// We could fail here, but this doesn't seem to work as it should right now (output not properly redirected. We might need a parser)
if (team == null) {
listener.getLogger().println(Messages.XCodeBuilder_teamNotConfigured());
} else {
String developmentTeamID = envs.expand(team.getTeamID());
if (!StringUtils.isEmpty(developmentTeamID)) {
listener.getLogger().println(Messages.XCodeBuilder_DebugInfoCanFindPProfile());
/*returnCode =*/
launcher.launch().envs(envs).cmds("/usr/bin/security", "find-certificate", "-a", "-c", developmentTeamID, "-Z", "|", "grep", "^SHA-1").stdout(listener).pwd(projectRoot).join();
// We could fail here, but this doesn't seem to work as it should right now (output not properly redirected. We might need a parser)
}
}

listener.getLogger().println(Messages.XCodeBuilder_DebugInfoAvailableSDKs());
Expand Down Expand Up @@ -693,11 +762,12 @@ private boolean _perform(Run<?,?> build, FilePath projectRoot, Launcher launcher
}

// handle code signing identities
if (!StringUtils.isEmpty(developmentTeamID)) {
if (manualSigning != null && manualSigning && !StringUtils.isEmpty(developmentTeamID)) {
commandLine.add("DEVELOPMENT_TEAM=" + developmentTeamID);
xcodeReport.append(", developmentTeamID: ").append(developmentTeamID);
} else {
xcodeReport.append(", developmentTeamID: DEFAULT");
commandLine.add("-allowProvisioningUpdates");
xcodeReport.append(", developmentTeamID: AUTOMATIC");
}

// Additional (custom) xcodebuild arguments
Expand Down Expand Up @@ -747,11 +817,32 @@ private boolean _perform(Run<?,?> build, FilePath projectRoot, Launcher launcher
listener.getLogger().println(Messages.XCodeBuilder_packagingIPA());


FilePath exportPlistLocation = ipaOutputPath.child(ipaExportMethod + developmentTeamID + "Export.plist");
String exportPlist = EXPORT_PLIST_TEMPLATE
.replace("${IPA_EXPORT_METHOD}", ipaExportMethod)
.replace("${DEVELOPMENT_TEAM}", developmentTeamID);
exportPlistLocation.write(exportPlist, "UTF-8");
FilePath exportOptionsPlistLocation = ipaOutputPath.child(ipaExportMethod + developmentTeamID + "ExportOptions.plist");
String exportOptionsPlist;
if (manualSigning != null && manualSigning) {
StringBuilder plistProvisioningProfiles = new StringBuilder("");
for (ProvisioningProfile pp : provisioningProfiles) {
plistProvisioningProfiles.append(pp.toPlist());
}

exportOptionsPlist = MANUAL_EXPORT_OPTIONS_PLIST_TEMPLATE
.replace("${DEVELOPMENT_TEAM}", developmentTeamID)
.replace("${PROVISIONING_PROFILES}", plistProvisioningProfiles.toString());
} else {
exportOptionsPlist = AUTOMATIC_EXPORT_OPTIONS_PLIST_TEMPLATE;
}
exportOptionsPlist = exportOptionsPlist.replace("${IPA_EXPORT_METHOD}", ipaExportMethod);
if (("app-store").equals(ipaExportMethod)) {
exportOptionsPlist = exportOptionsPlist.replace("${ICLOUD_CONTAINER_ENV}", PRODUCTION_ENV);
} else {
exportOptionsPlist = exportOptionsPlist.replace("${ICLOUD_CONTAINER_ENV}", DEVELOPMENT_ENV);
}
if ("development".equals(ipaExportMethod)) {
exportOptionsPlist = exportOptionsPlist.replace("${SIGNING_CERTIFICATE}", DEV_SIGNING_CERTIFICATE_SELECTOR);
} else {
exportOptionsPlist = exportOptionsPlist.replace("${SIGNING_CERTIFICATE}", DIST_SIGNING_CERTIFICATE_SELECTOR);
}
exportOptionsPlistLocation.write(exportOptionsPlist, "UTF-8");


List<FilePath> archives = buildDirectory.list(new XCArchiveFileFilter());
Expand Down Expand Up @@ -817,7 +908,10 @@ private boolean _perform(Run<?,?> build, FilePath projectRoot, Launcher launcher

List<String> packageCommandLine = new ArrayList<>();
packageCommandLine.add(getGlobalConfiguration().getXcodebuildPath());
packageCommandLine.addAll(Lists.newArrayList("-exportArchive", "-archivePath", archive.absolutize().getRemote(), "-exportPath", ipaOutputPath.absolutize().getRemote(), "-exportOptionsPlist", exportPlistLocation.absolutize().getRemote()));
packageCommandLine.addAll(Lists.newArrayList("-exportArchive", "-archivePath", archive.absolutize().getRemote(), "-exportPath", ipaOutputPath.absolutize().getRemote(), "-exportOptionsPlist", exportOptionsPlistLocation.absolutize().getRemote()));
if (manualSigning == null || !manualSigning) {
packageCommandLine.add("-allowProvisioningUpdates");
}
returnCode = launcher.launch().envs(envs).stdout(listener).pwd(projectRoot).cmds(packageCommandLine).join();
if (returnCode > 0) {
listener.getLogger().println("Failed to build " + ipaLocation.absolutize().getRemote());
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/au/com/rayh/Messages.properties
Expand Up @@ -26,7 +26,7 @@ XCodeBuilder.avgtoolNotFound=Cannot find agvtool with the configured path {0}.
XCodeBuilder.buildDirMacroError=Failure while expanding macros and variables for buildDir. Error: {0}
XCodeBuilder.symRootMacroError=Failure while expanding macros and variables for symRoot. Error: {0}
XCodeBuilder.keychainNotConfigured=No global keychain or local keychain path/password was configured.
XCodeBuilder.teamNotConfigured=No global development team or local team ID was configured.
XCodeBuilder.teamNotConfigured=No global development team or local team ID was configured, back to default automatic signing
XCodeBuilder.unlockKeychainFailed=Unable to unlock the keychain.
XCodeBuilder.xcrunNotFound=Cannot find xcrun with the configured path {0}.
XCodeBuilder.cleaningBuildDir=Cleaning build directory: {0}
Expand Down

0 comments on commit 5d4f521

Please sign in to comment.