Skip to content

Commit 5a36b34

Browse files
committed
Consolidate enviroment variables used to configuration SwiftPM
PR #9925 added new environment variables that would control authentication with the Package registry using environment variables. Move the newly added environment variables to the `ConfigurableEnvVar` enum, to have all configurable environment variables in a single location, so we can eventually generated documentation from this source.
1 parent f0c6371 commit 5a36b34

10 files changed

Lines changed: 112 additions & 76 deletions

File tree

Sources/Basics/AuthorizationProvider.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,16 @@ public struct EnvironmentAuthorizationProvider: AuthorizationProvider {
7171
public func authentication(for url: URL) -> (user: String, password: String)? {
7272
switch kind {
7373
case .registry:
74-
if let token = nonEmpty(environment[.swiftpmRegistryToken]) {
74+
if let token = nonEmpty(ConfigurableEnvVar.SWIFTPM_REGISTRY_TOKEN.getEnvVar()) {
7575
return (user: "token", password: token)
7676
}
77-
if let login = nonEmpty(environment[.swiftpmRegistryLogin]),
78-
let password = nonEmpty(environment[.swiftpmRegistryPassword]) {
77+
if let login = nonEmpty(ConfigurableEnvVar.SWIFTPM_REGISTRY_LOGIN.getEnvVar()),
78+
let password = nonEmpty(ConfigurableEnvVar.SWIFTPM_REGISTRY_PASSWORD.getEnvVar()) {
7979
return (user: login, password: password)
8080
}
8181
return nil
8282
case .sourceControl:
83-
if let token = nonEmpty(environment[.swiftpmSourceControlToken]) {
83+
if let token = nonEmpty(ConfigurableEnvVar.SWIFTPM_REGISTRY_TOKEN.getEnvVar()) {
8484
return (user: "token", password: token)
8585
}
8686
return nil
@@ -328,7 +328,7 @@ public final class KeychainAuthorizationProvider: AuthorizationProvider, Authori
328328
throw AuthorizationProviderError
329329
.other("Failed to extract credentials for '\(protocolHostPort)' from keychain")
330330
}
331-
331+
332332
let password = String(decoding: passwordData, as: UTF8.self)
333333

334334
return (user: account, password: password)

Sources/Basics/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ add_library(Basics
2626
Concurrency/ThreadSafeKeyValueStore.swift
2727
Concurrency/ThrowingDefer.swift
2828
Concurrency/TokenBucket.swift
29+
ConfigurableEnvVar.swift
2930
DispatchTimeInterval+Extensions.swift
3031
Environment/Environment.swift
3132
Environment/EnvironmentKey.swift
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Foundation
14+
import struct Basics.EnvironmentKey
15+
16+
package enum ConfigurableEnvVar: String, CaseIterable {
17+
/// SBOM specification(s) to generate (comma-separated list)
18+
case SWIFTPM_BUILD_SBOM_SPEC
19+
20+
/// Directory path to generate SBOM(s) in
21+
case SWIFTPM_BUILD_SBOM_OUTPUT_DIR
22+
23+
/// Filter SBOM components and dependencies by entity (package, product, or both)
24+
case SWIFTPM_BUILD_SBOM_FILTER
25+
26+
/// Whether to treat SBOM generation errors as warnings
27+
case SWIFTPM_BUILD_SBOM_WARNING_ONLY
28+
29+
/// Package Registra token
30+
case SWIFTPM_REGISTRY_TOKEN
31+
32+
/// Package Registra username
33+
case SWIFTPM_REGISTRY_LOGIN
34+
35+
/// Package Registra password
36+
case SWIFTPM_REGISTRY_PASSWORD
37+
38+
/// Swift Package Manager source countrol token
39+
case SWIFTPM_SOURCE_CONTROL_TOKEN
40+
41+
/// Swift Package Manager .netrc data contents
42+
case SWIFTPM_NETRC_DATA
43+
44+
private var isCachable : Bool {
45+
switch self {
46+
case .SWIFTPM_BUILD_SBOM_SPEC: false
47+
case .SWIFTPM_BUILD_SBOM_OUTPUT_DIR: false
48+
case .SWIFTPM_BUILD_SBOM_FILTER: false
49+
case .SWIFTPM_BUILD_SBOM_WARNING_ONLY: false
50+
case .SWIFTPM_REGISTRY_TOKEN: true
51+
case .SWIFTPM_REGISTRY_LOGIN: true
52+
case .SWIFTPM_REGISTRY_PASSWORD: true
53+
case .SWIFTPM_SOURCE_CONTROL_TOKEN: true
54+
case .SWIFTPM_NETRC_DATA: true
55+
56+
}
57+
}
58+
59+
package static func getNonCachableEnvVars() -> Set<EnvironmentKey> {
60+
return Set(
61+
Self.allCases.filter { !$0.isCachable }
62+
.map { variable in
63+
EnvironmentKey(variable.rawValue)
64+
}
65+
)
66+
}
67+
68+
package func getEnvVar() -> String? {
69+
// ProcessInfo.processInfo.environment[self.rawValue]
70+
Environment.current[EnvironmentKey(self.rawValue)]
71+
}
72+
}

Sources/Basics/Environment/Environment.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ extension Environment {
5959
_read { yield self.storage[key] }
6060
_modify { yield &self.storage[key] }
6161
}
62+
63+
package subscript(_ key: ConfigurableEnvVar) -> String? {
64+
_read { yield self.storage[EnvironmentKey(key.rawValue)] }
65+
_modify { yield &self.storage[EnvironmentKey(key.rawValue)] }
66+
}
67+
6268
}
6369

6470
// MARK: - Conversions between Dictionary<String, String>

Sources/Basics/Environment/EnvironmentKey.swift

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,6 @@ public struct EnvironmentKey {
2424
extension EnvironmentKey {
2525
package static let path: Self = "PATH"
2626

27-
package static let swiftpmRegistryToken: Self = "SWIFTPM_REGISTRY_TOKEN"
28-
package static let swiftpmRegistryLogin: Self = "SWIFTPM_REGISTRY_LOGIN"
29-
package static let swiftpmRegistryPassword: Self = "SWIFTPM_REGISTRY_PASSWORD"
30-
package static let swiftpmSourceControlToken: Self = "SWIFTPM_SOURCE_CONTROL_TOKEN"
31-
package static let swiftpmNetrcData: Self = "SWIFTPM_NETRC_DATA"
32-
3327
package static var libraryPath: Self {
3428
#if os(Windows)
3529
path
@@ -41,7 +35,7 @@ extension EnvironmentKey {
4135
}
4236

4337
/// A set of known keys which should not be included in cache keys.
44-
package static let nonCachable: Set<Self> = [
38+
package static let nonCachable: Set<Self> = Set([
4539
"TERM",
4640
"TERM_PROGRAM",
4741
"TERM_PROGRAM_VERSION",
@@ -57,12 +51,7 @@ extension EnvironmentKey {
5751
"VSCODE_IPC_HOOK_CLI",
5852
"HYPERFINE_RANDOMIZED_ENVIRONMENT_OFFSET",
5953
"SSH_AUTH_SOCK",
60-
.swiftpmRegistryToken,
61-
.swiftpmRegistryLogin,
62-
.swiftpmRegistryPassword,
63-
.swiftpmSourceControlToken,
64-
.swiftpmNetrcData,
65-
]
54+
]).union(ConfigurableEnvVar.getNonCachableEnvVars())
6655
}
6756

6857
extension EnvironmentKey: CodingKeyRepresentable {}

Sources/CoreCommands/Options.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import ArgumentParser
1414

1515
import struct Basics.AbsolutePath
16+
import enum Basics.ConfigurableEnvVar
1617
import var Basics.localFileSystem
1718
import enum Basics.TestingLibrary
1819
import struct Basics.Triple
@@ -35,7 +36,6 @@ import enum SBOMModel.SBOMCommandError
3536

3637
import struct SPMBuildCore.BuildParameters
3738
import struct SPMBuildCore.BuildSystemProvider
38-
import enum SPMBuildCore.ConfigurableEnvVar
3939

4040
import struct TSCBasic.StringError
4141

@@ -816,7 +816,7 @@ public struct SBOMOptions: ParsableArguments {
816816
if !_sbomSpecs.isEmpty {
817817
return _sbomSpecs
818818
}
819-
if let envSpecs = SPMBuildCore.ConfigurableEnvVar.SWIFTPM_BUILD_SBOM_SPEC.getEnvVar() {
819+
if let envSpecs = Basics.ConfigurableEnvVar.SWIFTPM_BUILD_SBOM_SPEC.getEnvVar() {
820820
let specStrings = envSpecs.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespaces) }
821821
var specs: Set<SBOMModel.Spec> = []
822822
for specString in specStrings {
@@ -838,7 +838,7 @@ public struct SBOMOptions: ParsableArguments {
838838
if let cmdLineDir = _sbomDirectory {
839839
return cmdLineDir
840840
}
841-
if let envDir = SPMBuildCore.ConfigurableEnvVar.SWIFTPM_BUILD_SBOM_OUTPUT_DIR.getEnvVar() {
841+
if let envDir = ConfigurableEnvVar.SWIFTPM_BUILD_SBOM_OUTPUT_DIR.getEnvVar() {
842842
guard let path = AbsolutePath(argument: envDir) else {
843843
return nil
844844
}
@@ -854,7 +854,7 @@ public struct SBOMOptions: ParsableArguments {
854854
if let cliFilter = _sbomFilter {
855855
return cliFilter
856856
}
857-
if let envFilter = SPMBuildCore.ConfigurableEnvVar.SWIFTPM_BUILD_SBOM_FILTER.getEnvVar() {
857+
if let envFilter = Basics.ConfigurableEnvVar.SWIFTPM_BUILD_SBOM_FILTER.getEnvVar() {
858858
guard let filter = SBOMModel.Filter(rawValue: envFilter) else {
859859
throw SBOMModel.SBOMCommandError.invalidFilterValue(value: envFilter)
860860
}
@@ -869,7 +869,7 @@ public struct SBOMOptions: ParsableArguments {
869869
if _sbomWarningOnly {
870870
return true
871871
}
872-
if let envWarningOnly = SPMBuildCore.ConfigurableEnvVar.SWIFTPM_BUILD_SBOM_WARNING_ONLY.getEnvVar() {
872+
if let envWarningOnly = Basics.ConfigurableEnvVar.SWIFTPM_BUILD_SBOM_WARNING_ONLY.getEnvVar() {
873873
let lowercased = envWarningOnly.lowercased()
874874
return !["false", "0", "no"].contains(lowercased)
875875
}

Sources/SPMBuildCore/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ add_library(SPMBuildCore
2626
Plugins/PluginMessages.swift
2727
Plugins/PluginScriptRunner.swift
2828
CommandPluginResult.swift
29-
ConfigurableEnvVar.swift
3029
MainAttrDetection.swift
3130
ResolvedPackage+Extensions.swift
3231
Triple+Extensions.swift

Sources/SPMBuildCore/ConfigurableEnvVar.swift

Lines changed: 0 additions & 31 deletions
This file was deleted.

Sources/Workspace/Workspace+Configuration.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -310,11 +310,11 @@ extension Workspace.Configuration {
310310
var providers = [AuthorizationProvider]()
311311

312312
let env = Environment.current
313-
if let token = env[.swiftpmSourceControlToken], !token.isEmpty {
313+
if let token = ConfigurableEnvVar.SWIFTPM_SOURCE_CONTROL_TOKEN.getEnvVar(), !token.isEmpty {
314314
providers.append(EnvironmentAuthorizationProvider(kind: .sourceControl))
315315
}
316316

317-
if let netrcData = env[.swiftpmNetrcData], !netrcData.isEmpty {
317+
if let netrcData = ConfigurableEnvVar.SWIFTPM_NETRC_DATA.getEnvVar(), !netrcData.isEmpty {
318318
do {
319319
providers.append(try InMemoryNetrcAuthorizationProvider(content: netrcData))
320320
} catch {
@@ -371,22 +371,22 @@ extension Workspace.Configuration {
371371
observabilityScope: ObservabilityScope
372372
) throws -> AuthorizationProvider? {
373373
let env = Environment.current
374-
if let token = env[.swiftpmRegistryToken], !token.isEmpty {
374+
if let token = ConfigurableEnvVar.SWIFTPM_REGISTRY_TOKEN.getEnvVar(), !token.isEmpty {
375375
return EnvironmentAuthorizationProvider(kind: .registry)
376376
}
377-
if let login = env[.swiftpmRegistryLogin], !login.isEmpty,
378-
let password = env[.swiftpmRegistryPassword], !password.isEmpty {
377+
if let login = ConfigurableEnvVar.SWIFTPM_REGISTRY_LOGIN.getEnvVar(), !login.isEmpty,
378+
let password = ConfigurableEnvVar.SWIFTPM_REGISTRY_PASSWORD.getEnvVar(), !password.isEmpty {
379379
return EnvironmentAuthorizationProvider(kind: .registry)
380-
} else if let login = env[.swiftpmRegistryLogin], !login.isEmpty {
380+
} else if let login = ConfigurableEnvVar.SWIFTPM_REGISTRY_LOGIN.getEnvVar(), !login.isEmpty {
381381
observabilityScope.emit(
382382
warning: "SWIFTPM_REGISTRY_LOGIN is set but SWIFTPM_REGISTRY_PASSWORD is not; both are required for login/password authentication"
383383
)
384-
} else if let password = env[.swiftpmRegistryPassword], !password.isEmpty {
384+
} else if let password = ConfigurableEnvVar.SWIFTPM_REGISTRY_PASSWORD.getEnvVar(), !password.isEmpty {
385385
observabilityScope.emit(
386386
warning: "SWIFTPM_REGISTRY_PASSWORD is set but SWIFTPM_REGISTRY_LOGIN is not; both are required for login/password authentication"
387387
)
388388
}
389-
if let netrcData = env[.swiftpmNetrcData], !netrcData.isEmpty {
389+
if let netrcData = ConfigurableEnvVar.SWIFTPM_NETRC_DATA.getEnvVar(), !netrcData.isEmpty {
390390
do {
391391
return try InMemoryNetrcAuthorizationProvider(content: netrcData)
392392
} catch {

Tests/BasicsTests/AuthorizationProviderTests.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ final class AuthorizationProviderTests: XCTestCase {
249249

250250
func testEnvironmentRegistryToken() {
251251
var env = Environment()
252-
env[.swiftpmRegistryToken] = "my-registry-token"
252+
env[.SWIFTPM_REGISTRY_TOKEN] = "my-registry-token"
253253

254254
let provider = EnvironmentAuthorizationProvider(environment: env, kind: .registry)
255255
let url = URL("https://registry.example.com")
@@ -261,8 +261,8 @@ final class AuthorizationProviderTests: XCTestCase {
261261

262262
func testEnvironmentRegistryLoginPassword() {
263263
var env = Environment()
264-
env[.swiftpmRegistryLogin] = "myuser"
265-
env[.swiftpmRegistryPassword] = "mypassword"
264+
env[.SWIFTPM_REGISTRY_LOGIN] = "myuser"
265+
env[.SWIFTPM_REGISTRY_PASSWORD] = "mypassword"
266266

267267
let provider = EnvironmentAuthorizationProvider(environment: env, kind: .registry)
268268
let url = URL("https://registry.example.com")
@@ -274,9 +274,9 @@ final class AuthorizationProviderTests: XCTestCase {
274274

275275
func testEnvironmentRegistryTokenPrecedence() {
276276
var env = Environment()
277-
env[.swiftpmRegistryToken] = "my-token"
278-
env[.swiftpmRegistryLogin] = "myuser"
279-
env[.swiftpmRegistryPassword] = "mypassword"
277+
env[.SWIFTPM_REGISTRY_TOKEN] = "my-token"
278+
env[.SWIFTPM_REGISTRY_LOGIN] = "myuser"
279+
env[.SWIFTPM_REGISTRY_PASSWORD] = "mypassword"
280280

281281
let provider = EnvironmentAuthorizationProvider(environment: env, kind: .registry)
282282
let url = URL("https://registry.example.com")
@@ -288,7 +288,7 @@ final class AuthorizationProviderTests: XCTestCase {
288288

289289
func testEnvironmentSourceControlToken() {
290290
var env = Environment()
291-
env[.swiftpmSourceControlToken] = "sc-token-123"
291+
env[.SWIFTPM_SOURCE_CONTROL_TOKEN] = "sc-token-123"
292292

293293
let provider = EnvironmentAuthorizationProvider(environment: env, kind: .sourceControl)
294294
let url = URL("https://github.com/org/repo")
@@ -310,18 +310,18 @@ final class AuthorizationProviderTests: XCTestCase {
310310

311311
func testEnvironmentPartialLoginOnly() {
312312
var env = Environment()
313-
env[.swiftpmRegistryLogin] = "myuser"
313+
env[.SWIFTPM_REGISTRY_LOGIN] = "myuser"
314314

315315
let provider = EnvironmentAuthorizationProvider(environment: env, kind: .registry)
316316
XCTAssertNil(provider.authentication(for: URL("https://registry.example.com")))
317317
}
318318

319319
func testEnvironmentEmptyStringTreatedAsUnset() {
320320
var env = Environment()
321-
env[.swiftpmRegistryToken] = ""
322-
env[.swiftpmRegistryLogin] = ""
323-
env[.swiftpmRegistryPassword] = ""
324-
env[.swiftpmSourceControlToken] = ""
321+
env[.SWIFTPM_REGISTRY_TOKEN] = ""
322+
env[.SWIFTPM_REGISTRY_LOGIN] = ""
323+
env[.SWIFTPM_REGISTRY_PASSWORD] = ""
324+
env[.SWIFTPM_SOURCE_CONTROL_TOKEN] = ""
325325

326326
let registryProvider = EnvironmentAuthorizationProvider(environment: env, kind: .registry)
327327
XCTAssertNil(registryProvider.authentication(for: URL("https://registry.example.com")))
@@ -332,7 +332,7 @@ final class AuthorizationProviderTests: XCTestCase {
332332

333333
func testEnvironmentInComposite() {
334334
var env = Environment()
335-
env[.swiftpmRegistryToken] = "env-token"
335+
env[.SWIFTPM_REGISTRY_TOKEN] = "env-token"
336336

337337
let url = URL("https://registry.example.com")
338338
let envProvider = EnvironmentAuthorizationProvider(environment: env, kind: .registry)

0 commit comments

Comments
 (0)