Releases and hosting
.github/workflows/build.yml has three channels:
| Channel | Trigger | What it produces |
|---|---|---|
ci |
every push to main and every pull request |
Lint, the unit and browser tests, and a web bundle that is thrown away |
nightly |
a daily cron at 07:17 UTC, or a manual run | Installers, a web zip and the updater files on one rolling prerelease, and scmjs.dev/nightly |
stable |
a pushed vX.Y.Z tag |
A permanent release, the container image, scmjs.dev/editor and the docs site |
The nightly cron exits early when main has not moved since the last nightly. The nightly
release is updated in place: its nightly tag is moved and its assets replaced, so there
is one prerelease however many nights run. Its notes link scmjs.dev/nightly, which runs
the same commit.
Download links#
GitHub redirects /releases/latest/download/<asset> to the newest release that is not a
prerelease or a draft, so the download buttons on the site are plain links that never need
updating and never resolve to a nightly:
https://github.com/scm-js/scm-js/releases/latest/download/scmJS-windows-x64-setup.exe
https://github.com/scm-js/scm-js/releases/latest/download/scmJS-windows-x64.zip
https://github.com/scm-js/scm-js/releases/latest/download/scmJS-macos-arm64.dmg
https://github.com/scm-js/scm-js/releases/latest/download/scmJS-macos-x64.dmg
https://github.com/scm-js/scm-js/releases/latest/download/scmJS-linux-x86_64.AppImage
https://github.com/scm-js/scm-js/releases/latest/download/scmJS-linux-amd64.deb
https://github.com/scm-js/scm-js/releases/latest/download/scmJS-web.zip
https://github.com/scm-js/scm-js/releases/download/nightly/scmJS-windows-x64-setup.exe
The redirect can only resolve a fixed file name, so asset names carry no version; the
version is in the release title, the notes and latest*.yml. To withdraw a bad release,
tick "This is a pre-release" on it and the redirect falls back to the one before.
The container image#
docker/Dockerfile is nginx with the built web bundle and the default plugins' runtimes,
pushed to ghcr.io/scm-js/scm-js on a tagged release only, as latest, the full version,
and the moving X.Y and X. One manifest covers linux/amd64 and linux/arm64.
docker run --rm -p 8080:80 ghcr.io/scm-js/scm-js:latest # http://localhost:8080
npm run build:image # build it locally, tagged scmjs
The image carries no game data: .dockerignore keeps the extracted trees out and
docker/nginx.conf answers 404 for those paths, so a container asks through Help ▸ Game
Data… like the hosted editor. Mounting an extracted tree is in
game-data.md.
Cutting a release#
Run the Release workflow (.github/workflows/release.yml) from the Actions tab. It
takes the version (0.3, 1.0 and 1 are padded to three parts; blank releases the
version the nightlies have been building), an optional notes box, and a dry_run tick
that does everything except push.
It refuses to run anywhere but main, refuses a version whose tag exists or that is not
newer than the last release, and runs lint, the tests (the network suite included), the
build and a check of the container image before writing anything. Then it commits the
version into package.json and the lock file, tags that commit vX.Y.Z, and starts the
Build workflow on the tag, which produces everything else.
Nothing moves package.json on to a next version afterwards: on main it names the last
release. A nightly is named <base>-nightly.<date>.<run number>, where the base is a
patch bump of the newest release tag (scripts/next-version.mjs). Every job writes the
version into package.json before building; Vite injects it as __APP_VERSION__, read
through src/version.ts, and electron-builder writes it into latest*.yml.
The hand-written part of a release's notes comes from docs/releases/<version>.md (the
full three-part name), committed to main before the release is cut, or from the Release
workflow's notes input, which wins over the file. Either goes above GitHub's generated
list of commits. The pre-flight prints what it found, so a dry run shows the body.
docs/releases/README.md is the convention.
The hosted builds#
| Address | Deployed | How |
|---|---|---|
scmjs.dev/editor |
on a tag, by the pages job |
GitHub Pages on this repository, built for /editor/, served by the site's Cloudflare Worker |
scmjs.dev/nightly |
each nightly, by the nightly-site job |
The same commit built again for /nightly/, pushed to scm-js/nightly, served by the same Worker |
docs.scmjs.dev |
on a tag, by the docs job, or by hand |
npm run build:docs, pushed to scm-js/docs |
The hosted editor is one release with everything else: the site, the installers, the image
and the notes come from one tag. To put scmjs.dev/editor back on an older version, run
Build on that tag. The docs site can also be deployed without a release, from Actions ▸
Docs site ▸ Run workflow, so a fix to the guides goes live at once; the next release
replaces it with the tag's own pages.
The nightly is built with SCMJS_SITE=nightly. The two editors share the scmjs.dev
origin, because both need the game's page at /play/ beside them, and the nightly keeps
its preferences, recent files and plugins under names of its own. The game data is the
exception: both editors and the play page read one copy, so a nightly that changes how
that copy is stored must keep it readable by the last release.
Variables and secrets#
None of these is required: a fork builds and releases without them, and a deploy that lacks its own is skipped with a notice.
| Name | Kind | For |
|---|---|---|
PAGES_BASE |
variable | The hosted editor's base path (/editor/); the repository name by default |
NIGHTLY_BASE |
variable | The nightly's path on the site (/nightly/); without it the nightly site is the web zip at a domain's root |
NIGHTLY_HOME |
variable | The site the nightly is a path of; https://scmjs.dev by default |
NIGHTLY_DOMAIN |
variable | The domain written into the nightly site's CNAME |
NIGHTLY_PAT |
secret | A token with Contents: write on <owner>/nightly |
DOCS_DOMAIN, DOCS_PAT |
variable, secret | The same pair for the docs site on <owner>/docs |
CI_BUILD_KEY |
secret | A deploy key on this repository, so the Release workflow can push its commit to a protected main |
PLUGIN_API_KEY |
secret | A deploy key on scm-js/plugin-api, for pushing the plugin typings |
PUBLISH_PLUGIN_API |
variable | Turns on the npm publish of @scm-js/plugin-api |
The two deploy keys must be listed as bypass actors in the target repository's branch ruleset.