Skip to content

1.1.5

Choose a tag to compare

@1m93 1m93 released this 27 Aug 19:58
· 22 commits to master since this release
32eeddc

EngineShelf 1.1.5

The project was Chromium-only and called ChromiumStack. It now holds four
engines — Chromium, Firefox, Edge and WebKit — and has the name to match. 288
versions on the shelf instead of 21, and none of them typed in by hand.

Renamed: ChromiumStack is now EngineShelf

The repository moved to github.com/1m93/EngineShelf, so download links from
older pages will not resolve.

Was Is
ChromiumStack-<ver>-macOS.zip EngineShelf-<ver>-macOS.zip
ChromiumStack.app / ChromiumStack.bat EngineShelf.app / EngineShelf.bat
chromium-stack.sh / .ps1 engineshelf.sh / .ps1
chromium-stack-docker.sh / .ps1 engineshelf-docker.sh / .ps1
~/.chromium-stack/ ~/.engineshelf/
chromium-stack:<rev> (Docker) engineshelf:<key>

Nothing has to be re-downloaded. On first run, ~/.chromium-stack is moved
to ~/.engineshelf — builds, profiles and logs intact. ~/.browsers-emu, the
name before that, is adopted the same way. Chromium builds keep the bare revision
as their on-disk name, so nothing already installed is stranded.

If you point the home somewhere else with an environment variable, rename it to
ENGINESHELF_HOME
. BROWSERS_EMU_HOME is still read everywhere;
CHROMIUM_STACK_HOME is read by the command line only, so leaving that one set
would have the manager and the container launcher looking in ~/.engineshelf
while the CLI works out of your chosen directory.

Docker is the one exception — see Upgrading.

Three more engines

A selector now names an engine and a version. A bare number still means Chromium,
which is every selector this tool used to accept:

./engineshelf.sh run 120          # Chromium, as before
./engineshelf.sh run firefox:115
./engineshelf.sh run edge:151
./engineshelf.sh run webkit:18.2
Engine Reaches back to Builds come from The catch
Chromium 2017 · milestone 60 the Chromium snapshot archive nothing is pruned — any revision works, not just catalogued ones
Firefox 2017 · Firefox 51 ftp.mozilla.org, every release ever shipped old builds trust only the roots they shipped with, so the OS trust store is switched on for them
Edge 2021 in a container · about six months natively on macOS the Linux package pool, and Microsoft's enterprise feed mac downloads carry a per-file GUID that cannot be constructed, so natively only what the feed still lists is reachable. Windows gets no native Edge at all — the container is the only route
WebKit 2021 Playwright's build CDN builds are deleted over time and older OS releases pin different revisions, so a version can exist for Linux with no macOS archive ever published

Native engines per host: macOS all four, Linux all four, Windows
Chromium, Firefox and WebKit.

Two things worth naming:

  • Firefox gets a prepared profile. security.enterprise_roots.enabled is
    turned on, because a 2019 Firefox has never heard of the certificate
    authorities issued since it was built and rejects most of today's HTTPS
    without it. The updater and telemetry are off — a pinned build that updates
    itself is not a pinned build. Chromium's switches are not passed to it: an
    unknown -flag opens a dialog in Firefox rather than being ignored.
  • WebKit is listed as WebKit, never Safari. What opens is a MiniBrowser
    shell around the engine — no Safari interface, no Intelligent Tracking
    Prevention, no Safari media stack. It will catch a CSS property Safari has not
    shipped; it will not reproduce a seven-day cookie cap or an HLS stream.

Everything an engine differs in — display name, host platforms, on-disk name,
binary path, extraction, launch environment, launch flags — is answered in
lib/engines.sh and lib/engines.ps1, one function per question. Adding a
fifth engine touches those two files and the catalog, not the launcher.

The shelf builds itself

The old tools/refresh-catalog.py carried a hand-written MILESTONES list, so
the shelf only grew when somebody edited a Python file. Firefox alone has shipped
158 majors; that does not scale to four vendors.

tools/discover.py reads each vendor's own live index instead — chromiumdash,
product-details.mozilla.org with ftp.mozilla.org, the Microsoft Edge package
pool with the enterprise feed, and Playwright's CDN cross-checked against
playwright-core's browsers.json. Nothing in it names a version.

1.0.4 1.1.5
Chromium 21 curated milestones 92 releases, 60 → 152
Firefox 104 releases, 51 → 154
Edge 39 releases, 95 → 151
WebKit 53 builds, r1446 → 26.5

Each engine degrades on its own: one vendor being unreachable costs that column,
not the run, and an engine that returns nothing keeps the rows it already had
rather than emptying its tab as though it had been discontinued.

A changelog on every row

The shelf's notes used to be twenty-odd hand-written lines on curated Chromium
milestones — "aspect-ratio and :is() (88)" — and nothing at all for anything
else. Nobody was going to hand-write 288 of those, and no vendor publishes one a
tool can read.

tools/features.py inverts MDN's browser-compat-data by version instead, which
answers exactly the question somebody browsing a shelf is asking: what can I
test in this one that I could not test in the one before.
It writes
features.tsv260 of the 288 rows now carry a real changelog, ordered by
how notable each entry is, with a count for what a row has no space for.

That also makes the search box a feature index. Type aspect-ratio and you get
Chromium 88 and Firefox 89. The box takes an engine, a version, a revision, a
release date or a feature name.

The engine-comparison grid is gone. Comparing across engines is what search
does now, and the grid was two hundred lines of CSS answering it worse.

The manager

  • A splash screen while the server comes up, naming the step it is on — a
    slow start says which part is slow rather than sitting on one word.
  • An engine filter in the sidebar. Four engines share one shelf and most of
    the time you want one of them.
  • Sticky era headings2017 – 2019, 2020 – 2021, 2022 – 2023,
    2024 – today — with all four engines interleaved by release date. The
    heading stays on screen for as long as its rows do, the way a phone gallery
    keeps the month in view, so the per-era caption is gone: it read once.
  • Progress on the row, not in one bar at the top. Each row shows its own
    phase and percentage. A native launch and a Docker build can both be going on
    the same row, and both are now visible — the single global bar could only ever
    show whichever one it reached first.
  • Cancel on installs, launches and Docker builds, from the row or the log
    panel. The half-fetched archive and the part-unpacked directory go with it.
  • One log per job. The panel has a tab per job and keeps finished ones, so a
    log is still reachable after the next job starts; one button hides every
    finished tab (nothing is deleted — writing to that log brings its tab back),
    and the panel's top edge drags to resize.
  • Open the native window again. A browser you have lost behind other windows
    has no handle the page can reach, so the server raises it.
  • One manager at a time. Opening the app while it is running used to start a
    second server on the next port; the copy of Chrome already running took the new
    window, so the process the new manager was watching exited at once, it read
    that as "window closed", and it quit a second later — taking the first
    manager's containers down with it
    , and leaving a window pointing at a server
    that was gone. A launch now asks first, and reopens the existing window. On
    macOS, ⌘N opens a second window onto the same shelf.
  • New endpoints: /api/alive, /api/features, /api/logs, /api/log/<id>,
    /api/raise.
  • Removed: the Rosetta only filter (each row still says what it needs) and
    the add by revision dialog — raw revisions are a command-line thing
    (./engineshelf.sh run 638880), and such builds still group under
    Added by revision.

macOS: the window belongs to the app

EngineShelf.app is a real Cocoa app with a WKWebView in it, replacing the
tiny launcher that shelled out to Chrome in --app mode.

  • The Dock icon is EngineShelf's, and pressing it brings the manager back — from
    Stage Manager, from a hidden desktop. Before, the Dock icon belonged to Chrome,
    which answered a click by finding no window of its own and opening an ordinary
    New Tab window on google.com. A window parked in Stage Manager could not be got
    back at all.
  • No second browser profile on disk for the manager's own window.
  • CFBundleExecutable is a compiled binary now, which it has to be: a shell
    script works as one until the project sits under ~/Documents, ~/Desktop or
    ~/Downloads, where macOS attributes the file access to /bin/bash, denies it
    outright and never asks. With a binary, macOS asks once and remembers.
  • The bundle still works both ways — self-contained release, or the repo checkout
    with the scripts beside it.

Docker: four images

Dockerfile.firefox, Dockerfile.edge and Dockerfile.webkit join the Chromium
one, and engineshelf-docker.sh takes the same engine selectors as the launcher:

