How to publish a release

Every step, in order, with the exact commands. Follow it top to bottom and you cannot get it wrong.

What a release actually is Step 0 — which page am I on? Releasing for Windows Releasing for Mac Testers Turning it on Checking it worked When something goes wrong

What a release actually is

Three separate things. Doing one without the others is the usual mistake:

StepWhat it meansIf you skip it
BuildTurn the code into an installer fileNothing to give anyone
UploadPut those files on this serverThe download link 404s
PublishSet Latest on the admin pageFiles are there, but no till is told to update
Tills check for updates when they start up, never mid-shift. So a release reaches the shop floor the next morning, not the moment you press Save.

Step 0 — which page am I on?

PageWho it affects
/admin — purpleReal shops. Every live till.
/admin/dev — tealTesters only. Cannot touch a real till.
Check the colour before you drop a file. The two pages look identical apart from that. Uploading a tester build to the purple page overwrites the real download link, and there is no undo.

Releasing for Windows

Windows installers must be built by GitHub Actions. Your Mac cannot build them — it has no Wine, and it is the wrong processor. The build now publishes itself, so for Windows there is nothing to download and nothing to upload.

  1. Pick a version number, higher than last time, and push it
    cd ~/Desktop/AYO/retailstack-pos-desktop
    git status                       # must be clean — see the warning below
    npm version 1.3.5 --no-git-tag-version
    git commit -am "1.3.5" && git push
    Push before you build. The runner builds the commit on GitHub, not what is on your Mac — an unpushed change simply is not in the installer.
    You sometimes edit the version down in package.json to test the update block. If that edit is still there, the build carries the wrong version and nothing updates. git status must be clean before you build.
    Reuse a number and nothing happens — tills compare versions and see no change. Tester builds count up -dev.1, -dev.2, and must be a pre-release of the next version: after 1.3.9 ships, a tester build is 1.4.0-dev.1.
  2. Start the build — it publishes itself
    gh workflow run build-windows.yml -f channel=stable -f version=1.3.5
    gh run watch
    About five minutes. When it finishes green, the installer is already on Cloudflare and the feed is updated. Go straight to Turning it on.
    Use -f channel=dev for a tester build.
    Do not also push a v1.3.5 tag. A tag push starts this same workflow on its own, so doing both runs two builds at once and they fight over the download link. Pick one. If it happens, cancel the extra run with gh run cancel <id>.
  3. Check it really published
    Open the run and look at the Publish to R2 step. It should end with Published. A green tick on the job is not enough on its own — the step can be skipped if the R2 keys are missing, and a skipped step still looks like success.
    If that step says skipped, or the run failed inside it, publish by hand:
    gh run download -n windows-installer-stable -D ~/Downloads/1.3.5
    cd ~/Desktop/AYO/retailstack-pos-desktop
    source ~/.retailstack-signing/r2.sh
    node scripts/publish.mjs ~/Downloads/1.3.5 --version 1.3.5
    Running it again costs nothing — it skips whatever already arrived.

Releasing for Mac

Mac builds happen on your own Mac. They take 25–40 minutes, most of it waiting on Apple, and they produce two of everything — one set for Apple Silicon, one for Intel.

  1. Load the Apple signing keys
    source ~/.retailstack-signing/env.sh
    Do this first, in the same Terminal window. Without it the build still succeeds but comes out unsigned — it will print skipped macOS notarization. An unsigned build is blocked by the Mac's security and cannot auto-update. Never upload one.
  2. Set the version, then build
    cd ~/Desktop/AYO/retailstack-pos-desktop
    git status                       # must be clean
    npm version 1.3.5 --no-git-tag-version
    npm run build
    Use npm run build:mac:dev for a tester build. Go and do something else — Apple's notarisation queue often sits for 20 minutes with no output. That is normal, not a hang.
  3. Check Apple actually approved it
    cd release
    spctl -a -t install "RetailStack POS-1.3.5-arm64.dmg"
    spctl -a -t install "RetailStack POS-1.3.5-x64.dmg"
    Both must say accepted and source=Notarized Developer ID.
    Anything else — especially rejected — means stop. Do not upload it. Check that you ran step 1, then build again.
  4. Publish it
    cd ~/Desktop/AYO/retailstack-pos-desktop
    source ~/.retailstack-signing/r2.sh
    node scripts/publish.mjs release --version 1.3.5
    Seven files go up — both dmgs, both zips, both blockmaps and latest-mac.yml — and it tells you which. About 450MB, so give it ten minutes on a normal connection. Add --channel dev for a tester build.
    release/ keeps every build you have ever made. That is why --version is there: without it the command stops and asks, rather than letting an older build quietly take over the public download link.
    It refuses to overwrite an installer that is already up under the same name with different contents. If you see that, you reused a version number — bump it and build again.

