How to Execute Commands Using the Main Entry Point of apple/container
The apple/container CLI uses the ContainerCLI struct in Sources/CLI/ContainerCLI.swift as its @main entry point, which forwards execution to Application.main() to bootstrap the command hierarchy and dispatch to subcommands like run, build, and machine.
The apple/container repository provides a Swift-based container management tool that compiles into a single executable binary. To execute commands using the main entry point, you interact with the container binary built from the ContainerCLI struct, which coordinates argument parsing and subcommand dispatch through the Application class defined in Sources/ContainerCommands/Application.swift.
Understanding the Entry Point Architecture
The CLI architecture separates entry point declaration from command implementation, using Swift's ArgumentParser framework for structured command handling.
The ContainerCLI Struct
In Sources/CLI/ContainerCLI.swift, the ContainerCLI struct is annotated with @main, identifying it as the program's entry point to the Swift runtime. According to the source code, this struct's main() method serves as a thin wrapper that immediately delegates to Application.main(), which handles the actual execution logic.
Application Bootstrapping
The Application class in Sources/ContainerCommands/Application.swift implements the core bootstrapping sequence. When invoked via Application.main(), it initializes logging infrastructure, registers all available subcommands, parses command-line arguments, and dispatches execution to the appropriate command handler. Each subcommand conforms to AsyncParsableCommand (via the AsyncLoggableCommand protocol) and resides in its own file under Sources/ContainerCommands/.
Building the Container Binary
Before executing commands, you must build the executable product named container declared in Package.swift. The Swift Package Manager compiles the ContainerCLI target and its dependencies into a single binary.
# Build the CLI in release mode
swift build -c release
# The binary appears at:
# .build/release/container
If you install the binary via the provided install scripts, you can invoke it directly as container from any terminal location.
Executing Commands Through the Entry Point
Once built, every command flows through the same entry point. The binary parses your arguments and routes to subcommands such as ContainerList, ContainerRun, BuildCommand, and MachineCommand.
Viewing Available Commands
To see all supported subcommands and global options, invoke the entry point with the --help flag:
.build/release/container --help
This triggers the Application class to print the command hierarchy defined in Sources/ContainerCommands/Application.swift.
Listing Containers
To list all containers using the ContainerList subcommand:
# List all containers including stopped ones
.build/release/container list --all
# Or if installed globally:
container list -a
Building Images
Execute the BuildCommand to build an image from a Dockerfile:
.build/release/container build --tag my-image --file Dockerfile .
# Or with short flags:
container build -t my-image -f Dockerfile .
Running Containers
Launch a container using the ContainerRun subcommand:
# Start a detached container with a specific name
.build/release/container run --name myapp --detach my-image
# Or shorthand:
container run -d --name myapp my-image
Managing Machines
Create and manage persistent container machines through the MachineCommand subcommand:
# Create a new machine named "devbox"
.build/release/container machine create --name devbox alpine:latest
# With the installed binary:
container machine create --name devbox ubuntu:latest
How Command Dispatch Works
When you execute any command through the container binary, the following flow occurs:
- Swift runtime instantiates the
ContainerCLIstruct marked with@maininSources/CLI/ContainerCLI.swift. - Entry point delegation:
ContainerCLI.main()callsApplication.main()to transfer control. - Bootstrap phase:
Application.main()initializes logging, validates the environment, and parses arguments usingArgumentParser. - Subcommand routing: Based on the parsed arguments, the application dispatches to the specific subcommand implementation (e.g.,
ContainerRun.swift,BuildCommand.swift) located inSources/ContainerCommands/.
This architecture ensures that all CLI commands execute through the same binary entry point, maintaining consistent initialization and error handling across the tool.
Summary
- The main entry point is the
ContainerCLIstruct inSources/CLI/ContainerCLI.swift, marked with the@mainattribute. - Execution immediately forwards to
Application.main()inSources/ContainerCommands/Application.swiftfor bootstrapping. - The
containerbinary (defined inPackage.swift) provides the unified interface for all commands. - Subcommands are implemented as separate Swift files conforming to
AsyncParsableCommandunderSources/ContainerCommands/. - Build the binary with
swift build -c release, then invoke.build/release/container <command>to execute any operation.
Frequently Asked Questions
What file contains the main entry point in apple/container?
The main entry point is defined in Sources/CLI/ContainerCLI.swift. This file contains the ContainerCLI struct annotated with @main, which tells the Swift compiler to use this type as the program's entry point. The struct's main() method simply forwards to Application.main() to begin execution.
How does the container binary route commands to the correct handler?
After the Swift runtime calls ContainerCLI.main(), execution transfers to Application.main() in Sources/ContainerCommands/Application.swift. This method bootstraps the ArgumentParser framework, registers all subcommands (such as run, build, and machine), and dispatches to the appropriate AsyncParsableCommand implementation based on the parsed arguments.
Can I execute commands without installing the container binary?
Yes. You can build the project using swift build -c release and then execute commands directly via the built binary at .build/release/container. This allows you to test and use the CLI without running install scripts or modifying your system PATH, though you must use the full relative path to the binary.
Where are the individual subcommand implementations located?
Individual subcommand implementations reside in Sources/ContainerCommands/. Each major operation has its own file, such as ContainerRun.swift for the run command, BuildCommand.swift for the build command, and MachineCommand.swift for machine management. These files contain types conforming to AsyncParsableCommand (via AsyncLoggableCommand) that define the specific behavior for each CLI operation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →