使用space管理provisionProfile

痛点

日常iOS开发中,描述文件的管理是一个费时的事情。通常开发者账号仅有部分开发者有权限可以操作,当添加一个测试设备时,需要更新所有的developmentADHoc描述文件,下载到本地,并替换本地的描述文件,如果有CI系统还需要更新CI上的描述文件。对于区分内外网的公司,可能中间还需要切换网路等操作。总之,这是一件繁琐的事情。

spaceship

spaceship作为fastlane中的一个组件,可以帮助我们处理开发者账号相关的功能:创建appid、更新appid属性、添加设备、更新描述文件等等。

Login
Spaceship::Portal.login("felix@krausefx.com", "password")

Spaceship::Portal.select_team # call this method to let the user select a team
App
# Fetch all available apps
all_apps = Spaceship::Portal.app.all

# Find a specific app based on the bundle identifier
app = Spaceship::Portal.app.find("com.krausefx.app")

# Show the names of all your apps
Spaceship::Portal.app.all.collect do |app|
  app.name
end

# Create a new app
app = Spaceship::Portal.app.create!(bundle_id: "com.krausefx.app_name", name: "fastlane App")
App Services
# Find a specific app based on the bundle identifier
app = Spaceship::Portal.app.find("com.krausefx.app")

# Get detail informations (e.g. see all enabled app services)
app.details

# Enable HealthKit, but make sure HomeKit is disabled
app.update_service(Spaceship::Portal.app_service.health_kit.on)
app.update_service(Spaceship::Portal.app_service.home_kit.off)
app.update_service(Spaceship::Portal.app_service.vpn_configuration.on)
app.update_service(Spaceship::Portal.app_service.passbook.off)
app.update_service(Spaceship::Portal.app_service.cloud_kit.cloud_kit)
App Groups
# Fetch all existing app groups
all_groups = Spaceship::Portal.app_group.all

# Find a specific app group, based on the identifier
group = Spaceship::Portal.app_group.find("group.com.example.application")

# Show the names of all the groups
Spaceship::Portal.app_group.all.collect do |group|
  group.name
end

# Create a new group
group = Spaceship::Portal.app_group.create!(group_id: "group.com.example.another",
                                        name: "Another group")

# Associate an app with this group (overwrites any previous associations)
# Assumes app contains a fetched app, as described above
app = app.associate_groups([group])
Certificates
# Fetch all available certificates (includes signing and push profiles)
certificates = Spaceship::Portal.certificate.all
Code Signing Certificates
# Production identities
prod_certs = Spaceship::Portal.certificate.production.all

# Development identities
dev_certs = Spaceship::Portal.certificate.development.all

# Download a certificate
cert_content = prod_certs.first.download
Push Certificates
# Production push profiles
prod_push_certs = Spaceship::Portal.certificate.production_push.all

# Development push profiles
dev_push_certs = Spaceship::Portal.certificate.development_push.all

# Download a push profile
cert_content = dev_push_certs.first.download

# Creating a push certificate

# Create a new certificate signing request
csr, pkey = Spaceship::Portal.certificate.create_certificate_signing_request

# Use the signing request to create a new push certificate
Spaceship::Portal.certificate.production_push.create!(csr: csr, bundle_id: "com.krausefx.app")
Create a Certificate
# Create a new certificate signing request
csr, pkey = Spaceship::Portal.certificate.create_certificate_signing_request

# Use the signing request to create a new distribution certificate
Spaceship::Portal.certificate.production.create!(csr: csr)
Provisioning Profiles
Receiving profiles
##### Finding #####

# Get all available provisioning profiles
profiles = Spaceship::Portal.provisioning_profile.all

# Get all App Store and Ad Hoc profiles
# Both app_store.all and ad_hoc.all return the same
# This is the case since September 2016, since the API has changed
# and there is no fast way to get the type when fetching the profiles
profiles_appstore_adhoc = Spaceship::Portal.provisioning_profile.app_store.all
profiles_appstore_adhoc = Spaceship::Portal.provisioning_profile.ad_hoc.all

