Skip to content

Command Line ​

Everything the GemShell window does, a terminal can do too: set up a game, change its settings, add plugins, sign and notarise the builds, build for every platform, and send it to Steam, itch.io or GitHub. It reads the same gemshell.json as the window, so a game set up in one works in the other, and it needs no window at all.

That makes it the way to automate releases: build and upload on every tag, from a build server, without anybody clicking anything.

Install ​

The command line tool is a single file, published with every release on the GemShell releases page:

SystemFile
macOS, Apple silicongemshell-<version>-cli-macos-arm64
macOS, Intelgemshell-<version>-cli-macos-x86_64
Windowsgemshell-<version>-cli-windows-x86_64.exe
Linux, x64gemshell-<version>-cli-linux-x86_64
Linux, ARM64gemshell-<version>-cli-linux-arm64

Rename it to gemshell (gemshell.exe on Windows), make it executable and put it somewhere on your PATH:

bash
mv gemshell-*-cli-macos-arm64 /usr/local/bin/gemshell
chmod +x /usr/local/bin/gemshell
gemshell help

macOS

A file downloaded with a browser is quarantined by macOS. If it refuses to start, remove the flag once:

bash
xattr -d com.apple.quarantine /usr/local/bin/gemshell

A first build ​

bash
gemshell new mygame
cd mygame
gemshell build

gemshell new writes a small game and its gemshell.json. gemshell build builds every platform the config switches on, by default into the game's dist/<Name>-<version>/, one folder per platform.

A game you already have, exported from an engine or written by hand, is set up with gemshell init instead. It writes a gemshell.json into the folder and names the game after its page's <title>:

bash
gemshell init path/to/mygame
gemshell build path/to/mygame

A game named on the command line is built from its own gemshell.json, into its own dist/. Every command that works on a game takes its folder the same way, and uses the current folder when none is given.

Commands ​

CommandWhat it does
gemshell new <folder>Start a game from a template
gemshell init [game]Set up a game that already exists
gemshell config [game]Show or change the game's settings
gemshell build [game]Build the game for every platform switched on in its config
gemshell preview [game]Start the build made for this machine
gemshell plugin ...Install plugins and switch them on for a game
gemshell signing ...Sign and notarise the builds
gemshell login <where>Sign in to itch, github or steam
gemshell deploy <where> [game]Send the build: itch, github or steam
gemshell licence [what]This copy's licence: status, login, unlock <key>, forget
gemshell updateInstall the newest GemShell
gemshell completion <shell>Print tab completion for bash, zsh or fish
gemshell versionShow which GemShell this is (also --version)

gemshell help <command>, or -h after any command, shows its arguments and flags. Flags can go before or after the other arguments. A command used wrongly ends with exit code 2, one that tried and failed with 1.

config ​

gemshell config prints the whole gemshell.json as GemShell reads it, every default filled in. get and set read and change one setting, named by its path in the file:

bash
gemshell config get window.width
gemshell config set app.version 1.2.0
gemshell config set window.resizable false
gemshell config set build.platforms.linux false
gemshell config set itch_deploy.project_slug yourname/your-game
gemshell config set github_deploy.repo yourname/your-game
gemshell config set steam_deploy.depots.windows 1234561
gemshell config set exclude '["node_modules", "src"]'

A value is read the way the setting wants it: words for a text, true or false (or on and off) for a switch, a number for a number, and JSON for a list. A key that does not exist, or a value of the wrong kind, is refused with the reason, and nothing is written. Every key is described in the Config File Reference.

build ​

FlagDefault
-config <file><game>/gemshell.jsonWhich config to read
-out <dir>build.output_path from the config, or <game>/distWhere the builds go
-plainoffPack the game as ordinary files instead of sealing it

new ​

FlagDefault
-framework <id>vanillaWhich template to start from
-listShow the templates there are

deploy ​

FlagDefault
-config <file><game>/gemshell.jsonWhich config to read
-from <dir><game>/distWhere the build is
-branch <name>steam_deploy.branchSteam branch to set live
-yesoffDo not ask before sending

deploy shows what it is about to send and asks first. Without -yes and without anybody to answer, nothing is sent. Where a build goes is set in the config: steam_deploy, itch_deploy and github_deploy, see the Config File Reference.

preview ​

FlagDefault
-config <file><game>/gemshell.jsonWhich config to read
-from <dir><game>/distWhere the build is
-folderoffOpen the folder instead of starting the game
-iosoffOpen the iOS project in Xcode
-androidoffOpen the folder of the Android project

