EN

jumphp2installer: an installer for your software

jumphp2installer makes real native installers for Windows, macOS and Linux, for your game, your JumPHP app or any software. You write a short, readable YAML script, and your users download one file. It shows them a wizard (welcome, license, folder, options), always installs the newest version and adds an uninstaller. It is free and open source: github.com/davidnurdin/jumphp2installer.

The welcome page of the install wizard
Welcome
The license to accept
License
Choosing the install folder
Folder
The options: shortcuts
Options
The end of the installation
Done

What it does

  • One file per system: My-App-setup.exe (Windows 10 and 11), My-App-setup-macos.zip (Apple silicon and Intel Macs), My-App-setup-linux-x86_64. Nothing to install first.
  • Always the newest version: the installer reads your script when it starts. An installer downloaded a year ago therefore installs today's version.
  • A wizard in your colours: your name, your icon, your picture on the side, your colour, your license and your texts. In English or French, following the computer's language.
  • The usual steps: download (with a SHA-256 check), unpack (.zip, .tar.gz), copy, write a file, make executable, run a command, create shortcuts (desktop, menu).
  • A real uninstaller: it shows in Windows' "Apps" list and in the Linux menu. It removes exactly what the install created, nothing else.
  • No administrator rights: everything installs for the user, in their own programs folder.
  • From the command line too: --silent installs without a window (deployments, scripts, CI).

The install script

An install.yaml file says what the installer shows (pages) and what it does (steps). The same script works on the three systems: variables ({folder}, {platform}, {exe}…) and the os: key take care of the differences.

app:
  name: My App
  publisher: Me
  website: https://example.com
  icon: icon.png
  banner: banner.png          # the picture on the left of the wizard (172 x 420)
  accent: "#2f6fed"

version:
  github: me/my-app           # the newest GitHub release gives {version}

pages:
  - welcome
  - license: LICENSE.txt
  - folder: "{programs}/{app}"
  - options:
      desktop: A shortcut on the desktop
  - install
  - finish: {run: "{folder}/my-app{exe}"}

steps:
  - download: https://github.com/me/my-app/releases/download/v{version}/my-app-{platform}.zip
  - extract: {strip: 1}
  - executable: "{folder}/my-app"
    os: [linux, macos]
  - shortcut: {target: "{folder}/my-app{exe}", where: desktop}
    if: desktop
  - uninstaller

The pages

Page What it shows
welcome a welcome text (default: "Welcome to the installation of {app} {version}")
license a file of the package, to accept before going on
folder the folder, with "Browse…", the space needed and the free space; warns when the folder is already used
options check boxes, used again by the steps (if: desktop)
install the progress bar and the details
finish the end, with a "Start {app}" box (run, which may differ per system)

The steps

Step
download an address, or a file of the package; sha256 stops everything if the file is not the right one
extract unpacks into {folder}; strip: 1 drops the archive's top folder
copy, mkdir, write, delete files and folders (delete only touches the install folder, the user's folder and the temporary folder)
executable makes a file executable (Linux, macOS)
shortcut desktop, menu or both: .lnk on Windows, .desktop on Linux, a link on macOS
run runs a command in the folder
uninstaller installs the uninstaller and registers it with the system

Every step accepts if: (a checked option, or "!option"), os: (windows, macos, linux) and label:.

The version

Written {version}
version: 1.4.0 fixed
version: {github: me/my-app} the repository's newest release (its tag, without the v)
version: {latest: https://…/latest.json} what the address answers: {"version": "1.4.0"} or plain text

The variables

Variable Windows Linux macOS
{programs} %LOCALAPPDATA%\Programs ~/.local/opt ~/Applications
{desktop}, {menu} desktop, Start menu desktop, ~/.local/share/applications ~/Desktop
{platform} windows-x86_64 linux-x86_64 macos-arm64 / macos-x86_64
{exe} .exe (empty) (empty)
{folder}, {app}, {version}, {home}, {temp}, {os}, {arch}, {download}

The full reference is in docs/installer/script.md.

Three ways to publish it

1. On jumphp.net, with nothing to install

In the studio, "New installer" opens an editor:

  • the YAML script is checked as you type (each error with its line);
  • a preview of the wizard updates beside it;
  • you drop in the pictures, the license and the package's small files.

When you publish, the installer gets its download page and its three links, one per system. Until it is published, only you can try it.

2. In GitHub releases, with no server

The package (install.yaml and its files) and its installers are attached to each release of your repository. The installers read the newest release, so there is nothing to change from one version to the next. A workflow does it at each release:

# .github/workflows/installer.yml
on:
  release:
    types: [published]
jobs:
  installer:
    uses: davidnurdin/jumphp2installer/.github/workflows/package.yml@main
    with:
      folder: installer        # install.yaml and its files
    permissions:
      contents: write

You can also do it by hand, from a clone of jumphp2installer: tools/github-release.sh me/my-app installer. Your links: https://github.com/me/my-app/releases/latest/download/My-App-setup.exe.

If your repository releases several products (v* tags for one, ide-v* for another), the "newest release" may belong to the other product. In that case, serve the package from the repository's files (https://raw.githubusercontent.com/…/installer/), with a version.json beside it. That is what JumPHP IDE does.

3. On your own server

jumphp2installer's server is a Symfony + MariaDB app, in Docker (server/ in the repository): accounts, editor, preview, download pages and statistics. One docker compose up -d starts it. A plain static web page can serve as a server too (a folder with install.yaml and its files):

php tools/package-release.php my-package/ https://my-site.com/installer/ bases/ out/

For a JumPHP game or app

bin/jumphp app:export my-app --windows --macos (or export:standalone for a game) makes one executable per system. Attach them to a release, then use the jumphp-app.yaml example. Your Claude knows how: ask it "make an installer for my game" (the repository has a jumphp2installer skill).

From the command line

My-App-setup-linux-x86_64 --silent --folder=/opt/my-app --no-option=desktop
My-App-setup.exe --silent
"C:\…\My App\uninstall.exe" --uninstall --silent
jumphp2installer-linux-x86_64 --check --script=install.yaml      # check a script
jumphp2installer-linux-x86_64 --script=install.yaml              # try the wizard with a local script
jumphp2installer-linux-x86_64 --make=My-App-setup.exe --server=github:me/my-app --from=jumphp2installer-windows-x86_64.exe

How it works

The installer is itself a JumPHP app: an HTML page for the wizard, PHP for the work, and StaticPHP's standalone PHP. The package's address is written into the executable's embedded php.ini, at a constant size. A server can therefore make an installer for each package in a fraction of a second, with nothing to compile. Downloads and unpacking move a little at each frame: the window never freezes. Before each version, GitHub Actions tries the installer for real on Linux, Windows and two Macs (Apple silicon and Intel): install, shortcuts, uninstall entry, then uninstall.

Limits

  • The installers are not signed: Windows SmartScreen and macOS Gatekeeper warn the first time (on a Mac: right click › Open).
  • Per-user installs only (no Program Files, services or file associations).
  • No Linux ARM, no phones.
  • On Windows, start it by its long path, never by an 8.3 short name (C:\Users\RUNNER~1\…).