乐趣区

关于前端:macOS-App-自动化分发-App-Store-探索与实践

前言

提及自动化一词,我想很多同学会想到 CI/CD,这 2 者之间的确是存在肯定的分割,能够简略了解成父、子集合之间的关系。

而正如文章题目所言,近期我在钻研 macOS App 自动化散发 App Store 的事件,艰深点讲就是心愿把原先手动构建 .xcarchive 文件、导出 .pkg 文件以及上传 App Store 的操作转为 用 Shell 脚本自动化实现这些步骤。其中,减少的 Shell 脚本会基于现有的 CI/CD 的实现,退出到适当的地位。

那么,明天本文也会从 CI 根底登程,循序渐进地带着大家意识下 macOS App 自动化散发 App Store 实现的所以然。

1 意识继续集成(CI)根底

继续集成,Continuous Integration,简称 CI。这里咱们来看下 Wikipedia 上对 CI 的介绍:

—— In software engineering, continuous integration (CI) is the practice of merging all developers’ working copies to a shared mainline several times a day. Grady Booch first proposed the term CI in his 1991 method, although he did not advocate integrating several times a day. Extreme programming (XP) adopted the concept of CI and did advocate integrating more than once per day – perhaps as many as tens of times per day.

通常状况下,这在咱们理论开发场景中,CI 指的是将我的项目的构建过程集成到某个独自的软件的实际。例如,在后面所说 macOS App 自动化散发 App Store 的 Shell 实现会退出到现有的 CI/CD 过程,它的 CI 的过程会是这样:

  • 开发人员(构建者)触发 Jenkins 的 Job
  • 执行 Job,这会由 Job 所在 Jenkins 的 Slave Node(构建机)执行,生产制品,例如一个 .dmg 或者 .pkg 文件
  • 上传制品到制品库

其中,比拟要害的则是构建机(Slave Node),咱们的整个构建过程的 脚本 实现都会 在构建机上执行,例如前面要讲的自动化散发 App Store 的实现。而这里脚本应用的是 Shell 编写,当然也能够用 Google 的 zx,有趣味的同学能够自行理解,这里不做开展。

因为,在意识 macOS App 自动化散发 App Store 之前,咱们须要先晓得 macOS App 手动散发的过程是怎么样的,以便于后续用自动化脚本一一实现手动散发的步骤。

2 手动散发(Distribute)

macOS App 手动散发 App Store 的过程,艰深点讲就是 应用 Xcode 提供的 GUI 界面操作 实现。然而,在进行正式的操作应用的前提是要有一个能够公布到 App Store 配置齐备的 macOS App,这要求你须要满足以下 3 点:

  • 注册成为 Apple Developer Program,在 Apple Store 中下载 Apple Developer,而后在利用的账户中注册成为一个“尊贵的 688 会员”
  • 在 https://developer.apple.com/ 后盾,别离在 Certificates, Identifiers & Profiles 和 App Store Connect 创立证书相干(Bundle Identifier、Provision Profile、Signing Certificate)和注册 App
  • 本地初始化创立一个简略的 macOS App,并关联上后面创立的 Bundle Identifier、Provision Profile、Signing Certificate

对于第 1 点,我想应该没什么难了解的。上面,咱们从创立一个 macOS App 登程来串联第 2、3 点要做的事件。

2.1 前置筹备(创立一个齐备的利用)

创立一个 macOS App 的我的项目,能够通过 Xcode 疾速创立一个,关上 Xcode ——> Create a new Xcode Project ——> 抉择创立利用的 Platform(macOS)——> 填写项目名称、Team、Organization Identifier 等信息 ——> 在 General 中抉择 App Category 和 App Icons,这里我创立的利用叫 FEKit。

应用 Xcode 关上该项目标 .xcodeproj 文件,或者在终端输出,在咱们这个例子则是:

open FEKit.xcodeproj

抉择构建的指标,这里咱们抉择 macOS 作为协定(Scheme)和指标(Target):

配置利用的 Bundle Identifier、Provision Profile、Signing Certificate,这能够 在 Apple Developer 后盾或者在 Xcode 的 Preferences 中增加 Apple ID 来创立,无论应用其中哪种形式创立的,都会在 Apple Developer 后盾的 Certificates, Identifiers & Profiles 中展现:

其中,如果咱们要散发 App Store,则须要这 2 个证书:

  • Mac App Distribution,用于签名散发 App Store 的利用和配置对应的 Provisioning Profile
  • Mac Installer Distribution,用于签名利用的安装包和提交到 App Store

当然,除开这 2 个证书,咱们还须要在 Certificates, Identifiers & Profiles 页面的 Identifiers 和 Profiles 中别离创立 App ID 和 Provisioning Profile,这 2 个步骤比较简单(这里不做开展)。

