Skip to content

Commit bfb61f1

Browse files
authored
Merge pull request #2 from djpfs/develop
Develop
2 parents 5a6d20f + ee80839 commit bfb61f1

26 files changed

Lines changed: 591 additions & 736 deletions

‎CONTEXTO.md‎

Lines changed: 0 additions & 467 deletions
This file was deleted.

‎Casks/macker.rb‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
cask "macker" do
2+
# Refresh version and sha256 after each stable release with:
3+
# ./Scripts/update-cask.sh v1.0.0.<run>
4+
version "1.0.0.9"
5+
sha256 "e6ef4ade4e8b06a116e669b5ee44903ccc4a44db27dd0b7f01a6510b52033f44"
6+
7+
url "https://github.com/djpfs/Macker/releases/download/v#{version}/Macker-#{version}.pkg",
8+
verified: "github.com/djpfs/Macker/"
9+
name "Macker"
10+
desc "Docker Desktop replacement on Apple's native container runtime"
11+
homepage "https://github.com/djpfs/Macker"
12+
13+
depends_on macos: :sequoia
14+
15+
pkg "Macker-#{version}.pkg"
16+
17+
uninstall pkgutil: "com.macker.app"
18+
19+
zap trash: [
20+
"~/Library/Application Support/Macker",
21+
"~/Library/Preferences/com.macker.app.plist",
22+
]
23+
end

‎README.md‎

Lines changed: 288 additions & 269 deletions
Large diffs are not rendered by default.

‎Scripts/update-cask.sh‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/bash
2+
#===----------------------------------------------------------------------===//
3+
# update-cask.sh — refresh Casks/macker.rb with the version and sha256 of a
4+
# published GitHub release.
5+
#
6+
# Usage: ./Scripts/update-cask.sh v1.0.0.1
7+
# Run this after each stable release (main branch) and commit the updated cask.
8+
#===----------------------------------------------------------------------===//
9+
set -euo pipefail
10+
11+
cd "$(dirname "$0")/.."
12+
13+
TAG="${1:?usage: update-cask.sh <release-tag>}"
14+
CASK="Casks/macker.rb"
15+
VERSION="${TAG#v}"
16+
PKG_URL="https://github.com/djpfs/Macker/releases/download/${TAG}/Macker-${VERSION}.pkg"
17+
18+
echo "==> Downloading ${PKG_URL}..."
19+
TMP="$(mktemp -d)"
20+
trap 'rm -rf "$TMP"' EXIT
21+
curl -fsSL -o "$TMP/Macker.pkg" "$PKG_URL"
22+
23+
echo "==> Computing sha256..."
24+
SHA="$(shasum -a 256 "$TMP/Macker.pkg" | awk '{print $1}')"
25+
26+
echo "==> Updating ${CASK} (version ${VERSION})..."
27+
sed -i '' \
28+
-e "s/version \".*\"/version \"${VERSION}\"/" \
29+
-e "s/sha256 \".*\"/sha256 \"${SHA}\"/" \
30+
"$CASK"
31+
32+
echo "[OK] ${CASK} updated to ${VERSION} (${SHA})"

‎docs/ARCHITECTURE.md‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Architecture
2+
3+
```
4+
┌─────────────────────────────────────────────────────────────┐
5+
│ PRESENTATION LAYER │
6+
│ Views (SwiftUI) ← AppState (@Observable) │
7+
├─────────────────────────────────────────────────────────────┤
8+
│ SERVICE LAYER │
9+
│ AppState (polling, state aggregation) │
10+
│ ContainerService | ComposeEngine | HotReloadService │
11+
├─────────────────────────────────────────────────────────────┤
12+
│ BACKEND LAYER │
13+
│ XPCClient (primary) | ProcessRunner (fallback) │
14+
│ LaunchdManager (daemon lifecycle) │
15+
├─────────────────────────────────────────────────────────────┤
16+
│ PLATFORM LAYER │
17+
│ container-apiserver (XPC) | container CLI | virtiofs/VZ │
18+
└─────────────────────────────────────────────────────────────┘
19+
```
20+
21+
## Modules
22+
23+
| Module | Purpose |
24+
|--------|---------|
25+
| `AppleDockerApp` | SwiftUI GUI + dual-binary dispatch |
26+
| `AppleDockerCLI` | ArgumentParser root + `docker`/`compose` shim |
27+
| `ContainerBackend` | Lightweight XPC client for `container-apiserver` |
28+
| `ComposeEngine` | YAML parser, resolver, orchestrator, DNS sync, health checks |
29+
| `HotReloadService` | FSEvents → synthetic inotify bridge for virtiofs mounts |

