抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

文章时效性提示

本文发布于 632 天前,且 632 天未更新,部分信息可能已有发展或改变!

当你准备发布你第一版的Rust工程时,大概率会要上传一些适配macOS、Linux和Windows的预构建程序到你的GitHub release页面。这是让你的程序大范围可用的最初一步,也极大的方便你为Homebrew设置各种安装方法。

然而,需要指出的是,交叉编译并不是一件容易的事,尤其是使用GitHub Action来做。

在本文的最后,你将会得到一个健全的部署流程,它可以自动创建releases并上传Windows、Linux和macOS各自的二进制程序。

你的releases页看起来将会是这个样子:

image

你甚至还能学到一些GitHub Actions的知识!

工作流触发器

首先我们需要设置触发action的条件。

通常情况下,软件发布每个版本时都会新建一个对应的标签,所以我们可以设置自动发布器运行的条件为:当一个新的标签推送(pushed)到GitHub的时候。

我们还需要限制一下标签的内容是具体的版本号(比如1.0.0或者0.2.4之类的)时才触发,这样GitHub才不会在我们新建不相关的标签时运行我们的action。

我们可以用以下代码做到:

.github/workflows/deploy.yml
# 设置我们action的名字,自定义
name: Deploy

on:
push:
tags:
# 通过正则匹配一个版本,比如0.2.1
- "[0-9]+.[0-9]+.[0-9]+"

好了,解决了上述触发的问题后,我们来看看我们的action具体要做些什么吧!

构建Action

为了编译我们的跨平台程序,我们需要创建一个新的作业(job),我们可以叫它build-and-upload,因为它负责构建二进制程序并上传到release页面。

一个作业(job)绑定了一系列的步骤(steps),在步骤分类下我们可以做几乎任何事情!

定义元数据

让我们开始写我们的工作流程,首先我们需要定义整个流程中所需要用到的一些元数据。

.github/workflows/deploy.yml
# ...

jobs:
# ...

build-and-upload:
name: Build and upload
# 此作业运行在什么操作系统上
runs-on: ${{ matrix.os }}

# strategy上下文,有关当前作业的矩阵执行策略的信息
strategy:
# matrix上下文,包含在工作流中定义的应用于当前作业的矩阵属性
matrix:
# 你可以添加更多你想要的构建平台
include:
- build: linux
os: ubuntu-latest
target: x86_64-unknown-linux-musl

- build: macos
os: macos-latest
target: x86_64-apple-darwin

- build: windows-gnu
os: windows-latest
target: x86_64-pc-windows-gnu

你可能注意到了matrix,这个matrix是很重要的一部分,我们的工作会遍历在matrix里包含(include)的每一个元素。matrix在runs-on这个关键字中用到了,所以我们的build-and-upload作业会在每一个os键所示的操作系统中运行一次。

安装依赖

这之后我们开始添加作业的步骤,最开始的两步很简单:克隆(clone)我们的仓库并安装Rust

.github/workflows/deploy.yml
# ...

build-and-upload:
# ...
steps:
- name: Clone repository
uses: actions/checkout@v3

- name: Install Rust
# 安装Rust的稳定版本
# 也可以使用@nightly来下载测试版
uses: dtolnay/rust-toolchain@stable
# 运行此步骤提供的参数
with:
# 让Rust编译到我们所需要的平台(这已经定义在matrix关键字里了)
targets: ${{ matrix.target }}

这一步使用了uses关键字,它会告诉执行器调用一个外部的别人已经做好了的action。

你可以在GitHub看到我们具体调用的这个actions/checkout,它用来克隆我们的仓库到本地。

使用with关键字来定义所需要的参数,我们用它来设置dtolnay/rust-toolchain,告诉它我们的Rust应该编译到哪个平台。

得到版本信息

然后来得到标签里的版本信息,我们待会会用到

.github/workflows/deploy.yml
- name: Get the release version from the tag
shell: bash
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV

你可能注意到了shell: bash,这告诉GitHub Actions我们想要运行一个shell脚本,然后在run关键字里输入我们想执行的命令。

这个脚本会把从标签里得到的版本信息放到环境变量里,叫$VERSION

在不同步骤中通信时,使用环境变量是非常常见的做法。我们可以在之后的作业中使用${{env.VERSION}}来得到这里设置的版本信息(如0.1.0)。

构建二进制程序

准备工作做完后,我们现在就可以来构建二进制程序了。

这一步我们会使用cross这个工具,它使用Docker来构建跨平台的工程。也已经有了非常简单的action来让我们用cross

让我们把它放到步骤数组里:

.github/workflows/deploy.yml
- name: Build
uses: actions-rs/cargo@v1
with:
use-cross: true
command: build
args: --verbose --release --target ${{ matrix.target }}

