Application distribution packer built with MoonBit
| Asset | Platform |
|---|---|
| lunapack-windows-x86_64.exe | Windows |
| lunapack-linux-x86_64 | Linux |
| lunapack-macos-aarch64 | macOS (Apple Silicon) |
| lunapack-wasm | WebAssembly, for moonx |
moonx riantr/LunaPack@latest \
--input-exe=[input-exe-path] \
--output-exe=[output-exe-path] \
--Files=[Pack-Folder]| Flag | Meaning |
|---|---|
| --input-exe | the application executable to distribute |
| --output-exe | the produced single-file bundle |
| --Files | folder of files to bundle with it (repeatable) |
myapp.exe = [ LunaPack launcher ] + [ archive: myapp.exe, res/app.cfg, ... ]$ ./myapp.exe
LunaPack: starting D:/apps/.lunapack/8e75898941d4c137/myapp.exe$ lunapack --input-exe=./BRLabeler.exe --output-exe=./BRLabeler.exe --Files=./build
bundled 392 files -> ./BRLabeler.exe (icon copied)lunapack verify myapp.exe
lunapack info myapp.exe
lunapack unpack -i myapp.exe -o ./outmoon build --release
# -> _build/native/release/build/cmd/main/main.exe (Windows)
# -> _build/native/release/build/cmd/main/main (Linux / macOS)lunapack pack -i <input_dir> -o <output.appkg> [options]
lunapack unpack -i <package> -o <dest_dir> [--skip-scripts]
lunapack verify <package>
lunapack info <package>| Option | Meaning |
|---|---|
| --name <name> | package name (default: input dir / exe name) |
| --version <ver> | package version (default: 0.1.0) |
| --os <os> | target OS tag (default: detected) |
| --arch <arch> | target arch tag (default: detected) |
| --pre-install <file> | script executed before extraction |
| --post-install <file> | script executed after extraction |
| --compression <mode> | none, gzip or auto (default: auto) |
# Bundle an application and its resources into one self-extracting file.
lunapack --input-exe=./build/myapp \
--output-exe=./dist/myapp \
--Files=./build
# Or produce a plain archive instead.
lunapack pack -i ./myapp -o myapp.appkg \
--name myapp --version 1.4.0 \
--pre-install setup.sh --post-install setup.sh
lunapack verify myapp.appkg
lunapack unpack -i myapp.appkg -o /opt/myapplunapack ^
--input-exe="D:/src/App/bin/Release/net10.0-windows/App.exe" ^
--output-exe="D:/dist/App.exe" ^
--Files="D:/src/App/bin/Release/net10.0-windows"moon login # once
moon publish --dry-run # validate
moon publish # publish riantr/LunaPack
moonx riantr/LunaPack@latest --input-exe=... --output-exe=... --Files=...| Extension | Target platform |
|---|---|
| .cmd, .bat, .ps1 | windows |
| .sh | posix (Linux + macOS) |
| anything else | any |
+--------------------+ offset 0
| magic "APPKGD02" | 8 bytes
+--------------------+ offset 8
| entry payloads | stored back to back, raw or gzip
+--------------------+
| manifest (UTF-8 |
| JSON) |
+--------------------+
| magic "APPKGD02" | 8 bytes
| manifest offset | 8 bytes, little-endian int64
| manifest length | 8 bytes, little-endian int64
+--------------------+ end of file{
"format_version": 2,
"name": "myapp",
"version": "1.4.0",
"os": "linux",
"arch": "x86_64",
"checksum_algo": "sha256",
"compression": "auto",
"created_at": "2026-11-03T06:55:56Z",
"main_program": "myapp.exe",
"pre_install": { "platform": "posix", "body": "#!/bin/sh\n..." },
"post_install": null,
"entries": [
{
"path": "res/app.cfg",
"mode": 420,
"size": 22500,
"stored_size": 913,
"codec": "gzip",
"checksum": "0a77c24c45d0c61f906862c4706f5dd739166985e8fc5c6344dadd9220507b28",
"offset": 8
}
]
}moon test # unit + integration tests
bash scripts/test_linux.sh # end-to-end suite (Linux / WSL)pub struct Entry {
path : String
mode : Int
size : Int64
stored_size : Int64
codec : String
checksum : String
offset : Int64
}pub struct PeFile {
data : Bytes
sections : Array[(Int64, Int64, Int64, Int64)]
resource_rva : Int64
resource_size : Int64
}pub struct Script {
platform : String
body : String
}let MAGIC : Stringasync fn bundle(input_exe : String, output_exe : String, file_dirs : Array[String], opts : PackOptions) -> Unitasync fn compress_if_smaller(data : Bytes, always : Bool) -> (Bytes, String)async fn copy_icon(source_exe : String, target_exe : String) -> Boolfn current_exe_path() -> String?fn default_arch() -> Stringfn dirname(p : String) -> Stringasync fn ensure_parent_dir(path : String) -> Unitasync fn extract_to(pkg : String, dest : String) -> Unitasync fn gzip_decompress(data : Bytes) -> Bytesfn host_platform() -> Stringfn i64_to_le8(v : Int64) -> Bytesfn is_native_target() -> Boolfn iso8601_utc(epoch_millis : UInt64) -> Stringfn le8_to_i64(b : Bytes, start : Int) -> Int64fn magic_bytes() -> Bytesfn new_pack_options(name : String, version : String, os : String, arch : String, pre_install : Script?, post_install : Script?, compression : String) -> PackOptionsfn norm_sep(p : String) -> Stringfn package_tag_of(text : String) -> Stringasync fn run_install_script(platform : String, kind : String, script : Script, dest : String) -> Intfn script_applies(script_platform : String, host : String) -> Boolfn script_platform_of(path : String) -> Stringasync fn set_permissions(platform : String, path : String, mode : Int) -> Unitfn sha256_hex(data : Bytes) -> Stringfn sha256_hex_of_string(s : String) -> Stringfn string_to_utf8_bytes(s : String) -> Bytesasync fn try_launch_self_extracting() -> Boolasync fn unpack(pkg : String, dest : String, run_scripts : Bool) -> Unitfn utf8_bytes_to_string(bs : Bytes) -> Stringasync fn verify(pkg : String) -> Boolfn with_defaults(opts : PackOptions, name : String, version : String, os : String, arch : String) -> PackOptions