How to build an iOS CI without Jenkins

I used Jenkins with some Xcode plugins as our iOS CI half a year ago.

The main function I want is to build our project daily in the middle night, and +1 on the build number, then push this commit to Phabricator, if any of these steps is fail, it will post a notification to Slack.

But after we are try to using Carthage instead of CocoaPod, it need to Carthage update before building. The Jenkins can’t run commands before building. So I tried using shell directly.

update code

First it should cd into the project’s folder, then checkout new commits and update new frameworks

1
2
3
4
5
cd $(dirname $0)
git checkout master
git reset --hard origin/master

/usr/local/bin/carthage update

then check that whether the last author is a coworker or the bot itself

1
2
3
4
5
6
author=$(git log -1 | head -2 | tail -1 | cut -d ' ' -f2)
echo $author

if [ $author != "bot" ]; then
# TODO:
fi

if it’s a coworker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
projectName=$(find . -name "*.xcodeproj" -depth 1 | cut -d '/'  -f 2 | cut -d '.' -f 1)
plist=$(find $projectName -name "*.plist" -d 1)

# buildnum +1
shortVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plist}")
buildnum=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${plist}")
buildnum=$(expr $buildnum + 1)

/usr/libexec/Plistbuddy -c "Set CFBundleVersion $buildnum" "${plist}"

# for AppleWatch
WatchAppPlist=$(find WatchApp -name "*.plist" -d 1)
WatchAppExtensionPlist=$(find 'WatchApp Extension' -name "*.plist" -d 1)

/usr/libexec/Plistbuddy -c "Set CFBundleVersion $buildnum" "${WatchAppPlist}"
/usr/libexec/Plistbuddy -c "Set CFBundleVersion $buildnum" "${WatchAppExtensionPlist}"

Here is how to xcodebuild your ipa in the build folder

1
2
3
4
5
6
7
8
9
10
rm -rf build

xcodebuild clean archive -archivePath build/$YourApp-Beta \
-scheme $YourAppScheme

xcodebuild -exportArchive \
-exportFormat ipa \
-archivePath "build/$YourApp-Beta.xcarchive" \
-exportPath "build/$YourApp-Beta.ipa" \
-exportProvisioningProfile "$YourtProvisioningProfile"

then you can upload your *.ipa to your server ( for instance using curl)

1
2
3
4
5
6
7
rm -rf build # clean up

# push to remote
git config user.email "bot@yourdomain.com"
git config user.name "bot"
git commit -a -m "$buildnum"
git push origin master

How to post a message to Slack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# post2Slack $msg
post2Slack() {

SLACK_HOSTNAME="hooks.slack.com"
SLACK_CHANNEL="#YourChannel"
SLACK_BOTNAME="bot"
ICON=":x:"
SLACK_BOTEMOJI=":joy:"
MESSAGE=$1

# Send message to Slack
PAYLOAD="payload={\"channel\": \"${SLACK_CHANNEL}\", \"username\": \"${SLACK_BOTNAME}\", \"text\": \"${ICON} ${MESSAGE}\", \"icon_emoji\": \"${SLACK_BOTEMOJI}\"}";
curl -X POST --data-urlencode "$PAYLOAD" $getThisURLInYourSlackChannel }

# post2Slack && exit
post2SlackAndExitWithMsg() {
post2Slack "${1}" && exit
}

then you can combine any commands like this:

1
git push origin master || post2SlackAndExitWithMsg "git push failed"

Translated by gpt-3.5-turbo