2020/12/03

GitHub Actions による VST 3 SDK のビルド

かなりはまったのでまとめておきます。

build.yml

以下は Windows のジョブだけを抜き出した `.github/workflows/build.yml` の内容です。

name: CI

on: [push]

jobs:
  build-windows:
    runs-on: windows-latest
    steps:
    - uses: actions/checkout@v2
      with:
        path: VSTPlugins
        submodules: recursive
    - name: Run script
      run: VSTPlugins/ci/ci_windows.ps1
    - name: Upload
      uses: actions/upload-artifact@v1
      with:
        name: vst_windows
        path: vst_windows

actions/checkout で自前のプラグインのリポジトリをクローン、細かいビルド作業を `ci_windows.ps1` で実行、 actions/upload-artifact でビルドしたプラグインをアップロードする、という単純な CI (continuous integration) です。 GitHub Actions では CI の終了後にアップロードするデータのことをアーティファクトと言います。

以下は実際のコードへのリンクです。

`build.yml` の macOS と Ubuntu のジョブは、ほとんどコピペです。スクリプトは `ci_windows.ps1` へのリンクをたどってディレクトリを一つ登ったところに置いています。

ビルドスクリプト

以下は `ci_windows.ps1` からアーティファクトのアップロードの前処理を省いた抜粋です。言語は PowerShell です。

git clone --recursive $VST3SDK_REPOSITORY_URL
mkdir build
mkdir target
$SRC_ROOT = (Get-Item .).FullName

cmake `
  -S vst3sdk `
  -B build `
  -G "Visual Studio 16 2019" `
  -A x64 `
  -DSMTG_MYPLUGINS_SRC_PATH="$SRC_ROOT\VSTPlugins" `
  -DSMTG_PLUGIN_TARGET_PATH="$SRC_ROOT\target" `
  -DSMTG_ADD_VST3_HOSTING_SAMPLES=FALSE `
  -DSMTG_ADD_VST3_PLUGINS_SAMPLES=FALSE `
  -DSMTG_CREATE_PLUGIN_LINK=FALSE
  
cmake --build build -j --config Release
if (!$?) { Exit $LASTEXITCODE }

VST 3 SDK 固有の cmake オプションの意味は以下の通りです。

  • SMTG_MYPLUGINS_SRC_PATH: 自分のプラグインのソースコードへのパス。
  • SMTG_PLUGIN_TARGET_PATH: プラグインのインストールパス。
  • SMTG_ADD_VST3_HOSTING_SAMPLES: サンプルホストのビルド。
  • SMTG_ADD_VST3_PLUGINS_SAMPLES: サンプルプラグインのビルド。
  • SMTG_CREATE_PLUGIN_LINK: ビルドしたプラグインのリンクをインストールパスに作る。

SMTG_ADD_VST3_HOSTING_SAMPLES 、 SMTG_ADD_VST3_PLUGINS_SAMPLES 、 SMTG_CREATE_PLUGIN_LINK は CI では不要なので FALSE に設定しています。

最後の行の `if (!$?) { Exit $LASTEXITCODE }` は cmake がビルドに失敗したときにエラーコードを出してスクリプトを終了する PowerShell 固有の処理です。 bash ではスクリプトの先頭に `set -e` と書けば似たような効果があります。これらの行が無いとビルドに失敗してもスクリプトが止まらず、問題が残ったまま CI が成功してしまいます。このワークアラウンドは以下のリンクのコメントを参考にしました。

Windows PowerShell failed still marks job successful (#3194) · Issues · GitLab.org / gitlab-runner · GitLab

アーティファクトの消去

何回もビルドしているとアーティファクトが増えてストレージの容量が圧迫されることがあります。アーティファクトは手でも消せますが、時間がかかるので GitHub の提供する REST API を使うと楽です。 GitHub の REST API の詳細は以下のマニュアルを参照してください。マニュアルの Guides のページを前から順に試せばだいたい理解できます。

GitHub REST API - GitHub Docs

まずは以下のページの手順に従ってアクセストークンを取得します。アクセストークンがあればアカウントを乗っ取れてしまうので、何があっても誰にも公開しないでください。

Creating a personal access token - GitHub Docs

以降ではアクセストークンを `GITHUB_TOKEN` 、ユーザ名を `GITHUB_USER` とします。

以下のコマンドを実行すれば curl を使ってアーティファクトの一覧を取得します。

curl \
  -H "Accept: application/vnd.github.v3+json"\
  -u "$GITHUB_USER:$GITHUB_TOKEN" \
  "$REPOSITORY_URL/actions/artifacts?per_page=100" \
  > artifacts.json

実行前に `$GITHUB_USER` と `$GITHUB_TOKEN` にそれぞれ適切な値を代入しておいてください。 `$REPOSITORY_URL` は GitHub リポジトリのトップページの URL です。 `https://github.com//` の形式になっています。 `` と `` の部分は自分のリポジトリに合わせて置き換えてください。

