Packaging and Platform Integration
This document describes the supported distribution and file-association behavior for Perastage.
This guide owns packaging procedures and platform distribution details. Repository and build-module ownership remain defined by Repository Layout and Architecture.
Packaging source of truth
Perastage currently has two different build contexts:
- Local developer builds use the CMake presets documented in Build and dependency guide.
- GitHub Actions release builds use dedicated workflow environments and may use their own dependency cache paths.
Do not assume that local dependency paths, such as a developer-selected VCPKG_ROOT, also apply to CI workflows. CI packaging workflows define their own paths inside the GitHub Actions workspace.
Experimental CLI payload (starting with 1.7.0)
The common install owner, cmake/PerastageInstall.cmake, installs the independent
console executable and makes perastage_stage build both the GUI and CLI from a
clean build. The GUI package remains the primary release product; there is no
separate CLI release artifact and the Rust MCP adapter is not packaged.
| Package | CLI location |
|---|---|
| Windows Inno Setup | perastage-cli.exe beside Perastage.exe, normally C:\Program Files\Perastage\perastage-cli.exe |
| macOS DMG (both supported variants) | Perastage.app/Contents/MacOS/perastage-cli, inside the single app bundle |
| Linux AppImage | usr/bin/perastage-cli in the AppDir; invoke the AppImage with --cli <CLI arguments...> |
| Arch Linux | /opt/perastage/perastage-cli, exposed by /usr/bin/perastage-cli symlink |
Windows and macOS do not add the CLI to PATH or create CLI shortcuts. Inno Setup
consumes the existing stage recursively. Both macOS workflows bundle dylibs for
both executables before signing and audit non-system dependency resolution in
the stage and mounted DMG. Linux deployment collects runtime dependencies for
both binaries; the exact first argument --cli dispatches to the CLI, preserving
all remaining argument boundaries and the caller's working directory. Other
AppImage launches retain the existing GUI behavior.
Package lanes require CLI presence and run --version and --help against the
staged/final payload (including an installed Windows payload and extracted Arch
package). See Developer CLI for grammar and safety contracts.
Windows Packaging
The official Windows installer workflow uses Inno Setup:
Microsoft Store packaging is an additional MSIX target consuming the same
validated release stage. Official MINOR releases explicitly enable build_msix: true
and build both the normal Inno Setup installer and the unsigned Store submission
MSIX from the same release source. Dry runs perform preflight validation only
and build neither package. Other Windows builds can opt in to MSIX packaging.
MSIX preserves full-trust Win32 behavior, registers file types and a CLI execution
alias, and keeps user data outside the package. See the
Store packaging maintainer guide for
identity setup, builds, virtualization audit, local testing, WACK and submission.
Its unsigned Store-only CI artifact is excluded from public GitHub releases;
Inno Setup remains the normal Windows direct download. Partner Center submission
remains manual.
Windows GUI and CLI builds embed native application manifests through the shared CMake Windows helper. The GUI declares system DPI awareness; the console CLI leaves rendering to its terminal host. MSIX keeps one Application entry and targets the CLI from its execution-alias extension. Store packaging validates the embedded manifests in the same release stage consumed by Inno Setup. The maintainer guide records STORE-110 WACK findings and the required runtime, DPI and certification retests; schema validation alone does not certify the app.
- Workflow:
.github/workflows/windows-installer.yml - Script:
packaging/windows/Perastage.iss - Build Release and run
perastage_stagebefore compiling the installer. - Optional associations can include
.pstgand.mvr. - Installer binaries target
DefaultDirName={autopf}\Perastage(standard Program Files install).
Windows CI packaging flow
The Windows installer workflow is the authoritative CI packaging flow. It performs these high-level steps:
- Check out the requested source ref.
- Bootstrap a CI-local vcpkg checkout inside the GitHub Actions workspace.
- Install the required Windows dependencies into the CI vcpkg cache.
- Configure and build Perastage in Release mode.
- Run the
perastage_stagetarget. - Collect debug symbols.
- Build the Inno Setup installer.
- Upload the staged application, installer, and symbols as workflow artifacts.
This CI flow intentionally uses workspace-local vcpkg paths and cache directories. That is separate from the recommended local Windows development setup, which uses the CMake presets described in Build and dependency guide.
The workflow intentionally configures CMake directly; it does not need to call
the root setup launcher or its implementation under scripts/windows/.
Local Windows installer flow
For local packaging work, first make sure the normal Windows Release build works from the repository root.
Recommended local staging flow:
.\setup_windows.ps1 -Configuration Release -CleanBuild -SkipBuild
cmake --preset win-x64-release-ninja
cmake --build --preset win-release-build-ninja
cmake --build --preset win-release-stage-ninja
Then compile the Inno Setup script:
$version = (cmake -P packaging/windows/get_project_version.cmake |
Select-String -Pattern '-- ([0-9]+\.[0-9]+\.[0-9]+)$').Matches[0].Groups[1].Value
iscc /DMyAppVersion=$version packaging/windows/Perastage.iss
The generated installer is written to:
out/installer/Perastage_<version>_Setup.exe
Writable data model for support
Windows packaging follows a split model:
- Installed tree (
{autopf}\Perastage): binaries and bundled base assets. - User-data tree (
%APPDATA%\Perastage\library): editable fixtures, trusses, scene objects, dictionaries, and related user library content.
Perastage bootstrap/migration behavior on startup is non-destructive:
- Seed content from the installed library is used as source.
- Missing files are copied into the user library.
- Existing files in user-data are kept (no overwrite).
Operationally, this means support can tell users:
- "You do not need admin rights to edit your library."
- "Use Tools -> Open user library folder to jump to the editable location."
Version source of truth
Perastage version is defined in one place:
VERSIONat the repository root.
When you want a new global version for generated artifacts, update that file only. The root CMake configuration reads it into project(Perastage VERSION ...), and the helper script packaging/windows/get_project_version.cmake passes it to Inno Setup (/DMyAppVersion=...).
Enabling .mvr association in the installer
The Inno Setup script already includes an optional task named assoc_mvr.
- In interactive installer mode, enable the checkbox:
- Associate .mvr files with Perastage (optional import workflow)
- In silent/automated installs, include task selection:
/TASKS="assoc_pstg,assoc_mvr"(or onlyassoc_mvrif preferred)
When selected, the installer registers:
- ProgID:
Perastage.MVR - Open command:
"Perastage.exe" "%1" - Extension mapping:
.mvr -> Perastage.MVR
The uninstall behavior is conservative and removes Perastage-owned registration values without aggressively deleting global extension ownership.
Association notes
- Installer writes association entries under
Software\Classes. - Uninstall behavior is conservative and avoids aggressive global extension ownership cleanup.
- Legacy CPack/NSIS wiring exists for compatibility but is not the primary path.
Linux Packaging
The official generic Linux release asset remains the AppImage produced by .github/workflows/linux-installer.yml. This is the recommended download for general Linux distribution because it keeps the existing staged application layout and bundles the runtime pieces expected by the AppImage flow.
AppImage compatibility contract
The Perastage x86_64 AppImage compatibility floor is Ubuntu 22.04, with GCC 11 selected as its compiler. The fixed runtime contract is:
- GLIBC maximum:
2.35 - GLIBCXX maximum:
3.4.30 - CXXABI maximum:
1.3.13
Ubuntu 22.04's supported libstdc++ runtime belongs to the GCC 12 ABI series,
even though Perastage is deliberately compiled with GCC 11. The compiler
selection and runtime libstdc++ ceiling are therefore related but distinct.
The workflow host may be newer (currently
ubuntu-26.04), but compilation, dependency builds, staging, tooling, and
AppImage creation all run inside an ubuntu:22.04 container. That container,
not the host runner image, defines the binary compatibility boundary. The
finished package is audited across every bundled ELF object for maximum
GLIBC_2.35, GLIBCXX_3.4.30, and CXXABI_1.3.13 requirements, and is then
started under X11 in the same compatibility environment.
Raising this floor must be an explicit maintainer decision; it must never occur
implicitly through an ubuntu-latest runner update. In particular, every
vcpkg baseline change must continue to pass the AppImage compatibility build.
Its Jammy/GCC 11 compiled caches are isolated from normal current-Linux caches.
The lane resolves its project-build CMake through the pinned vcpkg toolchain so
it can consume current package configurations; this build tool does not alter
the Jammy/GCC 11 runtime ABI floor.
The AppImage packaging tools use versioned release URLs and verified SHA-256
checksums. appimagetool is pinned to stable release 1.9.1; do not replace
its URL with the mutable continuous release, whose assets change upstream.
The Arch package is intentionally separate and follows Arch Linux's rolling,
current library environment rather than this portable compatibility contract.
Perastage also generates an experimental Arch Linux pacman package from .github/workflows/arch-package.yml:
- Artifact name:
Perastage-<version>-arch-x86_64.pkg.tar.zst - Packaging recipe:
packaging/arch/PKGBUILD - Build environment:
archlinux:base-develin GitHub Actions - Intended audience: Arch-based distributions such as Arch Linux, Manjaro, and EndeavourOS
The Arch package uses the project CMake install target, keeps runtime assets under /opt/perastage so resource lookup matches the current Linux application layout, and exposes a /usr/bin/Perastage launcher symlink. Desktop metadata, MIME metadata, and the application icon are installed into standard /usr/share locations.
This Arch package is experimental until it receives broader runtime testing on real Arch-based desktop systems. If a user only needs the most portable Linux release asset, use the AppImage.
Linux Desktop Integration
Install layouts include desktop and MIME metadata:
share/applications/perastage.desktopshare/mime/packages/perastage-mime.xmlshare/icons/hicolor/1024x1024/apps/perastage.png
During cmake --install, cache refresh commands may run to expose new associations.
macOS Document Association
Perastage app bundles declare .mvr document type metadata through bundle settings so Finder can route files to the application.
macOS DMG Packaging (Unsigned Builds)
Perastage macOS CI currently produces an unsigned/not-notarized .app and .dmg because there is no Apple Developer ID certificate configured for this project.
What CI validates
The macOS installer workflow validates that:
Perastage.appis staged with expected bundle layout (Contents/Info.plist, executable, and.icnsfiles).- The executable keeps its executable bit.
Info.plistpassesplutil -lint.- The app is ad-hoc signed (
codesign --sign -) and verified for internal signature consistency. - Quarantine metadata is cleared from the staged app and collected DMG in CI.
- The generated DMG mounts successfully and contains a launchable
Perastage.appbundle.
Expected first-run behavior on end-user macOS systems
When users download builds from the internet, macOS can add quarantine metadata again, even if CI removed it during packaging.
Because the app is unsigned/not notarized, users may see messages such as:
"Perastage" is damaged and can't be opened."Perastage" cannot be opened because the developer cannot be verified.
This is expected for non-notarized internet downloads. The long-term production fix is Developer ID signing + notarization.
User workaround for quarantine blocking
If the app is copied to /Applications:
xattr -dr com.apple.quarantine /Applications/Perastage.app
If running from Downloads:
xattr -dr com.apple.quarantine ~/Downloads/Perastage.app
GUI alternative:
- Open System Settings.
- Go to Privacy & Security.
- Use the Allow/Open anyway option shown for the blocked app.
Distribution note
GitHub Actions artifacts are downloaded as ZIP files, which may preserve or add quarantine metadata. For public releases, prefer attaching the .dmg directly as a GitHub Release asset.
Release debug symbols
Release workflows preserve matching debug symbols for crash analysis, but per-platform symbol artifacts are internal GitHub Actions artifacts only. The public GitHub Release attaches one developer-only archive named Perastage-<version>-Debug-Symbols-Developers-Only.zip. Users do not need this archive to install or run Perastage.
Platform symbol locations during CI are:
- Windows:
out/symbols/windows/contains generated.pdbfiles. These files are intentionally outsideout/install/Release, so the staged Windows distribution and Inno Setup installer do not contain PDB files or asymbolsdirectory. - Linux AppImage:
out/symbols/linux/Perastage.debugcontains separated debug data for the staged executable. - Arch Linux:
out/symbols/archlinux/contains the generatedperastage-debug-*.pkg.tar.zstpackage when makepkg produces one. - macOS 15 Apple Silicon:
out/symbols/macos15/Perastage.dSYMcontains symbols for the macOS 15-compatible build. - macOS 26 Apple Silicon:
out/symbols/macos26/Perastage.dSYMcontains symbols for the current-generation macOS 26 build.
Minor releases build both Apple Silicon DMG variants automatically:
Perastage-<version>-macOS15-arm64.dmgfrom themacos-15runner with deployment target15.0.Perastage-<version>-macOS26-arm64.dmgfrom the regularmacos-26runner.
Minor Draft Release prepares the version bump on a run-specific temporary branch named refs/heads/automation/release-v<version>-<run-id>. The workflow builds every required package and validates the release assets from that temporary commit before promoting it to main, creating the tag, and opening the draft release. Failed attempts delete only their exact temporary branch, leaving main, tags, and draft releases unchanged when package builds or asset validation fail.
The validated release asset step fails if any required platform package or symbol artifact is missing or duplicated. The unified developer-only debug-symbol archive is assembled from the actual PDB, separated Linux debug file, Arch debug package, and macOS dSYM bundle contents, and has this top-level layout:
README.txt
Windows-x64/
Linux-AppImage-x86_64/
ArchLinux-x86_64/
macOS15-arm64/
macOS26-arm64/
Symbols must match the exact released binary, version, platform, architecture, and build variant. The macOS 15 and macOS 26 .dSYM bundles are separate build outputs and are not interchangeable. Do not rename or alter symbol files before crash analysis.