From 1cce559669a220d7049aa67b88541ce28ecbad16 Mon Sep 17 00:00:00 2001 From: Ronit Sabhaya Date: Wed, 8 Oct 2025 16:44:49 -0500 Subject: [PATCH 01/12] adding the new labeler yml that explicilty gives labels and help the team to review the pr based on the lables --- .github/labeler.yml | 100 ++++++++++++++++++++++++++++++++++ .github/workflows/labeler.yml | 50 +++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/labeler.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..ab5445d3 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,100 @@ +# Configuration for automatic PR labeling based on file changes +# https://github.com/actions/labeler + +# Core areas +"area: cli": + - Sources/CLI/**/* + - Sources/ContainerCommands/**/* + +"area: build": + - Sources/ContainerBuild/**/* + - Makefile + - Package.swift + - Package.resolved + - Protobuf.Makefile + +"area: client": + - Sources/ContainerClient/**/* + +"area: networking": + - Sources/ContainerNetworkService/**/* + - Sources/DNSServer/**/* + - Sources/SocketForwarder/**/* + - config/container-network-vmnet-config.json + +"area: services": + - Sources/Services/**/* + - Sources/ContainerAPIService/**/* + - Sources/ContainerImagesService/**/* + - Sources/ContainerSandboxService/**/* + +"area: runtime": + - Sources/Helpers/RuntimeLinux/**/* + - config/container-runtime-linux-config.json + +"area: plugin": + - Sources/ContainerPlugin/**/* + +"area: xpc": + - Sources/ContainerXPC/**/* + +"area: logging": + - Sources/ContainerLog/**/* + +"area: persistence": + - Sources/ContainerPersistence/**/* + +"area: terminal": + - Sources/TerminalProgress/**/* + +"area: helpers": + - Sources/Helpers/**/* + +# Testing +"testing": + - Tests/**/* + - "**/*Test*.swift" + - "**/*Tests.swift" + +# Documentation +"documentation": + - docs/**/* + - "**/*.md" + - scripts/make-docs.sh + +# Configuration +"configuration": + - config/**/* + - "**/*.json" + - "**/*.toml" + - signing/**/* + +# Scripts and tooling +"tooling": + - scripts/**/* + - licenserc.toml + - Makefile + - Protobuf.Makefile + +# GitHub specific +"github": + - .github/**/* + +# Dependencies +"dependencies": + - Package.swift + - Package.resolved + +# C/C++ code +"c/c++": + - Sources/CVersion/**/* + - "**/*.c" + - "**/*.h" + +# Swift protocol buffers +"protobuf": + - "**/*.proto" + - "**/*.pb.swift" + - "**/*.grpc.swift" + - Protobuf.Makefile + diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 00000000..5dc9aade --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,50 @@ +name: "Pull Request Labeler" + +on: + pull_request_target: + types: [opened] + +permissions: + contents: read + pull-requests: write + +jobs: + label: + name: Label Pull Request + if: github.repository == 'apple/container' + runs-on: ubuntu-latest + outputs: + labels-applied: ${{ steps.labeler.outputs.labels-applied }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Apply labels based on file changes + id: labeler + uses: actions/labeler@v5 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + configuration-path: .github/labeler.yml + sync-labels: true + + - name: Trigger PR build workflow + uses: actions/github-script@v7 + with: + script: | + console.log('Labeling completed successfully, triggering PR build workflow'); + + // Trigger the PR build workflow using workflow_dispatch + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'pr-build.yml', + ref: context.payload.pull_request.head.ref, + inputs: { + pr_number: context.payload.pull_request.number.toString(), + pr_head_sha: context.payload.pull_request.head.sha, + pr_base_ref: context.payload.pull_request.base.ref + } + }); + From a784d3b2b46b2e514699754be02af9848496d484 Mon Sep 17 00:00:00 2001 From: Ronit Sabhaya Date: Wed, 8 Oct 2025 16:49:36 -0500 Subject: [PATCH 02/12] Fix repository name in workflow conditions --- .github/workflows/common.yml | 2 +- .github/workflows/labeler.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 4e7d6b95..b6bb3f88 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -11,7 +11,7 @@ on: jobs: buildAndTest: name: Build and test the project - if: github.repository == 'apple/container' + if: github.repository == 'Ronitsabhaya75/container' timeout-minutes: 60 runs-on: [self-hosted, macos, sequoia, ARM64] permissions: diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 5dc9aade..c0d01d67 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -1,7 +1,7 @@ name: "Pull Request Labeler" on: - pull_request_target: + pull_request: types: [opened] permissions: @@ -11,7 +11,7 @@ permissions: jobs: label: name: Label Pull Request - if: github.repository == 'apple/container' + if: github.repository == 'Ronitsabhaya75/container' runs-on: ubuntu-latest outputs: labels-applied: ${{ steps.labeler.outputs.labels-applied }} From 982d1f4e302e9971fc991286a9edecba895ffe5b Mon Sep 17 00:00:00 2001 From: Ronit Sabhaya Date: Wed, 8 Oct 2025 16:52:03 -0500 Subject: [PATCH 03/12] Remove workflow dispatch step from labeler - PR build no longer accepts it --- .github/workflows/labeler.yml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index c0d01d67..b7f11706 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -29,22 +29,4 @@ jobs: configuration-path: .github/labeler.yml sync-labels: true - - name: Trigger PR build workflow - uses: actions/github-script@v7 - with: - script: | - console.log('Labeling completed successfully, triggering PR build workflow'); - - // Trigger the PR build workflow using workflow_dispatch - await github.rest.actions.createWorkflowDispatch({ - owner: context.repo.owner, - repo: context.repo.repo, - workflow_id: 'pr-build.yml', - ref: context.payload.pull_request.head.ref, - inputs: { - pr_number: context.payload.pull_request.number.toString(), - pr_head_sha: context.payload.pull_request.head.sha, - pr_base_ref: context.payload.pull_request.base.ref - } - }); From ffe3ed059a60c1dc6f37c7546ab774b1e4a88608 Mon Sep 17 00:00:00 2001 From: Ronit Sabhaya Date: Wed, 8 Oct 2025 16:54:39 -0500 Subject: [PATCH 04/12] reseting things to apple again --- .github/workflows/common.yml | 2 +- .github/workflows/labeler.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index b6bb3f88..4e7d6b95 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -11,7 +11,7 @@ on: jobs: buildAndTest: name: Build and test the project - if: github.repository == 'Ronitsabhaya75/container' + if: github.repository == 'apple/container' timeout-minutes: 60 runs-on: [self-hosted, macos, sequoia, ARM64] permissions: diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index b7f11706..196df1ec 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -11,7 +11,7 @@ permissions: jobs: label: name: Label Pull Request - if: github.repository == 'Ronitsabhaya75/container' + if: github.repository == 'apple/container' runs-on: ubuntu-latest outputs: labels-applied: ${{ steps.labeler.outputs.labels-applied }} From 96d4a773ebf63770b0faa26232f66352c89f6b11 Mon Sep 17 00:00:00 2001 From: Ronit Sabhaya Date: Wed, 8 Oct 2025 19:20:47 -0500 Subject: [PATCH 05/12] Updated the paths for file configuration ( seek advice from AI ) --- .github/labeler.yml | 128 +++++++++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 48 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index ab5445d3..08c47e16 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -3,98 +3,130 @@ # Core areas "area: cli": - - Sources/CLI/**/* - - Sources/ContainerCommands/**/* + - changed-files: + - any-glob-to-any-file: + - 'Sources/CLI/**/*' + - 'Sources/ContainerCommands/**/*' "area: build": - - Sources/ContainerBuild/**/* - - Makefile - - Package.swift - - Package.resolved - - Protobuf.Makefile + - changed-files: + - any-glob-to-any-file: + - 'Sources/ContainerBuild/**/*' + - 'Makefile' + - 'Package.swift' + - 'Package.resolved' + - 'Protobuf.Makefile' "area: client": - - Sources/ContainerClient/**/* + - changed-files: + - any-glob-to-any-file: 'Sources/ContainerClient/**/*' "area: networking": - - Sources/ContainerNetworkService/**/* - - Sources/DNSServer/**/* - - Sources/SocketForwarder/**/* - - config/container-network-vmnet-config.json + - changed-files: + - any-glob-to-any-file: + - 'Sources/ContainerNetworkService/**/*' + - 'Sources/DNSServer/**/*' + - 'Sources/SocketForwarder/**/*' + - 'config/container-network-vmnet-config.json' "area: services": - - Sources/Services/**/* - - Sources/ContainerAPIService/**/* - - Sources/ContainerImagesService/**/* - - Sources/ContainerSandboxService/**/* + - changed-files: + - any-glob-to-any-file: + - 'Sources/Services/**/*' + - 'Sources/ContainerAPIService/**/*' + - 'Sources/ContainerImagesService/**/*' + - 'Sources/ContainerSandboxService/**/*' "area: runtime": - - Sources/Helpers/RuntimeLinux/**/* - - config/container-runtime-linux-config.json + - changed-files: + - any-glob-to-any-file: + - 'Sources/Helpers/RuntimeLinux/**/*' + - 'config/container-runtime-linux-config.json' "area: plugin": - - Sources/ContainerPlugin/**/* + - changed-files: + - any-glob-to-any-file: 'Sources/ContainerPlugin/**/*' "area: xpc": - - Sources/ContainerXPC/**/* + - changed-files: + - any-glob-to-any-file: 'Sources/ContainerXPC/**/*' "area: logging": - - Sources/ContainerLog/**/* + - changed-files: + - any-glob-to-any-file: 'Sources/ContainerLog/**/*' "area: persistence": - - Sources/ContainerPersistence/**/* + - changed-files: + - any-glob-to-any-file: 'Sources/ContainerPersistence/**/*' "area: terminal": - - Sources/TerminalProgress/**/* + - changed-files: + - any-glob-to-any-file: 'Sources/TerminalProgress/**/*' "area: helpers": - - Sources/Helpers/**/* + - changed-files: + - any-glob-to-any-file: 'Sources/Helpers/**/*' # Testing "testing": - - Tests/**/* - - "**/*Test*.swift" - - "**/*Tests.swift" + - changed-files: + - any-glob-to-any-file: + - 'Tests/**/*' + - '**/*Test*.swift' + - '**/*Tests.swift' # Documentation "documentation": - - docs/**/* - - "**/*.md" - - scripts/make-docs.sh + - changed-files: + - any-glob-to-any-file: + - 'docs/**/*' + - '**/*.md' + - 'scripts/make-docs.sh' # Configuration "configuration": - - config/**/* - - "**/*.json" - - "**/*.toml" - - signing/**/* + - changed-files: + - any-glob-to-any-file: + - 'config/**/*' + - '**/*.json' + - '**/*.toml' + - 'signing/**/*' # Scripts and tooling "tooling": - - scripts/**/* - - licenserc.toml - - Makefile - - Protobuf.Makefile + - changed-files: + - any-glob-to-any-file: + - 'scripts/**/*' + - 'licenserc.toml' + - 'Makefile' + - 'Protobuf.Makefile' # GitHub specific "github": - - .github/**/* + - changed-files: + - any-glob-to-any-file: '.github/**/*' # Dependencies "dependencies": - - Package.swift - - Package.resolved + - changed-files: + - any-glob-to-any-file: + - 'Package.swift' + - 'Package.resolved' # C/C++ code "c/c++": - - Sources/CVersion/**/* - - "**/*.c" - - "**/*.h" + - changed-files: + - any-glob-to-any-file: + - 'Sources/CVersion/**/*' + - '**/*.c' + - '**/*.h' # Swift protocol buffers "protobuf": - - "**/*.proto" - - "**/*.pb.swift" - - "**/*.grpc.swift" - - Protobuf.Makefile + - changed-files: + - any-glob-to-any-file: + - '**/*.proto' + - '**/*.pb.swift' + - '**/*.grpc.swift' + - 'Protobuf.Makefile' From 07ab03f77e25b935885b94ce51cbdd3f717ba9dd Mon Sep 17 00:00:00 2001 From: Kathryn Baldauf Date: Wed, 8 Oct 2025 16:08:08 -0700 Subject: [PATCH 06/12] Remove StandardErrorPath and StandardOutPath from launchd plists (#742) ## Type of Change - [x] Bug fix ## Motivation and Context We setup logging for the services in `container` using OSLog. See [here](https://github.com/apple/container/blob/73709232d2705b7008b7380fe90a373059b6074a/Sources/Helpers/APIServer/APIServer.swift#L31). This makes it unnecessary to redirect stderr and stdio for these services. Additionally, there are some cases where failure to open or write to the path given for StandardErrorPath or StandardOutPath in a service's plist could result in a failure to start a service through `launchctl`. Related to https://github.com/apple/container/discussions/713 ## Testing - [x] Tested locally Signed-off-by: Kathryn Baldauf --- Sources/ContainerCommands/System/SystemStart.swift | 3 --- Sources/ContainerPlugin/PluginLoader.swift | 3 --- 2 files changed, 6 deletions(-) diff --git a/Sources/ContainerCommands/System/SystemStart.swift b/Sources/ContainerCommands/System/SystemStart.swift index 8b22dd4f..4634a8aa 100644 --- a/Sources/ContainerCommands/System/SystemStart.swift +++ b/Sources/ContainerCommands/System/SystemStart.swift @@ -75,15 +75,12 @@ extension Application { env[ApplicationRoot.environmentName] = appRoot.path(percentEncoded: false) env[InstallRoot.environmentName] = installRoot.path(percentEncoded: false) - let logURL = apiServerDataUrl.appending(path: "apiserver.log") let plist = LaunchPlist( label: "com.apple.container.apiserver", arguments: args, environment: env, limitLoadToSessionType: [.Aqua, .Background, .System], runAtLoad: true, - stdout: logURL.path, - stderr: logURL.path, machServices: ["com.apple.container.apiserver"] ) diff --git a/Sources/ContainerPlugin/PluginLoader.swift b/Sources/ContainerPlugin/PluginLoader.swift index 13fcfcf9..5d352010 100644 --- a/Sources/ContainerPlugin/PluginLoader.swift +++ b/Sources/ContainerPlugin/PluginLoader.swift @@ -218,15 +218,12 @@ extension PluginLoader { env[ApplicationRoot.environmentName] = appRoot.path(percentEncoded: false) env[InstallRoot.environmentName] = installRoot.path(percentEncoded: false) - let logUrl = rootURL.appendingPathComponent("service.log") let plist = LaunchPlist( label: id, arguments: [plugin.binaryURL.path] + (args ?? serviceConfig.defaultArguments), environment: env, limitLoadToSessionType: [.Aqua, .Background, .System], runAtLoad: serviceConfig.runAtLoad, - stdout: logUrl.path, - stderr: logUrl.path, machServices: plugin.getMachServices(instanceId: instanceId) ) From fa93aec86b03336aa07f4535664c4eea96ca30b0 Mon Sep 17 00:00:00 2001 From: Bisman Sahni <139563284+bismansahni@users.noreply.github.com> Date: Wed, 8 Oct 2025 17:30:06 -0700 Subject: [PATCH 07/12] Fix race condition in ContainersService.create() (#721) Fixes #692 This PR resolves a race condition in `ContainersService.create()` that occurs when creating containers with the same name concurrently. --- .../Containers/ContainersService.swift | 126 +++++++++--------- 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/Sources/Services/ContainerAPIService/Containers/ContainersService.swift b/Sources/Services/ContainerAPIService/Containers/ContainersService.swift index 26dbc0af..1a91e31b 100644 --- a/Sources/Services/ContainerAPIService/Containers/ContainersService.swift +++ b/Sources/Services/ContainerAPIService/Containers/ContainersService.swift @@ -130,80 +130,82 @@ public actor ContainersService { public func create(configuration: ContainerConfiguration, kernel: Kernel, options: ContainerCreateOptions) async throws { self.log.debug("\(#function)") - guard containers[configuration.id] == nil else { - throw ContainerizationError( - .exists, - message: "container already exists: \(configuration.id)" - ) - } - - var allHostnames = Set() - for container in containers.values { - for attachmentConfiguration in container.snapshot.configuration.networks { - allHostnames.insert(attachmentConfiguration.options.hostname) + try await self.lock.withLock { context in + guard await self.containers[configuration.id] == nil else { + throw ContainerizationError( + .exists, + message: "container already exists: \(configuration.id)" + ) } - } - var conflictingHostnames = [String]() - for attachmentConfiguration in configuration.networks { - if allHostnames.contains(attachmentConfiguration.options.hostname) { - conflictingHostnames.append(attachmentConfiguration.options.hostname) + var allHostnames = Set() + for container in await self.containers.values { + for attachmentConfiguration in container.snapshot.configuration.networks { + allHostnames.insert(attachmentConfiguration.options.hostname) + } } - } - guard conflictingHostnames.isEmpty else { - throw ContainerizationError( - .exists, - message: "hostname(s) already exist: \(conflictingHostnames)" - ) - } + var conflictingHostnames = [String]() + for attachmentConfiguration in configuration.networks { + if allHostnames.contains(attachmentConfiguration.options.hostname) { + conflictingHostnames.append(attachmentConfiguration.options.hostname) + } + } - let runtimePlugin = self.runtimePlugins.filter { - $0.name == configuration.runtimeHandler - }.first - guard let runtimePlugin else { - throw ContainerizationError( - .notFound, - message: "unable to locate runtime plugin \(configuration.runtimeHandler)" - ) - } + guard conflictingHostnames.isEmpty else { + throw ContainerizationError( + .exists, + message: "hostname(s) already exist: \(conflictingHostnames)" + ) + } - let path = self.containerRoot.appendingPathComponent(configuration.id) - let systemPlatform = kernel.platform - let initFs = try await getInitBlock(for: systemPlatform.ociPlatform()) + let runtimePlugin = self.runtimePlugins.filter { + $0.name == configuration.runtimeHandler + }.first + guard let runtimePlugin else { + throw ContainerizationError( + .notFound, + message: "unable to locate runtime plugin \(configuration.runtimeHandler)" + ) + } - let bundle = try ContainerClient.Bundle.create( - path: path, - initialFilesystem: initFs, - kernel: kernel, - containerConfiguration: configuration - ) - do { - let containerImage = ClientImage(description: configuration.image) - let imageFs = try await containerImage.getCreateSnapshot(platform: configuration.platform) - try bundle.setContainerRootFs(cloning: imageFs) - try bundle.write(filename: "options.json", value: options) - - try Self.registerService( - plugin: runtimePlugin, - loader: self.pluginLoader, - configuration: configuration, - path: path - ) + let path = self.containerRoot.appendingPathComponent(configuration.id) + let systemPlatform = kernel.platform + let initFs = try await self.getInitBlock(for: systemPlatform.ociPlatform()) - let snapshot = ContainerSnapshot( - configuration: configuration, - status: .stopped, - networks: [] + let bundle = try ContainerClient.Bundle.create( + path: path, + initialFilesystem: initFs, + kernel: kernel, + containerConfiguration: configuration ) - self.containers[configuration.id] = ContainerState(snapshot: snapshot) - } catch { do { - try bundle.delete() + let containerImage = ClientImage(description: configuration.image) + let imageFs = try await containerImage.getCreateSnapshot(platform: configuration.platform) + try bundle.setContainerRootFs(cloning: imageFs) + try bundle.write(filename: "options.json", value: options) + + try Self.registerService( + plugin: runtimePlugin, + loader: self.pluginLoader, + configuration: configuration, + path: path + ) + + let snapshot = ContainerSnapshot( + configuration: configuration, + status: .stopped, + networks: [] + ) + await self.setContainerState(configuration.id, ContainerState(snapshot: snapshot), context: context) } catch { - self.log.error("failed to delete bundle for container \(configuration.id): \(error)") + do { + try bundle.delete() + } catch { + self.log.error("failed to delete bundle for container \(configuration.id): \(error)") + } + throw error } - throw error } } From 1f835b7a580e2d6637396188fafdc632ed255291 Mon Sep 17 00:00:00 2001 From: J Logan Date: Fri, 10 Oct 2025 10:17:42 -0700 Subject: [PATCH 08/12] Fix broken proxy configuration for default kernel fetch. (#747) - Closes #466. ## Type of Change - [x] Bug fix - [ ] New feature - [ ] Breaking change - [ ] Documentation update ## Motivation and Context Proxy logic worked well enough for CI but broken in general. ## Testing - [x] Tested locally - [x] Added/updated tests - [ ] Added/updated docs --- .github/workflows/common.yml | 1 - Package.resolved | 6 +- Package.swift | 2 +- Sources/ContainerClient/FileDownloader.swift | 20 ++--- .../System/SystemStart.swift | 5 +- Sources/ContainerPlugin/PluginLoader.swift | 20 ++++- .../PluginLoaderTest.swift | 88 +++++++++++++++++++ 7 files changed, 119 insertions(+), 23 deletions(-) diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 4e7d6b95..d2912ab4 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -67,7 +67,6 @@ jobs: - name: Test the container project run: | - launchctl setenv HTTP_PROXY $HTTP_PROXY 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}" diff --git a/Package.resolved b/Package.resolved index 6738b8e3..2ae54a2b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "e8a26d708e3d286b0e29d74d94b6ff1539848a7a9fd209a230faedae5e285003", + "originHash" : "ee0892935fd158d90844c467fb84568e8fd24f3fe7b04d49504fdf99e6322df6", "pins" : [ { "identity" : "async-http-client", @@ -15,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/containerization.git", "state" : { - "revision" : "992ed9f5aa3b0e875ef8ac6b605a4c352218463b", - "version" : "0.9.1" + "revision" : "d0383eb5bf97f27bceb51538597194b020bc8945", + "version" : "0.10.0" } }, { diff --git a/Package.swift b/Package.swift index 274c3452..679c12f0 100644 --- a/Package.swift +++ b/Package.swift @@ -23,7 +23,7 @@ import PackageDescription let releaseVersion = ProcessInfo.processInfo.environment["RELEASE_VERSION"] ?? "0.0.0" let gitCommit = ProcessInfo.processInfo.environment["GIT_COMMIT"] ?? "unspecified" let builderShimVersion = "0.6.1" -let scVersion = "0.9.1" +let scVersion = "0.10.0" let package = Package( name: "container", diff --git a/Sources/ContainerClient/FileDownloader.swift b/Sources/ContainerClient/FileDownloader.swift index 2de069e4..7ebd7ffa 100644 --- a/Sources/ContainerClient/FileDownloader.swift +++ b/Sources/ContainerClient/FileDownloader.swift @@ -49,24 +49,20 @@ public struct FileDownloader { } }) - let client = FileDownloader.createClient() + let client = FileDownloader.createClient(url: url) _ = try await client.execute(request: request, delegate: delegate).get() try await client.shutdown() } - private static func createClient() -> HTTPClient { + private static func createClient(url: URL) -> HTTPClient { var httpConfiguration = HTTPClient.Configuration() - let proxyConfig: HTTPClient.Configuration.Proxy? = { - let proxyEnv = ProcessInfo.processInfo.environment["HTTP_PROXY"] - guard let proxyEnv else { - return nil + if let host = url.host { + let proxyURL = ProxyUtils.proxyFromEnvironment(scheme: url.scheme, host: host) + if let proxyURL, let proxyHost = proxyURL.host { + httpConfiguration.proxy = HTTPClient.Configuration.Proxy.server(host: proxyHost, port: proxyURL.port ?? 8080) } - guard let url = URL(string: proxyEnv), let host = url.host(), let port = url.port else { - return nil - } - return .server(host: host, port: port) - }() - httpConfiguration.proxy = proxyConfig + } + return HTTPClient(eventLoopGroupProvider: .singleton, configuration: httpConfiguration) } } diff --git a/Sources/ContainerCommands/System/SystemStart.swift b/Sources/ContainerCommands/System/SystemStart.swift index 4634a8aa..57debedb 100644 --- a/Sources/ContainerCommands/System/SystemStart.swift +++ b/Sources/ContainerCommands/System/SystemStart.swift @@ -69,9 +69,8 @@ extension Application { args.append("start") let apiServerDataUrl = appRoot.appending(path: "apiserver") try! FileManager.default.createDirectory(at: apiServerDataUrl, withIntermediateDirectories: true) - var env = ProcessInfo.processInfo.environment.filter { key, _ in - key.hasPrefix("CONTAINER_") - } + + var env = PluginLoader.filterEnvironment() env[ApplicationRoot.environmentName] = appRoot.path(percentEncoded: false) env[InstallRoot.environmentName] = installRoot.path(percentEncoded: false) diff --git a/Sources/ContainerPlugin/PluginLoader.swift b/Sources/ContainerPlugin/PluginLoader.swift index 5d352010..5dd7d8b6 100644 --- a/Sources/ContainerPlugin/PluginLoader.swift +++ b/Sources/ContainerPlugin/PluginLoader.swift @@ -196,6 +196,12 @@ extension PluginLoader { } extension PluginLoader { + public static let proxyKeys = Set([ + "http_proxy", "HTTP_PROXY", + "https_proxy", "HTTPS_PROXY", + "no_proxy", "NO_PROXY", + ]) + public func registerWithLaunchd( plugin: Plugin, pluginStateRoot: URL? = nil, @@ -212,9 +218,8 @@ extension PluginLoader { log?.info("Registering plugin", metadata: ["id": "\(id)"]) let rootURL = pluginStateRoot ?? self.pluginResourceRoot.appending(path: plugin.name) try FileManager.default.createDirectory(at: rootURL, withIntermediateDirectories: true) - var env = ProcessInfo.processInfo.environment.filter { key, _ in - key.hasPrefix("CONTAINER_") - } + + var env = Self.filterEnvironment() env[ApplicationRoot.environmentName] = appRoot.path(percentEncoded: false) env[InstallRoot.environmentName] = installRoot.path(percentEncoded: false) @@ -244,4 +249,13 @@ extension PluginLoader { log?.info("Deregistering plugin", metadata: ["id": "\(plugin.getLaunchdLabel())"]) try ServiceManager.deregister(fullServiceLabel: label) } + + public static func filterEnvironment( + env: [String: String] = ProcessInfo.processInfo.environment, + additionalAllowKeys: Set = Self.proxyKeys + ) -> [String: String] { + env.filter { key, _ in + key.hasPrefix("CONTAINER_") || additionalAllowKeys.contains(key) + } + } } diff --git a/Tests/ContainerPluginTests/PluginLoaderTest.swift b/Tests/ContainerPluginTests/PluginLoaderTest.swift index 8033bdaf..b429f6fa 100644 --- a/Tests/ContainerPluginTests/PluginLoaderTest.swift +++ b/Tests/ContainerPluginTests/PluginLoaderTest.swift @@ -85,6 +85,94 @@ struct PluginLoaderTest { #expect(loader.findPlugin(name: "throw") == nil) } + @Test + func testFilterEnvironmentWithContainerPrefix() async throws { + let env = [ + "CONTAINER_FOO": "bar", + "CONTAINER_BAZ": "qux", + "OTHER_VAR": "value", + ] + let filtered = PluginLoader.filterEnvironment(env: env, additionalAllowKeys: []) + + #expect(filtered == ["CONTAINER_FOO": "bar", "CONTAINER_BAZ": "qux"]) + } + + @Test + func testFilterEnvironmentWithProxyKeys() async throws { + let env = [ + "http_proxy": "http://proxy:8080", + "HTTP_PROXY": "http://proxy:8080", + "https_proxy": "https://proxy:8443", + "HTTPS_PROXY": "https://proxy:8443", + "no_proxy": "localhost,127.0.0.1", + "NO_PROXY": "localhost,127.0.0.1", + "OTHER_VAR": "value", + ] + let filtered = PluginLoader.filterEnvironment(env: env) + + #expect( + filtered == [ + "http_proxy": "http://proxy:8080", + "HTTP_PROXY": "http://proxy:8080", + "https_proxy": "https://proxy:8443", + "HTTPS_PROXY": "https://proxy:8443", + "no_proxy": "localhost,127.0.0.1", + "NO_PROXY": "localhost,127.0.0.1", + ]) + } + + @Test + func testFilterEnvironmentWithBothContainerAndProxy() async throws { + let env = [ + "CONTAINER_FOO": "bar", + "http_proxy": "http://proxy:8080", + "OTHER_VAR": "value", + "ANOTHER_VAR": "value2", + ] + let filtered = PluginLoader.filterEnvironment(env: env) + + #expect( + filtered == [ + "CONTAINER_FOO": "bar", + "http_proxy": "http://proxy:8080", + ]) + } + + @Test + func testFilterEnvironmentWithCustomAllowKeys() async throws { + let env = [ + "CONTAINER_FOO": "bar", + "CUSTOM_KEY": "custom_value", + "OTHER_VAR": "value", + ] + let filtered = PluginLoader.filterEnvironment(env: env, additionalAllowKeys: ["CUSTOM_KEY"]) + + #expect( + filtered == [ + "CONTAINER_FOO": "bar", + "CUSTOM_KEY": "custom_value", + ]) + } + + @Test + func testFilterEnvironmentEmpty() async throws { + let filtered = PluginLoader.filterEnvironment(env: [:]) + + #expect(filtered.isEmpty) + } + + @Test + func testFilterEnvironmentNoMatches() async throws { + let env = [ + "PATH": "/usr/bin", + "HOME": "/Users/test", + "USER": "testuser", + ] + let filtered = PluginLoader.filterEnvironment(env: env, additionalAllowKeys: []) + + #expect(filtered.isEmpty) + } + private func setupMock(tempURL: URL) throws -> MockPluginFactory { let cliConfig = PluginConfig(abstract: "cli", author: "CLI", servicesConfig: nil) let cliPlugin: Plugin = Plugin(binaryURL: URL(filePath: "/bin/cli"), config: cliConfig) From 19717d4979a6e390d7fd6583df1275edfe237da2 Mon Sep 17 00:00:00 2001 From: J Logan Date: Fri, 10 Oct 2025 10:19:05 -0700 Subject: [PATCH 09/12] Moves `.build` directory to `builder`. (#749) Closes #416. ## Type of Change - [ ] Bug fix - [ ] New feature - [x] Breaking change (possibly, I would think we're just going to leave an unused `.build` directory behind which we can document, but can run more tests next week). - [ ] Documentation update ## Motivation and Context We have nothing to hide? ## Testing - [x] Tested locally - [ ] Added/updated tests - [ ] Added/updated docs --- Sources/ContainerCommands/BuildCommand.swift | 3 ++- Sources/ContainerCommands/Builder/Builder.swift | 1 + Sources/ContainerCommands/Builder/BuilderStart.swift | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Sources/ContainerCommands/BuildCommand.swift b/Sources/ContainerCommands/BuildCommand.swift index 6396df81..cc91a07b 100644 --- a/Sources/ContainerCommands/BuildCommand.swift +++ b/Sources/ContainerCommands/BuildCommand.swift @@ -186,7 +186,8 @@ extension Application { let dockerfile = try Data(contentsOf: URL(filePath: file)) let systemHealth = try await ClientHealthCheck.ping(timeout: .seconds(10)) - let exportPath = systemHealth.appRoot.appendingPathComponent(".build") + let exportPath = systemHealth.appRoot + .appendingPathComponent(Application.BuilderCommand.builderResourceDir) let buildID = UUID().uuidString let tempURL = exportPath.appendingPathComponent(buildID) try FileManager.default.createDirectory(at: tempURL, withIntermediateDirectories: true, attributes: nil) diff --git a/Sources/ContainerCommands/Builder/Builder.swift b/Sources/ContainerCommands/Builder/Builder.swift index a8c5b388..f01d8574 100644 --- a/Sources/ContainerCommands/Builder/Builder.swift +++ b/Sources/ContainerCommands/Builder/Builder.swift @@ -20,6 +20,7 @@ extension Application { public struct BuilderCommand: AsyncParsableCommand { public init() {} + public static let builderResourceDir = "builder" public static let configuration = CommandConfiguration( commandName: "builder", abstract: "Manage an image builder instance", diff --git a/Sources/ContainerCommands/Builder/BuilderStart.swift b/Sources/ContainerCommands/Builder/BuilderStart.swift index 65a3bcda..56abd546 100644 --- a/Sources/ContainerCommands/Builder/BuilderStart.swift +++ b/Sources/ContainerCommands/Builder/BuilderStart.swift @@ -74,7 +74,9 @@ extension Application { let builderImage: String = DefaultsStore.get(key: .defaultBuilderImage) let systemHealth = try await ClientHealthCheck.ping(timeout: .seconds(10)) - let exportsMount: String = systemHealth.appRoot.appendingPathComponent(".build").absolutePath() + let exportsMount: String = systemHealth.appRoot + .appendingPathComponent(Application.BuilderCommand.builderResourceDir) + .absolutePath() if !FileManager.default.fileExists(atPath: exportsMount) { try FileManager.default.createDirectory( From 1549f9b9cd9c7bf0688abb091446df9bbce7c5fa Mon Sep 17 00:00:00 2001 From: Ronit Sabhaya Date: Sat, 11 Oct 2025 13:20:32 -0500 Subject: [PATCH 10/12] Updated the labler.yml to only CLI --- .github/labeler.yml | 124 +------------------------------------------- 1 file changed, 1 insertion(+), 123 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 08c47e16..79f403a9 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,132 +1,10 @@ # Configuration for automatic PR labeling based on file changes # https://github.com/actions/labeler -# Core areas +# CLI - Command Line Interface "area: cli": - changed-files: - any-glob-to-any-file: - 'Sources/CLI/**/*' - 'Sources/ContainerCommands/**/*' -"area: build": - - changed-files: - - any-glob-to-any-file: - - 'Sources/ContainerBuild/**/*' - - 'Makefile' - - 'Package.swift' - - 'Package.resolved' - - 'Protobuf.Makefile' - -"area: client": - - changed-files: - - any-glob-to-any-file: 'Sources/ContainerClient/**/*' - -"area: networking": - - changed-files: - - any-glob-to-any-file: - - 'Sources/ContainerNetworkService/**/*' - - 'Sources/DNSServer/**/*' - - 'Sources/SocketForwarder/**/*' - - 'config/container-network-vmnet-config.json' - -"area: services": - - changed-files: - - any-glob-to-any-file: - - 'Sources/Services/**/*' - - 'Sources/ContainerAPIService/**/*' - - 'Sources/ContainerImagesService/**/*' - - 'Sources/ContainerSandboxService/**/*' - -"area: runtime": - - changed-files: - - any-glob-to-any-file: - - 'Sources/Helpers/RuntimeLinux/**/*' - - 'config/container-runtime-linux-config.json' - -"area: plugin": - - changed-files: - - any-glob-to-any-file: 'Sources/ContainerPlugin/**/*' - -"area: xpc": - - changed-files: - - any-glob-to-any-file: 'Sources/ContainerXPC/**/*' - -"area: logging": - - changed-files: - - any-glob-to-any-file: 'Sources/ContainerLog/**/*' - -"area: persistence": - - changed-files: - - any-glob-to-any-file: 'Sources/ContainerPersistence/**/*' - -"area: terminal": - - changed-files: - - any-glob-to-any-file: 'Sources/TerminalProgress/**/*' - -"area: helpers": - - changed-files: - - any-glob-to-any-file: 'Sources/Helpers/**/*' - -# Testing -"testing": - - changed-files: - - any-glob-to-any-file: - - 'Tests/**/*' - - '**/*Test*.swift' - - '**/*Tests.swift' - -# Documentation -"documentation": - - changed-files: - - any-glob-to-any-file: - - 'docs/**/*' - - '**/*.md' - - 'scripts/make-docs.sh' - -# Configuration -"configuration": - - changed-files: - - any-glob-to-any-file: - - 'config/**/*' - - '**/*.json' - - '**/*.toml' - - 'signing/**/*' - -# Scripts and tooling -"tooling": - - changed-files: - - any-glob-to-any-file: - - 'scripts/**/*' - - 'licenserc.toml' - - 'Makefile' - - 'Protobuf.Makefile' - -# GitHub specific -"github": - - changed-files: - - any-glob-to-any-file: '.github/**/*' - -# Dependencies -"dependencies": - - changed-files: - - any-glob-to-any-file: - - 'Package.swift' - - 'Package.resolved' - -# C/C++ code -"c/c++": - - changed-files: - - any-glob-to-any-file: - - 'Sources/CVersion/**/*' - - '**/*.c' - - '**/*.h' - -# Swift protocol buffers -"protobuf": - - changed-files: - - any-glob-to-any-file: - - '**/*.proto' - - '**/*.pb.swift' - - '**/*.grpc.swift' - - 'Protobuf.Makefile' - From 19ac6177701a541abc12f4fc8475b915a0f0b064 Mon Sep 17 00:00:00 2001 From: Ronit Sabhaya Date: Thu, 16 Oct 2025 14:36:15 -0500 Subject: [PATCH 11/12] Update Package.resolved with new originHash and version Updated the originHash and containerization package version. --- Package.resolved | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Package.resolved b/Package.resolved index 2ae54a2b..3a6e34f2 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "ee0892935fd158d90844c467fb84568e8fd24f3fe7b04d49504fdf99e6322df6", + "originHash" : "544a30d2ac05eddd41c7f5cf38fdf0599550a026c40495d7dd16dfbfc302f7f8", "pins" : [ { "identity" : "async-http-client", @@ -15,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/containerization.git", "state" : { - "revision" : "d0383eb5bf97f27bceb51538597194b020bc8945", - "version" : "0.10.0" + "revision" : "e283e023ab42f7bcda6d7d31d5dd61336efaeccc", + "version" : "0.10.1" } }, { From a600a858e9dd8836c0747fd9ea80633a52c0977d Mon Sep 17 00:00:00 2001 From: Ronit Sabhaya Date: Thu, 16 Oct 2025 14:37:07 -0500 Subject: [PATCH 12/12] Update scVersion to 0.10.1 --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 679c12f0..4b29c405 100644 --- a/Package.swift +++ b/Package.swift @@ -23,7 +23,7 @@ import PackageDescription let releaseVersion = ProcessInfo.processInfo.environment["RELEASE_VERSION"] ?? "0.0.0" let gitCommit = ProcessInfo.processInfo.environment["GIT_COMMIT"] ?? "unspecified" let builderShimVersion = "0.6.1" -let scVersion = "0.10.0" +let scVersion = "0.10.1" let package = Package( name: "container",