Skip to content
Open

2.0.0 #186

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
91e6f42
2.0.0: Rewrite CLI parsing onto CLI11 + a JSON settings pipeline (#175)
drowaudio Jun 7, 2026
c814266
v2: `Restructure CLI into validate/run-tests/strictness-help subcomma…
drowaudio Jun 8, 2026
247e42a
v2: Removed a planning doc
drowaudio Jun 8, 2026
febe6af
Renamed "Source" dir to "source" for consistency
drowaudio Jun 16, 2026
8b40536
Acceptance testing: Initial commit
drowaudio Jun 16, 2026
cb4d4ce
Acceptance testing: Added playhead support
drowaudio Jun 16, 2026
2964105
Acceptance testing: Added a missing dependency for Linux
drowaudio Jun 16, 2026
64faeda
Acceptance testing: Fail on missing reference, render off the message…
drowaudio Oct 7, 2026
e014cb3
Acceptance testing: Validate configs, fail on NaN, apply parameters i…
drowaudio Oct 7, 2026
aca9956
Version 2.0.0
drowaudio Oct 8, 2026
031b778
Skip the pluginval_cli target when the consuming project has no match…
sudara Jul 18, 2026
d841ba6
Bump JUCE to 9.0.3
drowaudio Oct 8, 2026
08ffb97
Test as-dependency against JUCE 9.0.3 and add libxi-dev to Linux CI deps
drowaudio Oct 8, 2026
05e169b
Add JUCE 9.0.3 update to the change list
drowaudio Oct 8, 2026
8504562
Update JUCE version mentions in docs
drowaudio Oct 8, 2026
09a7695
Add libxi-dev to the acceptance workflow's Linux deps
drowaudio Oct 8, 2026
6c3c7ae
Merge branch 'develop' into v2
drowaudio Oct 12, 2026
8f3b4a5
Refactored PR #144
drowaudio Dec 9, 2024
6b9855e
Fixed unused results
drowaudio Dec 9, 2024
47b1368
Fixed a linux build error by adding permissions to open
drowaudio Oct 8, 2026
afe4252
Removed numLinesToSkip as this probably won't work now it's been brok…
drowaudio Dec 16, 2024
1f1e34a
Crash handler: Updated change list and docs
drowaudio Oct 12, 2026
790ba5e
Merge pull request #190 from Tracktion/crash-handler-pr144
drowaudio Oct 12, 2026
f257fb4
Add --junit-xml and --json-results report output to validate and test…
drowaudio Oct 13, 2026
2c5c4e3
Add --plugin to pluginval test and GitHub Actions for validate and ac…
drowaudio Oct 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions .github/actions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# pluginval GitHub Actions

Two composite actions for using pluginval from another repository's workflows.
Both run on Linux, macOS and Windows runners, download the matching pluginval
release (or use a binary you give them), run everything they are given, write a
results table to the job summary and fail the step if anything failed.

On Linux the plugin is run under `xvfb-run` when there is no `DISPLAY` (install
`xvfb` on runners that don't have it).

## validate

Validates one or more plugins.

```yaml
- uses: Tracktion/pluginval/.github/actions/validate@v2
with:
plugins: |
build/MyPlugin_artefacts/Release/VST3/MyPlugin.vst3
build/MyPlugin_artefacts/Release/AU/MyPlugin.component
strictness-level: 10
args: --skip-gui-tests
```

| Input | Default | |
| --- | --- | --- |
| `plugins` | (required) | The plugins to validate, one per line. Each is a path, a glob or an AU id. A glob that matches nothing fails. |
| `strictness-level` | `5` | The strictness level (1-10). |
| `args` | | Extra options for `pluginval validate`, split on whitespace. |
| `pluginval-version` | `v2.0.0-beta1` | The pluginval release to download: a tag, or `latest`. |
| `pluginval-path` | | An existing pluginval binary to use instead of downloading one. |

Every `pluginval validate` option can also be set as an environment variable on
the step (e.g. `env: { TIMEOUT_MS: 60000 }`), which is the way to pass values
containing spaces.

## acceptance

Runs the [acceptance tests](../../docs/Acceptance%20testing.md) for every config
(`*.json`) found recursively under a directory, optionally against one built
plugin.

```yaml
- uses: Tracktion/pluginval/.github/actions/acceptance@v2
with:
config-dir: tests/acceptance
plugin: build/MyPlugin_artefacts/Release/VST3/MyPlugin.vst3
```

| Input | Default | |
| --- | --- | --- |
| `config-dir` | (required) | Directory searched recursively for configs. |
| `plugin` | | Plugin path or AU id to test every config against, instead of the `plugin` in each config. |
| `record-missing` | `false` | Record a missing reference and pass, instead of failing. |
| `timeout-ms` | | Fail a test if it takes longer than this (pluginval's default if empty). |
| `pluginval-version` | `v2.0.0-beta1` | The pluginval release to download: a tag, or `latest`. |
| `pluginval-path` | | An existing pluginval binary to use instead of downloading one. |

Notes:

- Every `*.json` under `config-dir` is treated as a config, apart from the
`<reference>.wav.json` manifests. Keep other JSON files (and plugin bundles,
which contain their own) out of it.
- A config holding an array of tests is reported as a failure: use one config
per file.
- Relative `plugin` paths resolve against the working directory; the input,
state and reference paths in a config resolve against the config's directory.

## Outputs

Both actions set:

| Output | |
| --- | --- |
| `passed` / `failed` | How many plugins / configs passed and failed. |
| `report-dir` | A directory holding a JUnit XML report for each one. For `acceptance` it also has a `diffs` directory with the `*-diff.wav` files found under `config-dir`. |

To keep the reports and diffs from a failed run:

```yaml
- uses: Tracktion/pluginval/.github/actions/acceptance@v2
id: acceptance
with:
config-dir: tests/acceptance
plugin: build/MyPlugin_artefacts/Release/VST3/MyPlugin.vst3

- uses: actions/upload-artifact@v4
if: always()
with:
name: pluginval-acceptance-${{ runner.os }}
path: ${{ steps.acceptance.outputs.report-dir }}
```
93 changes: 93 additions & 0 deletions .github/actions/acceptance/acceptance.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
# Runs "pluginval test" on every config found (recursively) under
# INPUT_CONFIG_DIR, runs them all, and fails if any failed. See action.yml for
# the inputs.

set -u
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../common/common.sh"

resolve_pluginval

config_dir="${INPUT_CONFIG_DIR:-}"

if [ ! -d "$config_dir" ]; then
echo "::error::config-dir is not a directory: $config_dir"
exit 1
fi

report_dir="${INPUT_REPORT_DIR:-$RUNNER_TEMP/pluginval-reports/acceptance}"
mkdir -p "$report_dir"

options=()
[ -n "${INPUT_PLUGIN:-}" ] && options+=(--plugin "$INPUT_PLUGIN")
[ "${INPUT_RECORD_MISSING:-false}" = "true" ] && options+=(--record-missing)
[ -n "${INPUT_TIMEOUT_MS:-}" ] && options+=(--timeout-ms "$INPUT_TIMEOUT_MS")

passed=0
failed=0
index=0
rows=""

# Every *.json is a config, except the <reference>.wav.json manifests that
# pluginval writes next to each reference.
IFS=$'\n'
configs=( $(find "$config_dir" -type f -name '*.json' ! -name '*.wav.json' | sort) )
unset IFS

for config in ${configs[@]+"${configs[@]}"}; do
index=$((index + 1))
name="${config#"$config_dir"/}"
report="$report_dir/$index-$(sanitise "$(basename "$config" .json)").xml"

echo "::group::pluginval test: $name"

# pluginval only runs the first entry of an array config, so reject them
# rather than silently skipping tests.
if [ "$(tr -d '[:space:]' < "$config" | head -c 1)" = "[" ]; then
echo "Array configs aren't supported: use one config per file."
code=1
else
run_pluginval test ${options[@]+"${options[@]}"} --junit-xml "$report" "$config"
code=$?
fi

echo "::endgroup::"

if [ $code -eq 0 ]; then
passed=$((passed + 1))
rows="$rows| \`$name\` | :white_check_mark: passed |"$'\n'
else
failed=$((failed + 1))
rows="$rows| \`$name\` | :x: failed |"$'\n'
echo "::error::pluginval acceptance test failed: $name"
fi
done

if [ $index -eq 0 ]; then
echo "::error::No configs (*.json) found under $config_dir"
exit 1
fi

# Gather the diff WAVs written next to failing references, so they can be
# uploaded along with the reports.
find "$config_dir" -type f -name '*-diff.wav' | while IFS= read -r diff; do
mkdir -p "$report_dir/diffs"
cp "$diff" "$report_dir/diffs/$(sanitise "${diff#"$config_dir"/}")"
done

{
echo "### pluginval acceptance tests"
echo
echo "| Config | Result |"
echo "| --- | --- |"
printf '%s' "$rows"
} >> "$GITHUB_STEP_SUMMARY"

{
echo "passed=$passed"
echo "failed=$failed"
echo "report-dir=$report_dir"
} >> "$GITHUB_OUTPUT"

echo "pluginval acceptance tests: $passed passed, $failed failed"
[ $failed -eq 0 ]
51 changes: 51 additions & 0 deletions .github/actions/acceptance/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: pluginval acceptance tests
description: Runs pluginval's golden-file acceptance tests for every config in a directory.

inputs:
config-dir:
description: >
Directory searched recursively for test configs (*.json). The
<reference>.wav.json manifests are skipped.
required: true
plugin:
description: >
Plugin path or AU id to test every config against, instead of the
"plugin" in each config.
default: ''
record-missing:
description: Record a missing reference and pass, instead of failing.
default: 'false'
timeout-ms:
description: Fail a test if it takes longer than this (pluginval's default if empty).
default: ''
pluginval-version:
description: The pluginval release to download - a tag (e.g. v2.0.0-beta1) or "latest".
default: v2.0.0-beta1
pluginval-path:
description: Path to an existing pluginval binary to use instead of downloading one.
default: ''

outputs:
passed:
description: The number of configs that passed.
value: ${{ steps.run.outputs.passed }}
failed:
description: The number of configs that failed.
value: ${{ steps.run.outputs.failed }}
report-dir:
description: Directory holding a JUnit XML report per config, plus diffs/ with any diff WAVs.
value: ${{ steps.run.outputs.report-dir }}

runs:
using: composite
steps:
- id: run
shell: bash
env:
INPUT_CONFIG_DIR: ${{ inputs.config-dir }}
INPUT_PLUGIN: ${{ inputs.plugin }}
INPUT_RECORD_MISSING: ${{ inputs.record-missing }}
INPUT_TIMEOUT_MS: ${{ inputs.timeout-ms }}
INPUT_PLUGINVAL_VERSION: ${{ inputs.pluginval-version }}
INPUT_PLUGINVAL_PATH: ${{ inputs.pluginval-path }}
run: bash "$GITHUB_ACTION_PATH/acceptance.sh"
74 changes: 74 additions & 0 deletions .github/actions/common/common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# Shared helpers for the pluginval composite actions (sourced, not run).
# Kept compatible with bash 3.2, which is what macOS runners ship.

# Defaults so the scripts can also be run outside of GitHub Actions.
if [ -z "${RUNNER_OS:-}" ]; then
if [ "$(uname -s)" = "Darwin" ]; then RUNNER_OS=macOS
elif [ "$(uname -s)" = "Linux" ]; then RUNNER_OS=Linux
else RUNNER_OS=Windows
fi
fi

: "${RUNNER_TEMP:=$(mktemp -d)}"
: "${GITHUB_STEP_SUMMARY:=/dev/null}"
: "${GITHUB_OUTPUT:=/dev/null}"

# Sets PLUGINVAL_BIN to the pluginval binary: INPUT_PLUGINVAL_PATH if given,
# otherwise the INPUT_PLUGINVAL_VERSION release ("latest" or a tag like v2.0.0-beta1)
# downloaded for this runner's OS.
resolve_pluginval()
{
if [ -n "${INPUT_PLUGINVAL_PATH:-}" ]; then
PLUGINVAL_BIN="$INPUT_PLUGINVAL_PATH"
else
local version="${INPUT_PLUGINVAL_VERSION:-latest}"
local dir="$RUNNER_TEMP/pluginval-$version"
local url="https://github.com/Tracktion/pluginval/releases/download/$version/pluginval_$RUNNER_OS.zip"

if [ "$version" = "latest" ]; then
url="https://github.com/Tracktion/pluginval/releases/latest/download/pluginval_$RUNNER_OS.zip"
fi

case "$RUNNER_OS" in
macOS) PLUGINVAL_BIN="$dir/pluginval.app/Contents/MacOS/pluginval" ;;
Windows) PLUGINVAL_BIN="$dir/pluginval.exe" ;;
*) PLUGINVAL_BIN="$dir/pluginval" ;;
esac

if [ ! -f "$PLUGINVAL_BIN" ]; then
echo "Downloading $url"
mkdir -p "$dir"

if ! curl -fsSL --retry 3 "$url" -o "$dir/pluginval.zip"; then
echo "::error::Couldn't download pluginval $version for $RUNNER_OS ($url)"
exit 1
fi

unzip -q -o "$dir/pluginval.zip" -d "$dir"
chmod +x "$PLUGINVAL_BIN"
fi
fi

if [ ! -f "$PLUGINVAL_BIN" ]; then
echo "::error::pluginval binary not found: $PLUGINVAL_BIN"
exit 1
fi
}

# Runs pluginval with the given arguments. On Linux without a display it is
# wrapped in xvfb-run, as plugins generally need an X server to load.
run_pluginval()
{
if [ "$RUNNER_OS" = "Linux" ] && [ -z "${DISPLAY:-}" ] && command -v xvfb-run > /dev/null; then
xvfb-run --auto-servernum "$PLUGINVAL_BIN" "$@"
else
"$PLUGINVAL_BIN" "$@"
fi
}

# Prints a file-name-safe version of the argument.
sanitise()
{
printf '%s' "$1" | tr -c 'A-Za-z0-9._-' '_'
}
48 changes: 48 additions & 0 deletions .github/actions/validate/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: pluginval validate
description: Validates one or more audio plugins with pluginval.

inputs:
plugins:
description: >
The plugins to validate, one per line. Each is a path, a glob (e.g.
build/*_artefacts/Release/VST3/*.vst3) or an AU id.
required: true
strictness-level:
description: The strictness level (1-10).
default: '5'
args:
description: >
Extra options for "pluginval validate", split on whitespace (e.g.
"--skip-gui-tests --timeout-ms 60000"). Any option can also be set as an
environment variable on the step.
default: ''
pluginval-version:
description: The pluginval release to download - a tag (e.g. v2.0.0-beta1) or "latest".
default: v2.0.0-beta1
pluginval-path:
description: Path to an existing pluginval binary to use instead of downloading one.
default: ''

outputs:
passed:
description: The number of plugins that passed.
value: ${{ steps.run.outputs.passed }}
failed:
description: The number of plugins that failed.
value: ${{ steps.run.outputs.failed }}
report-dir:
description: Directory holding a JUnit XML report per plugin.
value: ${{ steps.run.outputs.report-dir }}

runs:
using: composite
steps:
- id: run
shell: bash
env:
INPUT_PLUGINS: ${{ inputs.plugins }}
INPUT_STRICTNESS_LEVEL: ${{ inputs.strictness-level }}
INPUT_ARGS: ${{ inputs.args }}
INPUT_PLUGINVAL_VERSION: ${{ inputs.pluginval-version }}
INPUT_PLUGINVAL_PATH: ${{ inputs.pluginval-path }}
run: bash "$GITHUB_ACTION_PATH/validate.sh"
Loading
Loading