Build the Apple Container extension

Turns the empty scaffold into a working Nova extension for Apple Container
and container-compose, in the shape of the Docker Suite extension.

Sidebar (Scripts/Sidebar):
- System, Containers, Images, Volumes and Networks sections
- Containers grouped into compose projects via the com.docker.compose.project
  label container-compose stamps; project rows drive compose up/down/build
  against the matching workspace file
- Lifecycle, shell, logs, inspect, browse and copy commands per resource
- Apple Container publishes no event stream, so the sidebar polls while
  visible and only reloads a section when a fingerprint of its data changes,
  which keeps selection and expansion intact

Hostnames (Scripts/Hostnames.js):
- A container's hostname is <name>.<domain> and the domain lives in Apple
  Container's config.toml, which has no CLI setter, so the Hostname Domain
  preference reads and writes that file directly
- Changing it offers to register the domain with macOS and restart the
  services; the write preserves the file's other tables and its mode

Language support:
- Dockerfile and Compose syntaxes backed by tree-sitter grammars built by
  Tools/build-syntaxes.sh, pinned to revisions that generate ABI 14
- The Compose syntax is named dockercompose because that is the languageId
  docker-language-server recognises compose files by
- docker-language-server is downloaded on first use rather than bundled, at
  roughly 40 MB

