Portside V3: full native macOS rewrite in SwiftUI - #1
Merged
Conversation
Rebuild the Electron app as a native SwiftUI application (macOS 14+, Swift Package Manager), preserving all V2 functionality: - Docker Engine API over mutual TLS via a Network.framework HTTP/1.1 transport (client certificates, CA-pinned verification with only the hostname check waived, exec stream hijack for the terminal) - Dashboard with host rings, persisted Live/24h/7d history charts, and top-consumer lists; Insights action list; live Activity feed - Container grid/list with compose stacks, manual groups, per-container customization, bulk actions, and a detail inspector - Live logs with ANSI colors, a SwiftTerm-based exec console, and a volume-aware file browser with in-app editing - Deploy wizard, docker-compose import (Yams), container export to compose.yml / docker run, and edit-and-recreate with rollback - Image update checker with digest comparison, per-container auto-update, private registry credentials in the Keychain - GitHub watch and Git Deploy (notify / auto / nightly), crash log snapshots, per-event notification rules, certificate expiry warnings - Multi-host support with per-host certificates, menu bar companion, command palette, six accent themes with light/dark/system appearance - In-place self-updater against GitHub Releases (download, swap, relaunch), DMG packaging script, and a single build/release workflow - Reads the V2 config file and certificate layout, so existing setups migrate automatically Also: rewrite the README, switch the license to MIT, and remove the Electron sources, icons, and legacy workflows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GMD3Q7bnJ9vJDdkaiuBN4Y
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GMD3Q7bnJ9vJDdkaiuBN4Y
- Rename the NetworkSummary.IPAM property so it no longer shadows the nested IPAM type - Copy the image map before the update-check task group (Sendable capture) and split the too-complex shortName expression - Pass Unmanaged<CFError> to SecCertificateCopyValues - Use concrete Colors in the certificate-expiry ternary - Give explicit initializers to views constructed from other files (private @State storage makes the synthesized memberwise initializer private) - Color chart series explicitly instead of via chartForegroundStyleScale Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GMD3Q7bnJ9vJDdkaiuBN4Y
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rebuilds Portside from the ground up as a native macOS app in SwiftUI (macOS 14+, Swift Package Manager), replacing the Electron implementation while preserving all existing functionality. Architecture, styling, packaging, CI, release process, and the one-click update experience follow the patterns established in SentryHub and Buckett.
What's here
Core (Services/)
DockerTransport— a minimal HTTP/1.1 client over Network.framework with mutual TLS: client certificate auth, server verification anchored to the importedca.pem(only the hostname check is waived, since NAS certs name the device while we connect by IP), streaming bodies, and theConnection: Upgradehijack the exec API needs — something URLSession cannot do.TLSIdentity— PEM parsing (PKCS#1 / PKCS#8 / EC) into aSecIdentity, plus certificate summaries for expiry warnings.DockerClient— typed Engine API: containers, images, volumes, networks, events (with 24 h backfill), stats, logs (fetch + follow), exec (interactive + capture), archive get/put with a minimal tar implementation, pulls withX-Registry-Auth, prunes, and the shared stop → rename → create → start → delete replace dance with rollback.RegistryClient/ImageUpdateService— manifest digest comparison with bearer-token auth (list digest OR any platform digest counts as up-to-date), per-container auto-update with a same-digest guard.GitHubService— release/commit watching and Git Deploy via a throwawayalpine/gitcontainer (token passed only at run time).ComposeParser(Yams),ContainerExport(compose.yml / docker run),CrashLogStore,HistoryStore,Notifier,Keychain.UpdateChecker+SelfUpdater— the same in-place DMG update flow as Buckett/SentryHub: download, swap the bundle with rollback, relaunch.UI (Views/)
Destructive-action design carried over from V2: removing containers requires an explicit acknowledgment; destroying volumes requires typing the name.
Migration: V3 reads the same
~/Library/Application Support/Portside/config.jsonand certificate layout as V2, so hosts, groups, customizations, notification rules, and watch lists carry over. Registry credentials and the GitHub token move to the Keychain and are re-entered once.Repo cleanup
main.js,preload.js, renderer, HTML/CSS, icons, tests, npm manifests) and the oldci.yml/release.ymlremoved.scripts/make_app.sh(universal binary → .app → DMG) and a singlebuild.ymlworkflow: every push/PR builds and uploads a DMG artifact; tag pushes (v*) publish a GitHub Release the in-app updater consumes.Testing
There is no macOS toolchain in the authoring environment, so compilation is verified by the CI workflow in this PR (macos-14 runner). Runtime behavior against a real Docker host should be smoke-tested from the built DMG artifact: connect, dashboard metrics, logs, terminal, files, deploy, and an image update cycle.
🤖 Generated with Claude Code
https://claude.ai/code/session_01GMD3Q7bnJ9vJDdkaiuBN4Y
Generated by Claude Code