这一步告诉GitHub Actions我们想要使用cross来构建我们的工程。

类似于cargo build,我们的程序会被构建到target/<TARGET>/release/<BINARY_NAME>,这里的<TARGET>就是我们在matrix里定义的target键,名字则取决于你的项目。

压缩二进制程序

我们快要完成了!现在我们得到了构建完成的二进制程序,现在需要把它们放到.tar.gz或者.zip压缩文件里,这样其他人就能方便的从我们的assets页下载了。

通过以下步骤可以做到:

.github/workflows/deploy.yml
- name: Build archive
shell: bash
run: |
# 使用你程序的名称替换下面这个名字
binary_name="<BINARY_NAME>"

dirname="$binary_name-${{ env.VERSION }}-${{ matrix.target }}"
mkdir "$dirname"
if [ "${{ matrix.os }}" = "windows-latest" ]; then
mv "target/${{ matrix.target }}/release/$binary_name.exe" "$dirname"
else
mv "target/${{ matrix.target }}/release/$binary_name" "$dirname"
fi

if [ "${{ matrix.os }}" = "windows-latest" ]; then
7z a "$dirname.zip" "$dirname"
echo "ASSET=$dirname.zip" >> $GITHUB_ENV
else
tar -czf "$dirname.tar.gz" "$dirname"
echo "ASSET=$dirname.tar.gz" >> $GITHUB_ENV
fi

这一步很长,但并不复杂,让我们来拆解一下这一长段做了什么:

  1. 创建一个我们将要用来压缩的目录(如prj-0.2.1-x86_64-apple-darwin)
  2. 把我们构建好的二进制程序放到这个目录里,不同操作系统会有不同的名字
  3. 压缩,Windows使用.zip,Unix使用.tar.gz
  4. 把压缩文件的路径放到名为$ASSET的环境变量里

我们之前定义的$VERSION也在这里使用到了!

上传二进制程序

压缩完后,我们就准备上传了,这是我们作业的最后一个步骤!

.github/workflows/deploy.yml
- name: Upload the binaries
uses: softprops/action-gh-release@v1
with:
files: |
${{ env.ASSET }}

相比于其他步骤,这一步无疑非常好理解了。

我们只需要使用别人写好的softprops/action-gh-release来上传我们的文件即可(上一步我们把文件存到了$ASSET环境变量中)

然后就完成了!当我们把带有一个版本标签(如2.1.0)的修改推送到GitHub时,这个action将会帮我们创建release,构建多平台的二进制文件并上传!

总结与完整代码

我希望本文是轻松有趣又能让人学到知识的,在此祝你未来部署工程时顺风顺水!

如果对于本文有什么疑问或者困惑,欢迎在GitHub新建一个issue。

作为补充,下面所示的就是一个完整的action代码了(译者注:以下代码与上述代码不完全一致):

成品action代码
.github/workflows/deploy.yml
name: Deploy

on:
push:
tags:
- "[0-9]+.[0-9]+.[0-9]+"

permissions:
contents: write

jobs:
build-and-upload:
name: Build and upload
runs-on: ${{ matrix.os }}

strategy:
matrix:
# You can add more, for any target you'd like!
include:
- build: linux
os: ubuntu-latest
target: x86_64-unknown-linux-musl

- build: macos
os: macos-latest
target: x86_64-apple-darwin

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Get the release version from the tag
shell: bash
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV

- name: Install Rust
# Or @nightly if you want
uses: dtolnay/rust-toolchain@stable
# Arguments to pass in
with:
# Make Rust compile to our target (defined in the matrix)
targets: ${{ matrix.target }}

- name: Build
uses: actions-rs/cargo@v1
with:
use-cross: true
command: build
args: --verbose --release --target ${{ matrix.target }}

- name: Build archive
shell: bash
run: |
# Replace with the name of your binary
binary_name="<BINARY_NAME>"

dirname="$binary_name-${{ env.VERSION }}-${{ matrix.target }}"
mkdir "$dirname"
if [ "${{ matrix.os }}" = "windows-latest" ]; then
mv "target/${{ matrix.target }}/release/$binary_name.exe" "$dirname"
else
mv "target/${{ matrix.target }}/release/$binary_name" "$dirname"
fi

if [ "${{ matrix.os }}" = "windows-latest" ]; then
7z a "$dirname.zip" "$dirname"
echo "ASSET=$dirname.zip" >> $GITHUB_ENV
else
tar -czf "$dirname.tar.gz" "$dirname"
echo "ASSET=$dirname.tar.gz" >> $GITHUB_ENV
fi

- name: Release
uses: softprops/action-gh-release@v1
with:
files: |
${{ env.ASSET }}

评论