./engineshelf-docker.sh start edge:95        # no host can do this natively
./engineshelf-docker.sh start webkit:16.4    # no macOS archive was ever published
  • Edge is not a convenience here. The enterprise feed holds about six months,
    so on macOS and Windows the native launcher genuinely cannot reach Edge 95 or
    114. The Linux apt pool has kept every .deb since 2021 at a constructible
    URL, so the container is the only way to open an old Edge on any host.
  • WebKit likewise. Playwright deletes old builds and pins different revisions
    per OS release: r1860 (WebKit 16.4) is still served as
    webkit-ubuntu-22.04.zip and has no mac counterpart at all. That image is
    Ubuntu 22.04 rather than Debian on purpose — on Debian 12 the build dies at
    exec on libicudata.so.70, because Debian 12 carries ICU 72 and the bundle
    brings no ICU of its own.
  • Firefox's image is for the old ones. Firefox installs natively everywhere,
    but a 2017 build against a 2026 macOS is the same bet that fails for old
    Chromium.
  • Container URLs and versions resolve through lib/engines.sh with the platform
    forced, so the container and the native launcher cannot come to disagree about
    which build is Firefox 115.
  • The noVNC clipboard patch is one script shared by all four images
    (docker/novnc-clipboard.sh) instead of four copies of the same two sed
    lines — three chances for one to keep working while the others quietly stop
    patching anything, and a clipboard that does not work is not loud. It fails the
    build if noVNC's layout has moved.
  • docker/novnc-serve.py serves noVNC with cache headers. websockify sends none,
    so a browser fell back to a heuristic on each file's mtime — 2021 for almost
    everything in the Debian package, minutes for the one file the clipboard patch
    rewrites. Rebuild an image and the new ui.js met a core/ cached from the
    very first container ever opened, and noVNC died on
    SyntaxError: ... does not provide an export named 'dragThreshold', naming an
    export sitting right there in the file on disk.

Fixed

  • Some Chromium milestones cannot start natively at all, and now say so. On
    macOS 26, Chromium 120 dies with SIGSEGV before a window appears — and so does
    the x86_64 build of the same milestone under Rosetta, within seconds, at
    random. Both were tried. There is no second native build to fall back to, so
    offering one would cost 136 MB to fail again; the container, which runs the
    Linux build and never touches Rosetta, is suggested instead. Recorded the first
    time it happens, keyed by macOS major version as well as milestone, so an
    OS upgrade re-answers the question rather than inheriting the old verdict. Said
    before a launch already known to fail, not only after one that just did.
  • A trailing blank line in a command-substitution loop left the launch's exit
    status at 1, and set -e then killed the script with nothing printed.
  • Docker actions for a selector no image understands are refused up front. The
    image is built around a Chromium snapshot revision; engineshelf-docker.sh has
    no idea what firefox:115 would mean, and used to fail with the reason buried
    in a job log.
  • The landing page's screenshots were staged by hand and had gone stale — a
    Chromium-only list, under the project's previous name, with paths from a machine
    nobody has. tools/make-screenshots.py scripts the scene against a throwaway
    server borrowed from gui/server.py, so re-running it produces the same picture
    unless the page itself changed.

Changed

  • The landing page leads with four engines: per-engine tabs on the version
    scrubber and the newest build of each engine in the hero, both rewritten from
    the catalog by tools/sync-landing.py.
  • tools/check-phases.mjs is new and worth knowing about if you touch either
    side of the CLI/page boundary. The page reads a job's phase out of the CLI's own
    output, coupled by nothing but the wording of a few lines — and that coupling
    broke once already, silently: both patterns spelled "Chromium", so a Firefox,
    Edge or WebKit download reported no progress and a browser that was up never
    registered as open. Nothing failed loudly; the row was just wrong. node tools/check-phases.mjs asserts the round trip for all four engines against the
    real gui/app.js.
  • tools/release.sh now copies the whole of docker/. Naming files one by one is
    how three of the four Dockerfiles, and the script the fourth one COPYs, came
    to be left out of a build.
  • tools/refresh-catalog.py still pins one verified Chromium build per milestone
    per platform; tools/discover.py is what grows the shelf.

Upgrading

Rebuild your Docker images. Images, containers and volumes are named
engineshelf:* now, so anything built by an earlier version is invisible to this
one — it will not be found, reused, or listed in the disk read-out. Remove the old
ones by hand and rebuild:

docker rm -f $(docker ps -aq --filter name=chromium-stack-) 2>/dev/null
docker rmi -f $(docker images 'chromium-stack' -q) 2>/dev/null
docker volume rm $(docker volume ls -q --filter name=chromium-stack-) 2>/dev/null
./engineshelf-docker.sh rebuild 74

Volumes hold the container profiles, so dropping them resets cookies and logins
inside those containers — keep them if that matters and delete only the images.

Everything else needs nothing: extract over the old copy and start it. Browsers
and profiles move themselves out of ~/.chromium-stack on the first run and are
not re-downloaded.

Old download links break. The repository is 1m93/EngineShelf, and the
artifacts are EngineShelf-1.1.5-macOS.zip, EngineShelf-1.1.5-Windows.zip and
EngineShelf-1.1.5-Linux.tar.gz. Verify with SHA256SUMS.txt.