fastlane で Provisioning Profile をアンインストールする
fastlane を使っている場合、ビルドマシンに同じ Bundle Identifier の Provisioning Profile が複数インストールされていると、 本来使いたいのとは別の Profile が使われてしまうことがある。
ビルド後に使い終わった Profile をアンイストールすれば、この問題を回避できる。
概要
- fastlane における Provisioning Profile 重複問題
- Provisioning Profile のアンインストール
-
security
を使って Provisioning Profile を XML にして UUID を取得する - Profile をアンインストールする手順を fastlane の action 化する
環境
- macOS 10.11.6
- ruby 2.3.1
- fastlane 1.94.0
fastlane における Provisioning Profile 重複問題
fastlane を使っている場合、ビルドの度に sigh
アクションで Profile をダウンロード & インストールしていることが多いと思う。
通常これで問題ないのだが、何かの手違いで Profile が重複してしまうと、そのときダウンロードしてきた Profile が使われなくて困ることがある。
例えば、以下のような場合に、この問題に遭遇する。
- 同じ Bundle Identifier のプロファイルを作りなおした場合
- AdHoc 用と App Store 用のプロファイルを両方インストールした場合
そこで、ビルドが終わった後には、そのときインストールした Profile をアンインストールしたい。
Provisioning Profile のアンインストール
インストールした Profile は ~/Library/MobileDevice/Provisioning Profiles
ディレクトリに保存されている。
Profile をアンインストールするには、この中にあるファイルを削除すれば良い。
ファイル名は <UUID>.mobileprovision
となっており、特定の Profile だけアンインストールしたい場合は、
その UUID を知る必要がある。
Provisioning Profile から UUID を取得する
Profile はバイナリファイルなので、そのままでは中身を読み難い。そこで security
コマンドを使ってテキストに変換する。
security cms -D -i <Path to mobileprovision>
とすれば、Profile の中身が XML で出力される。
<?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>UUID</key>
<string>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</string>
<!-- 中略 -->
</dict>
</plist>
あとはいつもの plist なので、好きに parse すれば良い。
fastlane の action 化する
lane でさくっと使いたいので、action 化してみる。
sh
アクションで security
コマンドを実行して XML を取得したら、REXML で parse して UUID を取得する。
fastlane/actions/uninstall_profile.rb
module Fastlane
module Actions
class UninstallProfileAction < Action
def self.run(params)
UI.message ""
UI.message Terminal::Table.new(
title: "Uninstall Profile".green,
headings: ["Option", "Value"],
rows: params.values
)
UI.message ""
# see https://qiita.com/mattak@github/items/dcb25ad7e12501d1525d
xml = sh("security cms -D -i #{params[:file]}")
require 'rexml/document'
doc = REXML::Document.new xml
uuid = doc.elements.to_a('plist/dict/key')
.find { |e| e.text == 'UUID' }
.next_element
.text
profile_name = "#{uuid}.mobileprovision"
profile_path = File.join(Dir.home, 'Library/MobileDevice/Provisioning Profiles', profile_name)
unless File.exists?(profile_path)
UI.message "Provisioning Profile is not installed at #{profile_path}"
return
end
File.delete(profile_path)
end
#####################################################
# @!group Documentation
#####################################################
def self.description
"Uninstall provisioning profile from Xcode"
end
def self.available_options
[
FastlaneCore::ConfigItem.new(key: :file,
env_name: "FL_UNINSTALL_PROFILE_FILE",
description: "Provisioning profile",
verify_block: proc do |value|
UI.user_error!("Couldn't find file at path '#{value}'") unless File.exist?(value)
end)
]
end
def self.authors
["dddnuts"]
end
def self.is_supported?(platform)
platform == :ios
end
end
end
end
これで、lane では uninstall_profile(file: <Path to file>)
で使えるようになる。
ちなみに、sigh
でダウンロードしてきたファイルは lane_context[SharedValues::SIGH_PROFILE_PATH]
にある(sigh.rb のソースを参照)。
platform :ios do
lane :beta do
sigh
gym
uninstall_profile(
file: lane_context[SharedValues::SIGH_PROFILE_PATH]
)
end
end
まとめ・感想
- fastlane における Provisioning Profile 重複問題を Profile のアンインストールで回避した
- 特定の Profile のみアンインストールするためには UUID が必要
- Profile はバイナリなので
security
コマンドでテキスト(XML)にして読みやすくした - Profile をアンインストールする手順を action 化して fastlane で簡単に使えるようにした
CI で自動化する場合、そもそも Provisioning Profile は全てアンインストールした状態でビルドするとか、 環境依存を出来る限り無くす方が筋が良いことは分かっているんだけど、 他の人とひとつの Jenkins Slave を相乗りしてるとか、プロジェクトによっては自動化が全然進んでないとか、 諸事情でうまくいかないことはあるので、そんなときに役にたてば