# Get all Development profiles
profiles_dev = Spaceship::Portal.provisioning_profile.development.all

# Fetch all profiles for a specific app identifier for the App Store (Array of profiles)
filtered_profiles = Spaceship::Portal.provisioning_profile.app_store.find_by_bundle_id(bundle_id: "com.krausefx.app")

# Check if a provisioning profile is valid
profile.valid?

# Verify that the certificate of the provisioning profile is valid
profile.certificate_valid?

##### Downloading #####

# Download a profile
profile_content = profiles.first.download

# Download a specific profile as file
matching_profiles = Spaceship::Portal.provisioning_profile.app_store.find_by_bundle_id(bundle_id: "com.krausefx.app")
first_profile = matching_profiles.first

File.write("output.mobileprovision", first_profile.download)
Create a Provisioning Profile
# Choose the certificate to use
cert = Spaceship::Portal.certificate.production.all.first

# Create a new provisioning profile with a default name
# The name of the new profile is "com.krausefx.app AppStore"
profile = Spaceship::Portal.provisioning_profile.app_store.create!(bundle_id: "com.krausefx.app",
                                                         certificate: cert)

# AdHoc Profiles will add all devices by default
profile = Spaceship::Portal.provisioning_profile.ad_hoc.create!(bundle_id: "com.krausefx.app",
                                                      certificate: cert,
                                                             name: "Profile Name")

# Store the new profile on the filesystem
File.write("NewProfile.mobileprovision", profile.download)
Repair all broken provisioning profiles
# Select all 'Invalid' or 'Expired' provisioning profiles
broken_profiles = Spaceship::Portal.provisioning_profile.all.find_all do |profile|
  # the below could be replaced with `!profile.valid? || !profile.certificate_valid?`, which takes longer but also verifies the code signing identity
  (profile.status == "Invalid" or profile.status == "Expired")
end

# Iterate over all broken profiles and repair them
broken_profiles.each do |profile|
  profile.repair! # yes, that's all you need to repair a profile
end

# or to do the same thing, just more Ruby like
Spaceship::Portal.provisioning_profile.all.find_all { |p| !p.valid? || !p.certificate_valid? }.map(&:repair!)
Devices
# Get all enabled devices
all_devices = Spaceship::Portal.device.all

# Disable first device
all_devices.first.disable!

# Find disabled device and enable it
Spaceship::Portal.device.find_by_udid("44ee59893cb...", include_disabled: true).enable!

# Get list of all devices, including disabled ones, and filter the result to only include disabled devices use enabled? or disabled? methods
disabled_devices = Spaceship::Portal.device.all(include_disabled: true).select do |device|
  !device.enabled?
end

# or to do the same thing, just more Ruby like with disabled? method
disabled_devices = Spaceship::Portal.device.all(include_disabled: true).select(&:disabled?)

# Register a new device
Spaceship::Portal.device.create!(name: "Private iPhone 6", udid: "5814abb3...")
Enterprise
# Use the InHouse class to get all enterprise certificates
cert = Spaceship::Portal.certificate.in_house.all.first

# Create a new InHouse Enterprise distribution profile
profile = Spaceship::Portal.provisioning_profile.in_house.create!(bundle_id: "com.krausefx.*",
                                                        certificate: cert)

# List all In-House Provisioning Profiles
profiles = Spaceship::Portal.provisioning_profile.in_house.all
Multiple Spaceships
# Launch 2 spaceships
spaceship1 = Spaceship::Launcher.new("felix@krausefx.com", "password")
spaceship2 = Spaceship::Launcher.new("stefan@spaceship.airforce", "password")

# Fetch all registered devices from spaceship1
devices = spaceship1.device.all

# Iterate over the list of available devices
# and register each device from the first account also on the second one
devices.each do |device|
  spaceship2.device.create!(name: device.name, udid: device.udid)
end

脚本

从上面可以看到,spaceship可以帮我们处理几乎所有需要在苹果开发者中心中操作的所有事项,因此完全可以基于spaceship编写一个注册新设备并自动更新、下载、同步描述文件的脚本来解决我们的问题。spaceship是使用Ruby写的,因此我们可以使用Ruby来编写这个脚本。

