For contributors: the packaging, verification and release process for distributing Volta.
Design/history: Build and release process.
Local build gate#
Use Rust 1.97.1 from rust-toolchain.toml (which also pins its rustfmt component for the scripts/fmt-check.sh gate), Node 24.x and wasm-pack 0.13.1. Install the two npm trees with npm ci in runtime/ and web/, then run make test-fast (native only) and make test-browser. The browser gate builds the compiler WebAssembly package because the runtime and the Worker import it during typechecking; a fresh checkout must not depend on a leftover compiler-wasm package from an earlier ./build-wasm.sh. CI and the release workflow install the same pinned wasm-pack version.
make test-core and make test-plugin run their complete workspaces with --locked, including the patch-player plugin crate. The browser-facing engine and UI are separate builds (./build-engine-wasm.sh, ./build-ui-wasm.sh); passing the fast gate alone does not refresh or qualify those artifacts. The September 6 clean-dependency repair and local evidence are recorded in Launch Work Log. Hosted and supported-machine checks remain separate.
Change-aware merge gate#
make gate-smart (scripts/gate-smart.mjs) is the everyday merge gate. It keys each workspace crate, and each runtime/ and web/ vitest file, by the inputs its tests can see (scripts/gate-inputs.json; a vitest file's imports and the repo paths its code names are found by scripts/gate-scan.mjs), and runs only those whose key differs from the merge base's and has not already passed on this machine. make gate-smart ARGS="--plan" prints the verdict for each without running anything. A reference-doc change therefore runs runtime/src/docs-sync.test.ts, which requires every reference doc to be indexed in language.md §16. Two runtime files, docs-lint.test.ts and docs-status.test.ts, are not run: the gate runs those checks itself. No wasm is built: a vitest file that names a wasm bundle is keyed by the bundle's crates, and a verdict against a stale bundle is printed but not recorded. ARGS="--full" runs every crate and every vitest file. The standalone plugin-target/ workspace is one more unit ("external" in scripts/gate-inputs.json), keyed by its directory, the root crates it builds against and the root manifests; when it runs, a cargo metadata --locked check fails fast on a stale plugin-target/Cargo.lock before its suite (the make test-plugin command) builds. make premerge runs test-plugin too.
macOS beta artifact#
From a clean checkout on macOS:
make package-studio
This builds the shipping desktop feature set (the default CLAP/AU/VST3 hosting and neural features plus Ableton Link) and emits, under dist/:
volta-studio-<version>-macos-<arch>.tar.gz;- the same
.appas a conventional.zip; and - a SHA-256 manifest covering both archives.
Ad-hoc builds normalize bundle timestamps and archive metadata. Repeating the same build from the same binary and Git tree must produce identical hashes for both archives. Developer ID timestamping and notarization intentionally add Apple-issued state, so credentialed output is not expected to match an ad-hoc archive byte-for-byte.
The bundle has a plist, Volta document type, icon, microphone purpose string, and the tracked starter song, curated song corpus, Learn curriculum, sample bank, wavetables, notebooks, and shared-library manifest. Its resource tree preserves the checkout-relative layout used by the existing resolvers. The developer-only absolute dirt-samples symlink is explicitly excluded.
Both signing paths apply app-native/macos/Volta.entitlements: disable-library-validation (third-party CLAP/AU/VST3 code runs in-process), device.audio-input and allow-jit. The last two matter only under the hardened runtime: without device.audio-input a Developer ID build silently loses take recording and live input (ad-hoc dev builds never reproduce it); without allow-jit user kernels fall back from native code to the slower register interpreter.
volta-studio resolves Contents/Resources/examples/song.volta when launched bare. A command-line file wins, and so does a double-clicked/dropped .volta document: Launch Services delivers those as Apple Events, which app-native/src/macopen.rs routes through the same validated File → Open path (winit's app delegate drops them otherwise — see the module docs for the timing constraint).
Artifact smoke#
Packaging runs an audible, device-free smoke before creating archives. To verify an archive after copying/downloading it:
make smoke-release ARTIFACT=dist/volta-studio-0.1.0-macos-arm64.tar.gz
The verifier extracts into a fresh temporary directory, verifies the plist, code signature, icon and core resources, asks the packaged binary for its version, then compiles and renders the packaged starter for two seconds. A silent or non-finite render fails.
The binary-level probes are also available directly:
volta-studio --version
volta-studio --smoke-test path/to/song.volta
Signing and notarization#
With no credentials, local packaging applies an ad-hoc signature. That is a development fallback and will still trigger normal Gatekeeper treatment when distributed to another machine.
For a Developer ID build:
VOLTA_CODESIGN_IDENTITY="Developer ID Application: …" \
VOLTA_NOTARY_PROFILE=volta-notary \
make package-studio
VOLTA_NOTARY_PROFILE names credentials previously stored with xcrun notarytool store-credentials. CI can instead provide VOLTA_NOTARY_KEY, VOLTA_NOTARY_KEY_ID, and VOLTA_NOTARY_ISSUER.
The release workflow accepts these repository secrets:
MACOS_CERTIFICATE_P12— base64 PKCS#12 Developer ID certificate;MACOS_CERTIFICATE_PASSWORD;MACOS_SIGNING_IDENTITY;MACOS_NOTARY_KEY_P8— base64 App Store Connect API private key;MACOS_NOTARY_KEY_ID; andMACOS_NOTARY_ISSUER.
A manual workflow run may produce the ad-hoc development artifact. A v* tag fails before build unless all signing and notarization secrets exist; only a successful make test-fast, signed/notarized package, and artifact smoke reach the prerelease publication step. The release job installs both Node dependency trees with npm ci, so it validates the exact tagged commit rather than relying on the separately triggered main workflow.
Remaining release gate#
Stage 4 is not complete until a credentialed tag passes on GitHub-hosted macOS, the artifact installs on clean supported Intel/Apple Silicon machines, and browser, CLI, LSP, and plugin outputs have artifact-level smokes. The current plist declares macOS 13.0 as the desktop minimum; this is a packaging declaration, not yet the completed compatibility matrix.