在证书、Indentifiers、Profiles 创立完后,则能够下载证书(.cer)和 Provisioning Profile(.provisionprofile)文件到本地,而后别离双击关上,其中证书则会加载到电脑登录对应的钥匙串(keychain)中,而 Provisioning Profile 则会被 Xcode 应用。并且,值得一提的是每个证书都是加密的,须要配套的密钥来解密应用,也就是 一个证书(.cer 文件)对应一个密钥(.p12 文件),这个密钥则是由证书创建者生成的,所以,你在创立证书的时候须要抉择一个 .certSigningRequest 文件:

而后在加载证书(.cer 文件)到本地,并且确保有证书对应的密钥(.p12 文件)后,最终登录的钥匙串中的证书会是这样:

2.2 Xcode 手动散发 App Store

接着,咱们则能够应用 Xcode 的 Production -> Archive 来构建 .xcarchive 文件:

构建完后,Xcode 会弹出窗口让你抉择 Distribute App 或 Validate App:

这里,咱们抉择 Distribution App -> App Store Connect -> Export -> 抉择 Development Team -> Manually manage signing,此时会要求咱们抉择后面提及的 Distribution 证书、Installer 证书和 Provisioning Profile:

抉择 Next -> Export 后,须要抉择导出的文件目录,则在该目录下会生成 FEKit.pkg 文件,而后咱们能够通过 Transporter 工具来将该文件上传到 App Store Connect(或者后面 Xcode 抉择 Export 或 Upload 的时候抉择 Upload),之后则能够在 App Store Connect 后盾的 TestFlight 查看:

所以,咱们通常所说的上传 App Store,指的是 上传到 App Store Connect 的 TestFlight,后续再由这里的 App Store 中提交上传文件的审核,审核通过再进行上架的操作。并且,须要留神的是每次上传的 .pkg 文件的版本号都须要比上一次的版本号大一(相似于 NPM 的 Package Version)。

那么,到这里整个手动散发 App Store 的过程就介绍结束了,总结起来次要是这 3 个步骤:

  • 构建我的项目生成 .xcarchive 文件
  • 导出 .pkg 文件
  • 上传 .pkg 文件至 App Store Connect

所以,上面咱们须要 用 Shell 脚本自动化 实现这 3 个步骤,也就是 macOS App 自动化散发 App Store。

3 自动化散发(Distribute)

在介绍 macOS App 自动化散发 App Store 实现之前,咱们先来意识这 3 个工具:

  • xcodebuild 是 Xcode 的一个命令行工具包,次要用于构建我的项目相干
  • altool 是一个内置于 Xcode 中的命令行工具,用于验证 App 的二进制文件并将其上传至 App Store 或者对 App 进行公证(Notarize)
  • xcrun 也是 Xcode 的一个命令行工具包,次要用于执行 Xcode 相干的工具链,例如 xrun altoolxrun xcode-select

而在接下来解说 macOS App 自动化散发 App Store 过程中,则会别离提及应用这些工具提供的能力来实现后面的手动步骤。那么,上面就让咱们开始逐渐意识下自动化的实现过程,首先是构建 .xcarive 文件。

3.1 构建 .xcarchive 文件

咱们能够应用 xcodebuild.xcarchive 命令来构建生成 .xcarchive 文件:

xcodebuild -archive \
-scheme "FEKit (macOS)" \
-configuration Release \
-archivePath ./Output/FEKit

能够看到,这里咱们应用了 3 个 Option,它们别离的作用:

  • -scheme 构建的协定,不同的指标 Target 通常对应不同的协定,例如 FEKit (iOS)FEKit (macOS),前者是 IOS,后者是 macOS
  • -configuration 构建的配置,例如 Debug 或 Release,不同的配置对应的证书、签名配置会有不同
  • -archivePath 构建 .xcarchive 导出的目录和文件名,这里则会导出到 Output 目录下并命名为 FEKit.xcarchive

其中,对于我的项目已有的协定和配置,则能够应用 xcodebuild -list 命令查看。

3.2 导出 .pkg 文件

构建完 .xcarchive 文件后,则须要依据改文件导出 .pkg 文件,这同样能够应用 xcodebuild 提供的 Option 命令实现:

xcodebuild -exportArchive \
-archivePath ./Output/FEKit.xcarchive \
-exportPath ./Output/Pkgs \
-exportOptionsPlist ./Build/ExportOptions.plist

其中,对于 -exportOptionsPlist 则是你导出 .pkg 相干的配置,它会是这样:

