前言
学如逆水行舟,不进则退。共勉!
昨天在腾讯课堂听了一个关于iOS的直播课堂,是一名叫做cat的老师吧,印象还是比较深刻的。主要内容是讲使用xcconfig更改应用设置,听完这堂课了,基本上掌握了:
- xcconfig解析
- 通过xcconfig配置多个开发环境
- 通过xcconfig构建LLVM小工具
- 通过代码访问xcconfig
讲得还是非常不错的,然后今天主要是给大家分享一份cat老师整理的一份Github Action,我觉得还是非常有用的。也希望大家给我点点赞点点关注支持一下。tip:老师也经常逛掘金,希望老师能看到,嘿嘿。
一、GitHub Actions
GitHub Actions是一个由事件驱动的自动化平台,通过设置触发条件,在某些事件发生时自动运行指定的操作。
换句话讲, GitHub Actions 不仅允许开发人员在平台上托管代码,还可以操作它。通过 GitHub Actions ,我们能够自动化一个跨团队、手动且容易出错的流程,可以使每个团队能够独立运作,有助于提高生产力。
GitHub Actions 的一个常见实用场景就是进行自动化的持续集成和部署。持续集成由很多操作组成,比如拉取代码、推送代码、运行测试,发布到第三方服务等等。在这个过程中 GitHub 把开发者可以对代码执行的操作包装成了一个个功能模块,就叫action。开发者可以将多个action组合成一个workflow工作流程。
例如,当指定发生拉取或推送操作时,触发 GitHub CI服务器。执行由一个或多个action组合到一起的workflow工作流程。
在GitHub Actions中,通过workflow工作流程指定需要运行的action,以及执行它们的触发器条件。 workflow定义在当前操作的git仓库中的.github/ workflow目录中,可以定义一个或多个 workflow。 workflow文件必须使用YAML语法,必须以.yml或.yaml作为文件扩展名。
每个action都是一个独立的功能,存放到指定的Actions仓库中。意味着如果你需要功能,不必自己写,可以直接引用他人写好的action。 Github 自己维护了一个marketplace。还有一个awesome action,也可以找到一些好用的action。
要引用一个action,可使用 userName/repoName的引用action。 比如, actions/checkout就表示引用官方action仓库中的 github.com/actions/checkout 这个仓库,作用是checkout当前的使用的GitHub托管的服务器,以便workflow可以访问它。
actions/checkout@25a956c#指向一个 commit
actions/checkout@v1 #指向一个标签
actions/checkout@main #指向一个分支
复制代码
如果,要访问使用非官方提供的action,通过userName/repoName:
lukka/run-vcpkg@v6
lukka/run-cmake@v3
复制代码
接下来,我们来实际分析一个workflow。
Kingfisher
在 Kingfisher 的.github/workflows目录中有一个 build.yaml。专门用来通过fastlane的scan或者gym来进行编译和测试示例工程的。
#1
name:build
#2
on: [push, pull_request]
#3
jobs:
run-test:
# 4
runs-on: macOS-latest
# 5
strategy:
matrix:
destination: [
'macOS',
'iOS Simulator,name=iPhone 8',
'tvOS Simulator,name=Apple TV',
'watchOS Simulator,name=Apple Watch Series 5 - 40mm'
]
swift-version: [5.0]
# 6
steps:
# 7
- uses: actions/checkout@v1
- uses: actions/cache@v1 # 8
id: bundler-cache # 9
with: # 10
path: vendor/bundle
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gems-
# 11
- name: Bundle setup
run: bundle config path vendor/bundle
- name: Bundle install
if: steps.bundler-cache.outputs.cache-hit != 'true' # 12
run: bundle install --jobs 4 --retry 3
- name: Run tests
env: # 13
DESTINATION: platform=${{ matrix.destination }}
SWIFT_VERSION: ${{ matrix.swift-version }}
run: bundle exec fastlane test_ci # 14
复制代码
我们一个步骤一个步骤的来分析:
- name: workflow的名称。当前 workflow名称被设置为build;
- on必填。用来指定触发条件,触发条件被触发开始执行。当前workflow被触发的条件有两个:push和 pull_request 。当发生push或pull操作时,触发该 workflow ;
- jobs指定当前的workflow再被触发时可以运行的一项或多项jobs;
- jobs.<job_id>.runs-on必填。指定要运行job的服务器类型。当前的workflow指定的服务器器为 GitHub提供的 macOS-latest;
- jobs.<job_id>.strategy.matrix:构建矩阵,当前workflow中有两个key:
- destination 指明bundle的缓存key、fastlane的产物类型:
- swift-version用的Swift版本。
- steps指明当前job包含的具体步骤:
i. 第一步是运行github提供的 checkout action 。将当前仓库checkout到当前的服务器;
ii. 第二步,配置fastlane需要的ruby环境。使用官方提供 actions/cache来缓存 actions/cache。
iii.jobs.<job_id>.steps[*].id,当前step的唯一标识。用于在上下文环境中引用该step;
iv.jobs.<job_id>.steps[*].with :指明当前action序言的输入参数,使用map。每个输入参数都是一个键/值对。如果当前输入的不是action需要的输入参数,那么这些参数将被设置为环境变量。该变量的会自动加上前缀 INPUT_ ,并转换为大写;
v.当我们不需要action时,可以使用name表明步骤的名称和run指明步骤执行时构建服务器将运行的命令,来自定义一个step;
vi.jobs.<job_id>.steps[*].if,通过if:表达式,判断当前是否满足step运行需要的信息;
vii.jobs.<job_id>.steps[*].env,用于设置当前step的环境中变量;
viii:最后,通过fastlane执行在fastlane目录中的Fastfile定义的test_ci lane 。
接下来Github Action YAML语法细节部分。
二、Github Actions YAML语法详解
我们还是以 Kingfisher中出现的语法为准。
name
workflow 的名称。Github在仓库的Action页面上显示该仓库使用workflow的名称。如果省略name,Github将其设置为相对于仓库根目录的工作流程文件路径;
[图片上传失败...(image-6ed44a-1636379475823)]
on
用来指定触发条件,触发条件被触发开始执行
可以提供单一触发条件string、一组触发条件array、不同事件类型types的一组条件array或map,指明workflow的运行条件,或将workflow的执行限于特定文件、标记或分值更改。
jobs
指定当前的workflow再被触发时可以运行的一项或多项jobs。
jobs默认是并行运行。要按顺序运行jobs,可以使用 <job_id>needs关键词在job定义依赖项。每个job在runs-on指定的服务器环境中运行。
defaults和 jobs.<job_id>.defaults
设置将应用到workflow中所有job或指定job的默认设置。在job中定义的默认设置将覆盖在workflow中定义的同名默认设置。
defaults.run和 jobs.<job_id>.defaults.run
为workflow中的所有run步骤提供默认的shell和 working-directory选项。也可以设置只可用于job默认设置。
jobs.<job_id>.runs-on必填
指定要运行job的服务器类型。
服务器可以是Github托管的服务器器或自托管的服务器器:
[图片上传失败...(image-fe6aad-1636379475823)]
**jobs.<job_id>.strategy.matrix **
如果我们需要当前的workflow中的运行的job、执行的action或者执行的命令运行到多个操作系统、平台和语言,进行多个组合运行测试。
同时不想创建多个相同的操作,来区别进行区分。
这个时候可以使用构建矩阵:
- 构建矩阵是使用strategy关键字创建的,接受构建选项作为数组。构建矩阵在每次workflow运行时最多可生成256个jobs。此限制也适用于自托管服务器;
- 在matrix中定义的每个选项都有键和值。定义的键将成为matrix上下文中的属性,可以再workflow文件的其他区域中引用该属性。例如,如果定义包含操作系统创建一个job。定义的第一个选项将是工作流程中运行的第一个job;
step
指明当前job包含的具体步骤。
step可以运行命令、运行设置任务,或者运行action等等。每个step在服务器环境中以其自己的进程运行,且可以访问工作区和文件系统。
因为step以自己的进程运行,所以step之间不会保留环境变量的更改。在workflow的使用限制之内可运行无限数量的steps。接下来,开始执行具体操作;
jobs.<job_id>.steps[*].uses
指定当前step中要运行的action。action是一种可重复使用的代码单位;
jobs.<job_id>.steps[*].id
当前step的唯一标识。用于在上下文环境中引用该step;
jobs.<job_id>.steps[*].with
指明当前的action序言的输入参数,使用map
每个输入参数都是一个键/值对。如果当前输入的不是action需要的参数,那么这些参数将被设置为环境变量。该变量的会自动加上前缀INPUT_ ,并转换为大写;
jobs.<job_id>.steps[*].if
通过if:表达式,判断当前是否满足step运行需要的信息。
- 表达式可以是文字值、上下文引用或函数的任意组合。以使用运算符组合文字、上下文引用和函数。表达式通常在工作流程文件中与条件性if关键词一起用来确定步骤是否应该运行。当if条件为true时,步骤将会运行;
- 在if条件下使用表达式时,可以省略表达式语法 (<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">)</mo><mtext>,因为</mtext><mi>G</mi><mi>i</mi><mi>t</mi><mi>h</mi><mi>u</mi><mi>b</mi><mtext>会自动将</mtext><mi>i</mi><mi>f</mi><mtext>条件作为表达式求值,除非表达式包含任何运算符。如果表达式包含任何运算符,则表达式必须包含在</mtext></mrow><annotation encoding="application/x-tex">{{ }}),因为Github会自动将if条件作为表达式求值,除非表达式包含任何运算符。如果表达式包含任何运算符,则表达式必须包含在 {{ }}</annotation></semantics></math>),因为Github会自动将if条件作为表达式求值,除非表达式包含任何运算符。如果表达式包含任何运算符,则表达式必须包含在 内,以明确标记它进行计算。
jobs.<job_id>.steps[*].env ̵ env jobs.<job_id>.env
用于设置当前 workflow 、单个job或者单个step的环境中变量。当多个环境使用相同的名称定义时,Github有一套覆盖规则。
- step中有定义的环境变量在step执行时将覆盖名称相同的job和workflow变量;
- job定义的变量在job执行时将覆盖名称相同的workflow变量。
三、 Ruby Gems缓存
当执行bundle install时,需要根据Gemfile或者 Gemfile.lock下载指定的ruby gems。当在Github CI进行构建时,我们可以把需要下载的ruby gems通过使用 Github action缓存到当前的CI服务器,这样进行构建时,就能更快的加载我们需要的gems。
Github一共提供了两种action可以帮助我们完成缓存操作:
- actions/cache :最流行的缓存Ruby gem方案,仅缓存;
- ruby/setup-ruby :可以安装指定版本的Ruby并且使用bundler缓存Ruby gem。
** actions/cache **
actions/cache 将ruby gems保存到缓存中并在下一下CI构建期间恢复它。如果要指定Ruby版本,需要在当前的仓库目录,创建一个 .ruby-version 文件,将依赖的ruby版本写入该文
2.7.0
我们再来看一下 Kingfisher中的设置:
# 1
- uses: actions/cache@v1
id: bundler-cache
with:
path: vendor/bundle # 2
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} # 3
restore-keys:
| ${{ runner.os }}-gems- # 4
# 5
- name: Bundle setup
run: bundle config path vendor/bundle
- name: Bundle install
if: steps.bundler-cache.outputs.cache-hit != 'true' # 6
run: bundle install --jobs 4 --retry 3
- 对于 actions/cache的v1版本,仅支持单个路径,它必须是一个目录。不能缓存单个文件。使用 actions/cache的v2版本,可以指定单个路径,也可以在单独的行上添加多个路径;
- 输入参数path必要。指定缓存或还原的文件路径。路径可以是绝对路径或相对于工作目录的路径。当前是 vendor/bundle ;
- 输入参数key,必要。根据当前的操作系统和 Gemfile.lock文件哈希表达式生成缓存key作为搜索缓存的键。当操作系统或者 Gemfile.lock发生改变时,重新生成key值;当key匹配现有缓存时,被称为缓存命中,并且操作会将缓存的文件还原到path目录;
- restore-keys :可选。key没有发生缓存命中时用于查找缓存的其他密钥顺序列表;
i. 如果提供restore-keys,actions/cache将按顺序搜索与 restore-keys列表匹配的任何缓存; ii. 当精确匹配时,操作会将缓存中的文件恢复至设置path的目录; iii.如果没有精确匹配,操作将会搜索恢复键值的部分匹配。当操作找到部分匹配时,最近的缓存将恢复至设置path的目录 5. bundle config path vendor/bundle配置bundle,将需要的ruby gems安装到 vendor/bundle目录 6. cache-hit是actions/cache的输出参数。获取缓存命中的结果。
i.当key不匹配现有缓存时,则被称为缓存错过,在job成功完成时将创建一个新缓存。发生缓存错过时,操作将搜索restore-keys 设置的替代键值。 7. --jobs 4 --retry 3 ,并行安装 ruby gems 。如果当前有网络问题,将尝试3次。
** ruby/setup-ruby**
如果使用 ruby/setup-ruby来管理gem缓存和ruby版本就更加直观和简洁。首先,在Github中forkKingfisher到自己的仓库。把 .github/workflows中的build.ymal修改为:
name: build
on: [push, pull_request]
jobs:
run-test:
runs-on: macOS-latest
strategy:
matrix:
destination: [
'macOS',
'iOS Simulator,name=iPhone 8',
'tvOS Simulator,name=Apple TV',
'watchOS Simulator,name=Apple Watch Series 5 - 40mm'
]
swift-version: [5.0]
steps:
- uses: actions/checkout@v1
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7
bundler-cache: true true
name: Run tests
env:
DESTINATION: platform=${{ matrix.destination }}
SWIFT_VERSION: ${{ matrix.swift-version }}
run: bundle exec fastlane test_ci
接下来,将修改的代码push到自己的仓库中。此时已经触发Github CI服务器,开始执行workflow:
一共有四个任务,分别对应看,我们配置在构建矩阵中的参数。
实际执行的命令信息;
可以看到,仅仅1需要设置 bundler-cache: true。同时不再需要文件去指定ruby版本。相对来说就可以避免手动设置缓存时带来的问题。
四、 ̵What is YAML?
YAML是一种易于理解的数据序列化语言,是一种面向数据的语言,通常用于创建配置/描述文件。与JSON相比于YAML更强调数据序列化、可视化、可读性和层次性。
JSON的语法本身是 YAML1.2版的子集。换句话讲, YAML 是JSON的严格超集,它可以做的一切,甚至更多。和python一样, YAML 使用缩进来表示嵌套,我们可以使用空格作为缩进,但不能使用Tab,制表符缩进是被禁止的。并且换行符和缩进在 YAML 中具有某种含义。不像JSON,后者使用方括号和大括号。但是,json格式在 YAML 中是有效的。
YAML 文件使用.yml或.yaml作为扩展名。
** YAML 快速入门**
END
不知各位读者看完觉得怎么样,有没有帮助?有没有启发了?或者有什么其他想法都可以在留言区留言。
最后的话就是如果你也想听公开课,关注我主页。