Contributing
March 8, 2026 · View on GitHub
Thank you for your interest in contributing! This guide covers everything you need to get started.
Code of Conduct
Be respectful and constructive. We follow the Contributor Covenant.
Development Setup
Prerequisites
- macOS 26+ (for full container support) or macOS 14+ (for development with placeholders)
- Swift 6.0+
- Xcode 16+
Clone and build
git clone https://github.com/us/mocker.git
cd mocker
# Build in debug mode
swift build
# Run tests
swift test
# Run the CLI directly
swift run mocker --help
Recommended editor setup
VS Code with the Swift extension:
code .
# Install: Swift for VS Code (sswg.swift-lang)
Xcode:
open Package.swift
Clean state for testing
# Remove all Mocker state (containers, images, networks, volumes)
rm -rf ~/.mocker
# Rebuild and test
swift build && swift run mocker system info
Project Structure
See architecture.md for a full breakdown. Key directories:
Sources/MockerKit/ # Core library — most feature work goes here
Sources/Mocker/ # CLI commands — UI/formatting work goes here
Sources/MockerApp/ # MenuBar GUI — SwiftUI work goes here
Tests/ # Unit and integration tests
docs/ # Documentation
Making Changes
1. Fork and branch
git checkout -b feat/my-feature
# or
git checkout -b fix/bug-description
2. Make your changes
Follow the patterns in existing code:
- New commands: add a file in
Sources/Mocker/Commands/ - New engine methods: add to the relevant actor in
MockerKit - New models: add to
Sources/MockerKit/Models/
3. Write tests
Add tests for any new functionality in Tests/MockerKitTests/:
@Test("My new feature works")
func testMyFeature() throws {
// ...
}
4. Verify
swift build
swift test
swift run mocker --help # smoke test
Coding Standards
Swift style
- Use Swift 6 strict concurrency — all actors must be properly
awaited - Prefer
actorfor any stateful component - Use
async throwsfor all I/O operations - Prefer
structoverclassfor value types - Use
guardfor early returns
Docker compatibility
When implementing or modifying commands, check against Docker's output:
# Check Docker behavior
docker run --help
docker inspect <container>
# Compare with Mocker
swift run mocker run --help
swift run mocker inspect <container>
Key compatibility rules:
- Error messages must match:
Error response from daemon: ... inspectalways returns a JSON array[{...}]stopandrmecho back the user's input identifier (not the resolved name)- Short IDs are exactly 12 characters
Naming
- Container/image/network/volume names: follow Docker conventions
- Swift types: PascalCase for types, camelCase for properties/methods
- CLI flags: kebab-case matching Docker (
--no-stream,--project-name)
Commit messages
Use Conventional Commits:
feat: add mocker login command
fix: correct container name echo in stop command
docs: update cli-reference with new flags
test: add tests for network connect/disconnect
refactor: extract image validation into helper
chore: update Yams dependency to 5.2
Maximum 72 characters for the subject line.
Testing
Running tests
# All tests
swift test
# Specific suite
swift test --filter MockerKitTests
swift test --filter MockerTests
# Specific test
swift test --filter "ContainerConfigTests/testParsePortMapping"
Test structure
Tests/
├── MockerKitTests/
│ ├── ContainerConfigTests.swift # PortMapping, VolumeMount parsing
│ ├── ImageReferenceTests.swift # ImageReference.parse()
│ └── ComposeFileTests.swift # YAML parsing
└── MockerTests/
└── CLITests.swift # CLI smoke tests
Writing tests
Use the Testing framework (Swift 6):
import Testing
@testable import MockerKit
@Suite("MyFeature Tests")
struct MyFeatureTests {
@Test("Parse valid input")
func testParseValid() throws {
let result = try MyType.parse("valid-input")
#expect(result.value == "expected")
}
@Test("Parse invalid input throws")
func testParseInvalid() {
#expect(throws: MockerError.self) {
try MyType.parse("")
}
}
}
Integration testing
For commands that affect state, use a temporary config:
let tempDir = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString)
let config = MockerConfig(dataRoot: tempDir.path)
try config.ensureDirectories()
// ... test with this config
Submitting a Pull Request
- Ensure tests pass:
swift test - Ensure it builds:
swift build - Self-review your diff — remove debug prints, check for typos
- Write a clear PR description:
- What problem does this solve?
- How was it tested?
- Any breaking changes?
PR title follows Conventional Commits format:
feat: add mocker login commandfix: prevent duplicate images on repeated pulldocs: add compose guide
Implementing Apple Containerization TODOs
The highest-impact contributions are replacing placeholder implementations with real Apple Containerization framework calls. Look for // TODO: comments:
grep -r "TODO:" Sources/MockerKit/
Key integration points
Image Pull (ImageManager.swift):
// TODO: Use Containerization framework to actually pull the image
// Replace with Apple Containerization image pull API
Container Start (ContainerEngine.swift):
// TODO: Use Containerization framework to start the container
// Replace with Container() init + start()
Container Logs (ContainerEngine.swift):
// TODO: Stream real logs from the container process
Guidelines for TODO implementation
- Keep the existing method signature — callers should not change
- The placeholder
ContainerInforeturned should match the real framework's data - Add error handling for framework-specific errors, mapping to
MockerError - Update the relevant tests
Questions?
Open an issue with the question label, or start a GitHub Discussion.