‎docs/COMMANDS.md‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Supported commands
2+
3+
## `docker` (container)
4+
5+
| Group | Commands |
6+
|-------|----------|
7+
| **Containers** | `ps`, `run`, `create`, `start`, `stop`, `restart`, `kill`, `rm`, `exec`, `logs`, `stats`, `wait`, `port`, `top`, `pause`, `unpause`, `cp`, `inspect`, `events` |
8+
| **Images** | `images`, `pull`, `push`, `build`, `tag`, `rmi`, `prune`, `load`, `save`, `search` |
9+
| **Volumes** | `volume create`, `volume ls`, `volume rm`, `volume inspect`, `volume prune` |
10+
| **Networks** | `network create`, `network ls`, `network rm`, `network inspect`, `network connect`, `network disconnect`, `network prune` |
11+
| **System** | `system df`, `system prune`, `system info`, `system version` |
12+
13+
## `docker compose`
14+
15+
| Command | Description |
16+
|---------|-------------|
17+
| `up` | Create and start services (`-d` to detach) |
18+
| `down` | Stop and remove services (`-v` removes volumes) |
19+
| `ps` | List project containers |
20+
| `logs` | Stream logs (`-f` follow, `--tail N`) |
21+
| `stop` / `start` / `restart` | Manage service lifecycle |
22+
| `pull` | Pull service images |
23+
| `build` | Build service images |
24+
| `create` | Create services without starting |
25+
| `exec` | Run a command in a service container |
26+
| `run` | Run a one-off command |
27+
| `config` | Print the resolved compose config as JSON |
28+
| `images` | List service images |
29+
| `kill` | Kill service containers |
30+
| `port` | Print the public port for a service |
31+
| `rm` | Remove stopped service containers |
32+
33+
Unsupported commands fail with an explicit, actionable message.

‎docs/COMPOSE-ENGINE.md‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Compose engine & hot reload
2+
3+
## Compose engine
4+
5+
`docker compose up`:
6+
7+
1. Parses `docker-compose.yml` (Yams) with `${VAR}` interpolation and `.env`
8+
support.
9+
2. Resolves `depends_on` topologically (including `service_healthy` gating).
10+
3. Creates and starts containers in dependency order.
11+
4. Writes `<service-name> <ip>` entries into each container's `/etc/hosts` so
12+
services resolve each other by name.
13+
5. Recreates containers when their config hash changes.
14+
15+
**Supported compose keys:** `services`, `image`, `build`, `ports`, `volumes`,
16+
`networks`, `environment`, `env_file`, `depends_on` (with `service_healthy`),
17+
`healthcheck`, `profiles`, `command`, `entrypoint`, `restart`, `labels`,
18+
`deploy.resources.limits`, `${VAR}` interpolation, `.env`.
19+
20+
## Hot reload
21+
22+
virtiofs does not propagate inotify events into the guest, so watch tools
23+
(Vite, webpack, nodemon, Air) never see host-side edits. Following the
24+
Colima/Lima `--mount-inotify` pattern:
25+
26+
1. `FSEventWatcher` watches host directories (debounced, 100ms windows).
27+
2. `InotifyBridge` maps host paths to container paths and batches them.
28+
3. A tiny guest agent (`Resources/guest-agent`, built with
29+
`Scripts/build-guest-agent.sh`) `touch`es each file.
30+
4. The Linux kernel emits inotify ATTRIB → watch tools rebuild.
31+
32+
**Known limitations:** only ATTRIB events are synthesized (no MODIFY/CREATE/
33+
DELETE), and deletions on the host do not propagate.

‎docs/CONTRIBUTING.md‎

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Contributing
2+
3+
Contributions are welcome — bug reports, feature requests, docs, and pull
4+
requests. Here's how to get involved.
5+
6+
## Development
7+
8+
```bash
9+
make build # debug build
10+
make test # unit tests (needs full Xcode)
11+
make release # release build
12+
make lint # swift-format (if installed)
13+
make guest-agent # cross-compile the hot-reload agent
14+
make clean # remove build artifacts
15+
```
16+
17+
Tests run in CI on macOS with full Xcode (XCTest is not shipped with
18+
CommandLineTools). The XPC protocol is not a stable public API — client and
19+
`container-apiserver` ship in lockstep; bump `containerVersion` in
20+
`Package.swift` when updating the runtime.
21+
22+
## Development setup
23+
24+
In short:
25+
26+
```bash
27+
make build # debug build
28+
make test # unit tests (needs full Xcode)
29+
make lint # swift-format
30+
make run ARGS="docker ps" # run the CLI headless
31+
```
32+
33+
Requirements:
34+
35+
- **macOS 15+** on **Apple Silicon** (arm64).
36+
- **Full Xcode** — XCTest is not shipped with CommandLineTools, so `make test`
37+
and the test targets require Xcode.
38+
- [apple/container](https://github.com/apple/container) installed and running
39+
(`container-apiserver`). The XPC protocol is pinned to **1.2.2** in
40+
`Package.swift` — client and runtime ship in lockstep.
41+
42+
## Reporting issues
43+
44+
Open an [issue](https://github.com/djpfs/Macker/issues) with:
45+
46+
- A clear title and description of the problem.
47+
- Steps to reproduce, including your macOS version and `apple/container`
48+
version.
49+
- The output of `docker version` and `docker selftest` if relevant.
50+
51+
## Branch strategy
52+
53+
- `main` — stable releases. Each successful push builds the `.pkg` and
54+
publishes it as a GitHub Release.
55+
- `develop` — integration branch. **Open pull requests against `develop`.**
56+
57+
## Code style
58+
59+
- Run `make lint` (swift-format) before committing; CI enforces it.
60+
- Match the surrounding code — same naming, comment density, and structure.
61+
- Keep changes focused: one logical change per pull request.
62+
63+
## Testing
64+
65+
- Add or update tests for the code you change. Test targets live in `Tests/`.
66+
- Make sure `make test` passes locally (requires Xcode). CI runs the full
67+
suite (build, test, lint, CodeQL) on every push and pull request.
68+
69+
## Commit messages
70+
71+
- Write clear, imperative commit messages that describe the change.
72+
- Keep the history clean — amend or rebase locally before pushing.
73+
74+
## Releasing
75+
76+
Releases are cut from `main` by the CI: a push to `main` builds the `.pkg` and
77+
publishes it as a stable GitHub Release (`v1.0.0.<run>`). After a release,
78+
refresh the Homebrew cask with:
79+
80+
```bash
81+
./Scripts/update-cask.sh v1.0.0.<run>
82+
```

‎docs/GUI.md‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# GUI features
2+
3+
## Menu bar
4+
5+
The menu bar extra shows customizable metrics (CPU, memory, container counts,
6+
etc.) with per-container quick actions. Configure which metrics appear and
7+
their order in **Settings → Menu bar**. The menu bar also includes an
8+
**Open Macker** action that focuses the existing window instead of
9+
spawning a new instance.
10+
11+
## Storage & cleanup
12+
13+
The native runtime stores each image as a full ext4 snapshot (no layer
14+
deduplication like Docker's overlay2), so storage can grow quickly. Macker
15+
provides:
16+
17+
- **GUI:** Settings → Storage — disk usage, prune unused images, prune all
18+
images, delete the buildkit builder, and full cleanup.
19+
- **CLI:** `docker system prune [-a] [--builder]` — `--builder` also deletes
20+
the buildkit builder (frees the build cache).

‎docs/ROADMAP.md‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Roadmap & limitations
2+
3+
## Limitations
4+
5+
- **Apple Silicon only** — containers run as Linux VMs via Virtualization.framework.
6+
- **Protocol lockstep** — the XPC protocol is pinned to `apple/container 1.2.2`.
7+
- **No layer deduplication** — each image is a full snapshot; storage can grow
8+
quickly (see [GUI features](GUI.md)).
9+
- **Hot reload** — only ATTRIB events are synthesized.
10+
- **Not all docker commands** — unsupported commands fail with an explicit
11+
message (e.g. `attach`, `update`, `history`, `rename`).
12+
13+
## Next steps
14+
15+
Ideas and improvements on the roadmap, roughly ordered by impact:
16+
17+
### Reliability & correctness
18+
- **Layer deduplication** — the native runtime stores each image as a full
19+
snapshot. Investigate sharing base layers across images to cut storage.
20+
- **More docker commands** — implement `attach`, `update`, `history`, `rename`,
21+
and `docker compose` gaps (`top`, `events`, `pause`).
22+
- **Hot reload fidelity** — synthesize MODIFY/CREATE/DELETE inotify events, not
23+
just ATTRIB, and propagate host-side deletions.
24+
- **Graceful daemon handling** — auto-start `container-apiserver` if it is not
25+
running, and surface a clear onboarding flow when the runtime is missing.
26+
27+
### Distribution
28+
- **Notarization & signing** — sign the `.pkg` with a Developer ID and notarize
29+
it so Gatekeeper accepts it out of the box.
30+
- **Auto-update** — integrate Sparkle for seamless in-app updates.
31+
- **CI release pipeline** — build and attach signed `.pkg`/`.dmg` artifacts to
32+
GitHub Releases on tag push.
33+
34+
### GUI & UX
35+
- **Compose history** — persist recently used compose files for one-click
36+
reload.
37+
- **Container settings** — richer per-container configuration (networks, port
38+
mapping, resource limits) from the detail pane.
39+
- **Dashboard** — more chart types and per-container filtering.
40+
- **Localization** — add pt-BR and other locales.
41+
42+
### Performance
43+
- **Faster polling** — batch stats collection and reduce refresh overhead for
44+
large container counts.
45+
- **Build cache** — surface buildkit cache usage and per-image reclaimable
46+
space in the GUI.
47+
48+
### Testing
49+
- **More unit tests** — expand coverage for the compose parser, orchestrator,
50+
and docker shim.
51+
- **Integration tests** — run against a real `container-apiserver` in CI.

0 commit comments

Comments
 (0)