From 5a90a5dffba195120c1e2a6f3f17b1c3a2d63b9a Mon Sep 17 00:00:00 2001 From: Ray Hilton Date: Sat, 25 Sep 2010 09:35:02 +1000 Subject: [PATCH] initial commit --- README | 20 +++ pom.xml | 37 +++++ src/main/java/au/com/rayh/XCodeBuilder.java | 151 ++++++++++++++++++ .../au/com/rayh/XCodeBuilder/config.jelly | 28 ++++ .../au/com/rayh/XCodeBuilder/global.jelly | 12 ++ src/main/resources/index.jelly | 8 + src/main/webapp/help-buildIpa.html | 4 + src/main/webapp/help-cleanBeforeBuild.html | 5 + src/main/webapp/help-configuration.html | 5 + src/main/webapp/help-updateBuildNumber.html | 5 + 10 files changed, 275 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/au/com/rayh/XCodeBuilder.java create mode 100644 src/main/resources/au/com/rayh/XCodeBuilder/config.jelly create mode 100644 src/main/resources/au/com/rayh/XCodeBuilder/global.jelly create mode 100644 src/main/resources/index.jelly create mode 100644 src/main/webapp/help-buildIpa.html create mode 100644 src/main/webapp/help-cleanBeforeBuild.html create mode 100644 src/main/webapp/help-configuration.html create mode 100644 src/main/webapp/help-updateBuildNumber.html diff --git a/README b/README index e69de29b..3fe3323f 100644 --- a/README +++ b/README @@ -0,0 +1,20 @@ +This is a very simple plugin for invoking xcode builds from Hudson CI (http://hudson-ci.org). There are two builders provided, one for agvtool and one for xcodebuild. This plugin was build against Hudson 1.343. + +agvtool Builder +--- + +This builder allows the dynamic updating of the CFBundleVersion with the current build number and a pre-defined major/minor marketing version to produce a version number of the form MAJOR.MINOR.BUILD_NUMBER (e.g. 1.0.456) + +XCode Builder +--- + +This invokes the actual build. Currently it will invoke -alltargets. The configuration (Debug, Release, etc) can be specified in the per-project config along with whether to perform a clean before the build phase. + + +GOTCHAS +------- + +- Obviously, the build machine has to be an OSX machine with XCode developer tools installed +- Certificates, Identities and Provisions must be installed on the build machine separately +- When code-signing, a prompt may appear on the build machine asking whether to allow keychain access. This will block the build until it is dismissed. Just select 'Always Allow' the first time and it shouldnt need to ask again. +- Make sure you define the CFBundleShortVersionString in your Info.plist. This will be used as the marketing version and will be prepended to the build number when using agvtool \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..b93d8874 --- /dev/null +++ b/pom.xml @@ -0,0 +1,37 @@ + + 4.0.0 + + org.jvnet.hudson.plugins + plugin + 1.343 + ../pom.xml + + + + + rayhilton + Ray Yamamoto Hilton + ray.hilton@gmail.com + + + + hudson.plugins.xcode + xcode-builder + 1.0-SNAPSHOT + hpi + + + + + m.g.o-public + http://maven.glassfish.org/content/groups/public/ + + + + + + m.g.o-public + http://maven.glassfish.org/content/groups/public/ + + + diff --git a/src/main/java/au/com/rayh/XCodeBuilder.java b/src/main/java/au/com/rayh/XCodeBuilder.java new file mode 100644 index 00000000..3d538c94 --- /dev/null +++ b/src/main/java/au/com/rayh/XCodeBuilder.java @@ -0,0 +1,151 @@ +package au.com.rayh; +import com.google.common.collect.Lists; +import hudson.EnvVars; +import hudson.Launcher; +import hudson.Extension; +import hudson.util.FormValidation; +import hudson.model.AbstractBuild; +import hudson.model.BuildListener; +import hudson.model.AbstractProject; +import hudson.tasks.Builder; +import hudson.tasks.BuildStepDescriptor; +import net.sf.json.JSONObject; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.QueryParameter; + +import javax.servlet.ServletException; +import java.io.IOException; +import java.util.List; +import org.apache.commons.lang.StringUtils; + +/** + * @author Ray Hilton + */ +public class XCodeBuilder extends Builder { + private Boolean buildIpa; + private Boolean cleanBeforeBuild; + private Boolean updateBuildNumber; + private String configuration = "Release"; + + // Fields in config.jelly must match the parameter names in the "DataBoundConstructor" + @DataBoundConstructor + public XCodeBuilder(Boolean buildIpa, Boolean cleanBeforeBuild, Boolean updateBuildNumber, String configuration) { + this.buildIpa = buildIpa; + this.cleanBeforeBuild = cleanBeforeBuild; + this.updateBuildNumber = updateBuildNumber; + this.configuration = configuration; + } + + public String getConfiguration() { + return configuration; + } + + public Boolean getBuildIpa() { + return buildIpa; + } + + public Boolean getCleanBeforeBuild() { + return cleanBeforeBuild; + } + + public Boolean getUpdateBuildNumber() { + return updateBuildNumber; + } + + @Override + public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { + EnvVars envs = build.getEnvironment(listener); + + // XCode Version + launcher.launch().envs(envs).cmds(getDescriptor().xcodebuildPath(), "-version").stdout(listener.getLogger()).pwd(build.getProject().getWorkspace()).join(); + + // Set build number + if(updateBuildNumber) { + launcher.launch().envs(envs).cmds(getDescriptor().agvtoolPath(), "new-version", "-all", "`agvtool mvers -terse1`." + build.getNumber() ).stdout(listener.getLogger()).pwd(build.getProject().getWorkspace()).join(); + } + + // Build + List commandLine = Lists.newArrayList(getDescriptor().xcodebuildPath(), "-alltargets", "-configuration", configuration); + if (cleanBeforeBuild) { + commandLine.add("clean"); + } + commandLine.add("build"); + int returnCode = launcher.launch().envs(envs).cmds(commandLine).stdout(listener.getLogger()).pwd(build.getProject().getWorkspace()).join(); + + return returnCode==0; + } + + @Override + public DescriptorImpl getDescriptor() { + return (DescriptorImpl)super.getDescriptor(); + } + + @Extension + public static final class DescriptorImpl extends BuildStepDescriptor { + private String xcodebuildPath = "/usr/bin/xcodebuild"; + private String agvtoolPath = "/usr/bin/agvtool"; + + public FormValidation doCheckConfiguration(@QueryParameter String value) throws IOException, ServletException { + if (StringUtils.isEmpty(value)) { + return FormValidation.error("Please specify a configuration"); + } else { + // TODO: scan project file for specified configuration + } + return FormValidation.ok(); + } + + public FormValidation doCheckXcodebuildPath(@QueryParameter String value) throws IOException, ServletException { + if (StringUtils.isEmpty(value)) { + return FormValidation.error("Please specify the path to the xcodebuild executable (usually /usr/bin/xcodebuild)"); + } else { + // TODO: check that the file exists + } + return FormValidation.ok(); + } + + public FormValidation doCheckAgvtoolPath(@QueryParameter String value) throws IOException, ServletException { + if(StringUtils.isEmpty(value)) + return FormValidation.error("Please specify the path to the agvtool executable (usually /usr/bin/agvtool)"); + else { + // TODO: check that the file exists + } + return FormValidation.ok(); + } + + public boolean isApplicable(Class aClass) { + // indicates that this builder can be used with all kinds of project types + return true; + } + + public String getDisplayName() { + return "XCode"; + } + + @Override + public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { + // To persist global configuration information, + // set that to properties and call save(). +// updateBuildNumber = formData.getBoolean("updateBuildNumber"); +// buildIpa = formData.getBoolean("buildIpa"); +// cleanBeforeBuild = formData.getBoolean("cleanBeforeBuild"); +// configuration = formData.getString("configuration"); + xcodebuildPath = formData.getString("xcodebuildPath"); + agvtoolPath = formData.getString("agvtoolPath"); + // ^Can also use req.bindJSON(this, formData); + // (easier when there are many fields; need set* methods for this, like setUseFrench) + save(); + return super.configure(req, formData); + } + + public String agvtoolPath() { + return agvtoolPath; + } + + public String xcodebuildPath() { + return xcodebuildPath; + } + + } +} + diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/config.jelly b/src/main/resources/au/com/rayh/XCodeBuilder/config.jelly new file mode 100644 index 00000000..f9887afc --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/config.jelly @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/global.jelly b/src/main/resources/au/com/rayh/XCodeBuilder/global.jelly new file mode 100644 index 00000000..69af7e1c --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/global.jelly @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/main/resources/index.jelly b/src/main/resources/index.jelly new file mode 100644 index 00000000..b3c739b3 --- /dev/null +++ b/src/main/resources/index.jelly @@ -0,0 +1,8 @@ + +
+ This plugin provides builders to build xcode projects, invoke agvtool and package .ipa files +
\ No newline at end of file diff --git a/src/main/webapp/help-buildIpa.html b/src/main/webapp/help-buildIpa.html new file mode 100644 index 00000000..d9ca35de --- /dev/null +++ b/src/main/webapp/help-buildIpa.html @@ -0,0 +1,4 @@ +
+

+ Checking this option will create a .ipa for each .app found in the build directory. An .ipa is basically a zipped up .app. This is quite handy for distributing ad-hoc builds to testers as they can just double-click the .ipa and it will import into iTunes.

+
diff --git a/src/main/webapp/help-cleanBeforeBuild.html b/src/main/webapp/help-cleanBeforeBuild.html new file mode 100644 index 00000000..e12e9bd4 --- /dev/null +++ b/src/main/webapp/help-cleanBeforeBuild.html @@ -0,0 +1,5 @@ +
+

+ This invokes xcodebuild clean before build. This will force the rebuilding of ALL project files, including compressing of pngs, etc for every build and is quite a lot slower on large projects. +

+
diff --git a/src/main/webapp/help-configuration.html b/src/main/webapp/help-configuration.html new file mode 100644 index 00000000..2d165bf7 --- /dev/null +++ b/src/main/webapp/help-configuration.html @@ -0,0 +1,5 @@ +
+

+ This is the name of the configuration as defined in the XCode project. By default there are Debug and Release configurations. +

+
diff --git a/src/main/webapp/help-updateBuildNumber.html b/src/main/webapp/help-updateBuildNumber.html new file mode 100644 index 00000000..514b24fc --- /dev/null +++ b/src/main/webapp/help-updateBuildNumber.html @@ -0,0 +1,5 @@ +
+

+ Make sure CFBundleShortVersionString is set in your Info.plist to an appropriate marketing version number (e.g. 1.0). Checking this box will update the current project version in the project file as well as the CFBundleVersion in the Info.plist to the marketing version suffixed with the build number (e.g. 1.0.3234) +

+