<?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>destination</key>
    <string>export</string>
    <key>installerSigningCertificate</key>
    <string> 你的 Mac AppStore Install 证书 ID</string>
    <key>manageAppVersionAndBuildNumber</key>
    <true/>
    <key>method</key>
    <string>app-store</string>
    <key>provisioningProfiles</key>
    <dict>
        <key> 你的 Bundle ID</key>
        <string> 你的 Provisioning Profile 的名称 </string>
    </dict>
    <key>signingCertificate</key>
    <string> 你的证书 ID</string>
    <key>signingStyle</key>
    <string>manual</string>
    <key>teamID</key>
    <string> 你的 Team ID</string>
    <key>uploadSymbols</key>
    <true/>
</dict>
</plist>

当然,如果你不想手动创立或填写这些信息,能够用 Xcode 手动导出 .pkg 操作一次,ExportOptions.plist 文件会主动生成在导出的文件目录下。

执行完后面的命令后,导出的 .pkg 文件则会在 -exportPath 配置的文件门路下,在这里也就是在 /Outputs/Pkgs/ 文件目录下。

3.3 验证和散发 .pkg 文件

TODO: 这里能够补充应用 keychain 的形式生产专有明码

接着,则是最初一步验证和散发 .pkg 文件。这一步骤须要应用 xcrunaltool 实现。首先,执行 xcrun altool --validate-app 来验证 .pkg 文件,这个次要用于确认你的利用是否满足上传的条件,例如是否抉择 App Category、App Icon 以及版本号递增等,相应的命令则是:

xcrun altool --validate-app \
-f ./Output/Pkgs/FEKit.pkg \
-t macOS \
-u xxxxx \
-p xxxxx \
--show-progress

能够看到,这里应用到了 5 个 Options,它们各自的作用:

  • -f 须要验证的 .pkg 文件所在文件目录地位
  • -t 验证的指标类型,例如 macOS 或 IOS
  • -u 用于连贯 App Store Connect 的 Apple Developer 账号(Apple ID)
  • -p 和账号(Apple ID)对应的 App 专用明码
  • --show-progress 用于输入验证过程的执行状况

其中,对于 -p 的 App 专用明码则须要去 Apple ID 后盾申请。并且,为了防止将明码明文展现在执行的命令中,咱们能够独自保护一个文件来存储账号和明码:

#!/bin/bash
# App Developer 账号(Apple ID)user="xxxxxxxx"
# App 专用明码
pwd="xxxxxx"

相应地,还须要依据 xcrun altool --validate-app 命令执行的后果(胜利或失败)做不同的后续解决:

#!/bin/bash
# app_store_user_pwd.sh 能够独自放到一个暗藏目录,这里只是作为例子所以没有放到暗藏目录
source "./app_store_user_pwd.sh"
echo "Run xcrun altool --validate-app..."
xcrun altool --validate-app --f ./FEKit.pkg -t macOS -u $user -p $pwd --show-progress
if [$? -eq 0]; then
    echo "validate-app success"
    # 执行上传的命令
else
    echo "validate-app fail"
    exit -1;
fi

其中,$? 示意上个命令执行后果,0 示意胜利,1 示意失败,所以这里咱们应用 if [$? -eq 0]; then 判断验证命令执行后果是否等于 0,是则进行后续上传的解决,不是则输入验证失败的信息并退出。

那么,如果在验证通过 .pkg 文件后,咱们则能够上传 .pkg 文件至 App Store Connect,这须要执行 xcrun altool --upload-app 命令:

xcrun altool --upload-app \
-f ./Output/Pkgs/FEKit.pkg \
-t macOS \
-u xxxxx \
-p xxxxx \
--show-progress

能够看到上传的命令和验证的命令应用上大同小异,只有第一个 Option 不同。那么,到这里整个实现自动化散发 App Store 的过程曾经介绍完了,因为后面都是分步骤解说的,所以 Shell 脚本的实现都是离开的,这里咱们把下面讲到的 Shell 实现都合并到一个 .sh 文件中:

#!/bin/bash
echo "Run xcodebuild archive..."

xcodebuild archive \
-scheme "FEKit (macOS)" \
-configuration Release \
-archivePath ./Output/FEKit

ARCHIVE_FILE=./Output/FEKit.xcarchive
if [! -e "$ARCHIVE_FILE"]; then
    echo ".xarchive doesn't exist";
    exit -1;
fi

echo "Run xcodebuild -exportArchive..."
xcodebuild -exportArchive \
-archivePath ./Output/FEKit.xcarchive \
-exportPath ./Output/Pkgs \
-exportOptionsPlist ./Build/ExportOptions.plist

PKG_FILE=./Output/Pkgs/FEKit.pkg
if [! -e "$PKG_FILE"]; then
    echo ".pkg doesn't exist";
    exit -1;
fi

