Berthly 1.1.0
What's Changed
Berthly 1.1.0 is a feature release: automatic update detection for running
containers/images, drag-and-drop builds, and a round of security hardening,
on top of six days of bug fixes and infrastructure work since 1.0.1.
New features
- Watchtower-style recreate with the latest image — Berthly now detects
when a pulled image's registry has published a newer digest than your
local copy (or than a running container's pinned digest), badges the
affected Images/Compute rows, and can recreate a container from the
updated image while preserving its configuration and volumes. The
confirm sheet discloses that the container's writable layer is
discarded. A new per-host "insecure registry" memory lets the background
update checker and recreate's own pull reuse HTTP for registries you've
already trusted elsewhere (Pull/Push/Run/Sign-in/Machine Create), with a
Settings section to review or forget remembered hosts. - Drag-and-drop builds — drop a
Dockerfile/Containerfile(including
common prefix/suffix variants) from Finder onto the main window or
sidebar to open the Build sheet pre-filled with the resolved context and
path. Symlink-safe: the dropped item's visible name is validated, but
only the resolved target is used for the actual paths. - Push from the image row context menu — "Push…" is now reachable via
right-click, not justImageDetailView's toolbar.
Security hardening (#17)
- Verify Apple-signed installer packages for both installs and upgrades.
- Restrict privileged DNS execution to a trusted root-owned Apple binary
and a system-onlyPATH. - Bound build logs, clean up builder event loops, and serialize
timed-out pushes. - Make CID and persisted app-state files owner-only and non-overwriting.
- Warn users against placing secrets in persisted build arguments.
Fixes
- Insecure-registry sign-in on a non-standard HTTP port now mirrors
container registry login's own host/port handling — it previously
failed because the rawhost:portstring skipped the scheme override. - Deleting a network row via its context menu crashed the app (AppKit
"layout engine changed during its own layout pass," thrown while the row
animated out of the sidebar List) — reproduced against a real daemon,
not just mocks, and fixed. - Builder/Image/Volume rows could silently stay stale after a daemon-side
state change because their customEquatableconformances didn't cover
every rendered field (a builder staying "Running" after Stop was the
reported case; the fix audited and widened every model in
Core/Models.swift, not just that one). - Starting a stopped container while its Terminal tab was selected could
freeze the row at half height — the terminal's focus grab landed mid
row-insert animation; focus is now deferred until the animation
completes. - "Check for Updates" briefly looked like it was spinning inside the
adjacent Refresh button — both were icon-only and visually fused into
the same toolbar capsule; it now stays labeled while checking. - Removed redundant UI: the Registries scope strip (duplicated the
Keychain info banner), the ⌘K palette's Refresh entry (duplicated ⌘R),
and made toolbar Build/Pull contextual to Compute/Images instead of
always visible.
Performance
- Image metadata (arch/size/created/inspect data) is now cached by content
digest instead of being fully re-decoded from the content store on every
5-second poll tick — a digest is decoded at most once per poll even when
multiple references (retags) share it, with stale entries evicted when
their digest disappears.
Infrastructure
- Adopted SwiftLint at zero findings, gated in CI (
--strict, so any new
warning fails the build). - Added a CodeQL advanced-setup workflow with a manual Swift build (the
default autobuild can't handle Berthly's Xcode 26 / Metal toolchain /
SPM requirements) plus a workflow-analysis job. - Added a ShellCheck CI job; fixed the real issues it caught in
smoke.sh. - Branded, drag-to-install DMG layout replacing the bare Finder-default
window. - Expanded test coverage substantially: context-menu, System page, image
archive, large-inventory (correctness + performance), and several new
E2E journeys (Tag→Save→Load round-trip, Copy Files, log streaming,
restart/kill, credentialed push/pull, Watchtower recreate).
Testing
This release shipped only after its gate run passed on the release commit:
- 497 unit tests passed — 90% line coverage of the pure
logic layer (Berthly/Coremodels, mapping, and planning; 50%
including the daemon/terminal I/O plumbing, which the end-to-end suite
exercises against a real daemon instead). - 72 UI tests (deterministic mock-daemon XCUITest) and
18 real-daemon end-to-end journeys guard the UI wiring and the
daemon integration. SwiftUI view bodies are covered by these suites, not
unit tests, by design.
Full Changelog: v1.0.1...v1.1.0