概要
Jenkins Pluginを使うとJenkinsのバージョンを上げた時に動作しなくなったり、そもそもGUI設定が面倒だったりで、実はシェルで書いた方が楽じゃん。と思っているのでそうします。
(全く使わないわけではなく、Jenkins2でのお勧めPluginをそのまま入れとくぐらいはやります)
時が経つのは早いもので、Hudson兄さんでしたが、いつの間にやらJenkinsおじさんです。
Jenkinsもバージョン2が出て、Jenkinsfileなどがあり、便利になりつつあります。が、それでもシェルでやります。
そもそも、いまどきはfastlaneでビルドしたり、Webサービス使ったりと思います。が、Jenkins + シェルでやります。
シェルで書いておくメリットは、コマンドレベルで何をしているのかが理解できることでしょう。
インストール
homebrewで入れてます。
$ brew update
$ brew cask install java
$ brew install jenkins
設定はネットで探せばすぐ出てくるので割愛。
想定運用
jenkinsのパラメーター付きビルドを使い、以下の2点を指定してビルドできるようにします。
- gitのブランチ
- 接続先サーバー名(develop,staging,productionなど)
こんな運用を想定しています。
- githubでpushしたことを検知するJobがこのJobが呼びだします。(別の記事で書きます)
- ステージングと本番環境接続用アプリを共存できるように二つのプロビジニングファイル(別々のBundle Identifier)を用意しておきます。
- ビルド時にCFBundleShortVersionStringにどのbranch・接続先の組合せのアプリなのかがわかるようにsuffixを入れます。
(例えば、reviewブランチ+stagingサーバ=.alpha
, releaseブランチ+stagingサーバ=.beta
など) - ビルド後は、Fabricを利用して自動でテスト端末に配信します。
- Fabricで見たときにバージョンを見れば、2で設定したsuffixがあるので、接続先がわかるようになります。
Job
JenkinsのGUI設定
まずは、GUI上で記述します
- ビルドのパラメータ化にチェックを入れ、以下のようにします。
- ソースコード管理の Branch Specifierで上記のパラメータで設定した変数を記述します。
- ビルドのシェルの実行を記述します。
接続する環境によって利用するPROVISIONING_PROFILEが違うので分岐しています。
あとは、~~~
gitで管理しているシェルスクリプトを呼び出すだけです。
```
#!/bin/bash -l
export WORKSPACE
sh -x ${WORKSPACE}/jenkins/main.sh $branch $server
```
_**追記**_
_cocoapodsで、```use_frameworks!```のようにフレームワークとしてライブラリを取り込むとxcodebuild時にPROVISIONING_PROFILEの指定をするとできません。そもそも```-exportOptionsPlist```を使っていればPROVISIONING_PROFILEの指定は必要ないので削除しました。どのPROVISIONING_PROFILEを利用するかはXcodeでのBuild Settings内のCode Signing Identitiyで設定しておけば良いです。
## Jenkins外のシェル
gitで管理します。それぞれ動く単位で別のシェルにします。
```$shellFilePath``` はシェルファイルのパスです。
main.shは、それらシェルを呼び出すシェルになります。
```main.sh
#!/bin/bash -l
set -eux
: usage
: sh -x ~/jenkins/build.sh develop staging
: $1 $2
branch=$1
server=$2
shellFilePath="${WORKSPACE}/jenkins"
# クリーン処理
. ${shellFilePath}/clean.sh
clean
install
. ${shellFilePath}/setupVariable.sh
setupVar $branch $server
# suffix,buildType,fileName,groupAliasが設定される
. ${shellFilePath}/setupVariable.sh
. ${shellFilePath}/replaceVersion.sh
replaceVersion $suffix
# CFBundleShortVersionString,CFBundleVersionが設定される
. ${shellFilePath}/replaceVersion.sh
buildPath="${WORKSPACE}/build/${buildType}-iphoneos"
. ${shellFilePath}/archive.sh
archive $buildType "$buildPath"
. ${shellFilePath}/exportIpa.sh
exportIpa "$buildPath" "$fileName"
renameIpa "$buildPath" $fileName $CFBundleShortVersionString $CFBundleVersion
# newFileNameが設定される
. ${shellFilePath}/exportIpa.sh
newPathName="${buildPath}/$newFileName"
zipDSYM $fileName "$newPathName"
# crashlytics send
. ${shellFilePath}/sendFabric.sh
send "$newPathName" $groupAlias
```
### クリーンとCocoapodsの処理
以下を行います。
* ```xcodebuild clean``` と buildディレクトリ以下の削除
* ```pod install```
```clean.sh
#!/bin/bash -l
set -eux
function clean() {
xcodebuild clean -workspace ${WORKSPACE}/<プロジェクト名>.xcworkspace \
-scheme <プロジェクト名>
rm -rf ${WORKSPACE}/build/*
}
function install() {
pushd <プロジェクト名>
pod install
popd
}
```
### 変数設定処理
Job内の処理で利用する変数を設定します。
```$groupAlias``` はFabric Betaで配信するグループ名です。
```$suffix``` は表示されるバージョンのsuffixです。例:1.0.0.betaのbeta部分
```$buildType``` は、archive時の ```-configuration``` に指定する値。
ちなみにブランチ運用は通常developブランチで開発を行い、スプリントレビューの度にreviewブランチにPushして、総合テストが始まったらreleaseブランチにPushしています。
```setupVariable.sh
#!/bin/bash -l
set -ux
setupVar() {
: usage
: '$1=branch name, $2=server name'
: $1 $2
local branch=$1
local server=$2
# 変数設定
if [ $server = "staging" ]; then
buildType="Staging"
fileName="ステージング"
if [ $branch = "review" ]; then
suffix="alpha"
groupAlias='appteam,serverteam'
elif [ $branch = "release" ]; then
suffix="beta"
groupAlias='pmteam,appteam,serverteam,planningteam'
fi
elif [ $server = "production" ]; then
buildType="Release"
fileName="本番環境"
if [ $branch = "release" ]; then
suffix="rc"
groupAlias='pmteam,appteam,serverteam,planningteam'
elif [ $branch = "master" ]; then
suffix="gm"
groupAlias='pmteam,appteam,serverteam,planningteam,manager'
fi
fi
# 上記で設定されなかった場合(最初の「suffix=」は頭に:があればなくても良いがdebug用に記述)
suffix=${suffix:-"develop"}
buildType=${buildType:-"Debug"}
fileName=${fileName:-"開発環境"}
groupAlias=${groupAlias:-"appteam,serverteam"}
}
```
### 表示バージョン変更
Fabricで見たときにどの環境なのかがわかるようにバージョンにsuffixをつける処理です。
ビルド番号もJenkinsのビルド番号にします。
```replaceVersion.sh
#!/bin/bash -l
set -ux
replaceVersion(){
: usage
: 'arg $1=suffix'
: $1
local suffix=$1
CFBundleShortVersionString=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${WORKSPACE}/<プロジェクト名>/Supporting\ Files/<プロジェクト名>-Info.plist`
CFBundleVersion=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" ${WORKSPACE}/<プロジェクト名>/Supporting\ Files/<プロジェクト名>-Info.plist`
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${CFBundleShortVersionString}.${suffix}" ${WORKSPACE}/<プロジェクト名>/Supporting\ Files/<プロジェクト名>-Info.plist
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${BUILD_NUMBER}" ${WORKSPACE}/<プロジェクト名>/Supporting\ Files/<プロジェクト名>-Info.plist
CFBundleShortVersionString=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${WORKSPACE}/<プロジェクト名>/<プロジェクト名>/Supporting\ Files/<プロジェクト名>-Info.plist`
CFBundleVersion=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" ${WORKSPACE}/<プロジェクト名>/Supporting\ Files/<プロジェクト名>-Info.plist`
}
```
### アーカイブ処理
2番目の引数の ```$buildPath`` にはビルドパスが入っています。(例: ${WORKSPACE}/build/Debug-iphoneos)
そこで指定した場所に xcarchive が作成されます。
```
#!/bin/bash -l
set -ux
archive() {
: usage
: 'arg $1=buildType $2=buildPath $3=fileName
: $1 $2 $3
local buildType=$1
local buildPath=$2
local fileName=$3
# arhive処理
xcodebuild -workspace "${WORKSPACE}/<プロジェクト名>/<プロジェクト名>.xcworkspace" \
-scheme <プロジェクト名> \
-configuration "${buildType}" \
archive -archivePath "${buildPath}/${fileName}.xcarchive"
}
```
### IPA作成処理
xcarchiveからipaとそのdSYMを作成します。
```exportIpa.sh
#!/bin/bash -l
set -ux
exportIpa() {
: usage
: 'arg $1=buildPath $2=fileName $3=buildType
: $1 $2 $3
local buildPath=$1
local fileName=$2
local buildType=$3
xcodebuild \
-exportArchive -archivePath "${buildPath}/${fileName}.xcarchive" \
-exportPath "${buildPath}" \
-exportOptionsPlist "${WORKSPACE}/jenkins/exportOptions_${buildType}.plist"
}
renameIpa() {
: usage
: 'arg $1=buildPath $2=fileName $3=CFBundleShortVersionString $4=CFBundleVersion'
: $1 $2 $3 $4
local buildPath=$1
local fileName=$2
local CFBundleShortVersionString=$3
local CFBundleVersion=$4
local now=`date +"%Y.%m.%d"`
mv "${buildPath}/<プロダクト名>.ipa" "${buildPath}/${fileName}-v${CFBundleShortVersionString}-${CFBundleVersion}-${now}.ipa"
newFileName=${fileName}-v${CFBundleShortVersionString}-${CFBundleVersion}-${now}
}
zipDSYM() {
: usage
: 'arg $1=fileName $2=newPathName'
: $1 $2
local fileName=$1
local newPathName=$2
pushd $buildPath
zip -r "${newPathName}.app.dSYM.zip" "${fileName}.xcarchive"
popd
}
```
```exportOptions_Debug.plist
<?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>development</string>
</dict>
</plist>
```
```exportOptions_Staging.plist
<?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>ad-hoc</string>
</dict>
</plist>
```
### Fabricで転送
作成したIPAをあらかじめ登録していたグループ充に転送します。
```
#!/bin/bash -l
set -ux
send() {
: usage
: 'arg $1=newPathName $2=groupAlias'
: $1 $2
newPathName=$1
groupAlias=$2
$WORKSPACE/Pods/Crashlytics/submit <API_KEY> <BUILD_SECRET> -debug YES -groupAliases "${groupAlias}" -ipaPath "${newPathName}.ipa"
}
```