Icons are generated by Tools/make-icons.py so they can be regenerated rather
than hand-maintained.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01UnSBcR5Lywz5Fbj5FmVbfc
This commit is contained in:
2026-08-18 22:45:14 -03:00
co-authored by Claude Opus 5
parent 0879e5a163
commit 0506f44db3
80 changed files with 5201 additions and 3 deletions
+176
View File
@@ -0,0 +1,176 @@
# Apple Container for Nova
Manage [Apple Container](https://github.com/apple/container) and
[container-compose](https://github.com/mcrich23/container-compose) from Nova:
a sidebar for the things you run, commands for the things you do, and language
support for the files you write.
Built for people whose local development runs on `container` rather than
Docker, and modelled on the shape of Jonathan Coole's Docker Suite for Nova.
## Requirements
- macOS with [Apple Container](https://github.com/apple/container) installed
(`container` on your `PATH`, or point at it in the extension's preferences)
- [container-compose](https://github.com/mcrich23/container-compose) for the
compose commands — optional, everything else works without it
- Nova 11 or later
## The sidebar
**System** — the container services and the builder, with their state, plus
disk usage, the hostname domain and the DNS domains macOS resolves. Start, stop
and restart the services from here; when they are not running, the rest of the
sidebar stays out of your way until they are.
**Containers** — grouped into compose projects using the
`com.docker.compose.project` label that container-compose stamps on the
containers it creates. Containers created any other way collect in an ungrouped
bucket. Start, stop, restart, kill and delete them; open a shell or follow the
logs in your terminal; read logs or `inspect` output in a Nova tab; open a
published port in your browser; copy a name, address or URL. Containers
publishing more than one port ask which to open, and the list includes the
container's DNS name and IP as well.
Select a compose project to run `container-compose up`, `down` or `build`
against the compose file it came from, or to open that file.
**Images** — grouped by repository, with sizes and ages. Run, pull, push, tag,
inspect and delete, plus prune for the whole section. Apple Container's own
builder and init images are hidden unless you ask for them.
**Volumes** — inspect, reveal the backing disk image in Finder, create, delete
and prune.
**Networks** — subnets and gateways, create, inspect, delete and prune. The
built-in `default` network is protected from deletion.
Apple Container publishes no event stream, so the sidebar polls while it is
visible. The interval is configurable, and a section only reloads when
something actually changed, so your selection survives.
## Hostnames and DNS
Apple Container gives a container the hostname `<name>.<domain>`, where the
domain comes from its own `config.toml`, and macOS only resolves that domain
once a resolver entry exists for it. Those are two separate steps, and the
extension treats them as one.
Set **Hostname Domain** in the extension's preferences. Changing it writes the
domain to
`~/Library/Application Support/com.apple.container/config/config.toml`, offers
to register it with macOS through `sudo container system dns create` in your
terminal if it is not registered already, and offers to restart the services so
the change takes hold. After that, `mysql.test` resolves from your Mac and from
other containers. Clearing the field goes back to unqualified hostnames.
The setting starts out showing whatever `config.toml` already says, so it
reflects reality rather than overwriting it.
The System section shows both halves: a **Hostname Domain** row for the domain
containers are named under — flagged when macOS is not resolving it yet — and a
row per registered domain, which can be unregistered from its context menu.
**Register DNS Domain with macOS…** handles the resolver half on its own, and
**Open System Configuration** opens `config.toml` in Nova when you would rather
edit it by hand.
There is no per-container hostname to set: the hostname is the container's name
plus the domain, so name the container (or the compose service) accordingly.
## Commands
Under **Extensions → Apple Container**:
| Command | What it does |
| --- | --- |
| Reload Sidebar | Refetch everything |
| Start / Stop Container Services | `container system start` / `stop` |
| Show System Status / Logs | Status in a tab, logs followed in your terminal |
| Register DNS Domain with macOS… | Adds the resolver entry, via sudo in your terminal |
| Open System Configuration | Opens Apple Container's `config.toml` |
| Compose Up / Down / Build | Runs against the workspace's compose file |
| Build Image from Editor… | `container build` using the Dockerfile in the front editor |
| Pull Image… / Run Image… | Prompts for a reference and runs it in your terminal |
| Restart Language Server | Restarts docker-language-server |
Anything long-running or interactive — shells, followed logs, pulls, builds,
compose — runs in Terminal or iTerm2 so you can watch it. Everything else runs
in the background and reports failures as notifications.
## Language support
Syntax highlighting and folding for **Dockerfiles** (`Dockerfile`,
`Containerfile`, `*.dockerfile`) and **Compose files** (`compose.yaml`,
`docker-compose.yml`, and YAML files with a top-level `services:` key), both
backed by tree-sitter grammars bundled with the extension.
Completion, hover, validation and linting come from
[docker-language-server](https://github.com/docker/docker-language-server). The
binary is about 40 MB, too large to ship inside an extension, so the managed
mode downloads the pinned release into the extension's storage the first time
it is needed. Point at your own binary instead, or turn the server off, in the
extension's preferences.
The Compose syntax is registered under the name `dockercompose` because that is
the language identifier docker-language-server recognises compose files by.
## Preferences
| Setting | Default | Notes |
| --- | --- | --- |
| `container` / `container-compose` path | resolved from PATH | Set these if you install somewhere unusual |
| Container Shell | `/bin/sh` | Used by Open Shell |
| Terminal | Terminal | Or iTerm2 |
| Refresh Interval | 5s | 0 refreshes only on demand |
| Start Services Automatically | off | Runs `container system start` when the sidebar loads |
| Group by Compose Project | on | |
| Container label / description | Name, image and ports | What each row shows |
| Hostname Domain | from `config.toml` | Suffix for container hostnames; writes Apple Container's own config |
| Status Icons | Traffic Lights | Or toggles, or power symbols |
| Group Images by Repository | on | |
| Show Registry Host | off | `docker.io/library/nginx` rather than `nginx` |
| Show Infrastructure Images | off | Apple Container's builder and init images |
| Detach on Up | on | `--detach` for compose up |
| Run Compose in Terminal | on | Off runs it in the background instead |
| Language Server | Managed Binary | Or a custom path, or disabled |
Per-workspace, under **Project Settings**, you can pin the compose file the
compose commands use and the profiles they pass.
## Notes and limits
- `container-compose down` stops a project's containers but leaves them
defined, so they stay in the sidebar as stopped rows until you delete them.
- Volume rows show their driver and filesystem rather than a size: the backing
disk images are sparse, so the reported size is the maximum they may grow to,
not what they occupy.
- A compose project row can only run compose commands when the matching compose
file is in the open workspace, since project names are all the containers
carry.
- Changing the hostname domain only affects containers created afterwards, and
the services need a restart to pick it up.
## Development
```sh
./Tools/build-syntaxes.sh # rebuild the tree-sitter grammars (needs cc and git)
python3 Tools/make-icons.py # regenerate the icon set
```
Both write into the extension in place. The grammars are pinned to revisions
that generate tree-sitter ABI 14, which is what Nova loads.
To run the extension from source, open this folder in Nova and choose
**Extensions → Activate Project as Extension**.
## Credits
- Inspired by [Docker Suite](https://gitlab.com/joncoole/nova-docker-suite) by
Jonathan Coole.
- Bundled grammars:
[tree-sitter-dockerfile](https://github.com/camdencheek/tree-sitter-dockerfile)
and [tree-sitter-yaml](https://github.com/tree-sitter-grammars/tree-sitter-yaml),
both MIT licensed.
- Language features from
[docker-language-server](https://github.com/docker/docker-language-server),
Apache 2.0 licensed, downloaded at runtime rather than bundled.