releasing binaries with github actions

· shnpln's blog


Today I spent way to much time trying to figure out how to set up github actions to release a binary of my Rust application on MacOS, Ubuntu and Windows all at once. GitHub actions are powerful, but sadly very under documented. After reading many blog posts, talking to many differnt LLMs and making many git tags I finally landed on a solution that works!

Here it is! Some of this may not be needed, but this file does the job.

 1name: Create Release
 2
 3on:
 4  push:
 5    tags:
 6      - "v*"
 7
 8permissions:
 9  contents: write
10
11jobs:
12  create_release:
13    name: Create Release
14    runs-on: ubuntu-latest
15    outputs:
16      release_id: ${{ steps.create_release.outputs.id }}
17    steps:
18      - name: Checkout code
19        uses: actions/checkout@v3
20
21      - name: Create Release
22        id: create_release
23        uses: softprops/action-gh-release@v1
24        with:
25          tag_name: ${{ github.ref }}
26          name: Release ${{ github.ref_name }}
27          draft: false
28          prerelease: false
29        env:
30          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31
32  build_release:
33    name: Build & Upload Artifacts
34    needs: create_release
35    strategy:
36      matrix:
37        include:
38          - os: ubuntu-latest
39            target: x86_64-unknown-linux-gnu
40            artifact_name: tc-linux
41          - os: macos-latest
42            target: x86_64-apple-darwin
43            artifact_name: tc-macos
44          - os: windows-latest
45            target: x86_64-pc-windows-msvc
46            artifact_name: tc-windows.exe
47    runs-on: ${{ matrix.os }}
48    steps:
49      - name: Checkout code
50        uses: actions/checkout@v3
51
52      - name: Set up Rust
53        uses: actions-rs/toolchain@v1
54        with:
55          toolchain: stable
56          target: ${{ matrix.target }}
57          override: true
58
59      - name: Build
60        run: cargo build --release --target ${{ matrix.target }}
61
62      - name: Rename Binary
63        run: |
64          mkdir -p artifacts
65          if [[ "${{ matrix.os }}" == 'windows-latest' ]]; then
66            mv target/${{ matrix.target }}/release/tc.exe artifacts/${{ matrix.artifact_name }}
67          else
68            mv target/${{ matrix.target }}/release/tc artifacts/${{ matrix.artifact_name }}
69          fi
70        shell: bash
71
72      - name: Upload Artifact
73        uses: actions/upload-artifact@v4
74        with:
75          name: ${{ matrix.artifact_name }}
76          path: artifacts/${{ matrix.artifact_name }}
77
78      - name: Upload to Release
79        uses: softprops/action-gh-release@v1
80        with:
81          files: artifacts/${{ matrix.artifact_name }}
82        env:
83          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

The idea is that this workflow will kick off any time you push a tag to your repository that matches 'v*' (e.g. v0.1.0, v3.2.3, etc...). The matrix strategy allows this to run multiple builds in paralell for each value.

My binary was named 'tc', if you use this for your own work make sure to update all the 'tc's you see to match your binary name.

A big issue I kept running into was Linux and Mac shared a binary name, so when the slower build finished it deleted the others binary.

From the GitHub action logs:

♻️ Deleting previously uploaded asset tc...
⬆️ Uploading tc...

Simply renaming the binaries does the trick.

Also the versions of the different tools seems to silently go out of support. Don't be surprised if you come across this post and all of the versions are no longer supported. I will attempt to keep this post up to date as I use this with the most up to date versions.

last updated: