├── README.md ├── certificates ├── 650f2f4c-f93d-40c2-b91a-1111111111.mobileprovision ├── apple.cer ├── dist.cer └── dist.p12 ├── circle_sample_xcodeproj.yml ├── circle_sample_xcworkspace.yml └── scripts ├── add-key.sh ├── build-ipa.sh ├── deploy.sh ├── remove-key.sh └── upload-ipa-to-deploygate.sh /README.md: -------------------------------------------------------------------------------- 1 | # ios-circleci-deploygate-scripts 2 | ## これは何? 3 | iOSアプリケーションを[CircleCI](https://circleci.com/)でビルドして[DeployGate](https://deploygate.com/)でアドホックビルドを配布するための手順、設定ファイル、シェルスクリプトをまとめています。 4 | 5 | ## ファイル一覧 6 | 7 | |ファイル|内容| 8 | |:-------|:---| 9 | |certificates|ipaファイルを作成するために必要になる証明書、プロビジョニングファイルの見本| 10 | |circle_sample_xcodeproj.yml|CocoaPodsを使用しない場合のcircle.ymlの見本| 11 | |circle_sample_xcworkspace.yml|CocoaPodsを使用する場合のcircle.ymlの見本| 12 | |scripts|ビルドするためのシェルスクリプト| 13 | 14 | ## 手順 15 | 16 | 1. CircleCIの設定 17 | 2. 証明書、プロビジョニングファイル作成 18 | 3. circle.yml作成 19 | 4. 環境変数の設定 20 | 5. deploy.shの修正 21 | 22 | ## 1. CircleCIの設定 23 | CircleCIにログインして、Project Setting -> Experimental Settings の Build iOS projectをONにします。 24 | 25 | ## 2. 証明書、プロビジョニングファイル作成 26 | 以下のプロビジョニングファイルを作成します。 27 | 28 | - <PROFILE_UUID>.mobileprovision 29 | 30 | 31 | Appleの「Certificates, Identifiers & Profiles」サイトからアドホックビルド用のプロビジョニングファイルをダウンロードします。 32 | ファイル名は「<PROFILE_UUID>.mobileprovision」という形式とします。PROFILE_UUIDは、mobileprovisionファイルを開いて以下の箇所を確認します。 33 | 34 | ``` 35 | UUID 36 | xxxxxxxxxx-11111-0000-xxxx-111111111 37 | ``` 38 | 39 | 以下の証明書を作成します。 40 | 41 | - apple.cer 42 | - dist.cer 43 | - dist.p12 44 | 45 | ### apple.cer 46 | キーチェーンで"Apple Worldwide Developer Relations Certification Authority"という項目を選び、書き出します。出力するフォーマットとしては「証明書 (.cer)」を選びます。 47 | 48 | ### dist.cer 49 | 使用するプロビジョニングファイルに対応している証明書をキーチェーンで選び、書き出します。出力するフォーマットとしては「証明書 (.cer)」を選びます。 50 | 51 | ### dist.p12 52 | 使用するプロビジョニングファイルに対応している証明書をキーチェーンで選び、書き出します。出力するフォーマットとしては「個人情報交換 (.p12)」を選びます。書き出しのときにパスワードを設定します。このパスワードは後で使用するので覚えておきます。 53 | 54 | 作成した証明書、プロビジョニングファイルはcertificatesディレクトリ以下に配置します。 55 | 56 | ## 3. circle.yml作成 57 | circle.ymlを作成します。CocoaPodsを使用しない(.xcodeprojを使用する)場合はcircle_sample_xcodeproj.yml、CocoaPodsを使用する(.xcworkspaceを使用する)場合はcircle_sample_xcworkspace.ymlを元にして作成します。 58 | 59 | 「machine: environment:」の箇所は以下の表を参考に値を設定します。 60 | 61 | |変数名|内容|設定例| 62 | |:-----|:---|:-----| 63 | |XCODE_WORKSPACE|Xcodeのworkspace名|CircleCI-Sample.xcworkspace| 64 | |XCODE_SCHEME|Xcodeのビルド対象スキーム名|CircleCI-Sample| 65 | |XCODE_PROJECT|Xcodeのプロジェクト名|CircleCI-Sample.xcodeproj| 66 | |XCODE_TARGET|Xcodeのビルドターゲット名|CircleCI-Sample| 67 | |APPNAME|アプリケーション名|CircleCI-Sample| 68 | |DEPLOYGATE_USER_NAME|deploygateユーザ名|XXX| 69 | |DEVELOPER_NAME|キーチェーンの証明書の「通称」|"iPhone Distribution: XXX (CTQDM00000)"| 70 | |PROFILE_NAME|プロビジョニングファイル名|"650f2f4c-f93d-40c2-b91a-1111111111.mobileprovision"| 71 | 72 | ## 4. 環境変数の設定 73 | CircleCIのWeb画面で、以下のふたつの環境変数を設定します。 74 | 75 | - DEPLOYGATE_API_TOKEN 76 | - P12_FILE_PASSWORD 77 | 78 | DEPLOYGATE_API_TOKENは、deploygateのAPI keyです。[https://deploygate.com/settings](https://deploygate.com/settings)から確認できます。 79 | 80 | P12_FILE_PASSWORDは、p12ファイルを書き出したときに指定したパスワードです。 81 | 82 | ## 5. deploy.shの修正 83 | 84 | CocoaPodsを使うかどうかによってビルド方法が変わるので、scripts/deploy.shを修正します。 85 | 86 | ### CocoaPodsを使う場合の例 87 | 88 | ``` 89 | ./scripts/build-ipa.sh \ 90 | -d "$DEVELOPER_NAME" -a "$APPNAME" \ 91 | -p "$PROFILE_NAME" \ 92 | -s "$XCODE_SCHEME" \ 93 | -w "$XCODE_WORKSPACE" \ 94 | -c "$config" \ 95 | -o "$output_path" 96 | ``` 97 | 98 | ### CocoaPodsを使わない場合の例 99 | 100 | ``` 101 | ./scripts/build-ipa.sh \ 102 | -d "$DEVELOPER_NAME" -a "$APPNAME" \ 103 | -p "$PROFILE_NAME" \ 104 | -t "$XCODE_TARGET" \ 105 | -c "$config" \ 106 | -o "$output_path" 107 | ``` 108 | 109 | ## その他 110 | このビルドスクリプトでは、XcodeのConfigurationごとにAdhocビルドを作成するようになっています。ConfigurationごとにアプリのBundleIDを変更して、一度に複数のAdhocビルドを作成することを想定しています。 111 | 112 | 標準ではConfigurationとしてReleaseを指定してビルドします。それを変更するには、scripts/deploy.shの以下の箇所を変更します。 113 | 114 | ``` 115 | configuration_list=("Release") 116 | ``` 117 | 118 | ここにビルドしたいConfigurationを書きます。例えば「Release」「Adhoc」というふたつのConfigurationでビルドするときは次のように設定します。 119 | 120 | ``` 121 | configuration_list=("Release" "Adhoc") 122 | ``` 123 | 124 | この設定の場合、ReleaseとAdhocでそれぞれAdhocビルドを作成してdeploygateにデプロイします。 125 | 126 | ## 参考 127 | - [Test iOS applications - CircleCI](https://circleci.com/docs/ios) 128 | - [DeployGate API](https://deploygate.com/docs/api) 129 | - [infolens/CircleCI-iOS-TestFlight-Sample](https://github.com/infolens/CircleCI-iOS-TestFlight-Sample) 130 | 131 | -------------------------------------------------------------------------------- /certificates/650f2f4c-f93d-40c2-b91a-1111111111.mobileprovision: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faithcreates/ios-circleci-deploygate-scripts/c554987deb399c7b42108a1a2c01e26d94261cb6/certificates/650f2f4c-f93d-40c2-b91a-1111111111.mobileprovision -------------------------------------------------------------------------------- /certificates/apple.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faithcreates/ios-circleci-deploygate-scripts/c554987deb399c7b42108a1a2c01e26d94261cb6/certificates/apple.cer -------------------------------------------------------------------------------- /certificates/dist.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faithcreates/ios-circleci-deploygate-scripts/c554987deb399c7b42108a1a2c01e26d94261cb6/certificates/dist.cer -------------------------------------------------------------------------------- /certificates/dist.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faithcreates/ios-circleci-deploygate-scripts/c554987deb399c7b42108a1a2c01e26d94261cb6/certificates/dist.p12 -------------------------------------------------------------------------------- /circle_sample_xcodeproj.yml: -------------------------------------------------------------------------------- 1 | # CocoaPodsを使用しない(.xcodeprojを使用する)場合の例 2 | machine: 3 | environment: 4 | XCODE_SCHEME: CircleCI-Sample 5 | XCODE_PROJECT: CircleCI-Sample.xcodeproj 6 | XCODE_TARGET: CircleCI-Sample 7 | APPNAME: CircleCI-Sample 8 | DEPLOYGATE_USER_NAME: XXX 9 | DEVELOPER_NAME: "iPhone Distribution: XXX (CTQDM00000)" 10 | PROFILE_NAME: "650f2f4c-f93d-40c2-b91a-1111111111.mobileprovision" 11 | # DEPLOYGATE_API_TOKEN: set from web form 12 | # P12_FILE_PASSWORD: set from web form 13 | test: 14 | override: 15 | - xcodebuild 16 | CODE_SIGNING_REQUIRED=NO 17 | CODE_SIGN_IDENTITY= 18 | PROVISIONING_PROFILE= 19 | -project CircleCI-Sample.xcodeproj -scheme CircleCI-Sample -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,OS=8.1,name=iPhone 6' 20 | clean test 21 | 22 | deployment: 23 | deploygate: 24 | branch: master 25 | owner: xxx 26 | commands: 27 | - ./scripts/add-key.sh 28 | - ./scripts/deploy.sh 29 | - ./scripts/remove-key.sh 30 | 31 | -------------------------------------------------------------------------------- /circle_sample_xcworkspace.yml: -------------------------------------------------------------------------------- 1 | # CocoaPodsを使用する(.xcworkspaceを使用する)場合の例 2 | machine: 3 | environment: 4 | XCODE_SCHEME: CircleCI-Sample 5 | XCODE_WORKSPACE: CircleCI-Sample.xcworkspace 6 | APPNAME: CircleCI-Sample 7 | DEPLOYGATE_USER_NAME: XXX 8 | DEVELOPER_NAME: "iPhone Distribution: XXX (CTQDM00000)" 9 | PROFILE_NAME: "650f2f4c-f93d-40c2-b91a-1111111111.mobileprovision" 10 | # DEPLOYGATE_API_TOKEN: set from web form 11 | # P12_FILE_PASSWORD: set from web form 12 | dependencies: 13 | override: 14 | - pod install: 15 | timeout: 300 16 | test: 17 | override: 18 | - xctool -workspace CircleCI-Sample.xcworkspace -scheme CircleCI-Sample -sdk iphonesimulator clean build test 19 | deployment: 20 | deploygate: 21 | branch: master 22 | owner: xxx 23 | commands: 24 | - ./scripts/add-key.sh 25 | - ./scripts/deploy.sh 26 | - ./scripts/remove-key.sh 27 | 28 | -------------------------------------------------------------------------------- /scripts/add-key.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | main() 4 | { 5 | local keychain_password=circleci 6 | local certificates_dir=$1 7 | local p12_file_password=$2 8 | 9 | security create-keychain -p "$keychain_password" ios-build.keychain 10 | security import "${certificates_dir}/apple.cer" -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign 11 | security import "${certificates_dir}/dist.cer" -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign 12 | security import "${certificates_dir}/dist.p12" -k ~/Library/Keychains/ios-build.keychain -P "$p12_file_password" -T /usr/bin/codesign 13 | security list-keychain -s ~/Library/Keychains/ios-build.keychain 14 | security unlock-keychain -p "$keychain_password" ~/Library/Keychains/ios-build.keychain 15 | 16 | mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles 17 | cp "${certificates_dir}"/*.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/ 18 | } 19 | 20 | main "./certificates" "$P12_FILE_PASSWORD" 21 | 22 | -------------------------------------------------------------------------------- /scripts/build-ipa.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # ビルドしてipaファイルを作成する 5 | # 6 | 7 | declare -r SCRIPT_NAME=${0##*/} 8 | 9 | print_usage() 10 | { 11 | cat << EOF 12 | Usage: 13 | 1st form: 14 | $SCRIPT_NAME [-o OUTPUT_PATH] [-c CONFIGURATION] [-m PROVISIONING_FILE_DIR] -d DEVELOPER_NAME -a APPNAME -p PROVISIONING_FILE -s XCODE_SCHEME -w XCODE_WORKSPACE 15 | 16 | 2nd form: 17 | $SCRIPT_NAME [-o OUTPUT_PATH] [-c CONFIGURATION] [-m PROVISIONING_FILE_DIR] -d DEVELOPER_NAME -a APPNAME -p PROVISIONING_FILE -t XCODE_TARGET 18 | 19 | Build iOS project and create ipa file. Use 1st form to build iOS project with CocoaPods. Use 2nd form to build iOS project without CocoaPods. 20 | 21 | -o OUTPUT_PATH Output path (default: \$PWD/build) 22 | -c CONFIGURATION Build configuration (default: Release) 23 | -d DEVELOPER_NAME Identity in Keychanin 24 | -a APPNAME iOS application name 25 | -p PROVISIONING_FILE mobileprovision file name 26 | -m PROVISIONING_FILE_DIR mobileprovision file directory (default: \$HOME/Library/MobileDevice/Provisioning Profiles) 27 | -s XCODE_SCHEME Xcode scheme(build target name) 28 | -w XCODE_WORKSPACE Xcode workspace name 29 | -t XCODE_TARGET Xcode target 30 | -h display this help and exit 31 | 32 | Examples 33 | $SCRIPT_NAME -o \$PWD/build -d "iPhone Distribution: FaithCreates Inc." -a CircleCI-Sample -p 6d7927d4-5e5d-4d32-b4bc-111111111111.mobileprovision -s CircleCI-Sample -w CircleCI-Sample.xcworkspace 34 | 35 | $SCRIPT_NAME -o \$PWD/build -d "iPhone Distribution: FaithCreates Inc." -a CircleCI-Sample -p 6d7927d4-5e5d-4d32-b4bc-111111111111.mobileprovision -t CircleCI-Sample 36 | EOF 37 | } 38 | 39 | print_error() 40 | { 41 | echo "$SCRIPT_NAME: $*" 1>&2 42 | echo "Try \`-h' option for more information." 1>&2 43 | } 44 | 45 | main() 46 | { 47 | local output_path="$PWD/build" 48 | local developer_name='' 49 | local appname='' 50 | local provisioning_file='' 51 | local provisioning_file_dir="$HOME/Library/MobileDevice/Provisioning Profiles" 52 | local xcode_scheme='' 53 | local xcode_workspace='' 54 | local xcode_target='' 55 | local configuration='Release' 56 | 57 | local option 58 | local OPTARG 59 | local OPTIND 60 | while getopts ':o:c:d:a:p:m:s:w:t:h' option; do 61 | case $option in 62 | o) 63 | output_path=$OPTARG 64 | ;; 65 | c) 66 | configuration=$OPTARG 67 | ;; 68 | d) 69 | developer_name=$OPTARG 70 | ;; 71 | a) 72 | appname=$OPTARG 73 | ;; 74 | p) 75 | provisioning_file=$OPTARG 76 | ;; 77 | m) 78 | provisioning_file_dir=$OPTARG 79 | ;; 80 | s) 81 | xcode_scheme=$OPTARG 82 | ;; 83 | w) 84 | xcode_workspace=$OPTARG 85 | ;; 86 | t) 87 | xcode_target=$OPTARG 88 | ;; 89 | h) 90 | print_usage 91 | return 0 92 | ;; 93 | :) #オプション引数欠如 94 | print_error "option requires an argument -- $OPTARG" 95 | return 1 96 | ;; 97 | *) #不明なオプション 98 | print_error "invalid option -- $OPTARG" 99 | return 1 100 | ;; 101 | esac 102 | done 103 | shift $((OPTIND - 1)) 104 | 105 | if [ -z "$output_path" ]; then 106 | print_error 'you must specify output_path' 107 | return 1 108 | fi 109 | 110 | if [ -z "$configuration" ]; then 111 | print_error 'you must specify configuration' 112 | return 1 113 | fi 114 | 115 | if [ -z "$developer_name" ]; then 116 | print_error 'you must specify developer_name' 117 | return 1 118 | fi 119 | 120 | if [ -z "$appname" ]; then 121 | print_error 'you must specify appname' 122 | return 1 123 | fi 124 | 125 | if [ -z "$provisioning_file" ]; then 126 | print_error 'you must specify provisioning_file' 127 | return 1 128 | fi 129 | 130 | if [ -z "$provisioning_file_dir" ]; then 131 | print_error 'you must specify provisioning_file_dir' 132 | return 1 133 | fi 134 | 135 | # 1st formの場合、xcode_schemeは空でない、xcode_workspaceは空でない、xcode_targetは空 136 | # 2nd formの場合、xcode_schemeは空、xcode_workspaceは空、xcode_targetは空でない 137 | if [ -n "$xcode_target" ]; then 138 | # 2nd formの場合 139 | # if [ -n "$xcode_target" ]; then 140 | if [ -n "$xcode_scheme" ]; then 141 | print_error "you don't need to specify both xcode_target and xcode_scheme" 142 | return 1 143 | fi 144 | 145 | if [ -n "$xcode_workspace" ]; then 146 | print_error "you don't need to specify both xcode_target xcode_workspace" 147 | return 1 148 | fi 149 | else 150 | # 1st formの場合 151 | if [ -z "$xcode_scheme" ]; then 152 | print_error 'you must specify xcode_scheme or xcode_target' 153 | return 1 154 | fi 155 | 156 | if [ -z "$xcode_workspace" ]; then 157 | print_error 'you must specify xcode_workspace or xcode_target' 158 | return 1 159 | fi 160 | fi 161 | 162 | # 末尾の/を取り除きます 163 | # ただし、"/"である場合はそのままとします 164 | if [ "$provisioning_file_dir" != "/" ]; then 165 | provisioning_file_dir="${provisioning_file_dir%/}" 166 | fi 167 | 168 | local provisioning_profile_path="${provisioning_file_dir}/$provisioning_file" 169 | 170 | # Archive 171 | if [ -n "$xcode_target" ]; then 172 | # workspace指定なしの場合 173 | xcodebuild \ 174 | -target "$xcode_target" \ 175 | -configuration "$configuration" \ 176 | clean build \ 177 | CODE_SIGN_IDENTITY="$developer_name" \ 178 | CONFIGURATION_BUILD_DIR="$output_path" 179 | else 180 | # workspace指定ありの場合 181 | xcodebuild \ 182 | -scheme "$xcode_scheme" \ 183 | -workspace "$xcode_workspace" \ 184 | -configuration "$configuration" \ 185 | clean build \ 186 | CODE_SIGN_IDENTITY="$developer_name" \ 187 | CONFIGURATION_BUILD_DIR="$output_path" 188 | fi 189 | 190 | if [ "$?" -ne 0 ]; then 191 | print_error 'fail to archive' 192 | return 1 193 | fi 194 | 195 | # Signing 196 | xcrun -log -sdk iphoneos PackageApplication \ 197 | "${output_path}/${appname}.app" \ 198 | -o "${output_path}/${appname}.ipa" \ 199 | -sign "$developer_name" \ 200 | -embed "$provisioning_profile_path" 201 | 202 | # 作成したipaファイルのパス = ${output_path}/${appname}.ipa 203 | } 204 | 205 | main "$@" 206 | 207 | -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | configuration_list=("Release") 4 | 5 | release_date=$(TZ=JST-9 date '+%Y-%m-%d %H:%M:%S') 6 | message="Build: ${CIRCLE_BUILD_NUM}, Uploaded: $release_date" 7 | 8 | for config in "${configuration_list[@]}"; do 9 | output_path="$PWD/build/${config}" 10 | 11 | ./scripts/build-ipa.sh \ 12 | -d "$DEVELOPER_NAME" -a "$APPNAME" \ 13 | -p "$PROFILE_NAME" \ 14 | -t "$XCODE_TARGET" \ 15 | -c "$config" \ 16 | -o "$output_path" 17 | 18 | ./scripts/upload-ipa-to-deploygate.sh \ 19 | -u "$DEPLOYGATE_USER_NAME" -t "$DEPLOYGATE_API_TOKEN" -m "$message" \ 20 | "${output_path}/${APPNAME}.ipa" 21 | done 22 | 23 | -------------------------------------------------------------------------------- /scripts/remove-key.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | security delete-keychain ios-build.keychain 4 | rm -f ~/Library/MobileDevice/Provisioning\ Profiles/* 5 | -------------------------------------------------------------------------------- /scripts/upload-ipa-to-deploygate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # deploygate.comにipaファイルをアップロードする 5 | # 6 | 7 | declare -r SCRIPT_NAME=${0##*/} 8 | 9 | print_usage() 10 | { 11 | cat << EOF 12 | Usage: $SCRIPT_NAME -u USER_NAME -t TOKEN [-m MESSAGE] IPA_PATH 13 | Upload ipa file to deploygate.com 14 | 15 | -u USER_NAME deploygate user name 16 | -t TOKEN deploygate API token 17 | -h display this help and exit 18 | EOF 19 | } 20 | 21 | print_error() 22 | { 23 | echo "$SCRIPT_NAME: $*" 1>&2 24 | echo "Try \`-h' option for more information." 1>&2 25 | } 26 | 27 | main() 28 | { 29 | local user_name='' 30 | local api_token='' 31 | local file_path='' 32 | local message='' 33 | 34 | local option 35 | local OPTARG 36 | local OPTIND 37 | while getopts ':u:t:m:h' option; do 38 | case $option in 39 | u) 40 | user_name=$OPTARG 41 | ;; 42 | t) 43 | api_token=$OPTARG 44 | ;; 45 | m) 46 | message=$OPTARG 47 | ;; 48 | h) 49 | print_usage 50 | return 0 51 | ;; 52 | :) #オプション引数欠如 53 | print_error "option requires an argument -- $OPTARG" 54 | return 1 55 | ;; 56 | *) #不明なオプション 57 | print_error "invalid option -- $OPTARG" 58 | return 1 59 | ;; 60 | esac 61 | done 62 | shift $((OPTIND - 1)) 63 | 64 | file_path=$1 65 | 66 | if [ -z "$user_name" ]; then 67 | print_error 'you must specify user name' 68 | return 1 69 | fi 70 | 71 | if [ -z "$api_token" ]; then 72 | print_error 'you must specify api token' 73 | return 1 74 | fi 75 | 76 | if [ -z "$file_path" ]; then 77 | print_error 'you must specify file path' 78 | return 1 79 | fi 80 | 81 | curl \ 82 | -F file="@${file_path}" \ 83 | -F "token=${api_token}" \ 84 | -F message="${message}" \ 85 | -F disable_notify=yes \ 86 | "https://deploygate.com/api/users/${user_name}/apps" 87 | } 88 | 89 | main "$@" 90 | 91 | --------------------------------------------------------------------------------