Package and release audio plug-ins

Choose platform runners, validate VST3, Audio Unit and CLAP builds, prepare installers and downloads, and understand hardware delivery limits.

A musician needs a plug-in that installs, opens in their DAW, restores their session and survives an update. Reaching that point takes more than compiling the DSP: build the complete native bundle, validate it on its target platform, package its interface and resources, and test the delivered archive or installer.

Use the same Maru CLI locally and in CI. Pin the toolchain version in your project, install the components for your selected targets, and keep the build, validation and packaging reports alongside the resulting downloads.

Build on the platform you intend to support

VST3 and CLAP are cross-platform APIs. Their compiled binaries are specific to an operating system and architecture. Steinberg documents Windows VST3 binaries as DLLs and macOS VST3 binaries as Mach-O bundles. Sharing a TypeScript project does not make the resulting native file interchangeable between them. Steinberg's SDK requirements list the platform compiler families.

Our recommendation is native CI runners:

Intended outputBuild and validate onImportant boundary
macOS VST3, AU, CLAP and standalonemacOS, with the selected Apple SDK and architectureARM64 and Intel are separate outputs unless a universal binary is deliberately produced and verified.
Windows VST3 and CLAPWindows with the supported Windows SDK and compilerTest the editor, resources and installed bundles in the Windows hosts you support.
Linux VST3 and CLAPThe supported Linux distribution/toolchainQualify the native host, UI adapter and runtime dependencies; a Linux build is not established by macOS results.
AAXA supported runner with the authorized Avid SDK and signing workflowDistribution requires Avid signing and validation in Pro Tools.
HardwareA runner with the exact target compiler, followed by tests on the boardA desktop plug-in binary is not firmware.

Trigger the complete CI matrix from either macOS or Windows. Each runner builds and tests its own platform's outputs. Cross-compilation still requires matching SDKs, dependencies and target-host tests, so use native runners for releases. Audio Unit and Apple's signing/notarization tools use the macOS runner.

Keep the native bundle intact

On macOS, .vst3, .component, .clap and .app outputs are directory bundles. The compiled executable is under Contents/MacOS/; the embedded interface may contain HTML, CSS and JavaScript under Contents/Resources/. Both belong in the product. Shipping only the UI does not ship the plug-in, and removing it breaks the editor.

Keep the module's parameters, state, presets and resources with its canonical source. Define a version and stable identity deliberately before release. Use that identity for builds and retain it when testing updates and session recall. Avoid changing public parameter meaning without a planned state migration for already-released sessions.

From your project directory, build and verify the selected formats:

sh
maru --version
maru doctor
maru module doctor
maru plugin build src/index.ts --release --format vst3 --out build/vst3
maru plugin build src/index.ts --release --format au --out build/au
maru plugin build src/index.ts --release --format clap --out build/clap
maru plugin build src/index.ts --release --format standalone --out build/standalone
maru module verify src/index.ts --build-root build

--release selects the release identity and build instrumentation. Apply Developer ID signing, notarization and installer validation in the packaging steps below.

maru plugin build --format all builds the project's configured native formats and records unavailable targets. Set build.nativeFormats in .maru/config.json to choose them. The default set is VST3, CLAP, AU and standalone; add AAX when your project supplies its SDK and delivery requirements. This command does not also generate every installer, SDK package or hardware image. Explicitly choose the output set your project promises and fail its release when any required output is missing.

Validate what the customer will receive

Inspect the generated build-report.json and the combined module verification report. Compilation, format validation, signing and installation are separate results. An unavailable or skipped check is not passing evidence for that feature.

Run the format-owned checks, then exercise audio/MIDI, sample-rate and block-size changes, both parameter endpoints, presets, save/reopen, bypass and two independent instances. VST3 uses Steinberg's validator. CLAP has an upstream CLAP validator. AU uses Apple's auval and an AudioComponent host. Format checks supplement actual DAW tests; they do not prove every host or editor behaves correctly.

For a first manual session, follow Testing your VST. Retest the extracted archive or installed payload, including its editor and resources. Keep the output hashes, tool versions and reports for that exact file.

Package downloads and installers

Give customers a small, clearly named download. A module project puts its final outputs directly in build/ by default:

text
build/
  README.md
  macos-arm64/
    plugins/                 complete native bundles
    standalone/              application bundle
    installers/              actual installer, when built
    archives/                per-format ZIPs
    install/                 guide, manifest and matching uninstaller
  web/
    app/                     standalone website with playback host
    module/                  reusable browser module for another host
  documentation/             manual, image and release notes
  SHA256SUMS

Set build.output.directory in .maru/config.json to choose another output root. Enable build.output.productDirectory, build.output.versionDirectory, or both to group handoffs, for example build/meteora/1.0.0/. Both are off by default; the module folder, bundle metadata and descriptive archive names already identify the product and version. Paths are relative to the project root. Internal compiler and staging files stay under .maru/cache/.

Rebuilds safely replace the selected outputs after validation. They do not clean the whole directory first. With version grouping, a new version gets a new folder; source edits at the same unpublished version update its existing folder. Keep published releases immutable in your release storage. A failed attempt preserves the last successful output, and the README identifies the failed attempt separately. Remove old outputs through an explicit cleanup step.

Only list platform/architecture folders that were actually built. Keep compiler output, local caches, credentials and debug logs outside customer downloads. Version archive filenames and include the format, OS and architecture. Preserve bundle metadata, executable modes and links when archiving; use a platform-aware archiver, then extract and compare the resulting payload.

