Packaging

Preview

A packaged application is two things side by side: the prebuilt gpui-shell binary and the application’s directory. Nothing is compiled. The binary looks for an application next to itself when it is started with no arguments, which is how a desktop launcher — Finder, a .desktop entry, a Start menu shortcut — starts it, since none of them can pass a path.

PlatformWhere the executable isWhere the application goes
macOSClock.app/Contents/MacOS/Clock.app/Contents/Resources/app/
Linuxthe package directoryapp/ beside the executable
Windowsthe package directoryapp\ beside the executable

The executable may be renamed — Clock instead of gpui-shell — because the application is found by where the executable is, not by what it is called.

#Before packaging

The application needs a manifest with a stable id, because a packaged application keeps its storage under that id rather than under its path: moving the .app or installing an update does not lose the user’s data.

{
  "id": "com.example.clock",
  "name": "Clock",
  "version": "1.0.0",
  "shell-version": "0.7.3",
  "entry": "main.js",
  "capabilities": { "storage": true }
}

Its capabilities are granted as written, so ask only for what the application uses — see Capabilities. Run gpui-shell check -d clock before packaging, and leave out what only development needs: gpui-kit.d.ts, jsconfig.json, node_modules/ and AGENTS.md.

gpui-shell new clock creates a directory in this shape to start from.

#Getting the binary

Each GitHub release carries one archive per platform, with a .sha256 beside it:

TargetAsset
macOS, Apple silicongpui-shell-aarch64-apple-darwin.tar.gz
macOS, Intelgpui-shell-x86_64-apple-darwin.tar.gz
Linux, x86-64gpui-shell-x86_64-unknown-linux-gnu.tar.gz
Windows, x86-64gpui-shell-x86_64-pc-windows-msvc.zip
curl -fsSLO https://github.com/longbridge/gpui-kit/releases/latest/download/gpui-shell-aarch64-apple-darwin.tar.gz
tar -xzf gpui-shell-aarch64-apple-darwin.tar.gz

Pin a version in a build script by replacing latest/download with download/v0.7.3. To build it yourself instead, run cargo build -p gpui-shell --release in the repository; the binary is target/release/gpui-shell.

#macOS

Clock.app/
└── Contents/
    ├── Info.plist
    ├── MacOS/
    │   └── Clock              # the gpui-shell binary, renamed
    └── Resources/
        ├── Clock.icns
        └── app/
            ├── gpui-shell.json
            └── main.js

Info.plist names the executable and the icon. Use the manifest’s id as the bundle identifier so the two stay one identity:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleExecutable</key>
  <string>Clock</string>
  <key>CFBundleIdentifier</key>
  <string>com.example.clock</string>
  <key>CFBundleName</key>
  <string>Clock</string>
  <key>CFBundleIconFile</key>
  <string>Clock</string>
  <key>CFBundlePackageType</key>
  <string>APPL</string>
  <key>CFBundleShortVersionString</key>
  <string>1.0.0</string>
  <key>CFBundleVersion</key>
  <string>1</string>
  <key>LSMinimumSystemVersion</key>
  <string>11.0</string>
  <key>NSHighResolutionCapable</key>
  <true/>
</dict>
</plist>

Assembling it is a few copies:

set -e
app=Clock.app
mkdir -p "$app/Contents/MacOS" "$app/Contents/Resources"
cp Info.plist "$app/Contents/"
cp gpui-shell-aarch64-apple-darwin/gpui-shell "$app/Contents/MacOS/Clock"
cp Clock.icns "$app/Contents/Resources/"
rsync -a --exclude node_modules --exclude gpui-kit.d.ts --exclude jsconfig.json \
  --exclude AGENTS.md clock/ "$app/Contents/Resources/app/"

iconutil -c icns Clock.iconset makes Clock.icns from a folder of PNG images. For one bundle that runs on both Apple silicon and Intel, merge the two release binaries first:

lipo -create -output Clock \
  gpui-shell-aarch64-apple-darwin/gpui-shell \
  gpui-shell-x86_64-apple-darwin/gpui-shell

A bundle given to other people has to be signed and notarized, or Gatekeeper refuses to open it. Sign everything the bundle contains — the scripts are part of it:

codesign --force --options runtime --timestamp \
  --sign "Developer ID Application: Example Inc (TEAMID)" Clock.app
ditto -c -k --keepParent Clock.app Clock.zip
xcrun notarytool submit Clock.zip --keychain-profile notary --wait
xcrun stapler staple Clock.app

Signing seals the scripts too, so any change to Resources/app after signing breaks the signature: sign last.

#Linux

clock/
├── clock                     # the gpui-shell binary, renamed
├── clock.png
└── app/
    ├── gpui-shell.json
    └── main.js

Ship the directory as an archive:

mkdir -p clock/app
cp gpui-shell-x86_64-unknown-linux-gnu/gpui-shell clock/clock
cp -r my-clock-source/* clock/app/
tar -czf clock-1.0.0-x86_64.tar.gz clock

A desktop entry makes it a launcher item. Install it as ~/.local/share/applications/com.example.clock.desktop (or under /usr/share/applications for every user), with Exec pointing at the unpacked executable:

[Desktop Entry]
Type=Application
Name=Clock
Exec=/opt/clock/clock
Icon=/opt/clock/clock.png
Categories=Utility;
StartupWMClass=clock

The same layout goes into a .deb, an .rpm or an AppImage unchanged: what matters is that app/ stays beside the executable. The binary needs what any GPUI application needs at run time — a Vulkan driver, libxkbcommon, and a Wayland or X11 session.

#Windows

Clock\
├── Clock.exe                 # gpui-shell.exe, renamed
└── app\
    ├── gpui-shell.json
    └── main.js

Zip the folder, or hand it to an installer such as Inno Setup or WiX; a Start menu shortcut targets Clock.exe with no arguments.

PageWhat it covers
Getting StartedInstalling the runtime, run, new, check and installed applications
CapabilitiesWhat a manifest may ask for, and what a packaged application is granted
DependenciesGit dependencies, fetched on first launch into ~/.gpui-shell/cache

CC BY 4.0 · Documentation prose and original illustrations GPUI Kit has rights to license may be copied or adapted with attribution. License. Credit GPUI Kit, link to this source page and the CC BY 4.0 license, and indicate any changes. Code examples and software source remain under Apache-2.0. Existing Apache permissions remain; third-party material retains its own terms.