plugin ​

bash
gemshell plugin list                      # what is installed, and what is on for this game
gemshell plugin add ./my-plugin           # a folder, a .zip or a .plugin.js
gemshell plugin on my-plugin              # use it in this game's builds
gemshell plugin show my-plugin            # what it does, and its settings
gemshell plugin set my-plugin size 24     # change one of them
gemshell plugin off my-plugin
gemshell plugin remove my-plugin          # asks first, or -yes

Plugins are installed once per machine, in ~/.gemshell/plugins, and switched on per game. A setting is written as the plugin declares it: a switch as true or false, a number as a number, a choice as one of its options.

signing ​

bash
gemshell signing                                   # what the builds are signed with
gemshell signing mac developer-id.p12              # macOS: a Developer ID Application certificate
gemshell signing notary AuthKey_ABCD123456.p8      # notarise, with an App Store Connect API key
gemshell signing windows authenticode.pfx          # Windows: an Authenticode certificate
gemshell signing forget mac                        # or windows, or notary

The certificate's password is asked for, without showing what is typed, or piped in. It is checked by opening the certificate, then kept in the system's credential store, never in the project: the config only names the file. For notarising, the key identifier is read from the file's name, and the issuer ID from App Store Connect (Users and Access, Integrations) is asked for, or given with -issuer.

update ​

gemshell update shows what changed since this version, asks, and replaces the program with the newest one. The download is checked against the hash the release publishes. -check only says whether there is a newer one, -yes does not ask.

Tab completion ​

gemshell completion prints a script that completes commands, flags and targets as you type. Install it once for your shell:

bash
# bash
gemshell completion bash > /usr/local/etc/bash_completion.d/gemshell

# zsh: any folder on your $fpath
gemshell completion zsh > "${fpath[1]}/_gemshell"

# fish
gemshell completion fish > ~/.config/fish/completions/gemshell.fish

Credentials ​

gemshell login asks the service whether a key works, then keeps it in the system's own store: the Keychain on macOS, the Windows credential store, and the Secret Service (GNOME Keyring, KDE Wallet) on Linux. It never takes one as an argument, where every other process on the machine could read it. Type it when asked, which does not show it on screen, or pipe it in:

bash
gemshell login itch      # an itch.io API key, from itch.io/user/settings/api-keys
gemshell login github    # a GitHub token with access to the repository
gemshell login steam     # your Steam username and password, and Steam Guard

gemshell login steam signs steamcmd in, the way the window's Steam card does: it asks for the username and the password, then for a Steam Guard code when Steam sends one, or waits while you confirm the sign-in in the Steam app. Piped in, that is the username, the password and the code, one to a line. Only the username is stored. The session belongs to steamcmd, which keeps it from then on, so a deploy needs no password.

Pro ​

Deploying, mobile builds and plugins are GemShell Pro. Unlock a copy with the key from your itch.io purchase, the part after /download/ in the link (the whole link works too):

bash
gemshell licence unlock <key>
gemshell licence status

In CI ​

A release on every tag, built on a GitHub Actions runner and sent to itch.io:

yaml
name: Ship
on:
  push:
    tags: ['v*']

jobs:
  ship:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4

      - name: Get GemShell
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          gh release download -R eddime/GemShell -p 'gemshell-*-cli-macos-arm64' -O gemshell
          chmod +x gemshell

      - name: Unlock Pro and store the itch.io key
        run: |
          ./gemshell licence unlock "${{ secrets.GEMSHELL_KEY }}"
          echo "${{ secrets.ITCH_API_KEY }}" | ./gemshell login itch

      - name: Build
        run: ./gemshell build

      - name: Deploy
        run: ./gemshell deploy itch -yes

Put your GemShell key and your itch.io API key in the repository's secrets as GEMSHELL_KEY and ITCH_API_KEY. A GitHub release works the same way with login github and deploy github.

  • The runner: a macOS runner builds every desktop platform, macOS included, and has a Keychain for the credentials. A Windows runner works as well. A Linux runner has no Secret Service, so gemshell login fails there: run the deploy on macOS or Windows.
  • Signing: pipe the certificate's password in, echo "$P12_PASSWORD" | ./gemshell signing mac developer-id.p12, with the file and the password from the repository's secrets.
  • Steam: steamcmd needs a signed-in session, and Steam Guard asks for a code on every new machine. A fresh CI runner is a new machine each time, so deploy to Steam from a machine that keeps its steamcmd session: your own computer, or a self-hosted runner where you have signed in once.