applecontainer: add Swift bridge skeleton and runtime stub by bilby91 · Pull Request #58 · crunchloop/devcontainer · GitHub
Skip to content

applecontainer: add Swift bridge skeleton and runtime stub#58

Merged
bilby91 merged 2 commits into
mainfrom
m6/pr-a-applecontainer-bridge-skeleton
May 15, 2026
Merged

applecontainer: add Swift bridge skeleton and runtime stub#58
bilby91 merged 2 commits into
mainfrom
m6/pr-a-applecontainer-bridge-skeleton

Conversation

@bilby91

@bilby91 bilby91 commented May 14, 2026

Copy link
Copy Markdown
Member

Summary

First PR of M6 — a second Runtime backend that drives Apple's container
stack on macOS (Sequoia+, arm64). This PR lands the scaffolding only:
New + Ping. Every other Runtime method returns ErrNotImplemented
until PR-B onward.

Architecture: Go package cgo-links a Swift dynamic library
(libACBridge.dylib) that imports apple/container's
ContainerAPIClient and speaks XPC to the system container-apiserver
daemon. Same daemon model as Docker — brew install container +
container system start is the user's responsibility, analogous to
dockerd for runtime/docker.

  • applecontainer-bridge/ — SwiftPM dynamic-library package, exact-pinned
    to apple/container 0.12.3. Exports ac_version, ac_ping(timeout)
    (returns JSON-encoded SystemHealth), ac_free.
  • runtime/applecontainer/ — Go package, build-tagged darwin && arm64.
    New probes ClientHealthCheck.ping; returns
    *runtime.DaemonUnavailableError on failure. Ping is also exposed as
    a method so consumers can re-probe a live runtime. Compile-time
    _ runtime.Runtime = (*Runtime)(nil) assertion catches upstream
    interface drift.
  • Non-darwin/arm64 stub returns "platform unsupported" from New so the
    package imports cleanly cross-platform.
  • Makefile bridge + bridge-clean targets.

Deferred

PR-A links the dylib at build time via cgo LDFLAGS rpath into
applecontainer-bridge/.build/.../release. Single-binary packaging via
go:embed + dlopen lands in PR-A2.

Test plan

Local verification (darwin/arm64 with daemon running):

  • brew install container && container system start
  • make bridge
  • go test ./runtime/applecontainer/... — 2 tests pass:
    TestPing_DaemonRunning round-trips a real SystemHealth through
    the bridge; TestNew_TypedErrorOnFailure asserts typed-error
    contract.
  • go test ./... — all existing tests still green; no regression.
  • GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build ./runtime/applecontainer/... — stub compiles.
  • go vet ./runtime/applecontainer/... clean.

CI: this PR does not yet add a macOS arm64 runner. The Linux runner
exercises the stub path; make bridge + the daemon-dependent tests
run on a developer machine until the M6 CI job lands (PR-H).

Summary by CodeRabbit

  • New Features
    • Apple Container runtime added for macOS arm64 with bridge-based health checks and runtime version reporting
  • Documentation
    • Package-level docs describing macOS arm64 runtime behavior and platform fallbacks
  • Tests
    • Integration tests for daemon connectivity and typed error handling
  • Chores
    • Build helpers and ignore rules added for the bridge component

Review Change Stack

First PR of M6 (apple-container runtime backend). Lands the
applecontainer-bridge/ SwiftPM dynamic library and the
runtime/applecontainer/ Go package as a skeleton — New + Ping only,
every other Runtime method returns ErrNotImplemented for PR-B onward
to fill in.

Bridge (Swift):
- Pinned exact to apple/container 0.12.3.
- @_cdecl exports: ac_version, ac_ping(timeout) returning JSON-encoded
  SystemHealth, ac_free.

Runtime (Go, darwin && arm64):
- New probes ClientHealthCheck.ping; returns
  *runtime.DaemonUnavailableError if unreachable.
- Ping exposed as a method so consumers can re-probe a live runtime.
- Compile-time runtime.Runtime interface assertion catches upstream
  signature changes.
- Non-darwin/arm64 build tag links a stub so the package imports
  cleanly cross-platform.

Distribution: PR-A links the dylib at build time via cgo LDFLAGS rpath
into applecontainer-bridge/.build/.../release. Run `make bridge`
before any Go build that links runtime/applecontainer on darwin/arm64.
go:embed + dlopen single-binary packaging lands in PR-A2.