`$REPOSITORY_URL` のあとに続く `actions/artifacts?per_page=100` の部分が REST API です。アーティファクトに関する API は以下のリンクに載っています。

Actions - GitHub Docs

取得したアーティファクトの一覧を保存した `artifacts.json` を使って以下の Python3 のスクリプトでまとめてアーティファクトを消します。

import json
import subprocess

github_user = # ユーザ名
github_token = # アクセストークン
repository_name = # リポジトリ名

with open("artifacts.json", "r", encoding="utf-8") as fi:
    data = json.load(fi)

for artifact in data["artifacts"]:
    print(f"████ Deleting {artifact['id']}")
    subprocess.run([
        "curl",
        "-i",
        "-X",
        "DELETE",
        "-u",
        f"{github_user}:{github_token}",
        f"https://api.github.com/repos/{github_user}/{repository_name}/actions/artifacts/{artifact['id']}",
    ])

ユーザ名、アクセストークン、リポジトリ名は文字列で指定してください。

この方法では一度に 100 個のアーティファクトまでしか消せません。アーティファクトが多いときはスクリプト側で curl コマンドを呼び出すことが考えられます。ただし接続回数の制限には注意してください。

また、任意のアーティファクトだけ残すような処理も行っていません。この辺はマニュアルや `artifacts.json` を読んで個別に対応してください。

その他

今は解決しましたが、昨日までは Windows の CI スクリプトの最初の cmake が `C:\Users\RUNNER~1\AppData\Local\Temp` とだけ出力して止まっていました。この問題は GitHub Actions では VST 3 SDK のデフォルトのインストールパスである `C:/Program Files/Common Files/VST3` を作成できないことが原因だったようです。上のスクリプトでは適当なディレクトリ `target` を作って cmake のオプションから `SMTG_PLUGIN_TARGET_PATH` に設定するというワークアラウンドを使っています。

ここからは解決策が見つかるまでの道のりを書いています。まずは "github actions cmake C:\Users\RUNNER~1\AppData\Local\Temp" でグーグル検索したところ以下の issue が出てきました。

%TEMP% is broken on Windows · Issue #712 · actions/virtual-environments · GitHub

この issue に書いてあるワークアラウンドを試しても問題が解決しなかったので、 `vst3sdk/cmake` ディレクトリ内で "TEMP" という文字列を検索したところ SMTG_Platform_Windows.cmake が引っかかりました。 SMTG_Platform_Windows.cmake の中に唯一書いてある関数 "smtg_create_directory_as_admin_win" でもう一度 `vst3sdk/cmake` 内を検索したところ SMTG_AddVST3Options.cmake の以下の行が引っかかりました。

if(SMTG_WIN)
    smtg_create_directory_as_admin_win (${SMTG_PLUGIN_TARGET_PATH})
else()

ここでようやく SMTG_PLUGIN_TARGET_PATH が原因になっていそうなことが分かりました。