source "./Build/app_store_user_pwd.sh"
xcrun altool --validate-app --f $PKG_FILE -t macOS -u $user -p $pwd --show-progress
if [$? -eq 1]; then
    echo "altool validate-app fail"
    exit -1;
fi

echo "altool validate-app success"
xcrun altool --upload-app --f $PKG_FILE -t macOS -u $user -p $pwd --show-progress
if [$? -eq 0]; then
    echo "altool --upload-app success"
else
    echo "altool --upload-app fail"
fi

4 应用 fastlane 自动化散发

fastlane 是一个能够便捷地帮你实现 证书治理、代码签名和公布等相干 的工具,实用于 iOS、macOS 和 Android 利用。那么,咱们也就能够应用 fastlane 来实现下面应用 Shell 脚本实现的自动化散发 App Store 过程。

首先,必定是装置 fastlane,对于这方面的介绍官网文档解说的很是详尽,这里就不反复阐述。而当你装置好 fastlane,则能够在利用我的项目的根目录执行 fastlane init 来初始化它相干的配置,在初始化的过程会让你抉择应用 fastlane 的形式,这里咱们抉择手动配置即可,而后它会在我的项目根目录下创立一个 fastlane/Fastfile 目录和文件,后续咱们在执行 fastlane xxx 命令的时候则会依据该文件的代码实现执行具体的操作,默认生成的 Fastfile 文件的配置会是这样:

default_platform(:ios)

platform :ios do
  desc "Description of what the lane does"
  lane :custome_lane do
    # add actions here: https://docs.fastlane.tools/actions
  end
end

其中,default_platform 用于定义一个默认的平台 Platform,例如当咱们有 2 个平台(iOS 和 macOS)的时候,它的的配置须要这样:

default_platform(:ios)

platform :ios do
  desc "Description of what the lane does"
  lane :custome_lane do
    # add actions here: https://docs.fastlane.tools/actions
  end
end
platform :mac do
  desc "Description of what the lane does"
  lane :custome_lane do
    # add actions here: https://docs.fastlane.tools/actions
  end
end

此时,如果咱们执行 fastlane custome_lane,因为这里平台默认为 ios,所以则会执行 platorm:ios 下的 custome_lane,反之执行 fastlane mac custome_lane,则是 platform :mac 下的 custome_lane。那么,对于后面咱们这个例子而言只须要 platform:mac

default_platform(:ios)

platform :mac do
  desc "Description of what the lane does"
  lane :custome_lane do
    # add actions here: https://docs.fastlane.tools/actions
  end
end

接着,则能够在 platform:mac 写咱们须要实现的自动化散发 App Store 相干的代码。fastlane 便捷之处在于它实现了很多开箱即用的 Action,这里咱们须要应用 build_mac_app 和 upload_to_app_store 2 个 Action,前者能够用于构建导出 .pkg 文件,后者能够用于上传 .pkg 文件到 Apple Store Connect:

default_platform(:mac)

platform :mac do
  desc "Description of what the lane does"
  lane :build_upload_appstore do
    # 构建、导出 .pkg 文件
    build_mac_app(scheme: "FEKit (macOS)",
      export_method: "app-store",
      output_directory: "./Output/Test",
      output_name: "FEKit",
      export_team_id: "xxxxxx",
      export_options: {
        provisioningProfiles: {"com.xxxxxx.xxxx" => "macOSAppStore"}
      }
    )
    # 上传 .pkg 到 App Store Connect
    upload_to_app_store(
      pkg: "./Output/Pkgs/FEKit.pkg",
      platform: "osx",
      username: "xxxxxxxxx"
    )
  end
end

其中,在应用 upload_to_app_store 的时候须要留神的是,这里只是申明了你 App Store 的用户名,而专用明码须要事后在零碎环境变量中增加 FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD,而后 fastlane 在执行 upload_to_app_store Action 时会去读取该环境变量。

结语

最初,这里咱们用一个流程图再回顾下整个自动化散发 App Store 的过程:

并且,我想可能有同学会问,作为前端咱们须要懂这个吗?集体认为是须要的,因为在一些场景下,比如说做 React Native 或 Electron 开发的时候,不可避免地就会接触到原生利用的签名、构建、公证(Notarize)和散发 App Store 的概念,所以,通过亲自地体验一番原生利用实现这些的过程还是有肯定收益的(知其然使其然)。

如果,文中存在表白不当或谬误的中央,欢送各位同学提 Issue ~

点赞

通过浏览本篇文章,如果有播种的话,能够 点个赞,这将会成为我继续分享的能源,感激~

我是五柳,喜爱翻新、捣鼓源码,专一于源码(Vue 3、Vite)、前端工程化、跨端等技术学习和分享,欢送关注我的 微信公众号:Code center

退出移动版