```
├── .github/
├── ISSUE_TEMPLATE/
├── 01-bug.yml (400 tokens)
├── 02-feature.yml (200 tokens)
├── config.yml
├── labeler.yml
├── pull_request_template.md
├── workflows/
├── common.yml (700 tokens)
├── docs-release.yml (200 tokens)
├── merge-build.yml (100 tokens)
├── pr-build.yml (100 tokens)
├── pr-label-analysis.yml (100 tokens)
├── pr-label-apply.yml (300 tokens)
├── release-build.yml (300 tokens)
├── release.yml (300 tokens)
├── .gitignore (100 tokens)
├── .spi.yml (100 tokens)
├── .swift-format (500 tokens)
├── BUILDING.md (800 tokens)
├── CONTRIBUTING.md
├── LICENSE (omitted)
├── MAINTAINERS.txt
├── Makefile (2.2k tokens)
├── Package.resolved (1500 tokens)
├── Package.swift (3.3k tokens)
├── Protobuf.Makefile (500 tokens)
├── README.md (600 tokens)
├── SECURITY.md (300 tokens)
├── Sources/
├── CAuditToken/
├── AuditToken.c (200 tokens)
├── include/
├── AuditToken.h (200 tokens)
├── CLI/
├── ContainerCLI.swift (300 tokens)
├── CVersion/
├── Version.c (200 tokens)
├── include/
├── Version.h (200 tokens)
├── ContainerBuild/
├── BuildAPI+Extensions.swift (900 tokens)
├── BuildFSSync.swift (3.6k tokens)
├── BuildFile.swift (400 tokens)
├── BuildImageResolver.swift (1200 tokens)
├── BuildPipelineHandler.swift (1500 tokens)
├── BuildRemoteContentProxy.swift (1400 tokens)
├── BuildStdio.swift (500 tokens)
├── Builder.grpc.swift (7.7k tokens)
├── Builder.pb.swift (11.9k tokens)
├── Builder.swift (2.7k tokens)
├── Globber.swift (900 tokens)
├── TerminalCommand.swift (300 tokens)
├── URL+Extensions.swift (1900 tokens)
├── ContainerClient/
├── Arch.swift (200 tokens)
├── Archiver.swift (2.3k tokens)
├── Array+Dedupe.swift (200 tokens)
├── ContainerizationProgressAdapter.swift (400 tokens)
├── Core/
├── Bundle.swift (1100 tokens)
├── ClientContainer.swift (2.3k tokens)
├── ClientDiskUsage.swift (300 tokens)
├── ClientHealthCheck.swift (600 tokens)
├── ClientImage.swift (4k tokens)
├── ClientKernel.swift (800 tokens)
├── ClientNetwork.swift (700 tokens)
├── ClientProcess.swift (800 tokens)
├── ClientVolume.swift (700 tokens)
├── Constants.swift (200 tokens)
├── ContainerConfiguration.swift (1300 tokens)
├── ContainerCreateOptions.swift (200 tokens)
├── ContainerSnapshot.swift (300 tokens)
├── ContainerStats.swift (500 tokens)
├── ContainerStopOptions.swift (200 tokens)
├── DiskUsage.swift (400 tokens)
├── Filesystem.swift (1200 tokens)
├── ImageDescription.swift (300 tokens)
├── ImageDetail.swift (500 tokens)
├── ProcessConfiguration.swift (700 tokens)
├── PublishPort.swift (600 tokens)
├── PublishSocket.swift (300 tokens)
├── RuntimeStatus.swift (200 tokens)
├── SystemHealth.swift (300 tokens)
├── Volume.swift (800 tokens)
├── XPC+.swift (1300 tokens)
├── ExitMonitor.swift (700 tokens)
├── FileDownloader.swift (600 tokens)
├── Flags.swift (1600 tokens)
├── HostDNSResolver.swift (1000 tokens)
├── Measurement+Parse.swift (600 tokens)
├── Parser.swift (6.7k tokens)
├── ProcessIO.swift (2.3k tokens)
├── ProgressUpdateClient.swift (1300 tokens)
├── ProgressUpdateService.swift (800 tokens)
├── RequestScheme.swift (500 tokens)
├── SandboxClient.swift (2.2k tokens)
├── SandboxRoutes.swift (400 tokens)
├── SandboxSnapshot.swift (300 tokens)
├── SignalThreshold.swift (500 tokens)
├── String+Extensions.swift (400 tokens)
├── TableOutput.swift (400 tokens)
├── Utility.swift (3k tokens)
├── ContainerCommands/
├── Application.swift (1800 tokens)
├── BuildCommand.swift (3.6k tokens)
├── Builder/
├── Builder.swift (300 tokens)
├── BuilderDelete.swift (400 tokens)
├── BuilderStart.swift (2.2k tokens)
├── BuilderStatus.swift (700 tokens)
├── BuilderStop.swift (400 tokens)
├── Codable+JSON.swift (200 tokens)
├── Container/
├── ContainerCreate.swift (800 tokens)
├── ContainerDelete.swift (900 tokens)
├── ContainerExec.swift (800 tokens)
├── ContainerInspect.swift (300 tokens)
├── ContainerKill.swift (600 tokens)
├── ContainerList.swift (800 tokens)
├── ContainerLogs.swift (1100 tokens)
├── ContainerRun.swift (1200 tokens)
├── ContainerStart.swift (700 tokens)
├── ContainerStats.swift (2.1k tokens)
├── ContainerStop.swift (800 tokens)
├── ProcessUtils.swift (200 tokens)
├── DefaultCommand.swift (800 tokens)
├── Image/
├── ImageCommand.swift (300 tokens)
├── ImageDelete.swift (800 tokens)
├── ImageInspect.swift (400 tokens)
├── ImageList.swift (1400 tokens)
├── ImageLoad.swift (800 tokens)
├── ImagePrune.swift (700 tokens)
├── ImagePull.swift (800 tokens)
├── ImagePush.swift (600 tokens)
├── ImageSave.swift (900 tokens)
├── ImageTag.swift (300 tokens)
├── Network/
├── NetworkCommand.swift (300 tokens)
├── NetworkCreate.swift (400 tokens)
├── NetworkDelete.swift (900 tokens)
├── NetworkInspect.swift (300 tokens)
├── NetworkList.swift (700 tokens)
├── Registry/
├── Login.swift (800 tokens)
├── Logout.swift (300 tokens)
├── RegistryCommand.swift (200 tokens)
├── System/
├── DNS/
├── DNSCreate.swift (400 tokens)
├── DNSDelete.swift (400 tokens)
├── DNSList.swift (500 tokens)
├── Kernel/
├── KernelSet.swift (1100 tokens)
├── Property/
├── PropertyClear.swift (300 tokens)
├── PropertyGet.swift (300 tokens)
├── PropertyList.swift (600 tokens)
├── PropertySet.swift (700 tokens)
├── SystemCommand.swift (300 tokens)
├── SystemDF.swift (800 tokens)
├── SystemDNS.swift (300 tokens)
├── SystemKernel.swift (200 tokens)
├── SystemLogs.swift (600 tokens)
├── SystemProperty.swift (300 tokens)
├── SystemStart.swift (1400 tokens)
├── SystemStatus.swift (500 tokens)
├── SystemStop.swift (900 tokens)
├── Version.swift (600 tokens)
├── Volume/
├── VolumeCommand.swift (300 tokens)
├── VolumeCreate.swift (400 tokens)
├── VolumeDelete.swift (700 tokens)
├── VolumeInspect.swift (400 tokens)
├── VolumeList.swift (600 tokens)
├── VolumePrune.swift (500 tokens)
├── ContainerLog/
├── OSLogHandler.swift (600 tokens)
├── StderrLogHandler.swift (400 tokens)
├── ContainerPersistence/
├── DefaultsStore.swift (1500 tokens)
├── EntityStore.swift (900 tokens)
├── ContainerPlugin/
├── ApplicationRoot.swift (300 tokens)
├── InstallRoot.swift (300 tokens)
├── LaunchPlist.swift (800 tokens)
├── Plugin.swift (800 tokens)
├── PluginConfig.swift (1100 tokens)
├── PluginFactory.swift (700 tokens)
├── PluginLoader.swift (1900 tokens)
├── ServiceManager.swift (1100 tokens)
├── ContainerVersion/
├── Bundle+AppBundle.swift (300 tokens)
├── CommandLine+Executable.swift (300 tokens)
├── ReleaseVersion.swift (400 tokens)
├── ContainerXPC/
├── XPCClient.swift (1000 tokens)
├── XPCMessage.swift (1700 tokens)
├── XPCServer.swift (2000 tokens)
├── DNSServer/
├── DNSHandler.swift (200 tokens)
├── DNSServer+Handle.swift (700 tokens)
├── DNSServer.swift (600 tokens)
├── Handlers/
├── CompositeResolver.swift (300 tokens)
├── HostTableResolver.swift (700 tokens)
├── NxDomainResolver.swift (500 tokens)
├── StandardQueryValidator.swift (500 tokens)
├── Types.swift (400 tokens)
├── Helpers/
├── APIServer/
├── APIServer+Start.swift (2.5k tokens)
├── APIServer.swift (300 tokens)
├── ContainerDNSHandler.swift (800 tokens)
├── Images/
├── ImagesHelper.swift (1200 tokens)
├── NetworkVmnet/
├── NetworkVmnetHelper+Start.swift (700 tokens)
├── NetworkVmnetHelper.swift (300 tokens)
├── RuntimeLinux/
├── IsolatedInterfaceStrategy.swift (300 tokens)
├── NonisolatedInterfaceStrategy.swift (400 tokens)
├── RuntimeLinuxHelper+Start.swift (1100 tokens)
├── RuntimeLinuxHelper.swift (400 tokens)
├── Services/
├── ContainerAPIService/
├── Containers/
├── ContainersHarness.swift (1900 tokens)
├── ContainersService.swift (5.1k tokens)
├── DiskUsage/
├── DiskUsageHarness.swift (400 tokens)
├── DiskUsageService.swift (600 tokens)
├── HealthCheck/
├── HealthCheckHarness.swift (400 tokens)
├── Kernel/
├── KernelHarness.swift (800 tokens)
├── KernelService.swift (1500 tokens)
├── Networks/
├── NetworksHarness.swift (500 tokens)
├── NetworksService.swift (2.4k tokens)
├── Plugin/
├── PluginsHarness.swift (600 tokens)
├── PluginsService.swift (800 tokens)
├── Volumes/
├── VolumesHarness.swift (800 tokens)
├── VolumesService.swift (2k tokens)
├── ContainerImagesService/
├── Client/
├── ImageServiceXPCKeys.swift (600 tokens)
├── ImageServiceXPCRoutes.swift (300 tokens)
├── RemoteContentStoreClient.swift (1100 tokens)
├── Server/
├── ContentServiceHarness.swift (900 tokens)
├── ContentStoreService.swift (500 tokens)
├── ImageService.swift (2.3k tokens)
├── ImagesServiceHarness.swift (2.2k tokens)
├── SnapshotStore.swift (2.1k tokens)
├── ContainerNetworkService/
├── AllocationOnlyVmnetNetwork.swift (600 tokens)
├── Attachment.swift (300 tokens)
├── AttachmentAllocator.swift (400 tokens)
├── AttachmentConfiguration.swift (300 tokens)
├── Network.swift (200 tokens)
├── NetworkClient.swift (1000 tokens)
├── NetworkConfiguration.swift (900 tokens)
├── NetworkKeys.swift (200 tokens)
├── NetworkMode.swift (300 tokens)
├── NetworkRoutes.swift (300 tokens)
├── NetworkService.swift (1200 tokens)
├── NetworkState.swift (400 tokens)
├── ReservedVmnetNetwork.swift (1300 tokens)
├── ContainerSandboxService/
├── InterfaceStrategy.swift (300 tokens)
├── SandboxService.swift (9.3k tokens)
├── SocketForwarder/
├── ConnectHandler.swift (800 tokens)
├── GlueHandler.swift (700 tokens)
├── LRUCache.swift (600 tokens)
├── SocketForwarder.swift (200 tokens)
├── SocketForwarderResult.swift (200 tokens)
├── TCPForwarder.swift (500 tokens)
├── UDPForwarder.swift (1600 tokens)
├── TerminalProgress/
├── Int+Formatted.swift (400 tokens)
├── Int64+Formatted.swift (300 tokens)
├── ProgressBar+Add.swift (1800 tokens)
├── ProgressBar+State.swift (700 tokens)
├── ProgressBar+Terminal.swift (500 tokens)
├── ProgressBar.swift (2.3k tokens)
├── ProgressConfig.swift (1700 tokens)
├── ProgressTaskCoordinator.swift (500 tokens)
├── ProgressTheme.swift (300 tokens)
├── ProgressUpdate.swift (300 tokens)
├── StandardError.swift (200 tokens)
├── Tests/
├── CLITests/
├── Subcommands/
├── Build/
├── CLIBuildBase.swift (2.2k tokens)
├── CLIBuilderEnvOnlyTest.swift (1300 tokens)
├── CLIBuilderLifecycleTest.swift (300 tokens)
├── CLIBuilderLocalOutputTest.swift (2.4k tokens)
├── CLIBuilderTarExportTest.swift (1300 tokens)
├── CLIBuilderTest.swift (4.1k tokens)
├── CLIRunBase.swift (800 tokens)
├── TestCLITermIO.swift (400 tokens)
├── Containers/
├── TestCLICreate.swift (700 tokens)
├── TestCLIExec.swift (900 tokens)
├── TestCLIRmRace.swift (1200 tokens)
├── TestCLIStats.swift (1800 tokens)
├── Images/
├── TestCLIImagesCommand.swift (3k tokens)
├── Networks/
├── TestCLINetwork.swift (1500 tokens)
├── Plugins/
├── TestCLIPluginErrors.swift (300 tokens)
├── Run/
├── TestCLIRunCommand.swift (4.8k tokens)
├── TestCLIRunLifecycle.swift (700 tokens)
├── System/
├── TestCLIVersion.swift (900 tokens)
├── TestKernelSet.swift (900 tokens)
├── Volumes/
├── TestCLIAnonymousVolumes.swift (3.6k tokens)
├── TestCLIVolumes.swift (3.5k tokens)
├── TestCLINoParallelCases.swift (400 tokens)
├── Utilities/
├── CLITest.swift (3.4k tokens)
├── ContainerBuildTests/
├── BuildFile.swift (900 tokens)
├── BuilderExtensionsTests.swift (2.7k tokens)
├── GlobberTests.swift (2.3k tokens)
├── ContainerClientTests/
├── DiskUsageTests.swift (900 tokens)
├── HostDNSResolverTest.swift (900 tokens)
├── Measurement+ParseTests.swift (1600 tokens)
├── ParserTest.swift (4.3k tokens)
├── RequestSchemeTests.swift (500 tokens)
├── UtilityTests.swift (800 tokens)
├── VolumeValidationTests.swift (1100 tokens)
├── ContainerNetworkServiceTests/
├── NetworkConfigurationTest.swift (900 tokens)
├── ContainerPluginTests/
├── CommandLine+ExecutableTest.swift (200 tokens)
├── MockPluginFactory.swift (400 tokens)
├── PluginConfigTest.swift (700 tokens)
├── PluginFactoryTest.swift (2.2k tokens)
├── PluginLoaderTest.swift (1400 tokens)
├── PluginTest.swift (1100 tokens)
├── DNSServerTests/
├── CompositeResolverTest.swift (500 tokens)
├── HostTableResolverTest.swift (900 tokens)
├── MockHandlers.swift (400 tokens)
├── NxDomainResolverTest.swift (400 tokens)
├── StandardQueryValidatorTest.swift (800 tokens)
├── SocketForwarderTests/
├── LRUCacheTest.swift (400 tokens)
├── TCPEchoHandler.swift (200 tokens)
├── TCPEchoServer.swift (400 tokens)
├── TCPForwarderTest.swift (1100 tokens)
├── UDPEchoHandler.swift (200 tokens)
├── UDPEchoServer.swift (300 tokens)
├── UDPForwarderTest.swift (900 tokens)
├── TerminalProgressTests/
├── ProgressBarTests.swift (4.6k tokens)
├── config/
├── container-core-images-config.json (100 tokens)
├── container-network-vmnet-config.json (100 tokens)
├── container-runtime-linux-config.json (100 tokens)
├── docs/
├── assets/
├── functional-model-light.svg (17.5k tokens)
├── landing-movie.gif
├── command-reference.md (6.7k tokens)
├── how-to.md (4.8k tokens)
├── technical-overview.md (1800 tokens)
├── tutorial.md (2.6k tokens)
├── licenserc.toml (100 tokens)
├── scripts/
├── container-header-style.toml (100 tokens)
├── ensure-container-stopped.sh (200 tokens)
├── ensure-hawkeye-exists.sh (200 tokens)
├── install-hawkeye.sh (200 tokens)
├── install-init.sh (300 tokens)
├── license-header.txt (200 tokens)
├── make-docs.sh (300 tokens)
├── uninstall-container.sh (500 tokens)
├── signing/
├── container-network-vmnet.entitlements
├── container-runtime-linux.entitlements
```
## /.github/ISSUE_TEMPLATE/01-bug.yml
```yml path="/.github/ISSUE_TEMPLATE/01-bug.yml"
name: Bug report
description: File a bug report.
title: "[Bug]: "
type: "Bug"
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: checkboxes
id: prereqs
attributes:
label: I have done the following
description: Select that you have completed the following prerequisites.
options:
- label: I have searched the existing issues
required: true
- label: If possible, I've reproduced the issue using the 'main' branch of this project
required: false
- type: textarea
id: reproduce
attributes:
label: Steps to reproduce
description: Explain how to reproduce the incorrect behavior.
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: Current behavior
description: A concise description of what you're experiencing.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: A concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Environment
description: |
Examples:
- **OS**: macOS 26.0 (25A354)
- **Xcode**: Version 26.0 (17A324)
- **Container**: Container CLI version 0.1.0
value: |
- OS:
- Xcode:
- Container:
render: markdown
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
value: |
N/A
render: shell
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/apple/.github/blob/main/CODE_OF_CONDUCT.md).
options:
- label: I agree to follow this project's Code of Conduct
required: true
```
## /.github/ISSUE_TEMPLATE/02-feature.yml
```yml path="/.github/ISSUE_TEMPLATE/02-feature.yml"
name: Feature or enhancement request
description: File a request for a feature or enhancement
title: "[Request]: "
type: "Feature"
body:
- type: markdown
attributes:
value: |
Thanks for contributing to the container project!
- type: textarea
id: request
attributes:
label: Feature or enhancement request details
description: Describe your proposed feature or enhancement. Code samples that show what's missing, or what new capabilities will be possible, are very helpful! Provide links to existing issues or external references/discussions, if appropriate.
validations:
required: true
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/apple/.github/blob/main/CODE_OF_CONDUCT.md).
options:
- label: I agree to follow this project's Code of Conduct
required: true
```
## /.github/ISSUE_TEMPLATE/config.yml
```yml path="/.github/ISSUE_TEMPLATE/config.yml"
blank_issues_enabled: false
contact_links:
- name: Container community support
url: https://github.com/apple/container/discussions
about: Please ask and answer questions here.
```
## /.github/labeler.yml
```yml path="/.github/labeler.yml"
cli:
- changed-files:
- any-glob-to-any-file:
- 'Sources/CLI/**'
- 'Sources/ContainerCommands/**'
```
## /.github/pull_request_template.md
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Motivation and Context
[Why is this change needed?]
## Testing
- [ ] Tested locally
- [ ] Added/updated tests
- [ ] Added/updated docs
## /.github/workflows/common.yml
```yml path="/.github/workflows/common.yml"
name: container project - common jobs
permissions:
contents: read
on:
workflow_call:
inputs:
release:
type: boolean
description: "Publish this build for release"
default: false
jobs:
buildAndTest:
name: Build and test the project
if: github.repository == 'apple/container'
timeout-minutes: 60
runs-on: [self-hosted, macos, sequoia, ARM64]
permissions:
contents: read
packages: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check formatting
run: |
./scripts/install-hawkeye.sh
make fmt
if ! git diff --quiet -- . ; then
echo "❌ The following files require formatting or license header updates:"
git diff --name-only -- .
false
fi
- name: Check protobuf
run: |
make protos
if ! git diff --quiet -- . ; then
echo "❌ The following files require formatting or license header updates:"
git diff --name-only -- .
false
fi
env:
DEVELOPER_DIR: "/Applications/Xcode-latest.app/Contents/Developer"
- name: Set build configuration
run: |
echo "BUILD_CONFIGURATION=debug" >> $GITHUB_ENV
if [[ "${{ inputs.release }}" == "true" ]]; then
echo "BUILD_CONFIGURATION=release" >> $GITHUB_ENV
fi
- name: Make the container project and docs
run: |
make container dsym docs
tar cfz _site.tgz _site
env:
DEVELOPER_DIR: "/Applications/Xcode-latest.app/Contents/Developer"
- name: Create package
run: |
mkdir -p outputs
mv bin/${{ env.BUILD_CONFIGURATION }}/container-installer-unsigned.pkg outputs
mv bin/${{ env.BUILD_CONFIGURATION }}/bundle/container-dSYM.zip outputs
- name: Test the container project
run: |
APP_ROOT=$(mktemp -d -p "${RUNNER_TEMP}")
trap 'rm -rf "${APP_ROOT}"; echo Removing data directory ${APP_ROOT}' EXIT
echo "Created data directory ${APP_ROOT}"
make APP_ROOT="${APP_ROOT}" test install-kernel integration
env:
DEVELOPER_DIR: "/Applications/Xcode-latest.app/Contents/Developer"
- name: Save documentation artifact
uses: actions/upload-artifact@v4
with:
name: api-docs
path: "./_site.tgz"
retention-days: 14
- name: Save package artifacts
uses: actions/upload-artifact@v4
with:
name: container-package
path: ${{ github.workspace }}/outputs
uploadPages:
# Separate upload step required because upload-pages-artifact needs
# gtar which is not on the macOS runner.
name: Upload artifact for GitHub Pages
needs: buildAndTest
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Download a single artifact
uses: actions/download-artifact@v4
with:
name: api-docs
- name: Add API docs to documentation
run: |
tar xfz _site.tgz
- name: Upload Artifact
uses: actions/upload-pages-artifact@v3
with:
path: "./_site"
```
## /.github/workflows/docs-release.yml
```yml path="/.github/workflows/docs-release.yml"
# Manual workflow for releasing docs ad-hoc. Workflow can only be run for main or release branches.
# Workflow does NOT publish a release of container.
name: Deploy application website
permissions:
contents: read
on:
workflow_dispatch:
jobs:
checkBranch:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags') || startsWith(github.ref, 'refs/heads/release')
steps:
- name: Branch validation
run: echo "Branch ${{ github.ref_name }} is allowed"
buildSite:
name: Build application website
needs: checkBranch
uses: ./.github/workflows/common.yml
secrets: inherit
permissions:
contents: read
packages: write
pages: write
deployDocs:
runs-on: ubuntu-latest
needs: [checkBranch, buildSite]
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
```
## /.github/workflows/merge-build.yml
```yml path="/.github/workflows/merge-build.yml"
name: container project - merge build
permissions:
contents: read
on:
push:
branches:
- main
- release/*
jobs:
build:
name: Invoke build
uses: ./.github/workflows/common.yml
with:
release: true
secrets: inherit
permissions:
contents: read
packages: read
pages: write
```
## /.github/workflows/pr-build.yml
```yml path="/.github/workflows/pr-build.yml"
name: container project - PR build
permissions:
contents: read
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
build:
name: Invoke build
uses: ./.github/workflows/common.yml
with:
release: false
secrets: inherit
permissions:
contents: read
packages: read
pages: write
```
## /.github/workflows/pr-label-analysis.yml
```yml path="/.github/workflows/pr-label-analysis.yml"
name: PR Label Analysis
on:
pull_request:
types: [opened]
permissions:
contents: read
jobs:
analyze:
name: Analyze PR for labeling
runs-on: ubuntu-latest
steps:
- name: Save PR metadata
run: |
mkdir -p ./pr-metadata
echo "${{ github.event.pull_request.number }}" > ./pr-metadata/pr-number.txt
- name: Upload PR metadata as artifact
uses: actions/upload-artifact@v4
with:
name: pr-metadata-${{ github.event.pull_request.number }}
path: pr-metadata/
retention-days: 1
```
## /.github/workflows/pr-label-apply.yml
```yml path="/.github/workflows/pr-label-apply.yml"
name: PR Label Apply
on:
workflow_run:
workflows: ["PR Label Analysis"]
types:
- completed
permissions:
contents: read
jobs:
apply-labels:
name: Apply labels to PR
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download PR metadata artifact
uses: actions/download-artifact@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
pattern: pr-metadata-*
merge-multiple: false
continue-on-error: true
id: download-artifact
- name: Read PR number
id: pr-number
run: |
METADATA_DIR=$(find . -type d -name "pr-metadata-*" 2>/dev/null | head -n 1)
if [ -z "$METADATA_DIR" ]; then
echo "No metadata found"
exit 1
fi
PR_NUMBER=$(cat "${METADATA_DIR}/pr-number.txt")
echo "number=${PR_NUMBER}" >> $GITHUB_OUTPUT
echo "PR Number: ${PR_NUMBER}"
- name: Apply labels using labeler
uses: actions/labeler@v5
with:
pr-number: ${{ steps.pr-number.outputs.number }}
repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler.yml
sync-labels: true
```
## /.github/workflows/release-build.yml
```yml path="/.github/workflows/release-build.yml"
name: container project - release build
permissions:
contents: read
on:
push:
tags:
- "[0-9]+\\.[0-9]+\\.[0-9]+"
jobs:
build:
name: Invoke build and release
uses: ./.github/workflows/common.yml
with:
release: true
secrets: inherit
permissions:
contents: read
packages: read
pages: write
release:
if: startsWith(github.ref, 'refs/tags/')
name: Publish release
timeout-minutes: 30
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
packages: read
pages: write
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: outputs
- name: Verify artifacts exist
run: |
echo "Checking for expected artifacts..."
ls -la outputs/container-package/
test -e outputs/container-package/*.zip || (echo "Missing .zip file!" && exit 1)
test -e outputs/container-package/*.pkg || (echo "Missing .pkg file!" && exit 1)
- name: Create release
uses: softprops/action-gh-release@v2
with:
token: ${{ github.token }}
name: ${{ github.ref_name }}-prerelease
draft: true
make_latest: false
prerelease: true
fail_on_unmatched_files: true
files: |
outputs/container-package/*.zip
outputs/container-package/*.pkg
```
## /.github/workflows/release.yml
```yml path="/.github/workflows/release.yml"
name: container project - release build
permissions:
contents: read
on:
push:
tags:
- "[0-9]+\\.[0-9]+\\.[0-9]+"
jobs:
build:
name: Invoke build and release
uses: ./.github/workflows/common.yml
with:
release: true
secrets: inherit
permissions:
contents: read
packages: read
pages: write
release:
if: startsWith(github.ref, 'refs/tags/')
name: Publish release
timeout-minutes: 30
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
packages: read
pages: write
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: outputs
- name: Verify artifacts exist
run: |
echo "Checking for expected artifacts..."
ls -la outputs/container-package/
test -e outputs/container-package/*.zip || (echo "Missing .zip file!" && exit 1)
test -e outputs/container-package/*.pkg || (echo "Missing .pkg file!" && exit 1)
- name: Create release
uses: softprops/action-gh-release@v2
with:
token: ${{ github.token }}
name: ${{ github.ref_name }}-prerelease
draft: true
make_latest: false
prerelease: true
fail_on_unmatched_files: true
files: |
outputs/container-package/*.zip
outputs/container-package/*.pkg
```
## /.gitignore
```gitignore path="/.gitignore"
.DS_Store
bin
libexec
.build
.local
xcuserdata/
DerivedData/
Packages/
.swiftpm/
.netrc
.swiftpm
api-docs/
workdir/
installer/
.venv/
.claude/
.clitests/
test_results/
*.pid
*.log
*.zip
*.o
*.ext4
*.pkg
*.swp
# API docs for local preview only.
_site/
_serve/
```
## /.spi.yml
```yml path="/.spi.yml"
version: 1
builder:
configs:
-
documentation_targets:
- ContainerSandboxService
- ContainerNetworkService
- ContainerImagesService
- ContainerClient
- ContainerLog
- ContainerPlugin
- ContainerXPC
- TerminalProgress
swift_version: '6.2'
```
## /.swift-format
```swift-format path="/.swift-format"
{
"fileScopedDeclarationPrivacy" : {
"accessLevel" : "private"
},
"indentation" : {
"spaces" : 4
},
"indentConditionalCompilationBlocks" : false,
"indentSwitchCaseLabels" : false,
"lineBreakAroundMultilineExpressionChainComponents" : false,
"lineBreakBeforeControlFlowKeywords" : false,
"lineBreakBeforeEachArgument" : false,
"lineBreakBeforeEachGenericRequirement" : false,
"lineLength" : 180,
"maximumBlankLines" : 1,
"multiElementCollectionTrailingCommas" : true,
"noAssignmentInExpressions" : {
"allowedFunctions" : [
"XCTAssertNoThrow"
]
},
"prioritizeKeepingFunctionOutputTogether" : false,
"respectsExistingLineBreaks" : true,
"rules" : {
"AllPublicDeclarationsHaveDocumentation" : false,
"AlwaysUseLowerCamelCase" : true,
"AmbiguousTrailingClosureOverload" : false,
"BeginDocumentationCommentWithOneLineSummary" : false,
"DoNotUseSemicolons" : true,
"DontRepeatTypeInStaticProperties" : true,
"FileScopedDeclarationPrivacy" : true,
"FullyIndirectEnum" : true,
"GroupNumericLiterals" : true,
"IdentifiersMustBeASCII" : true,
"NeverForceUnwrap" : true,
"NeverUseForceTry" : true,
"NeverUseImplicitlyUnwrappedOptionals" : true,
"NoAccessLevelOnExtensionDeclaration" : true,
"NoAssignmentInExpressions" : true,
"NoBlockComments" : false,
"NoCasesWithOnlyFallthrough" : true,
"NoEmptyTrailingClosureParentheses" : true,
"NoLabelsInCasePatterns" : true,
"NoLeadingUnderscores" : false,
"NoParensAroundConditions" : true,
"NoPlaygroundLiterals" : true,
"NoVoidReturnOnFunctionSignature" : true,
"OmitExplicitReturns" : true,
"OneCasePerLine" : true,
"OneVariableDeclarationPerLine" : true,
"OnlyOneTrailingClosureArgument" : true,
"OrderedImports" : true,
"ReplaceForEachWithForLoop" : true,
"ReturnVoidInsteadOfEmptyTuple" : true,
"TypeNamesShouldBeCapitalized" : true,
"UseEarlyExits" : true,
"UseLetInEveryBoundCaseVariable" : true,
"UseShorthandTypeNames" : true,
"UseSingleLinePropertyGetter" : true,
"UseSynthesizedInitializer" : true,
"UseTripleSlashForDocumentationComments" : true,
"UseWhereClausesInForLoops" : false,
"ValidateDocumentationComments" : true
},
"spacesAroundRangeFormationOperators" : false,
"tabWidth" : 2,
"version" : 1
}
```
## /BUILDING.md
# Building the project
To build the `container` project, you need:
- Mac with Apple silicon
- macOS 15 minimum, macOS 26 recommended
- Xcode 26, set as the [active developer directory](https://developer.apple.com/library/archive/technotes/tn2339/_index.html#//apple_ref/doc/uid/DTS40014588-CH1-HOW_DO_I_SELECT_THE_DEFAULT_VERSION_OF_XCODE_TO_USE_FOR_MY_COMMAND_LINE_TOOLS_)
> [!IMPORTANT]
> There is a bug in the `vmnet` framework on macOS 26 that causes network creation to fail if the `container` helper applications are located under your `Documents` or `Desktop` directories. If you use `make install`, you can simply run the `container` binary in `/usr/local`. If you prefer to use the binaries that `make all` creates in your project `bin` and `libexec` directories, locate your project elsewhere, such as `~/projects/container`, until this issue is resolved.
## Compile and test
Build `container` and the background services from source, and run basic and integration tests:
```bash
make all test integration
```
Copy the binaries to `/usr/local/bin` and `/usr/local/libexec` (requires entering an administrator password):
```bash
make install
```
Or to install a release build, with better performance than the debug build:
```bash
BUILD_CONFIGURATION=release make all test integration
BUILD_CONFIGURATION=release make install
```
## Compile protobufs
`container` uses gRPC to communicate to the builder virtual machine that creates images from `Dockerfile`s, and depends on specific versions of `grpc-swift` and `swift-protobuf`. If you make changes to the gRPC APIs in the [container-builder-shim](https://github.com/apple/container-builder-shim) project, install the tools and re-generate the gRPC code in this project using:
```bash
make protos
```
## Develop using a local copy of Containerization
To make changes to `container` that require changes to the Containerization project, or vice versa:
1. Clone the [Containerization](https://github.com/apple/containerization) repository such that it sits next to your clone
of the `container` repository. Ensure that you [follow containerization instructions](https://github.com/apple/containerization/blob/main/README.md#prepare-to-build-package)
to prepare your build environment.
2. In your development shell, go to the `container` project directory.
```bash
cd container
```
3. If the `container` services are already running, stop them.
```bash
bin/container system stop
```
4. Use the Swift package manager to configure use your local `containerization` package and update your `Package.resolved` file.
```bash
/usr/bin/swift package edit --path ../containerization containerization
/usr/bin/swift package update containerization
```
> [!IMPORTANT]
> If you are using Xcode, you will need to temporarily modify `Package.swift` instead of using `swift package edit`, using a path dependency in place of the versioned `container` dependency:
>
> ```swift
> .package(path: "../containerization"),
> ```
5. Build `container`.
```
make clean all
```
6. Restart the `container` services.
```
bin/container system stop
bin/container system start
```
To revert to using the Containerization dependency from your `Package.swift`:
1. Use the Swift package manager to restore the normal `containerization` dependency and update your `Package.resolved` file. If you are using Xcode, revert your `Package.swift` change instead of using `swift package unedit`.
```bash
/usr/bin/swift package unedit containerization
/usr/bin/swift package update containerization
```
2. Rebuild `container`.
```bash
make clean all
```
3. Restart the `container` services.
```bash
bin/container system stop
bin/container system start
```
## /CONTRIBUTING.md
# Contributing
Contributions are welcome and encouraged! Read our [main contributing guide](https://github.com/apple/containerization/blob/main/CONTRIBUTING.md) to get started.
## /MAINTAINERS.txt
# Maintainers
See [MAINTAINERS](https://github.com/apple/containerization/blob/main/MAINTAINERS.txt) for the list of current and former maintainers of this project. Thank you for all your contributions!
## /Makefile
``` path="/Makefile"
# Copyright © 2025 Apple Inc. and the container project authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Version and build configuration variables
BUILD_CONFIGURATION ?= debug
WARNINGS_AS_ERRORS ?= true
SWIFT_CONFIGURATION := $(if $(filter-out false,$(WARNINGS_AS_ERRORS)),-Xswiftc -warnings-as-errors)
export RELEASE_VERSION ?= $(shell git describe --tags --always)
export GIT_COMMIT := $(shell git rev-parse HEAD)
# Commonly used locations
SWIFT := "/usr/bin/swift"
DEST_DIR ?= /usr/local/
ROOT_DIR := $(shell git rev-parse --show-toplevel)
BUILD_BIN_DIR = $(shell $(SWIFT) build -c $(BUILD_CONFIGURATION) --show-bin-path)
COV_DATA_DIR = $(shell $(SWIFT) test --show-coverage-path | xargs dirname)
COV_REPORT_FILE = $(ROOT_DIR)/code-coverage-report
STAGING_DIR := bin/$(BUILD_CONFIGURATION)/staging/
PKG_PATH := bin/$(BUILD_CONFIGURATION)/container-installer-unsigned.pkg
DSYM_DIR := bin/$(BUILD_CONFIGURATION)/bundle/container-dSYM
DSYM_PATH := bin/$(BUILD_CONFIGURATION)/bundle/container-dSYM.zip
CODESIGN_OPTS ?= --force --sign - --timestamp=none
# Conditionally use a temporary data directory for integration tests
ifeq ($(strip $(APP_ROOT)),)
SYSTEM_START_OPTS :=
else
SYSTEM_START_OPTS := --app-root "$(strip $(APP_ROOT))"
endif
MACOS_VERSION := $(shell sw_vers -productVersion)
MACOS_MAJOR := $(shell echo $(MACOS_VERSION) | cut -d. -f1)
SUDO ?= sudo
.DEFAULT_GOAL := all
include Protobuf.Makefile
.PHONY: all
all: container
all: init-block
.PHONY: build
build:
@echo Building container binaries...
@$(SWIFT) --version
@$(SWIFT) build -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION)
.PHONY: container
# Install binaries under project directory
container: build
@"$(MAKE)" BUILD_CONFIGURATION=$(BUILD_CONFIGURATION) DEST_DIR="$(ROOT_DIR)/" SUDO= install
.PHONY: release
release: BUILD_CONFIGURATION = release
release: all
.PHONY: init-block
init-block:
@scripts/install-init.sh
.PHONY: install
install: installer-pkg
@echo Installing container installer package
@if [ -z "$(SUDO)" ] ; then \
temp_dir=$(mktemp -d) ; \
xar -xf $(PKG_PATH) -C ${temp_dir} ; \
(cd ${temp_dir} && tar -xf Payload -C "$(DEST_DIR)") ; \
rm -rf ${temp_dir} ; \
else \
$(SUDO) installer -pkg $(PKG_PATH) -target / ; \
fi
$(STAGING_DIR):
@echo Installing container binaries from "$(BUILD_BIN_DIR)" into "$(STAGING_DIR)"...
@rm -rf "$(STAGING_DIR)"
@mkdir -p "$(join $(STAGING_DIR), bin)"
@mkdir -p "$(join $(STAGING_DIR), libexec/container/plugins/container-runtime-linux/bin)"
@mkdir -p "$(join $(STAGING_DIR), libexec/container/plugins/container-network-vmnet/bin)"
@mkdir -p "$(join $(STAGING_DIR), libexec/container/plugins/container-core-images/bin)"
@install "$(BUILD_BIN_DIR)/container" "$(join $(STAGING_DIR), bin/container)"
@install "$(BUILD_BIN_DIR)/container-apiserver" "$(join $(STAGING_DIR), bin/container-apiserver)"
@install "$(BUILD_BIN_DIR)/container-runtime-linux" "$(join $(STAGING_DIR), libexec/container/plugins/container-runtime-linux/bin/container-runtime-linux)"
@install config/container-runtime-linux-config.json "$(join $(STAGING_DIR), libexec/container/plugins/container-runtime-linux/config.json)"
@install "$(BUILD_BIN_DIR)/container-network-vmnet" "$(join $(STAGING_DIR), libexec/container/plugins/container-network-vmnet/bin/container-network-vmnet)"
@install config/container-network-vmnet-config.json "$(join $(STAGING_DIR), libexec/container/plugins/container-network-vmnet/config.json)"
@install "$(BUILD_BIN_DIR)/container-core-images" "$(join $(STAGING_DIR), libexec/container/plugins/container-core-images/bin/container-core-images)"
@install config/container-core-images-config.json "$(join $(STAGING_DIR), libexec/container/plugins/container-core-images/config.json)"
@echo Install uninstaller script
@install scripts/uninstall-container.sh "$(join $(STAGING_DIR), bin/uninstall-container.sh)"
.PHONY: installer-pkg
installer-pkg: $(STAGING_DIR)
@echo Signing container binaries...
@codesign $(CODESIGN_OPTS) --identifier com.apple.container.cli "$(join $(STAGING_DIR), bin/container)"
@codesign $(CODESIGN_OPTS) --identifier com.apple.container.apiserver "$(join $(STAGING_DIR), bin/container-apiserver)"
@codesign $(CODESIGN_OPTS) --prefix=com.apple.container. "$(join $(STAGING_DIR), libexec/container/plugins/container-core-images/bin/container-core-images)"
@codesign $(CODESIGN_OPTS) --prefix=com.apple.container. --entitlements=signing/container-runtime-linux.entitlements "$(join $(STAGING_DIR), libexec/container/plugins/container-runtime-linux/bin/container-runtime-linux)"
@codesign $(CODESIGN_OPTS) --prefix=com.apple.container. --entitlements=signing/container-network-vmnet.entitlements "$(join $(STAGING_DIR), libexec/container/plugins/container-network-vmnet/bin/container-network-vmnet)"
@echo Creating application installer
@pkgbuild --root "$(STAGING_DIR)" --identifier com.apple.container-installer --install-location /usr/local --version ${RELEASE_VERSION} $(PKG_PATH)
@rm -rf "$(STAGING_DIR)"
.PHONY: dsym
dsym:
@echo Copying debug symbols...
@rm -rf "$(DSYM_DIR)"
@mkdir -p "$(DSYM_DIR)"
@cp -a "$(BUILD_BIN_DIR)/container-runtime-linux.dSYM" "$(DSYM_DIR)"
@cp -a "$(BUILD_BIN_DIR)/container-network-vmnet.dSYM" "$(DSYM_DIR)"
@cp -a "$(BUILD_BIN_DIR)/container-core-images.dSYM" "$(DSYM_DIR)"
@cp -a "$(BUILD_BIN_DIR)/container-apiserver.dSYM" "$(DSYM_DIR)"
@cp -a "$(BUILD_BIN_DIR)/container.dSYM" "$(DSYM_DIR)"
@echo Packaging the debug symbols...
@(cd "$(dir $(DSYM_DIR))" ; zip -r $(notdir $(DSYM_PATH)) $(notdir $(DSYM_DIR)))
.PHONY: test
test:
@$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --skip TestCLI
.PHONY: install-kernel
install-kernel:
@bin/container system stop || true
@bin/container system start --enable-kernel-install $(SYSTEM_START_OPTS)
.PHONY: coverage
coverage: init-block
@echo Ensuring apiserver stopped before the CLI integration tests...
@bin/container system stop && sleep 3 && scripts/ensure-container-stopped.sh
@bin/container system start $(SYSTEM_START_OPTS) && \
echo "Starting unit tests" && \
{ \
exit_code=0; \
$(SWIFT) test --no-parallel --enable-code-coverage -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) || exit_code=1 ; \
echo Ensuring apiserver stopped after the CLI integration tests ; \
scripts/ensure-container-stopped.sh ; \
echo Generating code coverage report... ; \
xcrun llvm-profdata merge -sparse $(COV_DATA_DIR)/*.profraw -o $(COV_DATA_DIR)/default.profdata ; \
xcrun llvm-cov show --compilation-dir=`pwd` \
-instr-profile=$(COV_DATA_DIR)/default.profdata \
--ignore-filename-regex=".build/" \
--ignore-filename-regex=".pb.swift" \
--ignore-filename-regex=".proto" \
--ignore-filename-regex=".grpc.swift" \
$(BUILD_BIN_DIR)/containerPackageTests.xctest/Contents/MacOS/containerPackageTests > $(COV_REPORT_FILE) ; \
echo Code coverage report generated: $(COV_REPORT_FILE) ; \
exit ${exit_code} ; \
}
.PHONY: integration
integration: init-block
@echo Ensuring apiserver stopped before the CLI integration tests...
@bin/container system stop && sleep 3 && scripts/ensure-container-stopped.sh
@echo Running the integration tests...
@bin/container system start $(SYSTEM_START_OPTS) && \
echo "Starting CLI integration tests" && \
{ \
exit_code=0; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLINetwork || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIRunLifecycle || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIExecCommand || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLICreateCommand || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIRunCommand || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIStatsCommand || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIImagesCommand || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIRunBase || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIBuildBase || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIVolumes || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIKernelSet || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIAnonymousVolumes || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --no-parallel --filter TestCLINoParallelCases || exit_code=1 ; \
echo Ensuring apiserver stopped after the CLI integration tests ; \
scripts/ensure-container-stopped.sh ; \
exit ${exit_code} ; \
}
.PHONY: fmt
fmt: swift-fmt update-licenses
.PHONY: swift-fmt
SWIFT_SRC = $(shell find . -type f -name '*.swift' -not -path "*/.*" -not -path "*.pb.swift" -not -path "*.grpc.swift" -not -path "*/checkouts/*")
swift-fmt:
@echo Applying the standard code formatting...
@$(SWIFT) format --recursive --configuration .swift-format -i $(SWIFT_SRC)
.PHONY: update-licenses
update-licenses:
@echo Updating license headers...
@./scripts/ensure-hawkeye-exists.sh
@.local/bin/hawkeye format --fail-if-unknown --fail-if-updated false
.PHONY: check-licenses
check-licenses:
@echo Checking license headers existence in source files...
@./scripts/ensure-hawkeye-exists.sh
@.local/bin/hawkeye check --fail-if-unknown
.PHONY: serve-docs
serve-docs:
@echo 'to browse: open http://127.0.0.1:8000/container/documentation/'
@rm -rf _serve
@mkdir -p _serve
@cp -a _site _serve/container
@python3 -m http.server --bind 127.0.0.1 --directory ./_serve
.PHONY: docs
docs:
@echo Updating API documentation...
@rm -rf _site
@scripts/make-docs.sh _site container
.PHONY: cleancontent
cleancontent:
@bin/container system stop || true
@echo Cleaning the content...
@rm -rf ~/Library/Application\ Support/com.apple.container
.PHONY: clean
clean:
@echo Cleaning build files...
@rm -rf bin/ libexec/
@rm -rf _site _serve
@rm -f $(COV_REPORT_FILE)
@$(SWIFT) package clean
```
## /Package.resolved
```resolved path="/Package.resolved"
{
"originHash" : "a20d52336b96991c23765c8301254aeff44ab33c0ccc7dee59191e50652f5070",
"pins" : [
{
"identity" : "async-http-client",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swift-server/async-http-client.git",
"state" : {
"revision" : "60235983163d040f343a489f7e2e77c1918a8bd9",
"version" : "1.26.1"
}
},
{
"identity" : "containerization",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/containerization.git",
"state" : {
"revision" : "e8aff29be3b97afa18ccc256126466ae5e611bea",
"version" : "0.16.2"
}
},
{
"identity" : "dns",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Bouke/DNS",
"state" : {
"revision" : "78bbd1589890a90b202d11d5f9e1297050cf0eb2",
"version" : "1.2.0"
}
},
{
"identity" : "dnsclient",
"kind" : "remoteSourceControl",
"location" : "https://github.com/orlandos-nl/DNSClient",
"state" : {
"revision" : "551fbddbf4fa728d4cd86f6a5208fe4f925f0549",
"version" : "2.4.4"
}
},
{
"identity" : "grpc-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/grpc/grpc-swift.git",
"state" : {
"revision" : "a56a157218877ef3e9625f7e1f7b2cb7e46ead1b",
"version" : "1.26.1"
}
},
{
"identity" : "swift-algorithms",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-algorithms.git",
"state" : {
"revision" : "87e50f483c54e6efd60e885f7f5aa946cee68023",
"version" : "1.2.1"
}
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git",
"state" : {
"revision" : "309a47b2b1d9b5e991f36961c983ecec72275be3",
"version" : "1.6.1"
}
},
{
"identity" : "swift-asn1",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-asn1.git",
"state" : {
"revision" : "f70225981241859eb4aa1a18a75531d26637c8cc",
"version" : "1.4.0"
}
},
{
"identity" : "swift-async-algorithms",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-async-algorithms.git",
"state" : {
"revision" : "6c050d5ef8e1aa6342528460db614e9770d7f804",
"version" : "1.1.1"
}
},
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "b601256eab081c0f92f059e12818ac1d4f178ff7",
"version" : "1.3.0"
}
},
{
"identity" : "swift-certificates",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-certificates.git",
"state" : {
"revision" : "133a347911b6ad0fc8fe3bf46ca90c66cff97130",
"version" : "1.17.0"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "8c0c0a8b49e080e54e5e328cc552821ff07cd341",
"version" : "1.2.1"
}
},
{
"identity" : "swift-crypto",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "334e682869394ee239a57dbe9262bff3cd9495bd",
"version" : "3.14.0"
}
},
{
"identity" : "swift-docc-plugin",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-docc-plugin.git",
"state" : {
"revision" : "3e4f133a77e644a5812911a0513aeb7288b07d06",
"version" : "1.4.5"
}
},
{
"identity" : "swift-docc-symbolkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-docc-symbolkit",
"state" : {
"revision" : "b45d1f2ed151d057b54504d653e0da5552844e34",
"version" : "1.0.0"
}
},
{
"identity" : "swift-http-structured-headers",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-http-structured-headers.git",
"state" : {
"revision" : "76d7627bd88b47bf5a0f8497dd244885960dde0b",
"version" : "1.6.0"
}
},
{
"identity" : "swift-http-types",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-http-types.git",
"state" : {
"revision" : "45eb0224913ea070ec4fba17291b9e7ecf4749ca",
"version" : "1.5.1"
}
},
{
"identity" : "swift-log",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "ce592ae52f982c847a4efc0dd881cc9eb32d29f2",
"version" : "1.6.4"
}
},
{
"identity" : "swift-nio",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "1c30f0f2053b654e3d1302492124aa6d242cdba7",
"version" : "2.86.0"
}
},
{
"identity" : "swift-nio-extras",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-extras.git",
"state" : {
"revision" : "a55c3dd3a81d035af8a20ce5718889c0dcab073d",
"version" : "1.29.0"
}
},
{
"identity" : "swift-nio-http2",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-http2.git",
"state" : {
"revision" : "5e9e99ec96c53bc2c18ddd10c1e25a3cd97c55e5",
"version" : "1.38.0"
}
},
{
"identity" : "swift-nio-ssl",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-ssl.git",
"state" : {
"revision" : "173cc69a058623525a58ae6710e2f5727c663793",
"version" : "2.36.0"
}
},
{
"identity" : "swift-nio-transport-services",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-transport-services.git",
"state" : {
"revision" : "e645014baea2ec1c2db564410c51a656cf47c923",
"version" : "1.25.1"
}
},
{
"identity" : "swift-numerics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-numerics.git",
"state" : {
"revision" : "0c0290ff6b24942dadb83a929ffaaa1481df04a2",
"version" : "1.1.1"
}
},
{
"identity" : "swift-protobuf",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "102a647b573f60f73afdce5613a51d71349fe507",
"version" : "1.30.0"
}
},
{
"identity" : "swift-service-lifecycle",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swift-server/swift-service-lifecycle.git",
"state" : {
"revision" : "e7187309187695115033536e8fc9b2eb87fd956d",
"version" : "2.8.0"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system.git",
"state" : {
"revision" : "890830fff1a577dc83134890c7984020c5f6b43b",
"version" : "1.6.2"
}
}
],
"version" : 3
}
```
## /Package.swift
```swift path="/Package.swift"
// swift-tools-version: 6.2
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
// The swift-tools-version declares the minimum version of Swift required to build this package.
import Foundation
import PackageDescription
let releaseVersion = ProcessInfo.processInfo.environment["RELEASE_VERSION"] ?? "0.0.0"
let gitCommit = ProcessInfo.processInfo.environment["GIT_COMMIT"] ?? "unspecified"
let builderShimVersion = "0.7.0"
let scVersion = "0.16.2"
let package = Package(
name: "container",
platforms: [.macOS("15")],
products: [
.library(name: "ContainerAPIService", targets: ["ContainerAPIService"]),
.library(name: "ContainerSandboxService", targets: ["ContainerSandboxService"]),
.library(name: "ContainerNetworkService", targets: ["ContainerNetworkService"]),
.library(name: "ContainerImagesService", targets: ["ContainerImagesService", "ContainerImagesServiceClient"]),
.library(name: "ContainerCommands", targets: ["ContainerCommands"]),
.library(name: "ContainerClient", targets: ["ContainerClient"]),
.library(name: "ContainerBuild", targets: ["ContainerBuild"]),
.library(name: "ContainerLog", targets: ["ContainerLog"]),
.library(name: "ContainerPersistence", targets: ["ContainerPersistence"]),
.library(name: "ContainerPlugin", targets: ["ContainerPlugin"]),
.library(name: "ContainerVersion", targets: ["ContainerVersion"]),
.library(name: "ContainerXPC", targets: ["ContainerXPC"]),
.library(name: "SocketForwarder", targets: ["SocketForwarder"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"),
.package(url: "https://github.com/apple/swift-collections.git", from: "1.2.0"),
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.26.0"),
.package(url: "https://github.com/apple/swift-protobuf.git", from: "1.29.0"),
.package(url: "https://github.com/apple/swift-nio.git", from: "2.80.0"),
.package(url: "https://github.com/swiftlang/swift-docc-plugin.git", from: "1.1.0"),
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.20.1"),
.package(url: "https://github.com/orlandos-nl/DNSClient.git", from: "2.4.1"),
.package(url: "https://github.com/Bouke/DNS.git", from: "1.2.0"),
.package(url: "https://github.com/apple/containerization.git", exact: Version(stringLiteral: scVersion)),
],
targets: [
.executableTarget(
name: "container",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
"ContainerClient",
"ContainerCommands",
],
path: "Sources/CLI"
),
.target(
name: "ContainerCommands",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "Logging", package: "swift-log"),
.product(name: "SwiftProtobuf", package: "swift-protobuf"),
.product(name: "Containerization", package: "containerization"),
.product(name: "ContainerizationOCI", package: "containerization"),
.product(name: "ContainerizationOS", package: "containerization"),
"ContainerBuild",
"ContainerClient",
"ContainerLog",
"ContainerPersistence",
"ContainerPlugin",
"ContainerVersion",
"TerminalProgress",
],
path: "Sources/ContainerCommands"
),
.executableTarget(
name: "container-apiserver",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "Containerization", package: "containerization"),
.product(name: "ContainerizationExtras", package: "containerization"),
.product(name: "ContainerizationOS", package: "containerization"),
.product(name: "GRPC", package: "grpc-swift"),
.product(name: "Logging", package: "swift-log"),
"ContainerAPIService",
"ContainerClient",
"ContainerLog",
"ContainerNetworkService",
"ContainerPersistence",
"ContainerPlugin",
"ContainerVersion",
"ContainerXPC",
"DNSServer",
],
path: "Sources/Helpers/APIServer"
),
.target(
name: "ContainerAPIService",
dependencies: [
.product(name: "Containerization", package: "containerization"),
.product(name: "ContainerizationExtras", package: "containerization"),
.product(name: "ContainerizationOS", package: "containerization"),
.product(name: "Logging", package: "swift-log"),
"ContainerClient",
"ContainerNetworkService",
"ContainerPersistence",
"ContainerPlugin",
"ContainerSandboxService",
"ContainerVersion",
"ContainerXPC",
"CVersion",
"TerminalProgress",
],
path: "Sources/Services/ContainerAPIService"
),
.executableTarget(
name: "container-runtime-linux",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "Logging", package: "swift-log"),
.product(name: "GRPC", package: "grpc-swift"),
.product(name: "Containerization", package: "containerization"),
"ContainerLog",
"ContainerNetworkService",
"ContainerSandboxService",
"ContainerVersion",
"ContainerXPC",
],
path: "Sources/Helpers/RuntimeLinux"
),
.target(
name: "ContainerSandboxService",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "Containerization", package: "containerization"),
.product(name: "ContainerizationOS", package: "containerization"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
"ContainerClient",
"ContainerNetworkService",
"ContainerPersistence",
"ContainerXPC",
"SocketForwarder",
],
path: "Sources/Services/ContainerSandboxService"
),
.executableTarget(
name: "container-network-vmnet",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "Logging", package: "swift-log"),
.product(name: "Containerization", package: "containerization"),
.product(name: "ContainerizationExtras", package: "containerization"),
.product(name: "ContainerizationIO", package: "containerization"),
.product(name: "ContainerizationOS", package: "containerization"),
"ContainerLog",
"ContainerNetworkService",
"ContainerVersion",
"ContainerXPC",
],
path: "Sources/Helpers/NetworkVmnet"
),
.target(
name: "ContainerNetworkService",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "Containerization", package: "containerization"),
.product(name: "ContainerizationOS", package: "containerization"),
"ContainerPersistence",
"ContainerXPC",
],
path: "Sources/Services/ContainerNetworkService"
),
.testTarget(
name: "ContainerNetworkServiceTests",
dependencies: [
"ContainerNetworkService"
]
),
.executableTarget(
name: "container-core-images",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "Logging", package: "swift-log"),
.product(name: "Containerization", package: "containerization"),
"ContainerImagesService",
"ContainerLog",
"ContainerPlugin",
"ContainerVersion",
"ContainerXPC",
],
path: "Sources/Helpers/Images"
),
.target(
name: "ContainerImagesService",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "Containerization", package: "containerization"),
"ContainerXPC",
"ContainerLog",
"ContainerClient",
"ContainerImagesServiceClient",
],
path: "Sources/Services/ContainerImagesService/Server"
),
.target(
name: "ContainerImagesServiceClient",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "Containerization", package: "containerization"),
"ContainerXPC",
"ContainerLog",
],
path: "Sources/Services/ContainerImagesService/Client"
),
.target(
name: "ContainerBuild",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "NIO", package: "swift-nio"),
.product(name: "Containerization", package: "containerization"),
.product(name: "ContainerizationArchive", package: "containerization"),
.product(name: "ContainerizationOCI", package: "containerization"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
"ContainerClient",
]
),
.testTarget(
name: "ContainerBuildTests",
dependencies: [
"ContainerBuild"
]
),
.target(
name: "ContainerClient",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOPosix", package: "swift-nio"),
.product(name: "Containerization", package: "containerization"),
.product(name: "ContainerizationOCI", package: "containerization"),
.product(name: "ContainerizationOS", package: "containerization"),
.product(name: "ContainerizationArchive", package: "containerization"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
"ContainerImagesServiceClient",
"ContainerNetworkService",
"ContainerPlugin",
"ContainerXPC",
"TerminalProgress",
]
),
.testTarget(
name: "ContainerClientTests",
dependencies: [
.product(name: "Containerization", package: "containerization"),
"ContainerClient",
"ContainerPersistence",
]
),
.target(
name: "ContainerPersistence",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "Containerization", package: "containerization"),
"ContainerVersion",
"CVersion",
]
),
.target(
name: "ContainerPlugin",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "ContainerizationOS", package: "containerization"),
"ContainerVersion",
]
),
.testTarget(
name: "ContainerPluginTests",
dependencies: [
"ContainerPlugin"
]
),
.target(
name: "ContainerLog",
dependencies: [
.product(name: "Logging", package: "swift-log")
]
),
.target(
name: "ContainerXPC",
dependencies: [
.product(name: "ContainerizationExtras", package: "containerization"),
.product(name: "Logging", package: "swift-log"),
"CAuditToken",
]
),
.target(
name: "TerminalProgress",
dependencies: [
.product(name: "ContainerizationOS", package: "containerization")
]
),
.testTarget(
name: "TerminalProgressTests",
dependencies: ["TerminalProgress"]
),
.target(
name: "DNSServer",
dependencies: [
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOPosix", package: "swift-nio"),
.product(name: "DNSClient", package: "DNSClient"),
.product(name: "DNS", package: "DNS"),
.product(name: "Logging", package: "swift-log"),
]
),
.testTarget(
name: "DNSServerTests",
dependencies: [
.product(name: "DNS", package: "DNS"),
"DNSServer",
]
),
.target(
name: "SocketForwarder",
dependencies: [
.product(name: "Collections", package: "swift-collections"),
.product(name: "Logging", package: "swift-log"),
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOFoundationCompat", package: "swift-nio"),
]
),
.testTarget(
name: "SocketForwarderTests",
dependencies: ["SocketForwarder"]
),
.testTarget(
name: "CLITests",
dependencies: [
.product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "Containerization", package: "containerization"),
.product(name: "ContainerizationExtras", package: "containerization"),
.product(name: "ContainerizationOS", package: "containerization"),
"ContainerBuild",
"ContainerClient",
"ContainerNetworkService",
],
path: "Tests/CLITests"
),
.target(
name: "ContainerVersion",
dependencies: [
"CVersion"
],
),
.target(
name: "CVersion",
dependencies: [],
publicHeadersPath: "include",
cSettings: [
.define("CZ_VERSION", to: "\"\(scVersion)\""),
.define("GIT_COMMIT", to: "\"\(gitCommit)\""),
.define("RELEASE_VERSION", to: "\"\(releaseVersion)\""),
.define("BUILDER_SHIM_VERSION", to: "\"\(builderShimVersion)\""),
],
),
.target(
name: "CAuditToken",
dependencies: [],
publicHeadersPath: "include",
linkerSettings: [
.linkedLibrary("bsm")
]
),
]
)
```
## /Protobuf.Makefile
```Makefile path="/Protobuf.Makefile"
# Copyright © 2025 Apple Inc. and the container project authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
ROOT_DIR := $(shell git rev-parse --show-toplevel)
LOCAL_DIR := $(ROOT_DIR)/.local
LOCAL_BIN_DIR := $(LOCAL_DIR)/bin
BUILDER_SHIM_REPO ?= https://github.com/apple/container-builder-shim.git
# Versions
BUILDER_SHIM_VERSION ?= $(shell sed -n 's/let builderShimVersion *= *"\(.*\)"/\1/p' Package.swift)
PROTOC_VERSION := 26.1
# Protoc binary installation
PROTOC_ZIP := protoc-$(PROTOC_VERSION)-osx-universal_binary.zip
PROTOC := $(LOCAL_BIN_DIR)/protoc@$(PROTOC_VERSION)/protoc
$(PROTOC):
@echo Downloading protocol buffers...
@mkdir -p $(LOCAL_DIR)
@curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/$(PROTOC_ZIP)
@mkdir -p $(dir $@)
@unzip -jo $(PROTOC_ZIP) bin/protoc -d $(dir $@)
@unzip -o $(PROTOC_ZIP) 'include/*' -d $(dir $@)
@rm -f $(PROTOC_ZIP)
.PHONY: protoc-gen-swift
protoc-gen-swift:
@$(SWIFT) build --product protoc-gen-swift
@$(SWIFT) build --product protoc-gen-grpc-swift
.PHONY: protos
protos: $(PROTOC) protoc-gen-swift
@echo Generating protocol buffers source code...
@mkdir -p $(LOCAL_DIR)
@if [ ! -d "$(LOCAL_DIR)/container-builder-shim" ]; then \
cd $(LOCAL_DIR) && git clone --branch $(BUILDER_SHIM_VERSION) --depth 1 $(BUILDER_SHIM_REPO); \
fi
@$(PROTOC) $(LOCAL_DIR)/container-builder-shim/pkg/api/Builder.proto \
--plugin=protoc-gen-grpc-swift=$(BUILD_BIN_DIR)/protoc-gen-grpc-swift \
--plugin=protoc-gen-swift=$(BUILD_BIN_DIR)/protoc-gen-swift \
--proto_path=$(LOCAL_DIR)/container-builder-shim/pkg/api \
--grpc-swift_out="Sources/ContainerBuild" \
--grpc-swift_opt=Visibility=Public \
--swift_out="Sources/ContainerBuild" \
--swift_opt=Visibility=Public \
-I.
@"$(MAKE)" update-licenses
.PHONY: clean-proto-tools
clean-proto-tools:
@echo Cleaning proto tools...
@rm -rf $(LOCAL_DIR)/bin/protoc*
@rm -rf $(LOCAL_DIR)/container-builder-shim
```
## /README.md
# `container`
`container` is a tool that you can use to create and run Linux containers as lightweight virtual machines on your Mac. It's written in Swift, and optimized for Apple silicon.
The tool consumes and produces [OCI-compatible container images](https://github.com/opencontainers/image-spec), so you can pull and run images from any standard container registry. You can push images that you build to those registries as well, and run the images in any other OCI-compatible application.
`container` uses the [Containerization](https://github.com/apple/containerization) Swift package for low level container, image, and process management.

## Get started
### Requirements
You need a Mac with Apple silicon to run `container`. To build it, see the [BUILDING](./BUILDING.md) document.
`container` is supported on macOS 26, since it takes advantage of new features and enhancements to virtualization and networking in this release. We do not support older versions of macOS and the `container` maintainers typically will not address issues that cannot be reproduced on the macOS 26.
### Install or upgrade
If you're upgrading, first stop and uninstall your existing `container` (the `-k` flag keeps your user data, while `-d` removes it):
```bash
container system stop
uninstall-container.sh -k
```
Download the latest signed installer package for `container` from the [GitHub release page](https://github.com/apple/container/releases).
To install the tool, double-click the package file and follow the instructions. Enter your administrator password when prompted, to give the installer permission to place the installed files under `/usr/local`.
Start the system service with:
```bash
container system start
```
### Uninstall
Use the `uninstall-container.sh` script to remove `container` from your system. To remove your user data along with the tool, run:
```bash
uninstall-container.sh -d
```
To retain your user data so that it is available should you reinstall later, run:
```bash
uninstall-container.sh -k
```
## Next steps
- Take [a guided tour of `container`](./docs/tutorial.md) by building, running, and publishing a simple web server image.
- Learn how to [use various `container` features](./docs/how-to.md).
- Read a brief description and [technical overview](./docs/technical-overview.md) of `container`.
- Browse the [full command reference](./docs/command-reference.md).
- [Build and run](./BUILDING.md) `container` on your own development system.
- View the project [API documentation](https://apple.github.io/container/documentation/).
## Contributing
Contributions to `container` are welcomed and encouraged. Please see our [main contributing guide](https://github.com/apple/containerization/blob/main/CONTRIBUTING.md) for more information.
## Project Status
The container project is currently under active development. Its stability, both for consuming the project as a Swift package and the `container` tool, is only guaranteed within patch versions, such as between 0.1.1 and 0.1.2. Minor version number releases may include breaking changes until we achieve a 1.0.0 release.
## /SECURITY.md
# Security disclosure process
If you believe that you have discovered a security or privacy vulnerability in our open source software, please report it to us using the [GitHub private vulnerability feature](https://github.com/apple/container/security/advisories/new). Reports should include specific product and software version(s) that you believe are affected; a technical description of the behavior that you observed and the behavior that you expected; the steps required to reproduce the issue; and a proof of concept or exploit.
The project team will do their best to acknowledge receiving all security reports within 7 days of submission. This initial acknowledgment is neither acceptance nor rejection of your report. The project team may come back to you with further questions or invite you to collaborate while working through the details of your report.
Keep these additional guidelines in mind when submitting your report:
* Reports concerning known, publicly disclosed CVEs can be submitted as normal issues to this project.
* Output from automated security scans or fuzzers MUST include additional context demonstrating the vulnerability with a proof of concept or working exploit.
* Application crashes due to malformed inputs are typically not treated as security vulnerabilities, unless they are shown to also impact other processes on the system.
While we welcome reports for open source software projects, they are not eligible for Apple Security Bounties.
## /Sources/CAuditToken/AuditToken.c
```c path="/Sources/CAuditToken/AuditToken.c"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
// This file is required for Xcode to generate `CAuditToken.o`.
```
## /Sources/CAuditToken/include/AuditToken.h
```h path="/Sources/CAuditToken/include/AuditToken.h"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
#include <xpc/xpc.h>
#include <bsm/libbsm.h>
void xpc_dictionary_get_audit_token(xpc_object_t xdict, audit_token_t *token);
```
## /Sources/CLI/ContainerCLI.swift
```swift path="/Sources/CLI/ContainerCLI.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ArgumentParser
import ContainerClient
import ContainerCommands
@main
public struct ContainerCLI: AsyncParsableCommand {
public init() {}
@Argument(parsing: .captureForPassthrough)
var arguments: [String] = []
public static let configuration = Application.configuration
public static func main() async throws {
try await Application.main()
}
public func run() async throws {
var application = try Application.parse(arguments)
try application.validate()
try application.run()
}
}
```
## /Sources/CVersion/Version.c
```c path="/Sources/CVersion/Version.c"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
#include "Version.h"
const char* get_git_commit() {
return GIT_COMMIT;
}
const char* get_release_version() {
return RELEASE_VERSION;
}
const char* get_swift_containerization_version() {
return CZ_VERSION;
}
const char* get_container_builder_shim_version() {
return BUILDER_SHIM_VERSION;
}
```
## /Sources/CVersion/include/Version.h
```h path="/Sources/CVersion/include/Version.h"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
#ifndef CZ_VERSION
#define CZ_VERSION "latest"
#endif
#ifndef GIT_COMMIT
#define GIT_COMMIT "unspecified"
#endif
#ifndef RELEASE_VERSION
#define RELEASE_VERSION "0.0.0"
#endif
#ifndef BUILDER_SHIM_VERSION
#define BUILDER_SHIM_VERSION "0.0.0"
#endif
const char* get_git_commit();
const char* get_release_version();
const char* get_swift_containerization_version();
const char* get_container_builder_shim_version();
```
## /Sources/ContainerBuild/BuildAPI+Extensions.swift
```swift path="/Sources/ContainerBuild/BuildAPI+Extensions.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Containerization
import ContainerizationOCI
public typealias IO = Com_Apple_Container_Build_V1_IO
public typealias InfoRequest = Com_Apple_Container_Build_V1_InfoRequest
public typealias InfoResponse = Com_Apple_Container_Build_V1_InfoResponse
public typealias ClientStream = Com_Apple_Container_Build_V1_ClientStream
public typealias ServerStream = Com_Apple_Container_Build_V1_ServerStream
public typealias ImageTransfer = Com_Apple_Container_Build_V1_ImageTransfer
public typealias BuildTransfer = Com_Apple_Container_Build_V1_BuildTransfer
public typealias BuilderClient = Com_Apple_Container_Build_V1_BuilderNIOClient
public typealias BuilderClientAsync = Com_Apple_Container_Build_V1_BuilderAsyncClient
public typealias BuilderClientProtocol = Com_Apple_Container_Build_V1_BuilderClientProtocol
public typealias BuilderClientAsyncProtocol = Com_Apple_Container_Build_V1_BuilderAsyncClient
extension BuildTransfer {
func stage() -> String? {
let stage = self.metadata["stage"]
return stage == "" ? nil : stage
}
func method() -> String? {
let method = self.metadata["method"]
return method == "" ? nil : method
}
func includePatterns() -> [String]? {
guard let includePatternsString = self.metadata["include-patterns"] else {
return nil
}
return includePatternsString == "" ? nil : includePatternsString.components(separatedBy: ",")
}
func followPaths() -> [String]? {
guard let followPathString = self.metadata["followpaths"] else {
return nil
}
return followPathString == "" ? nil : followPathString.components(separatedBy: ",")
}
func mode() -> String? {
self.metadata["mode"]
}
func size() -> Int? {
guard let sizeStr = self.metadata["size"] else {
return nil
}
return sizeStr == "" ? nil : Int(sizeStr)
}
func offset() -> UInt64? {
guard let offsetStr = self.metadata["offset"] else {
return nil
}
return offsetStr == "" ? nil : UInt64(offsetStr)
}
func len() -> Int? {
guard let lenStr = self.metadata["length"] else {
return nil
}
return lenStr == "" ? nil : Int(lenStr)
}
}
extension ImageTransfer {
func stage() -> String? {
self.metadata["stage"]
}
func method() -> String? {
self.metadata["method"]
}
func ref() -> String? {
self.metadata["ref"]
}
func platform() throws -> Platform? {
let metadata = self.metadata
guard let platform = metadata["platform"] else {
return nil
}
return try Platform(from: platform)
}
func mode() -> String? {
self.metadata["mode"]
}
func size() -> Int? {
let metadata = self.metadata
guard let sizeStr = metadata["size"] else {
return nil
}
return Int(sizeStr)
}
func len() -> Int? {
let metadata = self.metadata
guard let lenStr = metadata["length"] else {
return nil
}
return Int(lenStr)
}
func offset() -> UInt64? {
let metadata = self.metadata
guard let offsetStr = metadata["offset"] else {
return nil
}
return UInt64(offsetStr)
}
}
extension ServerStream {
func getImageTransfer() -> ImageTransfer? {
if case .imageTransfer(let v) = self.packetType {
return v
}
return nil
}
func getBuildTransfer() -> BuildTransfer? {
if case .buildTransfer(let v) = self.packetType {
return v
}
return nil
}
func getIO() -> IO? {
if case .io(let v) = self.packetType {
return v
}
return nil
}
}
```
## /Sources/ContainerBuild/BuildFSSync.swift
```swift path="/Sources/ContainerBuild/BuildFSSync.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Collections
import ContainerClient
import ContainerizationArchive
import ContainerizationOCI
import Foundation
import GRPC
actor BuildFSSync: BuildPipelineHandler {
let contextDir: URL
init(_ contextDir: URL) throws {
guard FileManager.default.fileExists(atPath: contextDir.cleanPath) else {
throw Error.contextNotFound(contextDir.cleanPath)
}
guard try contextDir.isDir() else {
throw Error.contextIsNotDirectory(contextDir.cleanPath)
}
self.contextDir = contextDir
}
nonisolated func accept(_ packet: ServerStream) throws -> Bool {
guard let buildTransfer = packet.getBuildTransfer() else {
return false
}
guard buildTransfer.stage() == "fssync" else {
return false
}
return true
}
func handle(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ServerStream) async throws {
guard let buildTransfer = packet.getBuildTransfer() else {
throw Error.buildTransferMissing
}
guard let method = buildTransfer.method() else {
throw Error.methodMissing
}
switch try FSSyncMethod(method) {
case .read:
try await self.read(sender, buildTransfer, packet.buildID)
case .info:
try await self.info(sender, buildTransfer, packet.buildID)
case .walk:
try await self.walk(sender, buildTransfer, packet.buildID)
}
}
func read(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: BuildTransfer, _ buildID: String) async throws {
let offset: UInt64 = packet.offset() ?? 0
let size: Int = packet.len() ?? 0
var path: URL
if packet.source.hasPrefix("/") {
path = URL(fileURLWithPath: packet.source).standardizedFileURL
} else {
path =
contextDir
.appendingPathComponent(packet.source)
.standardizedFileURL
}
if !FileManager.default.fileExists(atPath: path.cleanPath) {
path = URL(filePath: self.contextDir.cleanPath)
path.append(components: packet.source.cleanPathComponent)
}
let data = try {
if try path.isDir() {
return Data()
}
let file = try LocalContent(path: path.standardizedFileURL)
return try file.data(offset: offset, length: size) ?? Data()
}()
let transfer = try path.buildTransfer(id: packet.id, contextDir: self.contextDir, complete: true, data: data)
var response = ClientStream()
response.buildID = buildID
response.buildTransfer = transfer
response.packetType = .buildTransfer(transfer)
sender.yield(response)
}
func info(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: BuildTransfer, _ buildID: String) async throws {
let path: URL
if packet.source.hasPrefix("/") {
path = URL(fileURLWithPath: packet.source).standardizedFileURL
} else {
path =
contextDir
.appendingPathComponent(packet.source)
.standardizedFileURL
}
let transfer = try path.buildTransfer(id: packet.id, contextDir: self.contextDir, complete: true)
var response = ClientStream()
response.buildID = buildID
response.buildTransfer = transfer
response.packetType = .buildTransfer(transfer)
sender.yield(response)
}
private struct DirEntry: Hashable {
let url: URL
let isDirectory: Bool
let relativePath: String
func hash(into hasher: inout Hasher) {
hasher.combine(relativePath)
}
static func == (lhs: DirEntry, rhs: DirEntry) -> Bool {
lhs.relativePath == rhs.relativePath
}
}
func walk(
_ sender: AsyncStream<ClientStream>.Continuation,
_ packet: BuildTransfer,
_ buildID: String
) async throws {
let wantsTar = packet.mode() == "tar"
var entries: [String: Set<DirEntry>] = [:]
let followPaths: [String] = packet.followPaths() ?? []
let followPathsWalked = try walk(root: self.contextDir, includePatterns: followPaths)
for url in followPathsWalked {
guard self.contextDir.absoluteURL.cleanPath != url.absoluteURL.cleanPath else {
continue
}
guard self.contextDir.parentOf(url) else {
continue
}
let relPath = try url.relativeChildPath(to: contextDir)
let parentPath = try url.deletingLastPathComponent().relativeChildPath(to: contextDir)
let entry = DirEntry(url: url, isDirectory: url.hasDirectoryPath, relativePath: relPath)
entries[parentPath, default: []].insert(entry)
if url.isSymlink {
let target: URL = url.resolvingSymlinksInPath()
if self.contextDir.parentOf(target) {
let relPath = try target.relativeChildPath(to: self.contextDir)
let entry = DirEntry(url: target, isDirectory: target.hasDirectoryPath, relativePath: relPath)
let parentPath: String = try target.deletingLastPathComponent().relativeChildPath(to: self.contextDir)
entries[parentPath, default: []].insert(entry)
}
}
}
var fileOrder = [String]()
try processDirectory("", inputEntries: entries, processedPaths: &fileOrder)
if !wantsTar {
let fileInfos = try fileOrder.map { rel -> FileInfo in
try FileInfo(path: contextDir.appendingPathComponent(rel), contextDir: contextDir)
}
let data = try JSONEncoder().encode(fileInfos)
let transfer = BuildTransfer(
id: packet.id,
source: packet.source,
complete: true,
isDir: false,
metadata: [
"os": "linux",
"stage": "fssync",
"mode": "json",
],
data: data
)
var resp = ClientStream()
resp.buildID = buildID
resp.buildTransfer = transfer
resp.packetType = .buildTransfer(transfer)
sender.yield(resp)
return
}
let tarURL = URL.temporaryDirectory
.appendingPathComponent(UUID().uuidString + ".tar")
defer { try? FileManager.default.removeItem(at: tarURL) }
let writerCfg = ArchiveWriterConfiguration(
format: .paxRestricted,
filter: .none)
try Archiver.compress(
source: contextDir,
destination: tarURL,
writerConfiguration: writerCfg
) { url in
guard let rel = try? url.relativeChildPath(to: contextDir) else {
return nil
}
guard let parent = try? url.deletingLastPathComponent().relativeChildPath(to: self.contextDir) else {
return nil
}
guard let items = entries[parent] else {
return nil
}
let include = items.contains { item in
item.relativePath == rel
}
guard include else {
return nil
}
return Archiver.ArchiveEntryInfo(
pathOnHost: url,
pathInArchive: URL(fileURLWithPath: rel))
}
for try await chunk in try tarURL.bufferedCopyReader() {
let part = BuildTransfer(
id: packet.id,
source: tarURL.path,
complete: false,
isDir: false,
metadata: [
"os": "linux",
"stage": "fssync",
"mode": "tar",
],
data: chunk
)
var resp = ClientStream()
resp.buildID = buildID
resp.buildTransfer = part
resp.packetType = .buildTransfer(part)
sender.yield(resp)
}
let done = BuildTransfer(
id: packet.id,
source: tarURL.path,
complete: true,
isDir: false,
metadata: [
"os": "linux",
"stage": "fssync",
"mode": "tar",
],
data: Data()
)
var finalResp = ClientStream()
finalResp.buildID = buildID
finalResp.buildTransfer = done
finalResp.packetType = .buildTransfer(done)
sender.yield(finalResp)
}
func walk(root: URL, includePatterns: [String]) throws -> [URL] {
let globber = Globber(root)
for p in includePatterns {
try globber.match(p)
}
return Array(globber.results)
}
private func processDirectory(
_ currentDir: String,
inputEntries: [String: Set<DirEntry>],
processedPaths: inout [String]
) throws {
guard let entries = inputEntries[currentDir] else {
return
}
// Sort purely by lexicographical order of relativePath
let sortedEntries = entries.sorted { $0.relativePath < $1.relativePath }
for entry in sortedEntries {
processedPaths.append(entry.relativePath)
if entry.isDirectory {
try processDirectory(
entry.relativePath,
inputEntries: inputEntries,
processedPaths: &processedPaths
)
}
}
}
struct FileInfo: Codable {
let name: String
let modTime: String
let mode: UInt32
let size: UInt64
let isDir: Bool
let uid: UInt32
let gid: UInt32
let target: String
init(path: URL, contextDir: URL) throws {
if path.isSymlink {
let target: URL = path.resolvingSymlinksInPath()
if contextDir.parentOf(target) {
self.target = target.relativePathFrom(from: path)
} else {
self.target = target.cleanPath
}
} else {
self.target = ""
}
self.name = try path.relativeChildPath(to: contextDir)
self.modTime = try path.modTime()
self.mode = try path.mode()
self.size = try path.size()
self.isDir = path.hasDirectoryPath
self.uid = 0
self.gid = 0
}
}
enum FSSyncMethod: String {
case read = "Read"
case info = "Info"
case walk = "Walk"
init(_ method: String) throws {
switch method {
case "Read":
self = .read
case "Info":
self = .info
case "Walk":
self = .walk
default:
throw Error.unknownMethod(method)
}
}
}
}
extension BuildFSSync {
enum Error: Swift.Error, CustomStringConvertible, Equatable {
case buildTransferMissing
case methodMissing
case unknownMethod(String)
case contextNotFound(String)
case contextIsNotDirectory(String)
case couldNotDetermineFileSize(String)
case couldNotDetermineModTime(String)
case couldNotDetermineFileMode(String)
case invalidOffsetSizeForFile(String, UInt64, Int)
case couldNotDetermineUID(String)
case couldNotDetermineGID(String)
case pathIsNotChild(String, String)
var description: String {
switch self {
case .buildTransferMissing:
return "buildTransfer field missing in packet"
case .methodMissing:
return "method is missing in request"
case .unknownMethod(let m):
return "unknown content-store method \(m)"
case .contextNotFound(let path):
return "context dir \(path) not found"
case .contextIsNotDirectory(let path):
return "context \(path) not a directory"
case .couldNotDetermineFileSize(let path):
return "could not determine size of file \(path)"
case .couldNotDetermineModTime(let path):
return "could not determine last modified time of \(path)"
case .couldNotDetermineFileMode(let path):
return "could not determine posix permissions (FileMode) of \(path)"
case .invalidOffsetSizeForFile(let digest, let offset, let size):
return "invalid request for file: \(digest) with offset: \(offset) size: \(size)"
case .couldNotDetermineUID(let path):
return "could not determine UID of file at path: \(path)"
case .couldNotDetermineGID(let path):
return "could not determine GID of file at path: \(path)"
case .pathIsNotChild(let path, let parent):
return "\(path) is not a child of \(parent)"
}
}
}
}
extension BuildTransfer {
fileprivate init(id: String, source: String, complete: Bool, isDir: Bool, metadata: [String: String], data: Data? = nil) {
self.init()
self.id = id
self.source = source
self.direction = .outof
self.complete = complete
self.metadata = metadata
self.isDirectory = isDir
if let data {
self.data = data
}
}
}
extension URL {
fileprivate func size() throws -> UInt64 {
let attrs = try FileManager.default.attributesOfItem(atPath: self.cleanPath)
if let size = attrs[FileAttributeKey.size] as? UInt64 {
return size
}
throw BuildFSSync.Error.couldNotDetermineFileSize(self.cleanPath)
}
fileprivate func modTime() throws -> String {
let attrs = try FileManager.default.attributesOfItem(atPath: self.cleanPath)
if let date = attrs[FileAttributeKey.modificationDate] as? Date {
return date.rfc3339()
}
throw BuildFSSync.Error.couldNotDetermineModTime(self.cleanPath)
}
fileprivate func isDir() throws -> Bool {
let attrs = try FileManager.default.attributesOfItem(atPath: self.cleanPath)
guard let t = attrs[.type] as? FileAttributeType, t == .typeDirectory else {
return false
}
return true
}
fileprivate func mode() throws -> UInt32 {
let attrs = try FileManager.default.attributesOfItem(atPath: self.cleanPath)
if let mode = attrs[FileAttributeKey.posixPermissions] as? NSNumber {
return mode.uint32Value
}
throw BuildFSSync.Error.couldNotDetermineFileMode(self.cleanPath)
}
fileprivate func uid() throws -> UInt32 {
let attrs = try FileManager.default.attributesOfItem(atPath: self.cleanPath)
if let uid = attrs[.ownerAccountID] as? UInt32 {
return uid
}
throw BuildFSSync.Error.couldNotDetermineUID(self.cleanPath)
}
fileprivate func gid() throws -> UInt32 {
let attrs = try FileManager.default.attributesOfItem(atPath: self.cleanPath)
if let gid = attrs[.groupOwnerAccountID] as? UInt32 {
return gid
}
throw BuildFSSync.Error.couldNotDetermineGID(self.cleanPath)
}
fileprivate func buildTransfer(
id: String,
contextDir: URL? = nil,
complete: Bool = false,
data: Data = Data()
) throws -> BuildTransfer {
let p = try {
if let contextDir { return try self.relativeChildPath(to: contextDir) }
return self.cleanPath
}()
return BuildTransfer(
id: id,
source: String(p),
complete: complete,
isDir: try self.isDir(),
metadata: [
"os": "linux",
"stage": "fssync",
"mode": String(try self.mode()),
"size": String(try self.size()),
"modified_at": try self.modTime(),
"uid": String(try self.uid()),
"gid": String(try self.gid()),
],
data: data
)
}
}
extension Date {
fileprivate func rfc3339() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) // Adjust if necessary
return dateFormatter.string(from: self)
}
}
extension String {
var cleanPathComponent: String {
let trimmed = self.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
if let clean = trimmed.removingPercentEncoding {
return clean
}
return trimmed
}
}
```
## /Sources/ContainerBuild/BuildFile.swift
```swift path="/Sources/ContainerBuild/BuildFile.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Foundation
import Logging
public struct BuildFile {
/// Tries to resolve either a Dockerfile or Containerfile relative to contextDir.
/// Checks for Dockerfile, then falls back to Containerfile.
public static func resolvePath(contextDir: String, log: Logger? = nil) throws -> String? {
// Check for Dockerfile then Containerfile in context directory
let dockerfilePath = URL(filePath: contextDir).appendingPathComponent("Dockerfile").path
let containerfilePath = URL(filePath: contextDir).appendingPathComponent("Containerfile").path
let dockerfileExists = FileManager.default.fileExists(atPath: dockerfilePath)
let containerfileExists = FileManager.default.fileExists(atPath: containerfilePath)
if dockerfileExists && containerfileExists {
log?.info("Detected both Dockerfile and Containerfile, choosing Dockerfile")
return dockerfilePath
}
if dockerfileExists {
return dockerfilePath
}
if containerfileExists {
return containerfilePath
}
return nil
}
}
```
## /Sources/ContainerBuild/BuildImageResolver.swift
```swift path="/Sources/ContainerBuild/BuildImageResolver.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ContainerClient
import Containerization
import ContainerizationOCI
import Foundation
import GRPC
import Logging
import TerminalProgress
struct BuildImageResolver: BuildPipelineHandler {
let contentStore: ContentStore
let quiet: Bool
let output: FileHandle
public init(_ contentStore: ContentStore, quiet: Bool = false, output: FileHandle = FileHandle.standardError) throws {
self.contentStore = contentStore
self.quiet = quiet
self.output = output
}
func accept(_ packet: ServerStream) throws -> Bool {
guard let imageTransfer = packet.getImageTransfer() else {
return false
}
guard imageTransfer.stage() == "resolver" else {
return false
}
guard imageTransfer.method() == "/resolve" else {
return false
}
return true
}
func handle(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ServerStream) async throws {
guard let imageTransfer = packet.getImageTransfer() else {
throw Error.imageTransferMissing
}
guard let ref = imageTransfer.ref() else {
throw Error.tagMissing
}
guard let platform = try imageTransfer.platform() else {
throw Error.platformMissing
}
let img = try await {
let progressConfig = try ProgressConfig(
terminal: self.output,
description: "Pulling \(ref)",
showPercent: true,
showProgressBar: true,
showSize: true,
showSpeed: true,
disableProgressUpdates: self.quiet
)
let progress = ProgressBar(config: progressConfig)
defer { progress.finish() }
progress.start()
// Use fetch() which checks cache first, then pulls if needed
return try await ClientImage.fetch(reference: ref, platform: platform, progressUpdate: progress.handler)
}()
let index: Index = try await img.index()
let buildID = packet.buildID
let platforms = index.manifests.compactMap { $0.platform }
for pl in platforms {
if pl == platform {
let manifest = try await img.manifest(for: pl)
guard let ociImage: ContainerizationOCI.Image = try await self.contentStore.get(digest: manifest.config.digest) else {
continue
}
let enc = JSONEncoder()
let data = try enc.encode(ociImage)
let transfer = try ImageTransfer(
id: imageTransfer.id,
digest: img.descriptor.digest,
ref: ref,
platform: platform.description,
data: data
)
var response = ClientStream()
response.buildID = buildID
response.imageTransfer = transfer
response.packetType = .imageTransfer(transfer)
sender.yield(response)
return
}
}
throw Error.unknownPlatformForImage(platform.description, ref)
}
}
extension ImageTransfer {
fileprivate init(id: String, digest: String, ref: String, platform: String, data: Data) throws {
self.init()
self.id = id
self.tag = digest
self.metadata = [
"os": "linux",
"stage": "resolver",
"method": "/resolve",
"ref": ref,
"platform": platform,
]
self.complete = true
self.direction = .into
self.data = data
}
}
extension BuildImageResolver {
enum Error: Swift.Error, CustomStringConvertible {
case imageTransferMissing
case tagMissing
case platformMissing
case imageNameMissing
case imageTagMissing
case imageNotFound
case indexDigestMissing(String)
case unknownRegistry(String)
case digestIsNotIndex(String)
case digestIsNotManifest(String)
case unknownPlatformForImage(String, String)
var description: String {
switch self {
case .imageTransferMissing:
return "imageTransfer is missing"
case .tagMissing:
return "tag parameter missing in metadata"
case .platformMissing:
return "platform parameter missing in metadata"
case .imageNameMissing:
return "image name missing in $ref parameter"
case .imageTagMissing:
return "image tag missing in $ref parameter"
case .imageNotFound:
return "image not found"
case .indexDigestMissing(let ref):
return "index digest is missing for image: \(ref)"
case .unknownRegistry(let registry):
return "registry \(registry) is unknown"
case .digestIsNotIndex(let digest):
return "digest \(digest) is not a descriptor to an index"
case .digestIsNotManifest(let digest):
return "digest \(digest) is not a descriptor to a manifest"
case .unknownPlatformForImage(let platform, let ref):
return "platform \(platform) for image \(ref) not found"
}
}
}
}
```
## /Sources/ContainerBuild/BuildPipelineHandler.swift
```swift path="/Sources/ContainerBuild/BuildPipelineHandler.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Foundation
import GRPC
import NIO
protocol BuildPipelineHandler: Sendable {
func accept(_ packet: ServerStream) throws -> Bool
func handle(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ServerStream) async throws
}
public actor BuildPipeline {
let handlers: [BuildPipelineHandler]
public init(_ config: Builder.BuildConfig) async throws {
self.handlers =
[
try BuildFSSync(URL(filePath: config.contextDir)),
try BuildRemoteContentProxy(config.contentStore),
try BuildImageResolver(config.contentStore, quiet: config.quiet, output: config.terminal?.handle ?? FileHandle.standardError),
try BuildStdio(quiet: config.quiet, output: config.terminal?.handle ?? FileHandle.standardError),
]
}
public func run(
sender: AsyncStream<ClientStream>.Continuation,
receiver: GRPCAsyncResponseStream<ServerStream>
) async throws {
defer { sender.finish() }
try await untilFirstError { group in
for try await packet in receiver {
try Task.checkCancellation()
for handler in self.handlers {
try Task.checkCancellation()
guard try handler.accept(packet) else {
continue
}
try Task.checkCancellation()
try await handler.handle(sender, packet)
break
}
}
}
}
/// untilFirstError() throws when any one of its submitted tasks fail.
/// This is useful for asynchronous packet processing scenarios which
/// have the following 3 requirements:
/// - the packet should be processed without blocking I/O
/// - the packet stream is never-ending
/// - when the first task fails, the error needs to be propagated to the caller
///
/// Usage:
///
/// \`\`\`
/// try await untilFirstError { group in
/// for try await packet in receiver {
/// group.addTask {
/// try await handler.handle(sender, packet)
/// }
/// }
/// }
/// \`\`\`
///
///
/// WithThrowingTaskGroup cannot accomplish this because it
/// doesn't provide a mechanism to exit when one of the tasks fail
/// before all the tasks have been added. i.e. it is more suitable for
/// tasks that are limited. Here's a sample code where withThrowingTaskGroup
/// doesn't solve the problem:
///
/// \`\`\`
/// withThrowingTaskGroup { group in
/// for try await packet in receiver {
/// group.addTask {
/// /* process packet */
/// }
/// } /* this loop blocks forever waiting for more packets */
/// try await group.next() /* this never gets called */
/// }
/// \`\`\`
/// The above closure never returns even when a handler encounters an error
/// because the blocking operation `try await group.next()` cannot be
/// called while iterating over the receiver stream.
private func untilFirstError(body: @Sendable @escaping (UntilFirstError) async throws -> Void) async throws {
let group = try await UntilFirstError()
var taskContinuation: AsyncStream<Task<(), Error>>.Continuation?
let tasks = AsyncStream<Task<(), Error>> { continuation in
taskContinuation = continuation
}
guard let taskContinuation else {
throw NSError(
domain: "untilFirstError",
code: 1,
userInfo: [NSLocalizedDescriptionKey: "failed to initialize task continuation"])
}
defer { taskContinuation.finish() }
let stream = AsyncStream<Error> { continuation in
let processTasks = Task {
let taskStream = await group.tasks()
defer {
continuation.finish()
}
for await item in taskStream {
try Task.checkCancellation()
let addedTask = Task {
try Task.checkCancellation()
do {
try await item()
} catch {
continuation.yield(error)
await group.continuation?.finish()
throw error
}
}
taskContinuation.yield(addedTask)
}
}
taskContinuation.yield(processTasks)
let mainTask = Task { @Sendable in
defer {
continuation.finish()
processTasks.cancel()
taskContinuation.finish()
}
do {
try Task.checkCancellation()
try await body(group)
} catch {
continuation.yield(error)
await group.continuation?.finish()
throw error
}
}
taskContinuation.yield(mainTask)
}
// when the first handler fails, cancel all tasks and throw error
for await item in stream {
try Task.checkCancellation()
Task {
for await task in tasks {
task.cancel()
}
}
throw item
}
// if none of the handlers fail, wait for all subtasks to complete
for await task in tasks {
try Task.checkCancellation()
try await task.value
}
}
private actor UntilFirstError {
var stream: AsyncStream<@Sendable () async throws -> Void>?
var continuation: AsyncStream<@Sendable () async throws -> Void>.Continuation?
init() async throws {
self.stream = AsyncStream { cont in
self.continuation = cont
}
guard let _ = continuation else {
throw NSError()
}
}
func addTask(body: @Sendable @escaping () async throws -> Void) {
if !Task.isCancelled {
self.continuation?.yield(body)
}
}
func tasks() -> AsyncStream<@Sendable () async throws -> Void> {
self.stream!
}
}
}
```
## /Sources/ContainerBuild/BuildRemoteContentProxy.swift
```swift path="/Sources/ContainerBuild/BuildRemoteContentProxy.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ContainerClient
import Containerization
import ContainerizationArchive
import ContainerizationOCI
import Foundation
import GRPC
struct BuildRemoteContentProxy: BuildPipelineHandler {
let local: ContentStore
public init(_ contentStore: ContentStore) throws {
self.local = contentStore
}
func accept(_ packet: ServerStream) throws -> Bool {
guard let imageTransfer = packet.getImageTransfer() else {
return false
}
guard imageTransfer.stage() == "content-store" else {
return false
}
return true
}
func handle(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ServerStream) async throws {
guard let imageTransfer = packet.getImageTransfer() else {
throw Error.imageTransferMissing
}
guard let method = imageTransfer.method() else {
throw Error.methodMissing
}
switch try ContentStoreMethod(method) {
case .info:
try await self.info(sender, imageTransfer, packet.buildID)
case .readerAt:
try await self.readerAt(sender, imageTransfer, packet.buildID)
default:
throw Error.unknownMethod(method)
}
}
func info(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ImageTransfer, _ buildID: String) async throws {
let descriptor = try await local.get(digest: packet.tag)
let size = try descriptor?.size()
let transfer = try ImageTransfer(
id: packet.id,
digest: packet.tag,
method: ContentStoreMethod.info.rawValue,
size: size
)
var response = ClientStream()
response.buildID = buildID
response.imageTransfer = transfer
response.packetType = .imageTransfer(transfer)
sender.yield(response)
}
func readerAt(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ImageTransfer, _ buildID: String) async throws {
let digest = packet.descriptor.digest
let offset: UInt64 = packet.offset() ?? 0
let size: Int = packet.len() ?? 0
guard let descriptor = try await local.get(digest: digest) else {
throw Error.contentMissing
}
if offset == 0 && size == 0 { // Metadata request
var transfer = try ImageTransfer(
id: packet.id,
digest: packet.tag,
method: ContentStoreMethod.readerAt.rawValue,
size: descriptor.size(),
data: Data()
)
transfer.complete = true
var response = ClientStream()
response.buildID = buildID
response.imageTransfer = transfer
response.packetType = .imageTransfer(transfer)
sender.yield(response)
return
}
guard let data = try descriptor.data(offset: offset, length: size) else {
throw Error.invalidOffsetSizeForContent(packet.descriptor.digest, offset, size)
}
let transfer = try ImageTransfer(
id: packet.id,
digest: packet.tag,
method: ContentStoreMethod.readerAt.rawValue,
size: UInt64(data.count),
data: data
)
var response = ClientStream()
response.buildID = buildID
response.imageTransfer = transfer
response.packetType = .imageTransfer(transfer)
sender.yield(response)
}
func delete(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ImageTransfer) async throws {
throw NSError(domain: "RemoteContentProxy", code: 1, userInfo: [NSLocalizedDescriptionKey: "unimplemented method \(ContentStoreMethod.delete)"])
}
func update(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ImageTransfer) async throws {
throw NSError(domain: "RemoteContentProxy", code: 1, userInfo: [NSLocalizedDescriptionKey: "unimplemented method \(ContentStoreMethod.update)"])
}
func walk(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ImageTransfer) async throws {
throw NSError(domain: "RemoteContentProxy", code: 1, userInfo: [NSLocalizedDescriptionKey: "unimplemented method \(ContentStoreMethod.walk)"])
}
enum ContentStoreMethod: String {
case info = "/containerd.services.content.v1.Content/Info"
case readerAt = "/containerd.services.content.v1.Content/ReaderAt"
case delete = "/containerd.services.content.v1.Content/Delete"
case update = "/containerd.services.content.v1.Content/Update"
case walk = "/containerd.services.content.v1.Content/Walk"
init(_ method: String) throws {
guard let value = ContentStoreMethod(rawValue: method) else {
throw Error.unknownMethod(method)
}
self = value
}
}
}
extension ImageTransfer {
fileprivate init(id: String, digest: String, method: String, size: UInt64? = nil, data: Data = Data()) throws {
self.init()
self.id = id
self.tag = digest
self.metadata = [
"os": "linux",
"stage": "content-store",
"method": method,
]
if let size {
self.metadata["size"] = String(size)
}
self.complete = true
self.direction = .into
self.data = data
}
}
extension BuildRemoteContentProxy {
enum Error: Swift.Error, CustomStringConvertible {
case imageTransferMissing
case methodMissing
case contentMissing
case unknownMethod(String)
case invalidOffsetSizeForContent(String, UInt64, Int)
var description: String {
switch self {
case .imageTransferMissing:
return "imageTransfer is missing"
case .methodMissing:
return "method is missing in request"
case .contentMissing:
return "content cannot be found"
case .unknownMethod(let m):
return "unknown content-store method \(m)"
case .invalidOffsetSizeForContent(let digest, let offset, let size):
return "invalid request for content: \(digest) with offset: \(offset) size: \(size)"
}
}
}
}
```
## /Sources/ContainerBuild/BuildStdio.swift
```swift path="/Sources/ContainerBuild/BuildStdio.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ContainerizationOS
import Foundation
import GRPC
import NIO
actor BuildStdio: BuildPipelineHandler {
public let quiet: Bool
public let handle: FileHandle
init(quiet: Bool = false, output: FileHandle = FileHandle.standardError) throws {
self.quiet = quiet
self.handle = output
}
nonisolated func accept(_ packet: ServerStream) throws -> Bool {
guard let _ = packet.getIO() else {
return false
}
return true
}
func handle(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ServerStream) async throws {
guard !quiet else {
return
}
guard let io = packet.getIO() else {
throw Error.ioMissing
}
if let cmdString = try TerminalCommand().json() {
var response = ClientStream()
response.buildID = packet.buildID
response.command = .init()
response.command.id = packet.buildID
response.command.command = cmdString
sender.yield(response)
}
handle.write(io.data)
}
}
extension BuildStdio {
enum Error: Swift.Error, CustomStringConvertible {
case ioMissing
case invalidContinuation
var description: String {
switch self {
case .ioMissing:
return "io field missing in packet"
case .invalidContinuation:
return "continuation could not created"
}
}
}
}
```
## /Sources/ContainerBuild/Builder.grpc.swift
```swift path="/Sources/ContainerBuild/Builder.grpc.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
//
// DO NOT EDIT.
// swift-format-ignore-file
//
// Generated by the protocol buffer compiler.
// Source: Builder.proto
//
import GRPC
import NIO
import NIOConcurrencyHelpers
import SwiftProtobuf
/// Builder service implements APIs for performing an image build with
/// Container image builder agent.
///
/// To perform a build:
///
/// 1. CreateBuild to create a new build
/// 2. StartBuild to start the build execution where client and server
/// both have a stream for exchanging data during the build.
///
/// The client may send:
/// a) signal packet to signal to the build process (e.g. SIGINT)
///
/// b) command packet for executing a command in the build file on the
/// server
/// NOTE: the server will need to switch on the command to determine the
/// type of command to execute (e.g. RUN, ENV, etc.)
///
/// c) transfer build data either to or from the server
/// - INTO direction is for sending build data to the server at specific
/// location (e.g. COPY)
/// - OUTOF direction is for copying build data from the server to be
/// used in subsequent build stages
///
/// d) transfer image content data either to or from the server
/// - INTO direction is for sending inherited image content data to the
/// server's local content store
/// - OUTOF direction is for copying successfully built OCI image from
/// the server to the client
///
/// The server may send:
/// a) stdio packet for the build progress
///
/// b) build error indicating unsuccessful build
///
/// c) command complete packet indicating a command has finished executing
///
/// d) handle transfer build data either to or from the client
///
/// e) handle transfer image content data either to or from the client
///
///
/// NOTE: The build data and image content data transfer is ALWAYS initiated
/// by the client.
///
/// Sequence for transferring from the client to the server:
/// 1. client send a BuildTransfer/ImageTransfer request with ID, direction
/// of 'INTO',
/// destination path, and first chunk of data
/// 2. server starts to receive the data and stream to a temporary file
/// 3. client continues to send all chunks of data until last chunk, which
/// client will
/// send with 'complete' set to true
/// 4. server continues to receive until the last chunk with 'complete' set
/// to true,
/// server will finish writing the last chunk and un-archive the
/// temporary file to the destination path
/// 5. server completes the transfer by sending a last
/// BuildTransfer/ImageTransfer with
/// 'complete' set to true
/// 6. client waits for the last BuildTransfer/ImageTransfer with 'complete'
/// set to true
/// before proceeding with the rest of the commands
///
/// Sequence for transferring from the server to the client:
/// 1. client send a BuildTransfer/ImageTransfer request with ID, direction
/// of 'OUTOF',
/// source path, and empty data
/// 2. server archives the data at source path, and starts to send chunks to
/// the client
/// 3. server continues to send all chunks until last chunk, which server
/// will send with
/// 'complete' set to true
/// 4. client starts to receive the data and stream to a temporary file
/// 5. client continues to receive until the last chunk with 'complete' set
/// to true,
/// client will finish writing last chunk and un-archive the temporary
/// file to the destination path
/// 6. client MAY choose to send one last BuildTransfer/ImageTransfer with
/// 'complete'
/// set to true, but NOT required.
///
///
/// NOTE: the client should close the send stream once it has finished
/// receiving the build output or abandon the current build due to error.
/// Server should keep the stream open until it receives the EOF that client
/// has closed the stream, which the server should then close its send stream.
///
/// Usage: instantiate `Com_Apple_Container_Build_V1_BuilderClient`, then call methods of this protocol to make API calls.
public protocol Com_Apple_Container_Build_V1_BuilderClientProtocol: GRPCClient {
var serviceName: String { get }
var interceptors: Com_Apple_Container_Build_V1_BuilderClientInterceptorFactoryProtocol? { get }
func createBuild(
_ request: Com_Apple_Container_Build_V1_CreateBuildRequest,
callOptions: CallOptions?
) -> UnaryCall<Com_Apple_Container_Build_V1_CreateBuildRequest, Com_Apple_Container_Build_V1_CreateBuildResponse>
func performBuild(
callOptions: CallOptions?,
handler: @escaping (Com_Apple_Container_Build_V1_ServerStream) -> Void
) -> BidirectionalStreamingCall<Com_Apple_Container_Build_V1_ClientStream, Com_Apple_Container_Build_V1_ServerStream>
func info(
_ request: Com_Apple_Container_Build_V1_InfoRequest,
callOptions: CallOptions?
) -> UnaryCall<Com_Apple_Container_Build_V1_InfoRequest, Com_Apple_Container_Build_V1_InfoResponse>
}
extension Com_Apple_Container_Build_V1_BuilderClientProtocol {
public var serviceName: String {
return "com.apple.container.build.v1.Builder"
}
/// Create a build request.
///
/// - Parameters:
/// - request: Request to send to CreateBuild.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func createBuild(
_ request: Com_Apple_Container_Build_V1_CreateBuildRequest,
callOptions: CallOptions? = nil
) -> UnaryCall<Com_Apple_Container_Build_V1_CreateBuildRequest, Com_Apple_Container_Build_V1_CreateBuildResponse> {
return self.makeUnaryCall(
path: Com_Apple_Container_Build_V1_BuilderClientMetadata.Methods.createBuild.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeCreateBuildInterceptors() ?? []
)
}
/// Perform the build.
/// Executes the entire build sequence with attaching input/output
/// to handling data exchange with the server during the build.
///
/// Callers should use the `send` method on the returned object to send messages
/// to the server. The caller should send an `.end` after the final message has been sent.
///
/// - Parameters:
/// - callOptions: Call options.
/// - handler: A closure called when each response is received from the server.
/// - Returns: A `ClientStreamingCall` with futures for the metadata and status.
public func performBuild(
callOptions: CallOptions? = nil,
handler: @escaping (Com_Apple_Container_Build_V1_ServerStream) -> Void
) -> BidirectionalStreamingCall<Com_Apple_Container_Build_V1_ClientStream, Com_Apple_Container_Build_V1_ServerStream> {
return self.makeBidirectionalStreamingCall(
path: Com_Apple_Container_Build_V1_BuilderClientMetadata.Methods.performBuild.path,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makePerformBuildInterceptors() ?? [],
handler: handler
)
}
/// Unary call to Info
///
/// - Parameters:
/// - request: Request to send to Info.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func info(
_ request: Com_Apple_Container_Build_V1_InfoRequest,
callOptions: CallOptions? = nil
) -> UnaryCall<Com_Apple_Container_Build_V1_InfoRequest, Com_Apple_Container_Build_V1_InfoResponse> {
return self.makeUnaryCall(
path: Com_Apple_Container_Build_V1_BuilderClientMetadata.Methods.info.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeInfoInterceptors() ?? []
)
}
}
@available(*, deprecated)
extension Com_Apple_Container_Build_V1_BuilderClient: @unchecked Sendable {}
@available(*, deprecated, renamed: "Com_Apple_Container_Build_V1_BuilderNIOClient")
public final class Com_Apple_Container_Build_V1_BuilderClient: Com_Apple_Container_Build_V1_BuilderClientProtocol {
private let lock = Lock()
private var _defaultCallOptions: CallOptions
private var _interceptors: Com_Apple_Container_Build_V1_BuilderClientInterceptorFactoryProtocol?
public let channel: GRPCChannel
public var defaultCallOptions: CallOptions {
get { self.lock.withLock { return self._defaultCallOptions } }
set { self.lock.withLockVoid { self._defaultCallOptions = newValue } }
}
public var interceptors: Com_Apple_Container_Build_V1_BuilderClientInterceptorFactoryProtocol? {
get { self.lock.withLock { return self._interceptors } }
set { self.lock.withLockVoid { self._interceptors = newValue } }
}
/// Creates a client for the com.apple.container.build.v1.Builder service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
/// - interceptors: A factory providing interceptors for each RPC.
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Com_Apple_Container_Build_V1_BuilderClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self._defaultCallOptions = defaultCallOptions
self._interceptors = interceptors
}
}
public struct Com_Apple_Container_Build_V1_BuilderNIOClient: Com_Apple_Container_Build_V1_BuilderClientProtocol {
public var channel: GRPCChannel
public var defaultCallOptions: CallOptions
public var interceptors: Com_Apple_Container_Build_V1_BuilderClientInterceptorFactoryProtocol?
/// Creates a client for the com.apple.container.build.v1.Builder service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
/// - interceptors: A factory providing interceptors for each RPC.
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Com_Apple_Container_Build_V1_BuilderClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
self.interceptors = interceptors
}
}
/// Builder service implements APIs for performing an image build with
/// Container image builder agent.
///
/// To perform a build:
///
/// 1. CreateBuild to create a new build
/// 2. StartBuild to start the build execution where client and server
/// both have a stream for exchanging data during the build.
///
/// The client may send:
/// a) signal packet to signal to the build process (e.g. SIGINT)
///
/// b) command packet for executing a command in the build file on the
/// server
/// NOTE: the server will need to switch on the command to determine the
/// type of command to execute (e.g. RUN, ENV, etc.)
///
/// c) transfer build data either to or from the server
/// - INTO direction is for sending build data to the server at specific
/// location (e.g. COPY)
/// - OUTOF direction is for copying build data from the server to be
/// used in subsequent build stages
///
/// d) transfer image content data either to or from the server
/// - INTO direction is for sending inherited image content data to the
/// server's local content store
/// - OUTOF direction is for copying successfully built OCI image from
/// the server to the client
///
/// The server may send:
/// a) stdio packet for the build progress
///
/// b) build error indicating unsuccessful build
///
/// c) command complete packet indicating a command has finished executing
///
/// d) handle transfer build data either to or from the client
///
/// e) handle transfer image content data either to or from the client
///
///
/// NOTE: The build data and image content data transfer is ALWAYS initiated
/// by the client.
///
/// Sequence for transferring from the client to the server:
/// 1. client send a BuildTransfer/ImageTransfer request with ID, direction
/// of 'INTO',
/// destination path, and first chunk of data
/// 2. server starts to receive the data and stream to a temporary file
/// 3. client continues to send all chunks of data until last chunk, which
/// client will
/// send with 'complete' set to true
/// 4. server continues to receive until the last chunk with 'complete' set
/// to true,
/// server will finish writing the last chunk and un-archive the
/// temporary file to the destination path
/// 5. server completes the transfer by sending a last
/// BuildTransfer/ImageTransfer with
/// 'complete' set to true
/// 6. client waits for the last BuildTransfer/ImageTransfer with 'complete'
/// set to true
/// before proceeding with the rest of the commands
///
/// Sequence for transferring from the server to the client:
/// 1. client send a BuildTransfer/ImageTransfer request with ID, direction
/// of 'OUTOF',
/// source path, and empty data
/// 2. server archives the data at source path, and starts to send chunks to
/// the client
/// 3. server continues to send all chunks until last chunk, which server
/// will send with
/// 'complete' set to true
/// 4. client starts to receive the data and stream to a temporary file
/// 5. client continues to receive until the last chunk with 'complete' set
/// to true,
/// client will finish writing last chunk and un-archive the temporary
/// file to the destination path
/// 6. client MAY choose to send one last BuildTransfer/ImageTransfer with
/// 'complete'
/// set to true, but NOT required.
///
///
/// NOTE: the client should close the send stream once it has finished
/// receiving the build output or abandon the current build due to error.
/// Server should keep the stream open until it receives the EOF that client
/// has closed the stream, which the server should then close its send stream.
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public protocol Com_Apple_Container_Build_V1_BuilderAsyncClientProtocol: GRPCClient {
static var serviceDescriptor: GRPCServiceDescriptor { get }
var interceptors: Com_Apple_Container_Build_V1_BuilderClientInterceptorFactoryProtocol? { get }
func makeCreateBuildCall(
_ request: Com_Apple_Container_Build_V1_CreateBuildRequest,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Com_Apple_Container_Build_V1_CreateBuildRequest, Com_Apple_Container_Build_V1_CreateBuildResponse>
func makePerformBuildCall(
callOptions: CallOptions?
) -> GRPCAsyncBidirectionalStreamingCall<Com_Apple_Container_Build_V1_ClientStream, Com_Apple_Container_Build_V1_ServerStream>
func makeInfoCall(
_ request: Com_Apple_Container_Build_V1_InfoRequest,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Com_Apple_Container_Build_V1_InfoRequest, Com_Apple_Container_Build_V1_InfoResponse>
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Com_Apple_Container_Build_V1_BuilderAsyncClientProtocol {
public static var serviceDescriptor: GRPCServiceDescriptor {
return Com_Apple_Container_Build_V1_BuilderClientMetadata.serviceDescriptor
}
public var interceptors: Com_Apple_Container_Build_V1_BuilderClientInterceptorFactoryProtocol? {
return nil
}
public func makeCreateBuildCall(
_ request: Com_Apple_Container_Build_V1_CreateBuildRequest,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Com_Apple_Container_Build_V1_CreateBuildRequest, Com_Apple_Container_Build_V1_CreateBuildResponse> {
return self.makeAsyncUnaryCall(
path: Com_Apple_Container_Build_V1_BuilderClientMetadata.Methods.createBuild.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeCreateBuildInterceptors() ?? []
)
}
public func makePerformBuildCall(
callOptions: CallOptions? = nil
) -> GRPCAsyncBidirectionalStreamingCall<Com_Apple_Container_Build_V1_ClientStream, Com_Apple_Container_Build_V1_ServerStream> {
return self.makeAsyncBidirectionalStreamingCall(
path: Com_Apple_Container_Build_V1_BuilderClientMetadata.Methods.performBuild.path,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makePerformBuildInterceptors() ?? []
)
}
public func makeInfoCall(
_ request: Com_Apple_Container_Build_V1_InfoRequest,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Com_Apple_Container_Build_V1_InfoRequest, Com_Apple_Container_Build_V1_InfoResponse> {
return self.makeAsyncUnaryCall(
path: Com_Apple_Container_Build_V1_BuilderClientMetadata.Methods.info.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeInfoInterceptors() ?? []
)
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Com_Apple_Container_Build_V1_BuilderAsyncClientProtocol {
public func createBuild(
_ request: Com_Apple_Container_Build_V1_CreateBuildRequest,
callOptions: CallOptions? = nil
) async throws -> Com_Apple_Container_Build_V1_CreateBuildResponse {
return try await self.performAsyncUnaryCall(
path: Com_Apple_Container_Build_V1_BuilderClientMetadata.Methods.createBuild.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeCreateBuildInterceptors() ?? []
)
}
public func performBuild<RequestStream>(
_ requests: RequestStream,
callOptions: CallOptions? = nil
) -> GRPCAsyncResponseStream<Com_Apple_Container_Build_V1_ServerStream> where RequestStream: Sequence, RequestStream.Element == Com_Apple_Container_Build_V1_ClientStream {
return self.performAsyncBidirectionalStreamingCall(
path: Com_Apple_Container_Build_V1_BuilderClientMetadata.Methods.performBuild.path,
requests: requests,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makePerformBuildInterceptors() ?? []
)
}
public func performBuild<RequestStream>(
_ requests: RequestStream,
callOptions: CallOptions? = nil
) -> GRPCAsyncResponseStream<Com_Apple_Container_Build_V1_ServerStream> where RequestStream: AsyncSequence & Sendable, RequestStream.Element == Com_Apple_Container_Build_V1_ClientStream {
return self.performAsyncBidirectionalStreamingCall(
path: Com_Apple_Container_Build_V1_BuilderClientMetadata.Methods.performBuild.path,
requests: requests,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makePerformBuildInterceptors() ?? []
)
}
public func info(
_ request: Com_Apple_Container_Build_V1_InfoRequest,
callOptions: CallOptions? = nil
) async throws -> Com_Apple_Container_Build_V1_InfoResponse {
return try await self.performAsyncUnaryCall(
path: Com_Apple_Container_Build_V1_BuilderClientMetadata.Methods.info.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeInfoInterceptors() ?? []
)
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public struct Com_Apple_Container_Build_V1_BuilderAsyncClient: Com_Apple_Container_Build_V1_BuilderAsyncClientProtocol {
public var channel: GRPCChannel
public var defaultCallOptions: CallOptions
public var interceptors: Com_Apple_Container_Build_V1_BuilderClientInterceptorFactoryProtocol?
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Com_Apple_Container_Build_V1_BuilderClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
self.interceptors = interceptors
}
}
public protocol Com_Apple_Container_Build_V1_BuilderClientInterceptorFactoryProtocol: Sendable {
/// - Returns: Interceptors to use when invoking 'createBuild'.
func makeCreateBuildInterceptors() -> [ClientInterceptor<Com_Apple_Container_Build_V1_CreateBuildRequest, Com_Apple_Container_Build_V1_CreateBuildResponse>]
/// - Returns: Interceptors to use when invoking 'performBuild'.
func makePerformBuildInterceptors() -> [ClientInterceptor<Com_Apple_Container_Build_V1_ClientStream, Com_Apple_Container_Build_V1_ServerStream>]
/// - Returns: Interceptors to use when invoking 'info'.
func makeInfoInterceptors() -> [ClientInterceptor<Com_Apple_Container_Build_V1_InfoRequest, Com_Apple_Container_Build_V1_InfoResponse>]
}
public enum Com_Apple_Container_Build_V1_BuilderClientMetadata {
public static let serviceDescriptor = GRPCServiceDescriptor(
name: "Builder",
fullName: "com.apple.container.build.v1.Builder",
methods: [
Com_Apple_Container_Build_V1_BuilderClientMetadata.Methods.createBuild,
Com_Apple_Container_Build_V1_BuilderClientMetadata.Methods.performBuild,
Com_Apple_Container_Build_V1_BuilderClientMetadata.Methods.info,
]
)
public enum Methods {
public static let createBuild = GRPCMethodDescriptor(
name: "CreateBuild",
path: "/com.apple.container.build.v1.Builder/CreateBuild",
type: GRPCCallType.unary
)
public static let performBuild = GRPCMethodDescriptor(
name: "PerformBuild",
path: "/com.apple.container.build.v1.Builder/PerformBuild",
type: GRPCCallType.bidirectionalStreaming
)
public static let info = GRPCMethodDescriptor(
name: "Info",
path: "/com.apple.container.build.v1.Builder/Info",
type: GRPCCallType.unary
)
}
}
/// Builder service implements APIs for performing an image build with
/// Container image builder agent.
///
/// To perform a build:
///
/// 1. CreateBuild to create a new build
/// 2. StartBuild to start the build execution where client and server
/// both have a stream for exchanging data during the build.
///
/// The client may send:
/// a) signal packet to signal to the build process (e.g. SIGINT)
///
/// b) command packet for executing a command in the build file on the
/// server
/// NOTE: the server will need to switch on the command to determine the
/// type of command to execute (e.g. RUN, ENV, etc.)
///
/// c) transfer build data either to or from the server
/// - INTO direction is for sending build data to the server at specific
/// location (e.g. COPY)
/// - OUTOF direction is for copying build data from the server to be
/// used in subsequent build stages
///
/// d) transfer image content data either to or from the server
/// - INTO direction is for sending inherited image content data to the
/// server's local content store
/// - OUTOF direction is for copying successfully built OCI image from
/// the server to the client
///
/// The server may send:
/// a) stdio packet for the build progress
///
/// b) build error indicating unsuccessful build
///
/// c) command complete packet indicating a command has finished executing
///
/// d) handle transfer build data either to or from the client
///
/// e) handle transfer image content data either to or from the client
///
///
/// NOTE: The build data and image content data transfer is ALWAYS initiated
/// by the client.
///
/// Sequence for transferring from the client to the server:
/// 1. client send a BuildTransfer/ImageTransfer request with ID, direction
/// of 'INTO',
/// destination path, and first chunk of data
/// 2. server starts to receive the data and stream to a temporary file
/// 3. client continues to send all chunks of data until last chunk, which
/// client will
/// send with 'complete' set to true
/// 4. server continues to receive until the last chunk with 'complete' set
/// to true,
/// server will finish writing the last chunk and un-archive the
/// temporary file to the destination path
/// 5. server completes the transfer by sending a last
/// BuildTransfer/ImageTransfer with
/// 'complete' set to true
/// 6. client waits for the last BuildTransfer/ImageTransfer with 'complete'
/// set to true
/// before proceeding with the rest of the commands
///
/// Sequence for transferring from the server to the client:
/// 1. client send a BuildTransfer/ImageTransfer request with ID, direction
/// of 'OUTOF',
/// source path, and empty data
/// 2. server archives the data at source path, and starts to send chunks to
/// the client
/// 3. server continues to send all chunks until last chunk, which server
/// will send with
/// 'complete' set to true
/// 4. client starts to receive the data and stream to a temporary file
/// 5. client continues to receive until the last chunk with 'complete' set
/// to true,
/// client will finish writing last chunk and un-archive the temporary
/// file to the destination path
/// 6. client MAY choose to send one last BuildTransfer/ImageTransfer with
/// 'complete'
/// set to true, but NOT required.
///
///
/// NOTE: the client should close the send stream once it has finished
/// receiving the build output or abandon the current build due to error.
/// Server should keep the stream open until it receives the EOF that client
/// has closed the stream, which the server should then close its send stream.
///
/// To build a server, implement a class that conforms to this protocol.
public protocol Com_Apple_Container_Build_V1_BuilderProvider: CallHandlerProvider {
var interceptors: Com_Apple_Container_Build_V1_BuilderServerInterceptorFactoryProtocol? { get }
/// Create a build request.
func createBuild(request: Com_Apple_Container_Build_V1_CreateBuildRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Container_Build_V1_CreateBuildResponse>
/// Perform the build.
/// Executes the entire build sequence with attaching input/output
/// to handling data exchange with the server during the build.
func performBuild(context: StreamingResponseCallContext<Com_Apple_Container_Build_V1_ServerStream>) -> EventLoopFuture<(StreamEvent<Com_Apple_Container_Build_V1_ClientStream>) -> Void>
func info(request: Com_Apple_Container_Build_V1_InfoRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Container_Build_V1_InfoResponse>
}
extension Com_Apple_Container_Build_V1_BuilderProvider {
public var serviceName: Substring {
return Com_Apple_Container_Build_V1_BuilderServerMetadata.serviceDescriptor.fullName[...]
}
/// Determines, calls and returns the appropriate request handler, depending on the request's method.
/// Returns nil for methods not handled by this service.
public func handle(
method name: Substring,
context: CallHandlerContext
) -> GRPCServerHandlerProtocol? {
switch name {
case "CreateBuild":
return UnaryServerHandler(
context: context,
requestDeserializer: ProtobufDeserializer<Com_Apple_Container_Build_V1_CreateBuildRequest>(),
responseSerializer: ProtobufSerializer<Com_Apple_Container_Build_V1_CreateBuildResponse>(),
interceptors: self.interceptors?.makeCreateBuildInterceptors() ?? [],
userFunction: self.createBuild(request:context:)
)
case "PerformBuild":
return BidirectionalStreamingServerHandler(
context: context,
requestDeserializer: ProtobufDeserializer<Com_Apple_Container_Build_V1_ClientStream>(),
responseSerializer: ProtobufSerializer<Com_Apple_Container_Build_V1_ServerStream>(),
interceptors: self.interceptors?.makePerformBuildInterceptors() ?? [],
observerFactory: self.performBuild(context:)
)
case "Info":
return UnaryServerHandler(
context: context,
requestDeserializer: ProtobufDeserializer<Com_Apple_Container_Build_V1_InfoRequest>(),
responseSerializer: ProtobufSerializer<Com_Apple_Container_Build_V1_InfoResponse>(),
interceptors: self.interceptors?.makeInfoInterceptors() ?? [],
userFunction: self.info(request:context:)
)
default:
return nil
}
}
}
/// Builder service implements APIs for performing an image build with
/// Container image builder agent.
///
/// To perform a build:
///
/// 1. CreateBuild to create a new build
/// 2. StartBuild to start the build execution where client and server
/// both have a stream for exchanging data during the build.
///
/// The client may send:
/// a) signal packet to signal to the build process (e.g. SIGINT)
///
/// b) command packet for executing a command in the build file on the
/// server
/// NOTE: the server will need to switch on the command to determine the
/// type of command to execute (e.g. RUN, ENV, etc.)
///
/// c) transfer build data either to or from the server
/// - INTO direction is for sending build data to the server at specific
/// location (e.g. COPY)
/// - OUTOF direction is for copying build data from the server to be
/// used in subsequent build stages
///
/// d) transfer image content data either to or from the server
/// - INTO direction is for sending inherited image content data to the
/// server's local content store
/// - OUTOF direction is for copying successfully built OCI image from
/// the server to the client
///
/// The server may send:
/// a) stdio packet for the build progress
///
/// b) build error indicating unsuccessful build
///
/// c) command complete packet indicating a command has finished executing
///
/// d) handle transfer build data either to or from the client
///
/// e) handle transfer image content data either to or from the client
///
///
/// NOTE: The build data and image content data transfer is ALWAYS initiated
/// by the client.
///
/// Sequence for transferring from the client to the server:
/// 1. client send a BuildTransfer/ImageTransfer request with ID, direction
/// of 'INTO',
/// destination path, and first chunk of data
/// 2. server starts to receive the data and stream to a temporary file
/// 3. client continues to send all chunks of data until last chunk, which
/// client will
/// send with 'complete' set to true
/// 4. server continues to receive until the last chunk with 'complete' set
/// to true,
/// server will finish writing the last chunk and un-archive the
/// temporary file to the destination path
/// 5. server completes the transfer by sending a last
/// BuildTransfer/ImageTransfer with
/// 'complete' set to true
/// 6. client waits for the last BuildTransfer/ImageTransfer with 'complete'
/// set to true
/// before proceeding with the rest of the commands
///
/// Sequence for transferring from the server to the client:
/// 1. client send a BuildTransfer/ImageTransfer request with ID, direction
/// of 'OUTOF',
/// source path, and empty data
/// 2. server archives the data at source path, and starts to send chunks to
/// the client
/// 3. server continues to send all chunks until last chunk, which server
/// will send with
/// 'complete' set to true
/// 4. client starts to receive the data and stream to a temporary file
/// 5. client continues to receive until the last chunk with 'complete' set
/// to true,
/// client will finish writing last chunk and un-archive the temporary
/// file to the destination path
/// 6. client MAY choose to send one last BuildTransfer/ImageTransfer with
/// 'complete'
/// set to true, but NOT required.
///
///
/// NOTE: the client should close the send stream once it has finished
/// receiving the build output or abandon the current build due to error.
/// Server should keep the stream open until it receives the EOF that client
/// has closed the stream, which the server should then close its send stream.
///
/// To implement a server, implement an object which conforms to this protocol.
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public protocol Com_Apple_Container_Build_V1_BuilderAsyncProvider: CallHandlerProvider, Sendable {
static var serviceDescriptor: GRPCServiceDescriptor { get }
var interceptors: Com_Apple_Container_Build_V1_BuilderServerInterceptorFactoryProtocol? { get }
/// Create a build request.
func createBuild(
request: Com_Apple_Container_Build_V1_CreateBuildRequest,
context: GRPCAsyncServerCallContext
) async throws -> Com_Apple_Container_Build_V1_CreateBuildResponse
/// Perform the build.
/// Executes the entire build sequence with attaching input/output
/// to handling data exchange with the server during the build.
func performBuild(
requestStream: GRPCAsyncRequestStream<Com_Apple_Container_Build_V1_ClientStream>,
responseStream: GRPCAsyncResponseStreamWriter<Com_Apple_Container_Build_V1_ServerStream>,
context: GRPCAsyncServerCallContext
) async throws
func info(
request: Com_Apple_Container_Build_V1_InfoRequest,
context: GRPCAsyncServerCallContext
) async throws -> Com_Apple_Container_Build_V1_InfoResponse
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Com_Apple_Container_Build_V1_BuilderAsyncProvider {
public static var serviceDescriptor: GRPCServiceDescriptor {
return Com_Apple_Container_Build_V1_BuilderServerMetadata.serviceDescriptor
}
public var serviceName: Substring {
return Com_Apple_Container_Build_V1_BuilderServerMetadata.serviceDescriptor.fullName[...]
}
public var interceptors: Com_Apple_Container_Build_V1_BuilderServerInterceptorFactoryProtocol? {
return nil
}
public func handle(
method name: Substring,
context: CallHandlerContext
) -> GRPCServerHandlerProtocol? {
switch name {
case "CreateBuild":
return GRPCAsyncServerHandler(
context: context,
requestDeserializer: ProtobufDeserializer<Com_Apple_Container_Build_V1_CreateBuildRequest>(),
responseSerializer: ProtobufSerializer<Com_Apple_Container_Build_V1_CreateBuildResponse>(),
interceptors: self.interceptors?.makeCreateBuildInterceptors() ?? [],
wrapping: { try await self.createBuild(request: $0, context: $1) }
)
case "PerformBuild":
return GRPCAsyncServerHandler(
context: context,
requestDeserializer: ProtobufDeserializer<Com_Apple_Container_Build_V1_ClientStream>(),
responseSerializer: ProtobufSerializer<Com_Apple_Container_Build_V1_ServerStream>(),
interceptors: self.interceptors?.makePerformBuildInterceptors() ?? [],
wrapping: { try await self.performBuild(requestStream: $0, responseStream: $1, context: $2) }
)
case "Info":
return GRPCAsyncServerHandler(
context: context,
requestDeserializer: ProtobufDeserializer<Com_Apple_Container_Build_V1_InfoRequest>(),
responseSerializer: ProtobufSerializer<Com_Apple_Container_Build_V1_InfoResponse>(),
interceptors: self.interceptors?.makeInfoInterceptors() ?? [],
wrapping: { try await self.info(request: $0, context: $1) }
)
default:
return nil
}
}
}
public protocol Com_Apple_Container_Build_V1_BuilderServerInterceptorFactoryProtocol: Sendable {
/// - Returns: Interceptors to use when handling 'createBuild'.
/// Defaults to calling `self.makeInterceptors()`.
func makeCreateBuildInterceptors() -> [ServerInterceptor<Com_Apple_Container_Build_V1_CreateBuildRequest, Com_Apple_Container_Build_V1_CreateBuildResponse>]
/// - Returns: Interceptors to use when handling 'performBuild'.
/// Defaults to calling `self.makeInterceptors()`.
func makePerformBuildInterceptors() -> [ServerInterceptor<Com_Apple_Container_Build_V1_ClientStream, Com_Apple_Container_Build_V1_ServerStream>]
/// - Returns: Interceptors to use when handling 'info'.
/// Defaults to calling `self.makeInterceptors()`.
func makeInfoInterceptors() -> [ServerInterceptor<Com_Apple_Container_Build_V1_InfoRequest, Com_Apple_Container_Build_V1_InfoResponse>]
}
public enum Com_Apple_Container_Build_V1_BuilderServerMetadata {
public static let serviceDescriptor = GRPCServiceDescriptor(
name: "Builder",
fullName: "com.apple.container.build.v1.Builder",
methods: [
Com_Apple_Container_Build_V1_BuilderServerMetadata.Methods.createBuild,
Com_Apple_Container_Build_V1_BuilderServerMetadata.Methods.performBuild,
Com_Apple_Container_Build_V1_BuilderServerMetadata.Methods.info,
]
)
public enum Methods {
public static let createBuild = GRPCMethodDescriptor(
name: "CreateBuild",
path: "/com.apple.container.build.v1.Builder/CreateBuild",
type: GRPCCallType.unary
)
public static let performBuild = GRPCMethodDescriptor(
name: "PerformBuild",
path: "/com.apple.container.build.v1.Builder/PerformBuild",
type: GRPCCallType.bidirectionalStreaming
)
public static let info = GRPCMethodDescriptor(
name: "Info",
path: "/com.apple.container.build.v1.Builder/Info",
type: GRPCCallType.unary
)
}
}
```
## /Sources/ContainerBuild/Builder.pb.swift
```swift path="/Sources/ContainerBuild/Builder.pb.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: Builder.proto
//
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
public enum Com_Apple_Container_Build_V1_TransferDirection: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case into // = 0
case outof // = 1
case UNRECOGNIZED(Int)
public init() {
self = .into
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .into
case 1: self = .outof
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .into: return 0
case .outof: return 1
case .UNRECOGNIZED(let i): return i
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Com_Apple_Container_Build_V1_TransferDirection] = [
.into,
.outof,
]
}
/// Standard input/output.
public enum Com_Apple_Container_Build_V1_Stdio: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case stdin // = 0
case stdout // = 1
case stderr // = 2
case UNRECOGNIZED(Int)
public init() {
self = .stdin
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .stdin
case 1: self = .stdout
case 2: self = .stderr
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .stdin: return 0
case .stdout: return 1
case .stderr: return 2
case .UNRECOGNIZED(let i): return i
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Com_Apple_Container_Build_V1_Stdio] = [
.stdin,
.stdout,
.stderr,
]
}
/// Build error type.
public enum Com_Apple_Container_Build_V1_BuildErrorType: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case buildFailed // = 0
case `internal` // = 1
case UNRECOGNIZED(Int)
public init() {
self = .buildFailed
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .buildFailed
case 1: self = .internal
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .buildFailed: return 0
case .internal: return 1
case .UNRECOGNIZED(let i): return i
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Com_Apple_Container_Build_V1_BuildErrorType] = [
.buildFailed,
.internal,
]
}
public struct Com_Apple_Container_Build_V1_InfoRequest: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Com_Apple_Container_Build_V1_InfoResponse: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Com_Apple_Container_Build_V1_CreateBuildRequest: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// The name of the build stage.
public var stageName: String = String()
/// The tag of the image to be created.
public var tag: String = String()
/// Any additional metadata to be associated with the build.
public var metadata: Dictionary<String,String> = [:]
/// Additional build arguments.
public var buildArgs: [String] = []
/// Enable debug logging.
public var debug: Bool = false
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Com_Apple_Container_Build_V1_CreateBuildResponse: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// A unique ID for the build.
public var buildID: String = String()
/// Any additional metadata to be associated with the build.
public var metadata: Dictionary<String,String> = [:]
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Com_Apple_Container_Build_V1_ClientStream: @unchecked Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// A unique ID for the build.
public var buildID: String {
get {return _storage._buildID}
set {_uniqueStorage()._buildID = newValue}
}
/// The packet type.
public var packetType: OneOf_PacketType? {
get {return _storage._packetType}
set {_uniqueStorage()._packetType = newValue}
}
public var signal: Com_Apple_Container_Build_V1_Signal {
get {
if case .signal(let v)? = _storage._packetType {return v}
return Com_Apple_Container_Build_V1_Signal()
}
set {_uniqueStorage()._packetType = .signal(newValue)}
}
public var command: Com_Apple_Container_Build_V1_Run {
get {
if case .command(let v)? = _storage._packetType {return v}
return Com_Apple_Container_Build_V1_Run()
}
set {_uniqueStorage()._packetType = .command(newValue)}
}
public var buildTransfer: Com_Apple_Container_Build_V1_BuildTransfer {
get {
if case .buildTransfer(let v)? = _storage._packetType {return v}
return Com_Apple_Container_Build_V1_BuildTransfer()
}
set {_uniqueStorage()._packetType = .buildTransfer(newValue)}
}
public var imageTransfer: Com_Apple_Container_Build_V1_ImageTransfer {
get {
if case .imageTransfer(let v)? = _storage._packetType {return v}
return Com_Apple_Container_Build_V1_ImageTransfer()
}
set {_uniqueStorage()._packetType = .imageTransfer(newValue)}
}
public var unknownFields = SwiftProtobuf.UnknownStorage()
/// The packet type.
public enum OneOf_PacketType: Equatable, Sendable {
case signal(Com_Apple_Container_Build_V1_Signal)
case command(Com_Apple_Container_Build_V1_Run)
case buildTransfer(Com_Apple_Container_Build_V1_BuildTransfer)
case imageTransfer(Com_Apple_Container_Build_V1_ImageTransfer)
}
public init() {}
fileprivate var _storage = _StorageClass.defaultInstance
}
public struct Com_Apple_Container_Build_V1_Signal: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// A POSIX signal to send to the build process.
/// Can be used for cancelling builds.
public var signal: Int32 = 0
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Com_Apple_Container_Build_V1_Run: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// A unique ID for the execution.
public var id: String = String()
/// The type of command to execute.
public var command: String = String()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Com_Apple_Container_Build_V1_RunComplete: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// A unique ID for the execution.
public var id: String = String()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Com_Apple_Container_Build_V1_BuildTransfer: @unchecked Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// A unique ID for the transfer.
public var id: String = String()
/// The direction for transferring data (either to the server or from the
/// server).
public var direction: Com_Apple_Container_Build_V1_TransferDirection = .into
/// The absolute path to the source from the server perspective.
public var source: String {
get {return _source ?? String()}
set {_source = newValue}
}
/// Returns true if `source` has been explicitly set.
public var hasSource: Bool {return self._source != nil}
/// Clears the value of `source`. Subsequent reads from it will return its default value.
public mutating func clearSource() {self._source = nil}
/// The absolute path for the destination from the server perspective.
public var destination: String {
get {return _destination ?? String()}
set {_destination = newValue}
}
/// Returns true if `destination` has been explicitly set.
public var hasDestination: Bool {return self._destination != nil}
/// Clears the value of `destination`. Subsequent reads from it will return its default value.
public mutating func clearDestination() {self._destination = nil}
/// The actual data bytes to be transferred.
public var data: Data = Data()
/// Signal to indicate that the transfer of data for the request has finished.
public var complete: Bool = false
/// Boolean to indicate if the content is a directory.
public var isDirectory: Bool = false
/// Metadata for the transfer.
public var metadata: Dictionary<String,String> = [:]
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
fileprivate var _source: String? = nil
fileprivate var _destination: String? = nil
}
public struct Com_Apple_Container_Build_V1_ImageTransfer: @unchecked Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// A unique ID for the transfer.
public var id: String = String()
/// The direction for transferring data (either to the server or from the
/// server).
public var direction: Com_Apple_Container_Build_V1_TransferDirection = .into
/// The tag for the image.
public var tag: String = String()
/// The descriptor for the image content.
public var descriptor: Com_Apple_Container_Build_V1_Descriptor {
get {return _descriptor ?? Com_Apple_Container_Build_V1_Descriptor()}
set {_descriptor = newValue}
}
/// Returns true if `descriptor` has been explicitly set.
public var hasDescriptor: Bool {return self._descriptor != nil}
/// Clears the value of `descriptor`. Subsequent reads from it will return its default value.
public mutating func clearDescriptor() {self._descriptor = nil}
/// The actual data bytes to be transferred.
public var data: Data = Data()
/// Signal to indicate that the transfer of data for the request has finished.
public var complete: Bool = false
/// Metadata for the image.
public var metadata: Dictionary<String,String> = [:]
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
fileprivate var _descriptor: Com_Apple_Container_Build_V1_Descriptor? = nil
}
public struct Com_Apple_Container_Build_V1_ServerStream: @unchecked Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// A unique ID for the build.
public var buildID: String {
get {return _storage._buildID}
set {_uniqueStorage()._buildID = newValue}
}
/// The packet type.
public var packetType: OneOf_PacketType? {
get {return _storage._packetType}
set {_uniqueStorage()._packetType = newValue}
}
public var io: Com_Apple_Container_Build_V1_IO {
get {
if case .io(let v)? = _storage._packetType {return v}
return Com_Apple_Container_Build_V1_IO()
}
set {_uniqueStorage()._packetType = .io(newValue)}
}
public var buildError: Com_Apple_Container_Build_V1_BuildError {
get {
if case .buildError(let v)? = _storage._packetType {return v}
return Com_Apple_Container_Build_V1_BuildError()
}
set {_uniqueStorage()._packetType = .buildError(newValue)}
}
public var commandComplete: Com_Apple_Container_Build_V1_RunComplete {
get {
if case .commandComplete(let v)? = _storage._packetType {return v}
return Com_Apple_Container_Build_V1_RunComplete()
}
set {_uniqueStorage()._packetType = .commandComplete(newValue)}
}
public var buildTransfer: Com_Apple_Container_Build_V1_BuildTransfer {
get {
if case .buildTransfer(let v)? = _storage._packetType {return v}
return Com_Apple_Container_Build_V1_BuildTransfer()
}
set {_uniqueStorage()._packetType = .buildTransfer(newValue)}
}
public var imageTransfer: Com_Apple_Container_Build_V1_ImageTransfer {
get {
if case .imageTransfer(let v)? = _storage._packetType {return v}
return Com_Apple_Container_Build_V1_ImageTransfer()
}
set {_uniqueStorage()._packetType = .imageTransfer(newValue)}
}
public var unknownFields = SwiftProtobuf.UnknownStorage()
/// The packet type.
public enum OneOf_PacketType: Equatable, Sendable {
case io(Com_Apple_Container_Build_V1_IO)
case buildError(Com_Apple_Container_Build_V1_BuildError)
case commandComplete(Com_Apple_Container_Build_V1_RunComplete)
case buildTransfer(Com_Apple_Container_Build_V1_BuildTransfer)
case imageTransfer(Com_Apple_Container_Build_V1_ImageTransfer)
}
public init() {}
fileprivate var _storage = _StorageClass.defaultInstance
}
public struct Com_Apple_Container_Build_V1_IO: @unchecked Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// The type of IO.
public var type: Com_Apple_Container_Build_V1_Stdio = .stdin
/// The IO data bytes.
public var data: Data = Data()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Com_Apple_Container_Build_V1_BuildError: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// The type of build error.
public var type: Com_Apple_Container_Build_V1_BuildErrorType = .buildFailed
/// Additional message for the build failure.
public var message: String = String()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
/// OCI Platform metadata.
public struct Com_Apple_Container_Build_V1_Platform: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var architecture: String = String()
public var os: String = String()
public var osVersion: String = String()
public var osFeatures: [String] = []
public var variant: String = String()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
/// OCI Descriptor metadata.
public struct Com_Apple_Container_Build_V1_Descriptor: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var mediaType: String = String()
public var digest: String = String()
public var size: Int64 = 0
public var urls: [String] = []
public var annotations: Dictionary<String,String> = [:]
public var platform: Com_Apple_Container_Build_V1_Platform {
get {return _platform ?? Com_Apple_Container_Build_V1_Platform()}
set {_platform = newValue}
}
/// Returns true if `platform` has been explicitly set.
public var hasPlatform: Bool {return self._platform != nil}
/// Clears the value of `platform`. Subsequent reads from it will return its default value.
public mutating func clearPlatform() {self._platform = nil}
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
fileprivate var _platform: Com_Apple_Container_Build_V1_Platform? = nil
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "com.apple.container.build.v1"
extension Com_Apple_Container_Build_V1_TransferDirection: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "INTO"),
1: .same(proto: "OUTOF"),
]
}
extension Com_Apple_Container_Build_V1_Stdio: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "STDIN"),
1: .same(proto: "STDOUT"),
2: .same(proto: "STDERR"),
]
}
extension Com_Apple_Container_Build_V1_BuildErrorType: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "BUILD_FAILED"),
1: .same(proto: "INTERNAL"),
]
}
extension Com_Apple_Container_Build_V1_InfoRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".InfoRequest"
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
// Load everything into unknown fields
while try decoder.nextFieldNumber() != nil {}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_InfoRequest, rhs: Com_Apple_Container_Build_V1_InfoRequest) -> Bool {
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_InfoResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".InfoResponse"
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
// Load everything into unknown fields
while try decoder.nextFieldNumber() != nil {}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_InfoResponse, rhs: Com_Apple_Container_Build_V1_InfoResponse) -> Bool {
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_CreateBuildRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".CreateBuildRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "stage_name"),
2: .same(proto: "tag"),
3: .same(proto: "metadata"),
4: .standard(proto: "build_args"),
5: .same(proto: "debug"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.stageName) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.tag) }()
case 3: try { try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: &self.metadata) }()
case 4: try { try decoder.decodeRepeatedStringField(value: &self.buildArgs) }()
case 5: try { try decoder.decodeSingularBoolField(value: &self.debug) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.stageName.isEmpty {
try visitor.visitSingularStringField(value: self.stageName, fieldNumber: 1)
}
if !self.tag.isEmpty {
try visitor.visitSingularStringField(value: self.tag, fieldNumber: 2)
}
if !self.metadata.isEmpty {
try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: self.metadata, fieldNumber: 3)
}
if !self.buildArgs.isEmpty {
try visitor.visitRepeatedStringField(value: self.buildArgs, fieldNumber: 4)
}
if self.debug != false {
try visitor.visitSingularBoolField(value: self.debug, fieldNumber: 5)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_CreateBuildRequest, rhs: Com_Apple_Container_Build_V1_CreateBuildRequest) -> Bool {
if lhs.stageName != rhs.stageName {return false}
if lhs.tag != rhs.tag {return false}
if lhs.metadata != rhs.metadata {return false}
if lhs.buildArgs != rhs.buildArgs {return false}
if lhs.debug != rhs.debug {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_CreateBuildResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".CreateBuildResponse"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "build_id"),
2: .same(proto: "metadata"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.buildID) }()
case 2: try { try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: &self.metadata) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.buildID.isEmpty {
try visitor.visitSingularStringField(value: self.buildID, fieldNumber: 1)
}
if !self.metadata.isEmpty {
try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: self.metadata, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_CreateBuildResponse, rhs: Com_Apple_Container_Build_V1_CreateBuildResponse) -> Bool {
if lhs.buildID != rhs.buildID {return false}
if lhs.metadata != rhs.metadata {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_ClientStream: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".ClientStream"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "build_id"),
2: .same(proto: "signal"),
3: .same(proto: "command"),
4: .standard(proto: "build_transfer"),
5: .standard(proto: "image_transfer"),
]
fileprivate class _StorageClass {
var _buildID: String = String()
var _packetType: Com_Apple_Container_Build_V1_ClientStream.OneOf_PacketType?
// This property is used as the initial default value for new instances of the type.
// The type itself is protecting the reference to its storage via CoW semantics.
// This will force a copy to be made of this reference when the first mutation occurs;
// hence, it is safe to mark this as `nonisolated(unsafe)`.
static nonisolated(unsafe) let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_buildID = source._buildID
_packetType = source._packetType
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &_storage._buildID) }()
case 2: try {
var v: Com_Apple_Container_Build_V1_Signal?
var hadOneofValue = false
if let current = _storage._packetType {
hadOneofValue = true
if case .signal(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._packetType = .signal(v)
}
}()
case 3: try {
var v: Com_Apple_Container_Build_V1_Run?
var hadOneofValue = false
if let current = _storage._packetType {
hadOneofValue = true
if case .command(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._packetType = .command(v)
}
}()
case 4: try {
var v: Com_Apple_Container_Build_V1_BuildTransfer?
var hadOneofValue = false
if let current = _storage._packetType {
hadOneofValue = true
if case .buildTransfer(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._packetType = .buildTransfer(v)
}
}()
case 5: try {
var v: Com_Apple_Container_Build_V1_ImageTransfer?
var hadOneofValue = false
if let current = _storage._packetType {
hadOneofValue = true
if case .imageTransfer(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._packetType = .imageTransfer(v)
}
}()
default: break
}
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if !_storage._buildID.isEmpty {
try visitor.visitSingularStringField(value: _storage._buildID, fieldNumber: 1)
}
switch _storage._packetType {
case .signal?: try {
guard case .signal(let v)? = _storage._packetType else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
}()
case .command?: try {
guard case .command(let v)? = _storage._packetType else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
}()
case .buildTransfer?: try {
guard case .buildTransfer(let v)? = _storage._packetType else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 4)
}()
case .imageTransfer?: try {
guard case .imageTransfer(let v)? = _storage._packetType else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 5)
}()
case nil: break
}
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_ClientStream, rhs: Com_Apple_Container_Build_V1_ClientStream) -> Bool {
if lhs._storage !== rhs._storage {
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
let _storage = _args.0
let rhs_storage = _args.1
if _storage._buildID != rhs_storage._buildID {return false}
if _storage._packetType != rhs_storage._packetType {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_Signal: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".Signal"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "signal"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularInt32Field(value: &self.signal) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.signal != 0 {
try visitor.visitSingularInt32Field(value: self.signal, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_Signal, rhs: Com_Apple_Container_Build_V1_Signal) -> Bool {
if lhs.signal != rhs.signal {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_Run: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".Run"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .same(proto: "command"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.id) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.command) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.id.isEmpty {
try visitor.visitSingularStringField(value: self.id, fieldNumber: 1)
}
if !self.command.isEmpty {
try visitor.visitSingularStringField(value: self.command, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_Run, rhs: Com_Apple_Container_Build_V1_Run) -> Bool {
if lhs.id != rhs.id {return false}
if lhs.command != rhs.command {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_RunComplete: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".RunComplete"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.id) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.id.isEmpty {
try visitor.visitSingularStringField(value: self.id, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_RunComplete, rhs: Com_Apple_Container_Build_V1_RunComplete) -> Bool {
if lhs.id != rhs.id {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_BuildTransfer: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".BuildTransfer"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .same(proto: "direction"),
3: .same(proto: "source"),
4: .same(proto: "destination"),
5: .same(proto: "data"),
6: .same(proto: "complete"),
7: .standard(proto: "is_directory"),
8: .same(proto: "metadata"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.id) }()
case 2: try { try decoder.decodeSingularEnumField(value: &self.direction) }()
case 3: try { try decoder.decodeSingularStringField(value: &self._source) }()
case 4: try { try decoder.decodeSingularStringField(value: &self._destination) }()
case 5: try { try decoder.decodeSingularBytesField(value: &self.data) }()
case 6: try { try decoder.decodeSingularBoolField(value: &self.complete) }()
case 7: try { try decoder.decodeSingularBoolField(value: &self.isDirectory) }()
case 8: try { try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: &self.metadata) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if !self.id.isEmpty {
try visitor.visitSingularStringField(value: self.id, fieldNumber: 1)
}
if self.direction != .into {
try visitor.visitSingularEnumField(value: self.direction, fieldNumber: 2)
}
try { if let v = self._source {
try visitor.visitSingularStringField(value: v, fieldNumber: 3)
} }()
try { if let v = self._destination {
try visitor.visitSingularStringField(value: v, fieldNumber: 4)
} }()
if !self.data.isEmpty {
try visitor.visitSingularBytesField(value: self.data, fieldNumber: 5)
}
if self.complete != false {
try visitor.visitSingularBoolField(value: self.complete, fieldNumber: 6)
}
if self.isDirectory != false {
try visitor.visitSingularBoolField(value: self.isDirectory, fieldNumber: 7)
}
if !self.metadata.isEmpty {
try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: self.metadata, fieldNumber: 8)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_BuildTransfer, rhs: Com_Apple_Container_Build_V1_BuildTransfer) -> Bool {
if lhs.id != rhs.id {return false}
if lhs.direction != rhs.direction {return false}
if lhs._source != rhs._source {return false}
if lhs._destination != rhs._destination {return false}
if lhs.data != rhs.data {return false}
if lhs.complete != rhs.complete {return false}
if lhs.isDirectory != rhs.isDirectory {return false}
if lhs.metadata != rhs.metadata {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_ImageTransfer: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".ImageTransfer"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .same(proto: "direction"),
3: .same(proto: "tag"),
4: .same(proto: "descriptor"),
5: .same(proto: "data"),
6: .same(proto: "complete"),
7: .same(proto: "metadata"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.id) }()
case 2: try { try decoder.decodeSingularEnumField(value: &self.direction) }()
case 3: try { try decoder.decodeSingularStringField(value: &self.tag) }()
case 4: try { try decoder.decodeSingularMessageField(value: &self._descriptor) }()
case 5: try { try decoder.decodeSingularBytesField(value: &self.data) }()
case 6: try { try decoder.decodeSingularBoolField(value: &self.complete) }()
case 7: try { try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: &self.metadata) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if !self.id.isEmpty {
try visitor.visitSingularStringField(value: self.id, fieldNumber: 1)
}
if self.direction != .into {
try visitor.visitSingularEnumField(value: self.direction, fieldNumber: 2)
}
if !self.tag.isEmpty {
try visitor.visitSingularStringField(value: self.tag, fieldNumber: 3)
}
try { if let v = self._descriptor {
try visitor.visitSingularMessageField(value: v, fieldNumber: 4)
} }()
if !self.data.isEmpty {
try visitor.visitSingularBytesField(value: self.data, fieldNumber: 5)
}
if self.complete != false {
try visitor.visitSingularBoolField(value: self.complete, fieldNumber: 6)
}
if !self.metadata.isEmpty {
try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: self.metadata, fieldNumber: 7)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_ImageTransfer, rhs: Com_Apple_Container_Build_V1_ImageTransfer) -> Bool {
if lhs.id != rhs.id {return false}
if lhs.direction != rhs.direction {return false}
if lhs.tag != rhs.tag {return false}
if lhs._descriptor != rhs._descriptor {return false}
if lhs.data != rhs.data {return false}
if lhs.complete != rhs.complete {return false}
if lhs.metadata != rhs.metadata {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_ServerStream: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".ServerStream"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "build_id"),
2: .same(proto: "io"),
3: .standard(proto: "build_error"),
4: .standard(proto: "command_complete"),
5: .standard(proto: "build_transfer"),
6: .standard(proto: "image_transfer"),
]
fileprivate class _StorageClass {
var _buildID: String = String()
var _packetType: Com_Apple_Container_Build_V1_ServerStream.OneOf_PacketType?
// This property is used as the initial default value for new instances of the type.
// The type itself is protecting the reference to its storage via CoW semantics.
// This will force a copy to be made of this reference when the first mutation occurs;
// hence, it is safe to mark this as `nonisolated(unsafe)`.
static nonisolated(unsafe) let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_buildID = source._buildID
_packetType = source._packetType
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &_storage._buildID) }()
case 2: try {
var v: Com_Apple_Container_Build_V1_IO?
var hadOneofValue = false
if let current = _storage._packetType {
hadOneofValue = true
if case .io(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._packetType = .io(v)
}
}()
case 3: try {
var v: Com_Apple_Container_Build_V1_BuildError?
var hadOneofValue = false
if let current = _storage._packetType {
hadOneofValue = true
if case .buildError(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._packetType = .buildError(v)
}
}()
case 4: try {
var v: Com_Apple_Container_Build_V1_RunComplete?
var hadOneofValue = false
if let current = _storage._packetType {
hadOneofValue = true
if case .commandComplete(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._packetType = .commandComplete(v)
}
}()
case 5: try {
var v: Com_Apple_Container_Build_V1_BuildTransfer?
var hadOneofValue = false
if let current = _storage._packetType {
hadOneofValue = true
if case .buildTransfer(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._packetType = .buildTransfer(v)
}
}()
case 6: try {
var v: Com_Apple_Container_Build_V1_ImageTransfer?
var hadOneofValue = false
if let current = _storage._packetType {
hadOneofValue = true
if case .imageTransfer(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._packetType = .imageTransfer(v)
}
}()
default: break
}
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if !_storage._buildID.isEmpty {
try visitor.visitSingularStringField(value: _storage._buildID, fieldNumber: 1)
}
switch _storage._packetType {
case .io?: try {
guard case .io(let v)? = _storage._packetType else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
}()
case .buildError?: try {
guard case .buildError(let v)? = _storage._packetType else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
}()
case .commandComplete?: try {
guard case .commandComplete(let v)? = _storage._packetType else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 4)
}()
case .buildTransfer?: try {
guard case .buildTransfer(let v)? = _storage._packetType else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 5)
}()
case .imageTransfer?: try {
guard case .imageTransfer(let v)? = _storage._packetType else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 6)
}()
case nil: break
}
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_ServerStream, rhs: Com_Apple_Container_Build_V1_ServerStream) -> Bool {
if lhs._storage !== rhs._storage {
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
let _storage = _args.0
let rhs_storage = _args.1
if _storage._buildID != rhs_storage._buildID {return false}
if _storage._packetType != rhs_storage._packetType {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_IO: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".IO"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "type"),
2: .same(proto: "data"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularEnumField(value: &self.type) }()
case 2: try { try decoder.decodeSingularBytesField(value: &self.data) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.type != .stdin {
try visitor.visitSingularEnumField(value: self.type, fieldNumber: 1)
}
if !self.data.isEmpty {
try visitor.visitSingularBytesField(value: self.data, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_IO, rhs: Com_Apple_Container_Build_V1_IO) -> Bool {
if lhs.type != rhs.type {return false}
if lhs.data != rhs.data {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_BuildError: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".BuildError"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "type"),
2: .same(proto: "message"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularEnumField(value: &self.type) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.message) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.type != .buildFailed {
try visitor.visitSingularEnumField(value: self.type, fieldNumber: 1)
}
if !self.message.isEmpty {
try visitor.visitSingularStringField(value: self.message, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_BuildError, rhs: Com_Apple_Container_Build_V1_BuildError) -> Bool {
if lhs.type != rhs.type {return false}
if lhs.message != rhs.message {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_Platform: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".Platform"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "architecture"),
2: .same(proto: "os"),
3: .standard(proto: "os_version"),
4: .standard(proto: "os_features"),
5: .same(proto: "variant"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.architecture) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.os) }()
case 3: try { try decoder.decodeSingularStringField(value: &self.osVersion) }()
case 4: try { try decoder.decodeRepeatedStringField(value: &self.osFeatures) }()
case 5: try { try decoder.decodeSingularStringField(value: &self.variant) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.architecture.isEmpty {
try visitor.visitSingularStringField(value: self.architecture, fieldNumber: 1)
}
if !self.os.isEmpty {
try visitor.visitSingularStringField(value: self.os, fieldNumber: 2)
}
if !self.osVersion.isEmpty {
try visitor.visitSingularStringField(value: self.osVersion, fieldNumber: 3)
}
if !self.osFeatures.isEmpty {
try visitor.visitRepeatedStringField(value: self.osFeatures, fieldNumber: 4)
}
if !self.variant.isEmpty {
try visitor.visitSingularStringField(value: self.variant, fieldNumber: 5)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_Platform, rhs: Com_Apple_Container_Build_V1_Platform) -> Bool {
if lhs.architecture != rhs.architecture {return false}
if lhs.os != rhs.os {return false}
if lhs.osVersion != rhs.osVersion {return false}
if lhs.osFeatures != rhs.osFeatures {return false}
if lhs.variant != rhs.variant {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Container_Build_V1_Descriptor: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".Descriptor"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "media_type"),
2: .same(proto: "digest"),
3: .same(proto: "size"),
4: .same(proto: "urls"),
5: .same(proto: "annotations"),
6: .same(proto: "platform"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.mediaType) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.digest) }()
case 3: try { try decoder.decodeSingularInt64Field(value: &self.size) }()
case 4: try { try decoder.decodeRepeatedStringField(value: &self.urls) }()
case 5: try { try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: &self.annotations) }()
case 6: try { try decoder.decodeSingularMessageField(value: &self._platform) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if !self.mediaType.isEmpty {
try visitor.visitSingularStringField(value: self.mediaType, fieldNumber: 1)
}
if !self.digest.isEmpty {
try visitor.visitSingularStringField(value: self.digest, fieldNumber: 2)
}
if self.size != 0 {
try visitor.visitSingularInt64Field(value: self.size, fieldNumber: 3)
}
if !self.urls.isEmpty {
try visitor.visitRepeatedStringField(value: self.urls, fieldNumber: 4)
}
if !self.annotations.isEmpty {
try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: self.annotations, fieldNumber: 5)
}
try { if let v = self._platform {
try visitor.visitSingularMessageField(value: v, fieldNumber: 6)
} }()
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Container_Build_V1_Descriptor, rhs: Com_Apple_Container_Build_V1_Descriptor) -> Bool {
if lhs.mediaType != rhs.mediaType {return false}
if lhs.digest != rhs.digest {return false}
if lhs.size != rhs.size {return false}
if lhs.urls != rhs.urls {return false}
if lhs.annotations != rhs.annotations {return false}
if lhs._platform != rhs._platform {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
```
## /Sources/ContainerBuild/Builder.swift
```swift path="/Sources/ContainerBuild/Builder.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ContainerClient
import Containerization
import ContainerizationOCI
import ContainerizationOS
import Foundation
import GRPC
import NIO
import NIOHPACK
import NIOHTTP2
public struct Builder: Sendable {
let client: BuilderClientProtocol
let clientAsync: BuilderClientAsyncProtocol
let group: EventLoopGroup
let builderShimSocket: FileHandle
let channel: GRPCChannel
public init(socket: FileHandle, group: EventLoopGroup) throws {
try socket.setSendBufSize(4 << 20)
try socket.setRecvBufSize(2 << 20)
var config = ClientConnection.Configuration.default(
target: .connectedSocket(socket.fileDescriptor),
eventLoopGroup: group
)
config.connectionIdleTimeout = TimeAmount(.seconds(600))
config.connectionKeepalive = .init(
interval: TimeAmount(.seconds(600)),
timeout: TimeAmount(.seconds(500)),
permitWithoutCalls: true
)
config.connectionBackoff = .init(
initialBackoff: TimeInterval(1),
maximumBackoff: TimeInterval(10)
)
config.callStartBehavior = .fastFailure
config.httpMaxFrameSize = 8 << 10
config.maximumReceiveMessageLength = 512 << 20
config.httpTargetWindowSize = 16 << 10
let channel = ClientConnection(configuration: config)
self.channel = channel
self.clientAsync = BuilderClientAsync(channel: channel)
self.client = BuilderClient(channel: channel)
self.group = group
self.builderShimSocket = socket
}
public func info() throws -> InfoResponse {
let resp = self.client.info(InfoRequest(), callOptions: CallOptions())
return try resp.response.wait()
}
public func info() async throws -> InfoResponse {
let opts = CallOptions(timeLimit: .timeout(.seconds(30)))
return try await self.clientAsync.info(InfoRequest(), callOptions: opts)
}
// TODO
// - Symlinks in build context dir
// - cache-to, cache-from
// - output (other than the default OCI image output, e.g., local, tar, Docker)
public func build(_ config: BuildConfig) async throws {
var continuation: AsyncStream<ClientStream>.Continuation?
let reqStream = AsyncStream<ClientStream> { (cont: AsyncStream<ClientStream>.Continuation) in
continuation = cont
}
guard let continuation else {
throw Error.invalidContinuation
}
defer {
continuation.finish()
}
if let terminal = config.terminal {
Task {
let winchHandler = AsyncSignalHandler.create(notify: [SIGWINCH])
let setWinch = { (rows: UInt16, cols: UInt16) in
var winch = ClientStream()
winch.command = .init()
if let cmdString = try TerminalCommand(rows: rows, cols: cols).json() {
winch.command.command = cmdString
continuation.yield(winch)
}
}
let size = try terminal.size
var width = size.width
var height = size.height
try setWinch(height, width)
for await _ in winchHandler.signals {
let size = try terminal.size
let cols = size.width
let rows = size.height
if cols != width || rows != height {
width = cols
height = rows
try setWinch(height, width)
}
}
}
}
let respStream = self.clientAsync.performBuild(reqStream, callOptions: try CallOptions(config))
let pipeline = try await BuildPipeline(config)
do {
try await pipeline.run(sender: continuation, receiver: respStream)
} catch Error.buildComplete {
_ = channel.close()
try await group.shutdownGracefully()
return
}
}
public struct BuildExport: Sendable {
public let type: String
public var destination: URL?
public let additionalFields: [String: String]
public let rawValue: String
public init(type: String, destination: URL?, additionalFields: [String: String], rawValue: String) {
self.type = type
self.destination = destination
self.additionalFields = additionalFields
self.rawValue = rawValue
}
public init(from input: String) throws {
var typeValue: String?
var destinationValue: URL?
var additionalFields: [String: String] = [:]
let pairs = input.components(separatedBy: ",")
for pair in pairs {
let parts = pair.components(separatedBy: "=")
guard parts.count == 2 else { continue }
let key = parts[0].trimmingCharacters(in: .whitespaces)
let value = parts[1].trimmingCharacters(in: .whitespaces)
switch key {
case "type":
typeValue = value
case "dest":
destinationValue = try Self.resolveDestination(dest: value)
default:
additionalFields[key] = value
}
}
guard let type = typeValue else {
throw Builder.Error.invalidExport(input, "type field is required")
}
switch type {
case "oci":
break
case "tar":
if destinationValue == nil {
throw Builder.Error.invalidExport(input, "dest field is required")
}
case "local":
if destinationValue == nil {
throw Builder.Error.invalidExport(input, "dest field is required")
}
default:
throw Builder.Error.invalidExport(input, "unsupported output type")
}
self.init(type: type, destination: destinationValue, additionalFields: additionalFields, rawValue: input)
}
public var stringValue: String {
get throws {
var components = ["type=\(type)"]
switch type {
case "oci", "tar", "local":
break // ignore destination
default:
throw Builder.Error.invalidExport(rawValue, "unsupported output type")
}
for (key, value) in additionalFields {
components.append("\(key)=\(value)")
}
return components.joined(separator: ",")
}
}
static func resolveDestination(dest: String) throws -> URL {
let destination = URL(fileURLWithPath: dest)
let fileManager = FileManager.default
if fileManager.fileExists(atPath: destination.path) {
let resourceValues = try destination.resourceValues(forKeys: [.isDirectoryKey])
let isDir = resourceValues.isDirectory
if isDir != nil && isDir == false {
throw Builder.Error.invalidExport(dest, "dest path already exists")
}
var finalDestination = destination.appendingPathComponent("out.tar")
var index = 1
while fileManager.fileExists(atPath: finalDestination.path) {
let path = "out.tar.\(index)"
finalDestination = destination.appendingPathComponent(path)
index += 1
}
return finalDestination
} else {
let parentDirectory = destination.deletingLastPathComponent()
try? fileManager.createDirectory(at: parentDirectory, withIntermediateDirectories: true, attributes: nil)
}
return destination
}
}
public struct BuildConfig: Sendable {
public let buildID: String
public let contentStore: ContentStore
public let buildArgs: [String]
public let contextDir: String
public let dockerfile: Data
public let labels: [String]
public let noCache: Bool
public let platforms: [Platform]
public let terminal: Terminal?
public let tags: [String]
public let target: String
public let quiet: Bool
public let exports: [BuildExport]
public let cacheIn: [String]
public let cacheOut: [String]
public init(
buildID: String,
contentStore: ContentStore,
buildArgs: [String],
contextDir: String,
dockerfile: Data,
labels: [String],
noCache: Bool,
platforms: [Platform],
terminal: Terminal?,
tags: [String],
target: String,
quiet: Bool,
exports: [BuildExport],
cacheIn: [String],
cacheOut: [String],
) {
self.buildID = buildID
self.contentStore = contentStore
self.buildArgs = buildArgs
self.contextDir = contextDir
self.dockerfile = dockerfile
self.labels = labels
self.noCache = noCache
self.platforms = platforms
self.terminal = terminal
self.tags = tags
self.target = target
self.quiet = quiet
self.exports = exports
self.cacheIn = cacheIn
self.cacheOut = cacheOut
}
}
}
extension Builder {
enum Error: Swift.Error, CustomStringConvertible {
case invalidContinuation
case buildComplete
case invalidExport(String, String)
var description: String {
switch self {
case .invalidContinuation:
return "continuation could not created"
case .buildComplete:
return "build completed"
case .invalidExport(let exp, let reason):
return "export entry \(exp) is invalid: \(reason)"
}
}
}
}
extension CallOptions {
public init(_ config: Builder.BuildConfig) throws {
var headers: [(String, String)] = [
("build-id", config.buildID),
("context", URL(filePath: config.contextDir).path(percentEncoded: false)),
("dockerfile", config.dockerfile.base64EncodedString()),
("progress", config.terminal != nil ? "tty" : "plain"),
("target", config.target),
]
for tag in config.tags {
headers.append(("tag", tag))
}
for platform in config.platforms {
headers.append(("platforms", platform.description))
}
if config.noCache {
headers.append(("no-cache", ""))
}
for label in config.labels {
headers.append(("labels", label))
}
for buildArg in config.buildArgs {
headers.append(("build-args", buildArg))
}
for output in config.exports {
headers.append(("outputs", try output.stringValue))
}
for cacheIn in config.cacheIn {
headers.append(("cache-in", cacheIn))
}
for cacheOut in config.cacheOut {
headers.append(("cache-out", cacheOut))
}
self.init(
customMetadata: HPACKHeaders(headers)
)
}
}
extension FileHandle {
@discardableResult
func setSendBufSize(_ bytes: Int) throws -> Int {
try setSockOpt(
level: SOL_SOCKET,
name: SO_SNDBUF,
value: bytes)
return bytes
}
@discardableResult
func setRecvBufSize(_ bytes: Int) throws -> Int {
try setSockOpt(
level: SOL_SOCKET,
name: SO_RCVBUF,
value: bytes)
return bytes
}
private func setSockOpt(level: Int32, name: Int32, value: Int) throws {
var v = Int32(value)
let res = withUnsafePointer(to: &v) { ptr -> Int32 in
ptr.withMemoryRebound(
to: UInt8.self,
capacity: MemoryLayout<Int32>.size
) { raw in
#if canImport(Darwin)
return setsockopt(
self.fileDescriptor,
level, name,
raw,
socklen_t(MemoryLayout<Int32>.size))
#else
fatalError("unsupported platform")
#endif
}
}
if res == -1 {
throw POSIXError(POSIXErrorCode(rawValue: errno) ?? .EPERM)
}
}
}
```
## /Sources/ContainerBuild/Globber.swift
```swift path="/Sources/ContainerBuild/Globber.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Foundation
public class Globber {
let input: URL
var results: Set<URL> = .init()
public init(_ input: URL) {
self.input = input
}
public func match(_ pattern: String) throws {
let adjustedPattern =
pattern
.replacingOccurrences(of: #"^\./(?=.)"#, with: "", options: .regularExpression)
.replacingOccurrences(of: "^\\.[/]?{{contextString}}quot;, with: "*", options: .regularExpression)
.replacingOccurrences(of: "\\*{2,}[/]", with: "*/**/", options: .regularExpression)
.replacingOccurrences(of: "[/]\\*{2,}([^/])", with: "/**/*$1", options: .regularExpression)
.replacingOccurrences(of: "^\\*{2,}([^/])", with: "**/*$1", options: .regularExpression)
for child in input.children {
try self.match(input: child, components: adjustedPattern.split(separator: "/").map(String.init))
}
}
private func match(input: URL, components: [String]) throws {
if components.isEmpty {
var dir = input.standardizedFileURL
while dir != self.input.standardizedFileURL {
results.insert(dir)
guard dir.pathComponents.count > 1 else { break }
dir.deleteLastPathComponent()
}
return input.childrenRecursive.forEach { results.insert($0) }
}
let head = components.first ?? ""
let tail = components.tail
if head == "**" {
var tail: [String] = tail
while tail.first == "**" {
tail = tail.tail
}
try self.match(input: input, components: tail)
for child in input.children {
try self.match(input: child, components: components)
}
return
}
if try glob(input.lastPathComponent, head) {
try self.match(input: input, components: tail)
for child in input.children where try glob(child.lastPathComponent, tail.first ?? "") {
try self.match(input: child, components: tail)
}
return
}
}
func glob(_ input: String, _ pattern: String) throws -> Bool {
let regexPattern =
"^"
+ NSRegularExpression.escapedPattern(for: pattern)
.replacingOccurrences(of: "\\*", with: "[^/]*")
.replacingOccurrences(of: "\\?", with: "[^/]")
.replacingOccurrences(of: "[\\^", with: "[^")
.replacingOccurrences(of: "\\[", with: "[")
.replacingOccurrences(of: "\\]", with: "]") + "{{contextString}}quot;
// validate the regex pattern created
let _ = try Regex(regexPattern)
return input.range(of: regexPattern, options: .regularExpression) != nil
}
}
extension URL {
var children: [URL] {
(try? FileManager.default.contentsOfDirectory(at: self, includingPropertiesForKeys: nil))
?? []
}
var childrenRecursive: [URL] {
var results: [URL] = []
if let enumerator = FileManager.default.enumerator(
at: self, includingPropertiesForKeys: [.isDirectoryKey, .isSymbolicLinkKey])
{
while let child = enumerator.nextObject() as? URL {
results.append(child)
}
}
return [self] + results
}
}
extension [String] {
var tail: [String] {
if self.count <= 1 {
return []
}
return Array(self.dropFirst())
}
}
```
## /Sources/ContainerBuild/TerminalCommand.swift
```swift path="/Sources/ContainerBuild/TerminalCommand.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Foundation
struct TerminalCommand: Codable {
let commandType: String
let code: String
let rows: UInt16
let cols: UInt16
enum CodingKeys: String, CodingKey {
case commandType = "command_type"
case code
case rows
case cols
}
init(rows: UInt16, cols: UInt16) {
self.commandType = "terminal"
self.code = "winch"
self.rows = rows
self.cols = cols
}
init() {
self.commandType = "terminal"
self.code = "ack"
self.rows = 0
self.cols = 0
}
func json() throws -> String? {
let encoder = JSONEncoder()
let data = try encoder.encode(self)
return data.base64EncodedString().trimmingCharacters(in: CharacterSet(charactersIn: "="))
}
}
```
## /Sources/ContainerBuild/URL+Extensions.swift
```swift path="/Sources/ContainerBuild/URL+Extensions.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Foundation
extension String {
fileprivate var fs_cleaned: String {
var value = self
if value.hasPrefix("file://") {
value.removeFirst("file://".count)
}
if value.count > 1 && value.last == "/" {
value.removeLast()
}
return value.removingPercentEncoding ?? value
}
fileprivate var fs_components: [String] {
var parts: [String] = []
for segment in self.split(separator: "/", omittingEmptySubsequences: true) {
switch segment {
case ".":
continue
case "..":
if !parts.isEmpty { parts.removeLast() }
default:
parts.append(String(segment))
}
}
return parts
}
fileprivate var fs_isAbsolute: Bool { first == "/" }
}
extension URL {
var cleanPath: String {
self.path.fs_cleaned
}
func parentOf(_ url: URL) -> Bool {
let parentPath = self.absoluteURL.cleanPath
let childPath = url.absoluteURL.cleanPath
guard parentPath.fs_isAbsolute else {
return true
}
let parentParts = parentPath.fs_components
let childParts = childPath.fs_components
guard parentParts.count <= childParts.count else { return false }
return zip(parentParts, childParts).allSatisfy { $0 == $1 }
}
func relativeChildPath(to context: URL) throws -> String {
guard context.parentOf(self) else {
throw BuildFSSync.Error.pathIsNotChild(cleanPath, context.cleanPath)
}
let ctxParts = context.cleanPath.fs_components
let selfParts = cleanPath.fs_components
return selfParts.dropFirst(ctxParts.count).joined(separator: "/")
}
func relativePathFrom(from base: URL) -> String {
let destParts = cleanPath.fs_components
let baseParts = base.cleanPath.fs_components
let common = zip(destParts, baseParts).prefix { $0 == $1 }.count
guard common > 0 else { return cleanPath }
let ups = Array(repeating: "..", count: baseParts.count - common)
let remainder = destParts.dropFirst(common)
return (ups + remainder).joined(separator: "/")
}
func zeroCopyReader(
chunk: Int = 1024 * 1024,
buffer: AsyncStream<Data>.Continuation.BufferingPolicy = .unbounded
) throws -> AsyncStream<Data> {
let path = self.cleanPath
let fd = open(path, O_RDONLY | O_NONBLOCK)
guard fd >= 0 else { throw POSIXError.fromErrno() }
let channel = DispatchIO(
type: .stream,
fileDescriptor: fd,
queue: .global(qos: .userInitiated)
) { errno in
close(fd)
}
channel.setLimit(highWater: chunk)
return AsyncStream(bufferingPolicy: buffer) { continuation in
channel.read(
offset: 0, length: Int.max,
queue: .global(qos: .userInitiated)
) { done, ddata, err in
if err != 0 {
continuation.finish()
return
}
if let ddata, ddata.count > -1 {
let data = Data(ddata)
switch continuation.yield(data) {
case .terminated:
channel.close(flags: .stop)
default: break
}
}
if done {
channel.close(flags: .stop)
continuation.finish()
}
}
}
}
func bufferedCopyReader(chunkSize: Int = 4 * 1024 * 1024) throws -> BufferedCopyReader {
try BufferedCopyReader(url: self, chunkSize: chunkSize)
}
}
/// A synchronous buffered reader that reads one chunk at a time from a file
/// Uses a configurable buffer size (default 4MB) and only reads when nextChunk() is called
/// Implements AsyncSequence for use with `for await` loops
public final class BufferedCopyReader: AsyncSequence {
public typealias Element = Data
public typealias AsyncIterator = BufferedCopyReaderIterator
private let inputStream: InputStream
private let chunkSize: Int
private var isFinished: Bool = false
private let reusableBuffer: UnsafeMutablePointer<UInt8>
/// Initialize a buffered copy reader for the given URL
/// - Parameters:
/// - url: The file URL to read from
/// - chunkSize: Size of each chunk to read (default: 4MB)
public init(url: URL, chunkSize: Int = 4 * 1024 * 1024) throws {
guard let stream = InputStream(url: url) else {
throw CocoaError(.fileReadNoSuchFile)
}
self.inputStream = stream
self.chunkSize = chunkSize
self.reusableBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: chunkSize)
self.inputStream.open()
}
deinit {
inputStream.close()
reusableBuffer.deallocate()
}
/// Create an async iterator for this sequence
public func makeAsyncIterator() -> BufferedCopyReaderIterator {
BufferedCopyReaderIterator(reader: self)
}
/// Read the next chunk of data from the file
/// - Returns: Data chunk, or nil if end of file reached
/// - Throws: Any file reading errors
public func nextChunk() throws -> Data? {
guard !isFinished else { return nil }
// Read directly into our reusable buffer
let bytesRead = inputStream.read(reusableBuffer, maxLength: chunkSize)
// Check for errors
if bytesRead < 0 {
if let error = inputStream.streamError {
throw error
}
throw CocoaError(.fileReadUnknown)
}
// If we read no data, we've reached the end
if bytesRead == 0 {
isFinished = true
return nil
}
// If we read less than the chunk size, this is the last chunk
if bytesRead < chunkSize {
isFinished = true
}
// Create Data object only with the bytes actually read
return Data(bytes: reusableBuffer, count: bytesRead)
}
/// Check if the reader has finished reading the file
public var hasFinished: Bool {
isFinished
}
/// Reset the reader to the beginning of the file
/// Note: InputStream doesn't support seeking, so this recreates the stream
/// - Throws: Any file opening errors
public func reset() throws {
inputStream.close()
// Note: InputStream doesn't provide a way to get the original URL,
// so reset functionality is limited. Consider removing this method
// or storing the original URL if reset is needed.
throw CocoaError(
.fileReadUnsupportedScheme,
userInfo: [
NSLocalizedDescriptionKey: "reset not supported with InputStream-based implementation"
])
}
/// Get the current file offset
/// Note: InputStream doesn't provide offset information
/// - Returns: Current position in the file
/// - Throws: Unsupported operation error
public func currentOffset() throws -> UInt64 {
throw CocoaError(
.fileReadUnsupportedScheme,
userInfo: [
NSLocalizedDescriptionKey: "offset tracking not supported with InputStream-based implementation"
])
}
/// Seek to a specific offset in the file
/// Note: InputStream doesn't support seeking
/// - Parameter offset: The byte offset to seek to
/// - Throws: Unsupported operation error
public func seek(to offset: UInt64) throws {
throw CocoaError(
.fileReadUnsupportedScheme,
userInfo: [
NSLocalizedDescriptionKey: "seeking not supported with InputStream-based implementation"
])
}
/// Close the input stream explicitly (called automatically in deinit)
public func close() {
inputStream.close()
isFinished = true
}
}
/// AsyncIteratorProtocol implementation for BufferedCopyReader
public struct BufferedCopyReaderIterator: AsyncIteratorProtocol {
public typealias Element = Data
private let reader: BufferedCopyReader
init(reader: BufferedCopyReader) {
self.reader = reader
}
/// Get the next chunk of data asynchronously
/// - Returns: Next data chunk, or nil when finished
/// - Throws: Any file reading errors
public mutating func next() async throws -> Data? {
// Yield control to allow other tasks to run, then read synchronously
await Task.yield()
return try reader.nextChunk()
}
}
```
## /Sources/ContainerClient/Arch.swift
```swift path="/Sources/ContainerClient/Arch.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
public enum Arch: String {
case arm64, amd64
public static func hostArchitecture() -> Arch {
#if arch(arm64)
return .arm64
#elseif arch(x86_64)
return .amd64
#endif
}
}
```
## /Sources/ContainerClient/Array+Dedupe.swift
```swift path="/Sources/ContainerClient/Array+Dedupe.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
extension Array where Element: Hashable {
func dedupe() -> [Element] {
var elems = Set<Element>()
return filter { elems.insert($0).inserted }
}
}
```
## /Sources/ContainerClient/Core/Constants.swift
```swift path="/Sources/ContainerClient/Core/Constants.swift"
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
public enum Constants {
public static let keychainID = "com.apple.container.registry"
}
```
## /docs/assets/landing-movie.gif
Binary file available at https://raw.githubusercontent.com/apple/container/refs/heads/main/docs/assets/landing-movie.gif
The content has been capped at 50000 tokens. The user could consider applying other filters to refine the result. The better and more specific the context, the better the LLM can follow instructions. If the context seems verbose, the user can refine the filter using uithub. Thank you for using https://uithub.com - Perfect LLM context for any GitHub repo.