Distribute an application
On macOS, crykit dist builds an application bundle with embedded assets:
crykit distThe output defaults to dist/<app.name>.app beside the selected crykit.yml. Use -f FILE to select another configuration and -o PATH.app to choose the bundle path. Crystal release optimizations are enabled by default; use --skip-release to disable them. Distribution builds select production mode and embed the assets declared with CryKit::Assets.directory; packaging does not run the application.
Distribution builds for the current macOS host and produces an ad hoc signed .app without requiring a signing identity. CryKit does not automate Developer ID signing, notarization, or disk image creation. Complete signing and notarization separately when distributing the application to other Macs.
Project files
crykit init creates the distribution recipe and editable macOS files:
targets/
dist.cr
macos/
macos.cr
templates/
info.plist.ecrFor an existing application, add targets/dist.cr:
require "crykit/dist"
require "./macos/macos"
CryKit::Dist.register(MacOS)Then add targets/macos/macos.cr:
struct MacOS
include CryKit::Dist::MacOS
plist_file "templates/info.plist.ecr"
endCopy the example plist to targets/macos/templates/info.plist.ecr. Every macOS target declares its project-owned plist template with plist_file.
The CLI compiles and runs this recipe separately from the application entry point. CryKit supplies the compilation, bundle layout, validation, and output publication. The recipe registers the macOS target; the target selects the project's plist template.
Template paths resolve relative to the file declaring plist_file. The generated files belong to the project and can be edited; framework upgrades do not overwrite them.
Metadata
Application identity and platform settings come from crykit.yml:
app:
name: My Notes
entry: ./main.cr
id: com.example.my-notes
# Optional when a shard.yml supplies the application version.
version: "1.2.3"
build_number: "1"
icon: assets/icon.png
macos:
min_system_version: "26.0"app.version uses the nearest ancestor shard.yml through shards version when omitted. app.build_number defaults to "1", and macos.min_system_version defaults to "26.0". The minimum version feeds both the bundle metadata and the executable/native deployment settings.
The bundle preserves app.name for display. Its executable uses app.name.underscore, matching crykit build. Distribution validates the required metadata before building:
app.namemust be a nonempty file name without directory separators or control characters.app.idmust contain at least one dot and use ASCII letters, digits, dots, and hyphens.app.versionmust have three numeric components, such as1.2.3. For a shard prerelease such as1.2.3-dev, set a numericapp.versionexplicitly.app.build_numberaccepts one to three numeric components.macos.min_system_versionaccepts two or three numeric components.
Application icon
Set the optional app.icon to a square PNG or a valid .icns file:
app:
icon: assets/icon.pngRelative icon paths resolve beside the selected crykit.yml, independently of the application entry path or the directory where you run crykit dist. Absolute paths are also supported.
For PNG input, CryKit uses the macOS sips and iconutil tools to create Contents/Resources/AppIcon.icns with standard and Retina representations. Smaller square images, including 256 × 256 PNGs, work; use 1024 × 1024 artwork for the full resolution of Apple's icon representations. An existing .icns file is validated and copied unchanged. The source image is never modified. Missing, unsupported, corrupt, and non-square PNG inputs fail distribution with an app.icon diagnostic.
The generated project template emits CFBundleIconFile as AppIcon.icns when app.icon is set. Keep that conditional entry in your project template when using app.icon. Packaging renders and validates the project plist without rewriting its metadata. When app.icon is omitted, no icon resource is generated.
Customize the plist
The ECR template receives @config, the resolved executable_name, and plist_string(value) for XML escaping. For example:
<key>CFBundleDisplayName</key>
<string><%= plist_string(@config.app.name) %></string>
<key>CFBundleExecutable</key>
<string><%= plist_string(executable_name) %></string>Keep the executable name, identifier, application version, build number, and minimum system version consistent with the configuration. CryKit validates the rendered plist before publishing the bundle. Additional platform keys can be added to the project template.
The bundle contains Contents/Info.plist, the compiled executable under Contents/MacOS/, and a Contents/Resources/ directory. Application assets stay embedded in the executable. Non-system dynamic libraries are copied recursively into Contents/Frameworks/, their references are rewritten to bundle-relative paths, and the modified binaries receive ad hoc signatures. macOS continues to supply system libraries and frameworks. External custom .framework bundles are not supported.
Builds happen in a temporary staging directory; a compilation, dependency, or validation failure preserves the previous successful bundle.
The Notes example contains a complete recipe and plist template. From the repository root, build it with:
crystal run src/cli.cr -- dist -f examples/notes/crykit.ymlTroubleshooting
Distribution recipe not found
Error: Distribution recipe not found: /path/to/project/targets/dist.crcrykit dist could not find targets/dist.cr beside the selected crykit.yml. New projects created with crykit init include this file. For an existing application, add the recipe, macOS target, and plist template described in Project files. If the recipe already exists, check that -f selects the intended application's configuration file.