当你准备发布你第一版的Rust工程时,大概率会要上传一些适配macOS、Linux和Windows的预构建程序到你的GitHub release页面。这是让你的程序大范围可用的最初一步,也极大的方便你为Homebrew 设置各种安装方法。
然而,需要指出的是,交叉编译并不是一件容易的事,尤其是使用GitHub Action 来做。
在本文的最后,你将会得到一个健全的部署流程,它可以自动创建releases并上传Windows、Linux和macOS各自的二进制程序。
你的releases页看起来将会是这个样子:
你甚至还能学到一些GitHub Actions的知识!
工作流触发器 首先我们需要设置触发action的条件。
通常情况下,软件发布每个版本时都会新建一个对应的标签,所以我们可以设置自动发布器 运行的条件为:当一个新的标签推送(pushed)到GitHub的时候。
我们还需要限制一下标签的内容是具体的版本号(比如1.0.0或者0.2.4之类的)时才触发,这样GitHub才不会在我们新建不相关的标签时运行我们的action。
我们可以用以下代码做到:
.github/workflows/deploy.yml name: Deploy on: push: tags: - "[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: 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 uses: dtolnay/rust-toolchain@stable with: 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
这一步很长,但并不复杂,让我们来拆解一下这一长段做了什么:
创建一个我们将要用来压缩的目录(如prj-0.2.1-x86_64-apple-darwin
)
把我们构建好的二进制程序放到这个目录里,不同操作系统会有不同的名字
压缩,Windows使用.zip
,Unix使用.tar.gz
把压缩文件的路径 放到名为$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: 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 uses: dtolnay/rust-toolchain@stable with: 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 }}