Skip to content

Package a playtest

scripts/package_game.py packages a saved project with a release build of the player for a local playtest. It builds optimised binaries, verifies every asset against its recorded hash, produces a macOS app on Macs, and writes a release manifest and checksums.

You need Python 3.12 and the native build prerequisites. On macOS this includes the Xcode Command Line Tools, which provide codesign. Run from the engine repository root:

Terminal window
python3 scripts/package_game.py \
--project path/to/my-game/project.feather.json \
--output dist/my-game-playtest
  1. The script builds feather-player and feather-cli with cargo build --release --locked --no-default-features, unless you supply existing binaries.
  2. It stages the project and only the models, textures, sounds and fonts it declares.
  3. It checks each file’s size, SHA-256 name and file type, then validates the staged project with feather-cli and project.validate.
  4. It writes the package and prints the output folder.

Packaging never launches the game, and it never overwrites an existing output: an existing folder, even an empty one, is refused. Without --output, a uniquely named folder is created under dist/. On failure, the staging folder is removed.

Priority Source
1 --assets DIR, if given. It is authoritative
2 The assets/ folder beside the project, if it exists
3 --cache-assets DIR, an absolute FEATHER_ASSET_ROOT, or the per-user asset cache

Point these options at the folder that contains imported/, not at imported/ itself. An incomplete or corrupt assets/ folder fails verification; it is not filled in from another source.

On macOS the output folder contains <name>.app:

<name>.app/Contents/Info.plist
<name>.app/Contents/MacOS/feather-player
<name>.app/Contents/Resources/game/project.feather.json
<name>.app/Contents/Resources/game/assets/imported/...

On other platforms, feather-player (or feather-player.exe), project.feather.json and assets/ sit together in the output folder.

Every package also contains:

File Purpose
README.txt Launch instructions
release-manifest.json Release metadata and file hashes
SHA256SUMS Checksums for the whole package
licenses/Inter-OFL.txt Licence of the font embedded in the player
PLAYTEST.txt Copied from the project folder when present
licenses/project/ The project’s own licenses/*.txt notices, when present

These records sit outside the app bundle, so they can be written after signing. To verify a package, run shasum -a 256 -c SHA256SUMS on macOS or sha256sum -c SHA256SUMS on Linux from inside the output folder.

Option Description
--project FILE The project to package
--assets DIR Authoritative asset folder
--cache-assets DIR Fallback asset folder, used only if the project has no sibling assets/
--output DIR A new output folder
--player FILE, --cli FILE Reuse existing binaries instead of building them
--name Package and app name; defaults to the project name
--version Version as x.y.z; defaults to the engine’s Cargo version
--bundle-id macOS bundle identifier. Keep it stable between builds of the same game
--build-number macOS build number; default 1
--sign-identity macOS signing identity. - (default) signs ad hoc; pass a Developer ID Application identity to use it
--target-platform macos, linux or windows. A different platform requires a matching --player; there is no cross-compilation

When you pass your own binaries, you are responsible for their source revision, release flags and architecture.

Signing runs offline with --timestamp=none. A named Developer ID identity also enables the hardened runtime:

Terminal window
python3 scripts/package_game.py \
--project path/to/my-game/project.feather.json \
--sign-identity 'Developer ID Application: Your Name (TEAMID)'

Neither mode notarises the app. Review content licences, notices and platform requirements before sharing a package beyond your intended testers.

The packaged game is read-only. Settings, key bindings, language, saves and crash reports are written to a per-game folder in the Feather data folder; see Saves and settings. Keep the project name stable between builds, because the folder name is derived from it.

To package the editor itself as a macOS app, use scripts/package_macos.py; see Install. The app registers the .feather project type, so double-clicking a project in Finder opens it in the editor.

The packaging tests use temporary fixtures and need neither licensed content nor a Rust build:

Terminal window
python3 -m unittest discover -s scripts/tests -p 'test_package_game.py'