使用 GitHub Actions 部署跨平台 Rust 二进制文件
如果您准备发布你的第一个版本 Rust 项目,您可能希望将一些包含预构建二进制文件(适用于 macOS、Linux 和 Windows)的资产上传到您的 GitHub 发布页面。这是使您的项目广泛可用的最早步骤之一,并且在设置其他安装方法(如Homebrew)时将有很大帮助。但是,弄清楚交叉编译并不容易,尤其是在通过 GitHub Actions 进行交叉编译时。
在这篇文章结束时,你将拥有一个完全有效的部署管道,它会自动创建版本并将二进制文件上传到适用于 Windows、Linux 和 macOS 的版本。你将有一个如下所示的发布页面:
在此过程中,您甚至可能会学到一些有关 GitHub Actions 的信息!
workflow 流触发器
首先,我们需要运行我们的 Actions 的条件。我们希望我们的发布器在将新 git tag 推送到 GitHub 时自动运行。不仅如此,我们还希望它是一个版本标记(如 1.0.0 或 0.2.4),因此 GitHub 不会在我们推送版本无关的 git tag 时运行我们的操作。
这可以通过以下代码完成:
# .github/workflows/deploy
# Just setting the name of our action
name: Deploy
on:
push:
tags:
# Regex for a version number such as 0.2.1
- "[0-9]+.[0-9]+.[0-9]+"
好了,说完了,让我们来看看我们的 Actions 实际上会做什么!
构建 Actions
若要跨平台编译二进制文件,我们需要创建一个新 workflow。我们称之为 build-and-upload
,因为此 workflow 还将负责构建二进制文件并将其上传到新版本。workflow 是一系列的步骤,而步骤就是我们的 Actions 所做的任何事情!
定义元数据
让我们开始编写我们的 workflow。首先,我们需要在进入步骤之前定义元数据。
# .github/workflows/deploy.yml
# ...
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
- build: windows-gnu
os: windows-latest
target: x86_64-pc-windows-gnu
您可能会注意到 matrix
矩阵是一个重要的部分,因为这意味着我们的 workflow 将针对矩阵中包含的每个项目运行。您可以看到矩阵在 runs-on 键中使用,这意味着我们的 workflow 每次都会在不同的操作系统上运行,其中操作系统由每个矩阵项的 os 键定义。
安装依赖项
好的,完成后,我们需要开始添加步骤。前两个很简单:只需克隆我们的存储库并安装 Rust!
# .github/workflows/deploy.yml
# ...
build-and-upload:
# ...
steps:
- name: Clone repository
uses: actions/checkout@v3
- 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 }}
这些步骤使用 uses 键。 uses 告诉我们的 Actions 我们正在调用一个外部动作,这是预制的。您甚至可以在 GitHub 上看到我们正在调用的操作! actions/checkout
是一个常见的 Action - 它克隆了我们的存储库。操作的参数使用 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
,而不是 uses 键。这基本上告诉 GitHub Actions 我们将运行一个 shell 脚本。之后,我们使用 run 键来指定我们实际要执行的内容。
脚本本身将标签中的版本放入环境变量 $VERSION 。使用环境变量是获取相互通信步骤的一个非常常见的习惯用法,因为我们现在可以访问我们的版本标记名称(如“0.1.0”),对于此 workflow的其余部分,使用 ${{ env.VERSION }}
。
构建二进制文件
好的,现在我们可以开始构建二进制文件了。为此,我们将使用 cross,这是一个 Rust 工具,它使用 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/ 我们快完成了!现在我们已经构建了二进制文件,我们需要将它们压缩成 .tar.gz 文件(或 .zip ),以便更容易从我们的资产页面下载它们。让我们通过此步骤执行此操作: 这一步很长,但并不复杂。让我们来看看它是如何工作的: 您可以在此处看到我们的 $VERSION 环境变量正在发挥作用! 压缩二进制文件后,我们就可以上传它们了!这是我们 workflow 的最后一步! 与其他一些步骤相比,这个步骤很容易理解。我们要做的就是使用预制的 softprops/action-gh-release 上传我们的压缩文件(使用我们在上一步中创建的 $ASSET 环境变量)。 我们完成了!每当我们使用 2.1.0 这样的标签推送到 GitHub 时,我们都会创建一个版本,构建我们的跨平台二进制文件,然后上传它们! 我希望这篇文章内容丰富且有趣,祝您部署未来的项目好运!如果这篇文章有任何问题,请在 GitHub 上提交问题。 这是最后的操作,只是为了更好地衡量:压缩二进制文件
# .github/workflows/deploy.yml
- 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
上传二进制文件
# .github/workflows/deploy.yml
- name: Upload the binaries
uses: softprops/action-gh-release@v1
with:
files: |
${{ env.ASSET }}
总结 + 最终代码
# .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 }}