Automated building and releasing Plugins on Github with Github Actions

Hi @qno, I just wanted to take the time to thank you for your efforts in sharing this work with all of us VCV Rack developers. It is tremendously helpful to be able to cross-compile for all platforms every time I push a change to my plugin. Finding build errors before submitting is very nice! Thank you very much.

5 Likes

I updated the workflow, now using the recently introduced RACK-SDK-latest-$platform-$arch.zip SDK version. This will now also for the Mac builds always use the most recent version of the Rack-SDK.

name: Build VCV Rack Plugin
on: [push, pull_request]

env:
  rack-sdk-version: latest
  rack-plugin-toolchain-dir: /home/build/rack-plugin-toolchain

defaults:
  run:
    shell: bash

jobs:

  modify-plugin-version:
    name: Modify plugin version
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/cache@v3
        id: plugin-version-cache
        with:
          path: plugin.json
          key: ${{ github.sha }}-${{ github.run_id }}
      - run: |
          gitrev=`git rev-parse --short HEAD`
          pluginversion=`jq -r '.version' plugin.json`
          echo "Set plugin version from $pluginversion to $pluginversion-$gitrev"
          cat <<< `jq --arg VERSION "$pluginversion-$gitrev" '.version=$VERSION' plugin.json` > plugin.json
        # only modify plugin version if no tag was created
        if: "! startsWith(github.ref, 'refs/tags/v')"

  build:
    name: ${{ matrix.platform }}
    needs: modify-plugin-version
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/qno/rack-plugin-toolchain-win-linux
      options: --user root
    strategy:
      matrix:
        platform: [win-x64, lin-x64]
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: recursive
      - uses: actions/cache@v3
        id: plugin-version-cache
        with:
          path: plugin.json
          key: ${{ github.sha }}-${{ github.run_id }}
      - name: Build plugin
        run: |
          export PLUGIN_DIR=$GITHUB_WORKSPACE
          pushd ${{ env.rack-plugin-toolchain-dir }}
          make plugin-build-${{ matrix.platform }}
      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          path: ${{ env.rack-plugin-toolchain-dir }}/plugin-build
          name: ${{ matrix.platform }}

  build-mac:
    name: mac
    needs: modify-plugin-version
    runs-on: macos-latest
    strategy:
      fail-fast: false
      matrix:
        platform: [x64, arm64]
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: recursive
      - uses: actions/cache@v3
        id: plugin-version-cache
        with:
          path: plugin.json
          key: ${{ github.sha }}-${{ github.run_id }}
      - name: Get Rack-SDK
        run: |
          pushd $HOME
          wget -O Rack-SDK.zip https://vcvrack.com/downloads/Rack-SDK-${{ env.rack-sdk-version }}-mac-${{ matrix.platform }}.zip
          unzip Rack-SDK.zip
      - name: Build plugin
        run: |
          CROSS_COMPILE_TARGET_x64=x86_64-apple-darwin
          CROSS_COMPILE_TARGET_arm64=arm64-apple-darwin
          export RACK_DIR=$HOME/Rack-SDK
          export CROSS_COMPILE=$CROSS_COMPILE_TARGET_${{ matrix.platform }}
          make dep
          make dist
      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          path: dist/*.vcvplugin
          name: mac-${{ matrix.platform }}

  publish:
    name: Publish plugin
    # only create a release if a tag was created that is called e.g. v1.2.3
    # see also https://vcvrack.com/manual/Manifest#version
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    needs:  [build, build-mac]
    steps:
      - uses: actions/checkout@v3
      - uses: FranzDiebold/github-env-vars-action@v2
      - name: Check if plugin version matches tag
        run: |
          pluginversion=`jq -r '.version' plugin.json`
          if [ "v$pluginversion" != "${{ env.CI_REF_NAME }}" ]; then
            echo "Plugin version from plugin.json 'v$pluginversion' doesn't match with tag version '${{ env.CI_REF_NAME }}'"
            exit 1
          fi
      - name: Create Release
        uses: softprops/action-gh-release@v1
        with:
          tag_name: ${{ github.ref }}
          name: Release ${{ env.CI_REF_NAME }}
          body: |
            ${{ env.CI_REPOSITORY_NAME }} VCV Rack Plugin ${{ env.CI_REF_NAME }}
          draft: false
          prerelease: false
      - uses: actions/download-artifact@v3
        with:
          path: _artifacts
      - name: Upload release assets
        uses: svenstaro/upload-release-action@v2
        with:
          repo_token: ${{ secrets.GITHUB_TOKEN }}
          file: _artifacts/**/*.vcvplugin
          tag: ${{ github.ref }}
          file_glob: true
