iOS: Using Jenkins for nightly internal builds (TestFlight), plus frequent client builds [closed]

I’m an iOS dev, working for a small agency. I’m currently on a few smaller projects where I’m the only developer. We recently acquired a Jenkins server, but each project is left to fend for themselves as for how to use it.

I’d like to use it for making and distributing builds. My ideal would be:

  1. Every commit is built as a single IPA that is placed in a HTTP-accessible location. (It only needs to keep the latest one, otherwise the disk would fill up — some of our apps are 500MB or more.)
  2. Once a day it makes a build, signs it with our internal provisioning profile, tacks a build number onto the end of the version number, and sends it to our internal TestFlight team.
  3. When manually prompted, it builds the latest commit, gives it a manually specified version number, signs it with the client’s provisioning profile and sends it to TestFlight.

I’m pretty new to Jenkins. The developer who set up the server is running it on one of our projects, so I know it has the right stuff installed to do Xcode builds. I believe he’s only using it to run unit tests though, not to do any of the code signing, IPA creation or TestFlight stuff.

So my questions:

  • I’ve listed three distinct kinds of build. How does Jenkins cope with that? I see there’s a “build triggers” section in the config for a Jenkins project, but it doesn’t seem to mention different types of build. Should I just set up multiple Jenkins projects, called “App X (continuous)”, “App X (nightly)” and “App X (client)”?
  • How do I specify the provisioning profile through Jenkins? If there isn’t a way, I guess I could make different configurations in the Xcode project…
  • Has anyone else used Jenkins to actually do the release (i.e. build and push to TestFlight) of beta builds of their apps? Is it a good idea? Or should I continue just doing it manually?

The good news is that this is all possible. The bad news is that it is all manual. You will burn a lot of evenings fine-tuning things, and whether it ultimately pays off in time saved is not necessarily knowable in advance.

Here’s an info-dump of things I’ve learned in the last few months of fiddling with Jenkins and Xcode.

There are Xcode and Testflight plugins for Jenkins, which will get you started. After you outgrow them, every chunk of logic becomes a shell/ruby/perl/python/whatever script.

For instance, I outgrew the Xcode plugin as soon as I realized I needed multi-configuration projects to create additional, closely-related builds, identical to the main one except for a single variable like an additional scheme, or a different $DEVELOPER_DIR for a beta Xcode. There was no way to pass those configuration parameters to the fields exposed by typical plugins. So, I reviewed the job logs to figure out how the Xcode plugin manipulated xcodebuild, and transplanted that into a script phase.

Signing from arbitrary identities or provisioning profiles, another thing the plugin was doing for me, became another script phase. A boolean job parameter like SIGN_WITH_DEVELOPER_ID communicates with a script phase like this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>if [ "${SIGN_WITH_DEVELOPER_ID}" = true ]; then
echo "Re-signing build with Developer ID"
$WORKSPACE/ci-scripts/developer-id-sign --path [...]
fi
</code>
<code>if [ "${SIGN_WITH_DEVELOPER_ID}" = true ]; then echo "Re-signing build with Developer ID" $WORKSPACE/ci-scripts/developer-id-sign --path [...] fi </code>
if [ "${SIGN_WITH_DEVELOPER_ID}" = true ]; then
  echo "Re-signing build with Developer ID"
  $WORKSPACE/ci-scripts/developer-id-sign --path [...]
fi

The script it calls boils down to something like:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>/usr/bin/security unlock-keychain -p yourkeychainpassword ~/Library/Keychains/login.keychain
/usr/bin/codesign -f -vv -s 'Developer ID Application' path/to/binary 2>&1
</code>
<code>/usr/bin/security unlock-keychain -p yourkeychainpassword ~/Library/Keychains/login.keychain /usr/bin/codesign -f -vv -s 'Developer ID Application' path/to/binary 2>&1 </code>
/usr/bin/security unlock-keychain -p yourkeychainpassword ~/Library/Keychains/login.keychain
/usr/bin/codesign -f -vv -s 'Developer ID Application' path/to/binary 2>&1

You’ll outgrow the Testflight plugin as soon as you no longer want to upload every single build output by a given job. Instead, you’ll probably want to invoke Testflight’s upload API programmatically, signaled by another job parameter.

(I haven’t fully automated the Testflight upload process yet, because we mostly use Testflight to distribute to clients, and I’ve been burned a time or two sending out a client build that defaulted to the wrong profile or which got mangled in transit. So, I vet those things by hand. The process of uploading to Testflight, though, is just a matter of invoking curl on your build server. Without having done it, I’m confident that it’s viable because we have Jenkins doing something almost identical to upload dsyms to HockeyApp for in-the-wild releases of Mac apps, and that’s working great.)

I recommend multi-configuration jobs as the best way to achieve multiple closely-related builds without job duplication, which creates a bunch of housekeeping for yourself as you then have to keep those related jobs in sync. Multi-configuration jobs are generally incompatible with plugins, unless you want them to run on every build variant, every time. In this way, multi-configuration jobs are a tradeoff between initial complexity and down-the-road flexibility.

However, your three builds as outlined aren’t really that different, and don’t strictly need to be multi-configuration jobs. The continuous build, kicked off after every landed commit, is the default. Whether or not you update the CFBundleVersion, and/or upload to Testflight with any given distribution, can be runtime configuration decisions, controlled via job parameters and script phases watching for them, or by the logic which kicks off your build in the first place.

More miscellaneous thoughts:

  • Without specifying a shebang line in a script phase, /bin/bash -xe behavior is assumed, which prints each line to the log as it goes, and fails the whole build as soon as it encounters a non-zero return value. This combination is usually what you want, and makes troubleshooting less awful.

  • If you don’t like bash, and you wind up writing in some other scripting language, be vigilant about exit codes and emulate the bash -xe behavior yourself. I’ve found it preferable to embed significant logic in scripts we attach to projects, and to conditionally invoke them from Jenkins, via script phases that are as short and declarative as possible.

  • Without getting too far into the weeds, having Jenkins write things (like updated version numbers) into source control is tricky, and considered something of a CI code smell. It’s also tremendously handy for nightly builds, but I haven’t found a well-sanctioned way to pull it off. I wound up grafting the ability to update the bundleversion into the script which initiates the nightly build, fetches the build artifacts, stores them on a fileserver, and uploads a copy to Dropbox. In this scenario, the updated version number is in place before Jenkins wakes up, which is preferable to trying to make Jenkins do it in the middle of a build.)

  • It’s not the focus of the talk, but I got religion on a lot of Jenkins capabilities, including multi-configuration jobs, after seeing some examples tossed off in WWDC 2012 session 404.

  • I recommend running Jenkins in a virtual machine, probably Linux, with Mac build nodes to do the Xcode gruntwork. I’m a Mac guy, but Jenkins is tested most thoroughly on Linux, its weekly-ish updates are easy to apply, and you can roll back to a working snapshot in the event of a problem. A Linux VM is low-overhead, and the time investment will pay you back the first time you update to a new version that gives you trouble.

In the long run, it will give you trouble, not because Jenkins is unreliable, but because CI is a permanent addition to your company’s workflow, and sooner or later, everything breaks.

2

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật