Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
467 changes: 0 additions & 467 deletions CONTEXTO.md

This file was deleted.

23 changes: 23 additions & 0 deletions Casks/macker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
cask "macker" do
# Refresh version and sha256 after each stable release with:
# ./Scripts/update-cask.sh v1.0.0.<run>
version "1.0.0.9"
sha256 "e6ef4ade4e8b06a116e669b5ee44903ccc4a44db27dd0b7f01a6510b52033f44"

url "https://github.com/djpfs/Macker/releases/download/v#{version}/Macker-#{version}.pkg",
verified: "github.com/djpfs/Macker/"
name "Macker"
desc "Docker Desktop replacement on Apple's native container runtime"
homepage "https://github.com/djpfs/Macker"

depends_on macos: :sequoia

pkg "Macker-#{version}.pkg"

uninstall pkgutil: "com.macker.app"

zap trash: [
"~/Library/Application Support/Macker",
"~/Library/Preferences/com.macker.app.plist",
]
end
557 changes: 288 additions & 269 deletions README.md

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions Scripts/update-cask.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/bash
#===----------------------------------------------------------------------===//
# update-cask.sh — refresh Casks/macker.rb with the version and sha256 of a
# published GitHub release.
#
# Usage: ./Scripts/update-cask.sh v1.0.0.1
# Run this after each stable release (main branch) and commit the updated cask.
#===----------------------------------------------------------------------===//
set -euo pipefail

cd "$(dirname "$0")/.."

TAG="${1:?usage: update-cask.sh <release-tag>}"
CASK="Casks/macker.rb"
VERSION="${TAG#v}"
PKG_URL="https://github.com/djpfs/Macker/releases/download/${TAG}/Macker-${VERSION}.pkg"

echo "==> Downloading ${PKG_URL}..."
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
curl -fsSL -o "$TMP/Macker.pkg" "$PKG_URL"

echo "==> Computing sha256..."
SHA="$(shasum -a 256 "$TMP/Macker.pkg" | awk '{print $1}')"

echo "==> Updating ${CASK} (version ${VERSION})..."
sed -i '' \
-e "s/version \".*\"/version \"${VERSION}\"/" \
-e "s/sha256 \".*\"/sha256 \"${SHA}\"/" \
"$CASK"

echo "[OK] ${CASK} updated to ${VERSION} (${SHA})"
29 changes: 29 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Architecture

```
┌─────────────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ Views (SwiftUI) ← AppState (@Observable) │
├─────────────────────────────────────────────────────────────┤
│ SERVICE LAYER │
│ AppState (polling, state aggregation) │
│ ContainerService | ComposeEngine | HotReloadService │
├─────────────────────────────────────────────────────────────┤
│ BACKEND LAYER │
│ XPCClient (primary) | ProcessRunner (fallback) │
│ LaunchdManager (daemon lifecycle) │
├─────────────────────────────────────────────────────────────┤
│ PLATFORM LAYER │
│ container-apiserver (XPC) | container CLI | virtiofs/VZ │
└─────────────────────────────────────────────────────────────┘
```

## Modules

| Module | Purpose |
|--------|---------|
| `AppleDockerApp` | SwiftUI GUI + dual-binary dispatch |
| `AppleDockerCLI` | ArgumentParser root + `docker`/`compose` shim |
| `ContainerBackend` | Lightweight XPC client for `container-apiserver` |
| `ComposeEngine` | YAML parser, resolver, orchestrator, DNS sync, health checks |
| `HotReloadService` | FSEvents → synthetic inotify bridge for virtiofs mounts |
33 changes: 33 additions & 0 deletions docs/COMMANDS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Supported commands

## `docker` (container)

| Group | Commands |
|-------|----------|
| **Containers** | `ps`, `run`, `create`, `start`, `stop`, `restart`, `kill`, `rm`, `exec`, `logs`, `stats`, `wait`, `port`, `top`, `pause`, `unpause`, `cp`, `inspect`, `events` |
| **Images** | `images`, `pull`, `push`, `build`, `tag`, `rmi`, `prune`, `load`, `save`, `search` |
| **Volumes** | `volume create`, `volume ls`, `volume rm`, `volume inspect`, `volume prune` |
| **Networks** | `network create`, `network ls`, `network rm`, `network inspect`, `network connect`, `network disconnect`, `network prune` |
| **System** | `system df`, `system prune`, `system info`, `system version` |

## `docker compose`

| Command | Description |
|---------|-------------|
| `up` | Create and start services (`-d` to detach) |
| `down` | Stop and remove services (`-v` removes volumes) |
| `ps` | List project containers |
| `logs` | Stream logs (`-f` follow, `--tail N`) |
| `stop` / `start` / `restart` | Manage service lifecycle |
| `pull` | Pull service images |
| `build` | Build service images |
| `create` | Create services without starting |
| `exec` | Run a command in a service container |
| `run` | Run a one-off command |
| `config` | Print the resolved compose config as JSON |
| `images` | List service images |
| `kill` | Kill service containers |
| `port` | Print the public port for a service |
| `rm` | Remove stopped service containers |

Unsupported commands fail with an explicit, actionable message.
33 changes: 33 additions & 0 deletions docs/COMPOSE-ENGINE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Compose engine & hot reload

## Compose engine

`docker compose up`:

1. Parses `docker-compose.yml` (Yams) with `${VAR}` interpolation and `.env`
support.
2. Resolves `depends_on` topologically (including `service_healthy` gating).
3. Creates and starts containers in dependency order.
4. Writes `<service-name> <ip>` entries into each container's `/etc/hosts` so
services resolve each other by name.
5. Recreates containers when their config hash changes.

**Supported compose keys:** `services`, `image`, `build`, `ports`, `volumes`,
`networks`, `environment`, `env_file`, `depends_on` (with `service_healthy`),
`healthcheck`, `profiles`, `command`, `entrypoint`, `restart`, `labels`,
`deploy.resources.limits`, `${VAR}` interpolation, `.env`.

## Hot reload

virtiofs does not propagate inotify events into the guest, so watch tools
(Vite, webpack, nodemon, Air) never see host-side edits. Following the
Colima/Lima `--mount-inotify` pattern:

1. `FSEventWatcher` watches host directories (debounced, 100ms windows).
2. `InotifyBridge` maps host paths to container paths and batches them.
3. A tiny guest agent (`Resources/guest-agent`, built with
`Scripts/build-guest-agent.sh`) `touch`es each file.
4. The Linux kernel emits inotify ATTRIB → watch tools rebuild.

**Known limitations:** only ATTRIB events are synthesized (no MODIFY/CREATE/
DELETE), and deletions on the host do not propagate.
82 changes: 82 additions & 0 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Contributing

Contributions are welcome — bug reports, feature requests, docs, and pull
requests. Here's how to get involved.

## Development

```bash
make build # debug build
make test # unit tests (needs full Xcode)
make release # release build
make lint # swift-format (if installed)
make guest-agent # cross-compile the hot-reload agent
make clean # remove build artifacts
```

Tests run in CI on macOS with full Xcode (XCTest is not shipped with
CommandLineTools). The XPC protocol is not a stable public API — client and
`container-apiserver` ship in lockstep; bump `containerVersion` in
`Package.swift` when updating the runtime.

## Development setup

In short:

```bash
make build # debug build
make test # unit tests (needs full Xcode)
make lint # swift-format
make run ARGS="docker ps" # run the CLI headless
```

Requirements:

- **macOS 15+** on **Apple Silicon** (arm64).
- **Full Xcode** — XCTest is not shipped with CommandLineTools, so `make test`
and the test targets require Xcode.
- [apple/container](https://github.com/apple/container) installed and running
(`container-apiserver`). The XPC protocol is pinned to **1.2.2** in
`Package.swift` — client and runtime ship in lockstep.

## Reporting issues

Open an [issue](https://github.com/djpfs/Macker/issues) with:

- A clear title and description of the problem.
- Steps to reproduce, including your macOS version and `apple/container`
version.
- The output of `docker version` and `docker selftest` if relevant.

## Branch strategy

- `main` — stable releases. Each successful push builds the `.pkg` and
publishes it as a GitHub Release.
- `develop` — integration branch. **Open pull requests against `develop`.**

## Code style

- Run `make lint` (swift-format) before committing; CI enforces it.
- Match the surrounding code — same naming, comment density, and structure.
- Keep changes focused: one logical change per pull request.

## Testing

- Add or update tests for the code you change. Test targets live in `Tests/`.
- Make sure `make test` passes locally (requires Xcode). CI runs the full
suite (build, test, lint, CodeQL) on every push and pull request.

## Commit messages

- Write clear, imperative commit messages that describe the change.
- Keep the history clean — amend or rebase locally before pushing.

## Releasing

Releases are cut from `main` by the CI: a push to `main` builds the `.pkg` and
publishes it as a stable GitHub Release (`v1.0.0.<run>`). After a release,
refresh the Homebrew cask with:

```bash
./Scripts/update-cask.sh v1.0.0.<run>
```
20 changes: 20 additions & 0 deletions docs/GUI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# GUI features

## Menu bar

The menu bar extra shows customizable metrics (CPU, memory, container counts,
etc.) with per-container quick actions. Configure which metrics appear and
their order in **Settings → Menu bar**. The menu bar also includes an
**Open Macker** action that focuses the existing window instead of
spawning a new instance.

## Storage & cleanup

The native runtime stores each image as a full ext4 snapshot (no layer
deduplication like Docker's overlay2), so storage can grow quickly. Macker
provides:

- **GUI:** Settings → Storage — disk usage, prune unused images, prune all
images, delete the buildkit builder, and full cleanup.
- **CLI:** `docker system prune [-a] [--builder]` — `--builder` also deletes
the buildkit builder (frees the build cache).
51 changes: 51 additions & 0 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Roadmap & limitations

## Limitations

- **Apple Silicon only** — containers run as Linux VMs via Virtualization.framework.
- **Protocol lockstep** — the XPC protocol is pinned to `apple/container 1.2.2`.
- **No layer deduplication** — each image is a full snapshot; storage can grow
quickly (see [GUI features](GUI.md)).
- **Hot reload** — only ATTRIB events are synthesized.
- **Not all docker commands** — unsupported commands fail with an explicit
message (e.g. `attach`, `update`, `history`, `rename`).

## Next steps

Ideas and improvements on the roadmap, roughly ordered by impact:

### Reliability & correctness
- **Layer deduplication** — the native runtime stores each image as a full
snapshot. Investigate sharing base layers across images to cut storage.
- **More docker commands** — implement `attach`, `update`, `history`, `rename`,
and `docker compose` gaps (`top`, `events`, `pause`).
- **Hot reload fidelity** — synthesize MODIFY/CREATE/DELETE inotify events, not
just ATTRIB, and propagate host-side deletions.
- **Graceful daemon handling** — auto-start `container-apiserver` if it is not
running, and surface a clear onboarding flow when the runtime is missing.

### Distribution
- **Notarization & signing** — sign the `.pkg` with a Developer ID and notarize
it so Gatekeeper accepts it out of the box.
- **Auto-update** — integrate Sparkle for seamless in-app updates.
- **CI release pipeline** — build and attach signed `.pkg`/`.dmg` artifacts to
GitHub Releases on tag push.

### GUI & UX
- **Compose history** — persist recently used compose files for one-click
reload.
- **Container settings** — richer per-container configuration (networks, port
mapping, resource limits) from the detail pane.
- **Dashboard** — more chart types and per-container filtering.
- **Localization** — add pt-BR and other locales.

### Performance
- **Faster polling** — batch stats collection and reduce refresh overhead for
large container counts.
- **Build cache** — surface buildkit cache usage and per-image reclaimable
space in the GUI.

### Testing
- **More unit tests** — expand coverage for the compose parser, orchestrator,
and docker shim.
- **Integration tests** — run against a real `container-apiserver` in CI.
File renamed without changes.
Binary file added docs/img/layers.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-05.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-06.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-07.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-08.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-09.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-11.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-12.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-13.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/screenshot-14.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading