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_stage before compiling the installer.
  • Optional associations can include .pstg and .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:

  1. Check out the requested source ref.
  2. Bootstrap a CI-local vcpkg checkout inside the GitHub Actions workspace.
  3. Install the required Windows dependencies into the CI vcpkg cache.
  4. Configure and build Perastage in Release mode.
  5. Run the perastage_stage target.
  6. Collect debug symbols.
  7. Build the Inno Setup installer.
  8. 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:

  1. Seed content from the installed library is used as source.
  2. Missing files are copied into the user library.
  3. 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:

  • VERSION at 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 only assoc_mvr if 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-devel in 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.desktop
  • share/mime/packages/perastage-mime.xml
  • share/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.app is staged with expected bundle layout (Contents/Info.plist, executable, and .icns files).
  • The executable keeps its executable bit.
  • Info.plist passes plutil -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.app bundle.

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:

  1. Open System Settings.
  2. Go to Privacy & Security.
  3. 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 .pdb files. These files are intentionally outside out/install/Release, so the staged Windows distribution and Inno Setup installer do not contain PDB files or a symbols directory.
  • Linux AppImage: out/symbols/linux/Perastage.debug contains separated debug data for the staged executable.
  • Arch Linux: out/symbols/archlinux/ contains the generated perastage-debug-*.pkg.tar.zst package when makepkg produces one.
  • macOS 15 Apple Silicon: out/symbols/macos15/Perastage.dSYM contains symbols for the macOS 15-compatible build.
  • macOS 26 Apple Silicon: out/symbols/macos26/Perastage.dSYM contains symbols for the current-generation macOS 26 build.

Minor releases build both Apple Silicon DMG variants automatically:

  • Perastage-<version>-macOS15-arm64.dmg from the macos-15 runner with deployment target 15.0.
  • Perastage-<version>-macOS26-arm64.dmg from the regular macos-26 runner.

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.

Back to top