由于脚本可能会分享给内部或外部人员,可以将脚本编写得更通用、安全一些:

  • 通用性,其他人员拿到脚本即可使用无需修改

  • 安全性,无需将账号、密码等信息硬编码在脚本中

  • 便捷性,能较安全地记住密码(比如借助keychain)

由于对Ruby相对陌生,可以借助Dash查询相应的API,在Dash中下载相应的文档即可。

Ruby的断点调试可以使用pry:

gem install pry

安装完毕后,在需要断点的地方使用:

require 'pry'

name = 'jack'
binding.pry
name += ' jones'
puts "name:${name}"

代码运行后,会在设置了binding的地方暂停,即可进行调试。

最终示例代码:

require "spaceship"
# require 'io/console'
require 'Open3'
require 'fileutils'

puts "Enter Your Account:"
account = gets.chomp

# get password from keychain
service = account + "_DeveloperService"
cmd = "security find-generic-password -a $USER -s #{service} -w"
# puts cmd
$pwd = ''
# puts $pwd
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
    while line = stdout.gets
        line = line.strip
        if line && line.length > 0
            # puts "line: #{line}"
            $pwd = line
        end
    end
end

if not $pwd
    $pwd = ''
end

if $pwd.length > 0
    puts "Use Keychain Password?(y/n)"
    use = gets.chomp
    if use.downcase != 'y' and use.downcase != 'yes'
        $pwd = ''
    end
end

if $pwd.length == 0
    puts "Enter Your Password:"
    $pwd = STDIN.noecho(&:gets).chomp
end

Spaceship.login(account, $pwd)

# save password to keychain
# puts "Updating Keychain"
cmd = "security add-generic-password -U -a $USER -s #{service} -w #$pwd"
# puts cmd
system(cmd)

# 更新设备
fileDir = File.dirname(__FILE__)
deviceFile = File.join(fileDir, "multiple-device-upload-ios.txt")
file = File.open(deviceFile) #文本文件里录入的udid和设备名用tab分隔
puts "\n-ADDING DEVICES"
file.each do |line|
    # puts "line:#{line}"
    arr = line.strip.split(" ")
    # puts "arr=#{arr}"
    udid = arr[0]
    name = arr[1]
    puts "\t-DeviceName:#{name}, udid:#{udid}"
    device = Spaceship.device.create!(name: arr[1], udid: arr[0])
    puts "\t-add device: #{device.name} #{device.udid} #{device.model}"
end

devices = Spaceship.device.all

profiles = Array.new
profiles += Spaceship.provisioning_profile.development.all 
profiles += Spaceship.provisioning_profile.ad_hoc.all

puts "\n-UPDATING PROFILES"
profiles.each do |p|
    puts "\t-Updating #{p.name}"
    p.devices = devices
    p.update!
end


downloadProfiles = Array.new
downloadProfiles += Spaceship.provisioning_profile.development.all 
downloadProfiles += Spaceship.provisioning_profile.ad_hoc.all

puts "\n-DOWNLOADING PROFILES"
downloadProfiles.each do |p|
    puts "\t-Downloading #{p.name}"
    fileName = p.name
    # save to Downloads floder
    downloadPath = File.expand_path("~/Downloads/#{fileName}.mobileprovision")
    File.write(downloadPath, p.download)
    puts "\t-File at: #{downloadPath}"
    # rename and copy to Provisioning Profiles floder
    dest = File.expand_path("~/Library/MobileDevice/Provisioning Profiles/#{p.uuid}.mobileprovision")
    FileUtils.copy(downloadPath, dest)
    puts "\t-Replace #{p.name} in Provisioning Profiles"
end
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 229,001评论 6 537
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,786评论 3 423
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 176,986评论 0 381
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,204评论 1 315
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,964评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,354评论 1 324
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,410评论 3 444
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,554评论 0 289
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,106评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,918评论 3 356
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,093评论 1 371
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,648评论 5 362
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,342评论 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,755评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,009评论 1 289
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,839评论 3 395
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,107评论 2 375

推荐阅读更多精彩内容