7 Likes

Hi @qno, I notice you switched from using curl to wget. I’m curious why you changed that.

Because with curl it is not possible to rename the file when the download url has been redirected, which just works fine with wget.

1 Like

I’m trying out your script (thank you so much btw, this is so useful!) but getting an error when trying to create a release. The builds on each platform succeed but the Publish Plugin step fails with the following error:

👩‍🏭 Creating new GitHub release for tag refs/tags/v2.0.1...
⚠️ GitHub release failed with status: 403
undefined
retrying... (2 retries remaining)
👩‍🏭 Creating new GitHub release for tag refs/tags/v2.0.1...
⚠️ GitHub release failed with status: 403
undefined
retrying... (1 retries remaining)
👩‍🏭 Creating new GitHub release for tag refs/tags/v2.0.1...
⚠️ GitHub release failed with status: 403
undefined
retrying... (0 retries remaining)
❌ Too many retries. Aborting...
Error: Too many retries.

I’m using the same version number in plugin.json and the git tag. What am I doing wrong?

Here’s a link to the failed job: Build VCV Rack Plugin · epadilla/patina@c48da1f · GitHub

Thanks!

Most likely permissions. Put the following at the top of your workflow:

name: Build VCV Rack Plugin
permissions:
  contents: write
...
2 Likes

That fixed it! Thank you

1 Like

Just as a heads-up (or an appeal for help), a few days ago I started seeing this warning in GitHub actions:

Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20: actions/checkout@v3, actions/cache@v3. For more information see: GitHub Actions: Transitioning from Node 16 to Node 20 - The GitHub Blog.

Might be blatantly obvious what to do, I admit, but figured that since this is the place where the knowledge is…

mahlen

Oh I saw that too

A quick google convinced me swapping v3 to v4 would do it but I didnt try that yet

I was just doing the same thing. I’ve verified in the history notes for both cache and checkout that the v4 versions are on node 20.

I just pushed these changes to one of my repos, and I’ll let y’all know what happens.

1 Like

Thanks Paul!

Depending on your particular workflow, you’ll probably have multiple references to these actions, so make sure you update them all.

In addition to actions/checkout and actions/cache, I had to update actions/upload-artifact, actions/download-artifact.

actions/upload-artifact has breaking changes as artifacts are now immutable, but you can add overwrite: true to the with:.

I still have one last node deprecation warning for svenstaro/upload-release-action@v2 for my “nightlies”. The repo for the action includes the change (from 3 months ago) to use node 20, but apparently requires a new release to carry the change. The latest issue opened last week is about the node 16 deprecation, requesting a release.

I don’t know what to do about this one.

For now I’m tolerating the warning. Everything seems to be working with the updates to all the actions with available updates.

2 Likes

Am I missing something obvious or does the toolchain not work when submodules are used? It looks like build runs make cleandep which removes everything from folder dep.

But the next line it’s re-making the deps, isn’t it? I don’t use submodules myself, so I can’t say anything from direct experience, but I think the toolchain is what’s used to build library versions isn’t it?

Yes, but at this point all files and folders have been deleted which were previously checked out by git.

1 Like

Works fine with sub modules for me. But I have my submodules in a libs directory

@qno hey there. Thanks again for setting up the toolchain and the builds. They work really well. I did notice GitHub - qno/rack-plugin-toolchain is still on rack SDK 2.4.1. Its not a huge deal, but I was wondering if you were planning on updating that at some point?

For other developers using this script, I had to make this change to get Sapphire building for Mac. It looks like the VCV Rack SDK now ships as a combined zip file for both Mac processors, instead of a separate one for each. My change resolved a 404 error trying to download the Mac version of the SDK.

5 Likes

I ran into an error for linux and windows builds.

fatal: detected dubious ownership

I’m not sure what triggered it. I suspect it is because I include a git submodule in dep that includes another submodule. Posting this just in case anyone else runs into the same problem.

The solution (as discussed here) was to add a run step:

--- build-plugin.yml.old        2024-05-26 10:59:59.237245518 +0200
+++ build-plugin.yml    2024-05-26 11:03:44.303180249 +0200
@@ -22,6 +22,8 @@
    matrix:
      platform: [win-x64, lin-x64]
  steps:
+      - name: Set up Git safe directory
+        run: git config --global --add safe.directory '*'
    - uses: actions/checkout@v4
    - uses: actions/cache@v4
      id: plugin-version-cache
1 Like

Interesting. First time I ever heard of “safe.directory” for git.