Releasing to testers instead

A tester build installs alongside the real till app, with its own database, and talks to the dev API. It can never be offered to a real till: it publishes to a different feed.

  1. Number it as a pre-release of the NEXT version
    Stable on 1.3.9 means testers get 1.4.0-dev.1, then -dev.2. Going backwards or reusing a number means nothing is offered.
  2. Same two commands, with the dev channel
    gh workflow run build-windows.yml -f channel=dev -f version=1.4.0-dev.1
    
    npm run build:mac:dev
    node scripts/publish.mjs release --channel dev --version 1.4.0-dev.1
  3. Set Latest on the tester page, not this one
    It is at /admin/dev. Leave Force off for testers.
Links to give testers: https://dl.retailstack.co/Retailstack-dev.exe, https://dl.retailstack.co/Retailstack-dev.dmg, https://dl.retailstack.co/Retailstack-dev-intel.dmg

Turning it on

  1. Set Latest, press Save channel
    Top of the admin page. Type the version you just uploaded — 1.3.5 — into Latest version and press Save channel. That is the release done. Tills pick it up at their next launch and see an update prompt they can dismiss.
  2. Only if it is urgent: force everyone
    Also set Minimum supported to 1.3.5.
    This stops every till below that version from selling until it updates. Only for a breaking change or a bug that loses data. Make absolutely sure the installer is uploaded and downloadable first — otherwise you have blocked shops with nothing to upgrade to.
    It is still safe in one way: it never interrupts a sale. A till with items in the cart or unsynced sales finishes first, and applies the block at its next launch.

Checking it worked

  1. Click the links in the "Share links" panel
    Each one should start downloading. If any 404s, that file did not upload — drag it in again.
  2. Watch the Fleet table over the next day
    It lists what every till is actually running. That is how you know a rollout landed, rather than assuming it did.

The links to give people

WhoLink
Mac — Apple Siliconhttps://download.retailstack.rivrafrica.com/Retailstack.dmg
Mac — Intelhttps://download.retailstack.rivrafrica.com/Retailstack-intel.dmg
Windowshttps://download.retailstack.rivrafrica.com/Retailstack.exe
These never change. They always point at whatever you uploaded last, so you never have to edit the website after a release. They now hand the download to Cloudflare's network rather than serving it from our own server, which is why they are far quicker than they used to be — the addresses are the same ones you have always given out, so nothing you have already shared needs changing. Not sure which Mac someone has? Apple menu → About This Mac. "Chip: Apple M1/M2/M3/M4" is Apple Silicon; "Processor: Intel" is Intel.

When something goes wrong

An upload failed halfway

Run the same publish.mjs command again. Files go up in 16MB pieces and each piece retries on its own, so a wobble costs seconds. Anything that did finish is skipped on the second run, so it picks up roughly where it stopped.

The download link 404s

The file never reached Cloudflare. publish.mjs checks every file at the end and refuses to say Published if one is missing, so this means the command did not finish — scroll back and look for the error, then run it again.

Tills are not updating

Almost always one of three things: the feed file (latest.yml / latest-mac.yml) was never uploaded; Latest was never changed; or the version number is not higher than what they already run. Check in that order.

npm run dev stopped working after a Mac build

A Mac build rebuilds the database library for each processor it packages, and leaves it set to the last one. Running the app from source then fails with incompatible architecture. It is not a code problem — put it back with npx @electron/rebuild -f -w better-sqlite3.

The Mac build says "skipped macOS notarization"

You forgot source ~/.retailstack-signing/env.sh. Run it and build again. Delete the unsigned files from release/ first so you cannot upload them by accident.

Nothing here is working and shops are calling

Nothing on this server can stop a till from selling unless Minimum supported is set above what they run. If you set it by mistake, lower it and press Save — they recover at their next launch. If this whole service is down, tills simply carry on trading.

← Back to the admin page