Installers should let musicians select the formats their DAW needs. Derive installer destinations, manual-install instructions and uninstall paths from one install plan. Include the matching uninstaller and test upgrades and removal without deleting presets or unrelated products. An installer XML/script is a plan; call it an installer only after creating and inspecting its .pkg or Windows installer executable. Build an unsigned local candidate when supported, then test the signed distribution package separately.

For macOS downloads outside the App Store, use the appropriate Developer ID signatures, notarize the distribution container and staple where supported. Verify the final signatures and acceptance, including plug-ins loaded inside a DAW. An ad-hoc signature can provide a consistent local seal without providing Developer ID trust or notarization. See Apple's Developer ID guidance and notarization workflow. Use the Windows signing process for Windows delivery; an Apple signature does not sign a Windows binary.

Include the product license, third-party notices, minimum requirements, exact formats/architectures, install/uninstall steps, version, support contact and release notes. Check your distributor's current submission requirements before uploading; a ZIP that works locally is not evidence of marketplace acceptance.

Run GitHub Actions as a native matrix

Use one job per supported OS/architecture, with strategy.fail-fast: false so one failed row does not cancel the other evidence. GitHub's matrix documentation explains runner selection and failure handling. Use explicit runner images and record the actual architecture; do not infer it from a moving latest label.

Each job needs the pinned project dependency lockfile, Node.js, the exact Maru CLI and component versions, its native compiler/SDK, the required build tools, and compatible validators. Browser qualification also needs its supported Chromium runtime. A headless validator does not replace interactive editor, audio-device, clean-install or hardware tests.

Provision these tools explicitly for the selected lane:

  • macOS: Xcode/Command Line Tools and the selected macOS SDK, CMake, Maru's pinned native compiler components, the VST3 SDK/validator, CLAP SDK/validator, and Chromium for browser qualification. auval, codesign, pkgbuild and productbuild supply the Apple format and packaging tools; distribution also uses notarytool and the appropriate signing credentials.
  • Windows: the supported MSVC/Windows SDK environment, CMake, the pinned native compiler components, Chromium and Windows builds of the applicable validators.
  • Linux: the chosen C/C++ compiler, CMake, pinned native components, Chromium, applicable validators and the declared UI/runtime dependencies. Record the distribution and ABI used for the actual host checks.
  • Hardware: the exact board SDK/cross-compiler, adapter and emulator or device runner selected by its profile. A CPU architecture match is insufficient.

The public CLI supplies headless build and validation commands, runtime harnesses, fixture loaders and structured reports. Install and verify pinned compiler, browser and validator components explicitly. System and proprietary SDKs and signing credentials are runner inputs.

Run the pipeline in this order:

  1. Install locked project dependencies and check CLI/component readiness.
  2. Resolve the requested targets and report missing SDKs/runners early.
  3. Build and qualify the exact module on each supported runner.
  4. Package and verify archives and installers on that same platform.
  5. Retain reports even on failure; expose successful local candidates separately.
  6. In an explicit release job, apply signing/notarization and publish only the complete required output set.

Call the same public commands in CI as in local builds. Keep platform provisioning and signing in explicit job steps so the workflow is reproducible on a clean runner.

Run untrusted pull-request checks without release credentials. Use a separate release job with narrowly scoped permissions and protected signing credentials. Cache compilers/components by exact CLI, component, OS, architecture and relevant input identity. Cached build bytes are not fresh validation results.

Retrieve and test the outputs

Upload already-created ZIPs, installers, checksums and reports as Actions artifacts. Avoid uploading a raw macOS bundle as loose files: GitHub's artifact action documents that executable permissions can be lost. A prebuilt archive preserves its internal bundle layout. See the artifact action's permission guidance.

For example, after your project's build/package steps have succeeded, a GitHub.com workflow can use a step like this. Adjust the paths to the project's real layout and pin the reviewed action commit in a release workflow:

yaml
- name: Retain native download candidates
  uses: actions/upload-artifact@v7
  with:
    name: plugins-${{ runner.os }}-${{ runner.arch }}-${{ github.sha }}
    path: |
      build/**/archives/*.zip
      build/**/installers/*
      build/**/SHA256SUMS
    if-no-files-found: error

Add a separate report upload using if: always() with the actual report paths; it must not turn a failed build into a successful release. The upload step alone does not verify that every requested installer or format exists; the preceding build/qualification/package gates must enforce completeness.

Download a candidate from the workflow run's Artifacts section, verify its checksum, extract it with a suitable archiver and test that exact payload on its intended OS/architecture. Keep failed-run reports for diagnosis. Actions artifacts have retention limits; stable customer downloads should use an explicit GitHub Release or your delivery service, with versioned files and permanent release notes.

Hardware is a separate delivery target

A hardware project references the same module through a board profile and host adapter. It needs that board's compiler, runtime, resources, control mapping, flash image/package and recovery procedure. Build and emulator results should be kept separately from physical-device qualification.

Choose a board profile from Hardware platforms and profiles, then follow From module to hardware for the project workflow. Use CI to build and emulate the selected target; measured latency, xruns, memory fit, codec/MIDI/control behavior and recovery need the actual device. Flashing or installation is an explicit step. Provide the exact device/profile/version, checksums and recovery instructions when handing an image to a tester; do not offer a desktop VST3 or a generic ARM archive as firmware.

Continue with Publishing for the product release journey and Native plugins for command discovery.