Test plan (run on darwin/arm64 with `container system start` first):
- `make bridge && go test ./runtime/applecontainer/...`

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@applecontainer-bridge/Sources/ACBridge/bridge.swift`:
- Around line 14-29: ac_ping blocks indefinitely because sem.wait() is
unbounded; replace the plain sem.wait() with a timed wait using
DispatchSemaphore.wait(timeout:) based on the same timeoutSeconds (use the same
default 5s logic), check for .timedOut, and if timed out set a deterministic
error payload (e.g. call encodePingErr with a small PingTimeout Error type or
set json to a timeout JSON) before returning; refer to ac_ping,
DispatchSemaphore, Task, ClientHealthCheck.ping, encodePingOK and encodePingErr
to locate and update the logic.

In `@Makefile`:
- Around line 36-44: The Makefile targets bridge and bridge-clean currently
invoke swift unconditionally; update both targets (bridge and bridge-clean) to
first check the platform and architecture (e.g., uname -s and uname -m or
GOOS/GOARCH env) and only run the swift commands when on darwin and arm64,
otherwise treat the target as a no-op (print a short message or exit 0). Ensure
you change the bridge recipe (cd applecontainer-bridge && swift build -c
release) and bridge-clean recipe (cd applecontainer-bridge && swift package
clean && rm -rf .build) to be guarded by that platform check so they don't run
swift on unsupported systems.

In `@runtime/applecontainer/runtime_darwin_arm64.go`:
- Around line 81-86: The Ping method currently ignores ctx's deadline; change
Runtime.Ping to compute an effective timeout by checking ctx.Deadline() and
using the minimum of timeoutSeconds and the remaining time until the deadline
(rounding up to whole seconds), returning ctx.Err() immediately if the deadline
has already passed, then call C.ac_ping with that effective timeout (the
C.ac_ping invocation and cstr handling remain the same). Locate the Ping
function, replace the direct use of timeoutSeconds for C.ac_ping with the
computed effectiveTimeout (int32), and ensure you still respect ctx.Err() at the
top and before invoking C.ac_ping.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fb277a14-cb5d-424d-93e7-9703471b6e71

📥 Commits

Reviewing files that changed from the base of the PR and between 2178286 and 8d01be7.

📒 Files selected for processing (10)
  • .gitignore
  • Makefile
  • applecontainer-bridge/.gitignore
  • applecontainer-bridge/Package.swift
  • applecontainer-bridge/Sources/ACBridge/bridge.swift
  • applecontainer-bridge/include/ac_bridge.h
  • runtime/applecontainer/doc.go
  • runtime/applecontainer/runtime_darwin_arm64.go
  • runtime/applecontainer/runtime_darwin_arm64_test.go
  • runtime/applecontainer/runtime_unsupported.go

Comment thread applecontainer-bridge/Sources/ACBridge/bridge.swift
Comment thread Makefile Outdated
Comment thread runtime/applecontainer/runtime_darwin_arm64.go
Three fixes from CodeRabbit review on #58, all valid:

bridge.swift: ac_ping's `sem.wait()` was unbounded — if the Swift
Task never signals (cancellation race, runtime hang) the cgo caller
would block forever. ClientHealthCheck.ping already enforces its own
Duration timeout, but we guard the semaphore as belt-and-suspenders
with a deterministic "bridge-timeout" error payload.

Makefile: bridge / bridge-clean recipes were unconditional but the
comment claimed "no-op on other platforms." Guard with uname so
invoking on linux/amd64 prints a clear skip message rather than
failing with "swift: command not found." Comment rewritten to match.

runtime_darwin_arm64.go: Ping ignored ctx.Deadline(). Clamp the
effective bridge timeout to min(timeoutSeconds, ceil(time.Until(
deadline).Seconds())) and return ctx.Err() if the deadline has
already passed. The bridge call is synchronous from Go's view so we
can't cancel mid-flight; bounding the argument is the closest we get
to context semantics.

Test plan:
- `make bridge` still builds the dylib on darwin/arm64
- `make bridge` on non-darwin/arm64 prints "skipped" and exits 0
- `go test ./runtime/applecontainer/...` — 2 tests pass
- `GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build` of stub — ok
- `go vet ./runtime/applecontainer/...` — clean

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant