Skip to content

Commit 739c587

Browse files
authored
use idiomatic location for security directory and update location of configuration directory (#3942)
motivation: use idiomatic location for security directory on macOS changes: * allow users to customize security directory location with new --security-path CLI option * on macOS use <user>/Library/org.swiftpm/security for security files and symlink from ~/.swiftpm/security * move configuration directory from <user>/Library/org.swiftpm to <user>/Library/org.swiftpm/configuration * add migration code from old configuraiton location to new one * add and adjust tests * update docker setup for new locationis
1 parent c2aac08 commit 739c587

File tree

10 files changed

+251
-105
lines changed

10 files changed

+251
-105
lines changed

Sources/Basics/FileSystem+Extensions.swift

Lines changed: 104 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@ extension FileSystem {
2121
public var dotSwiftPM: AbsolutePath {
2222
self.homeDirectory.appending(component: ".swiftpm")
2323
}
24-
25-
/// SwiftPM security directory
26-
public var swiftPMSecurityDirectory: AbsolutePath {
27-
self.dotSwiftPM.appending(component: "security")
24+
25+
fileprivate var idiomaticSwiftPMDirectory: AbsolutePath? {
26+
return FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first.flatMap { AbsolutePath($0.path) }?.appending(component: "org.swift.swiftpm")
2827
}
2928
}
3029

@@ -69,52 +68,128 @@ extension FileSystem {
6968
}
7069
}
7170

72-
// MARK: - config
71+
// MARK: - configuration
7372

7473
extension FileSystem {
75-
private var idiomaticUserConfigDirectory: AbsolutePath? {
76-
return FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first.flatMap { AbsolutePath($0.path) }
77-
}
78-
7974
/// SwiftPM config directory under user's config directory (if exists)
80-
public var swiftPMConfigDirectory: AbsolutePath {
81-
if let path = self.idiomaticUserConfigDirectory {
82-
return path.appending(component: "org.swift.swiftpm")
75+
public var swiftPMConfigurationDirectory: AbsolutePath {
76+
if let path = self.idiomaticSwiftPMDirectory {
77+
return path.appending(component: "configuration")
8378
} else {
84-
return self.dotSwiftPMConfigDirectory
79+
return self.dotSwiftPMConfigurationDirectory
8580
}
8681
}
8782

88-
fileprivate var dotSwiftPMConfigDirectory: AbsolutePath {
89-
return self.dotSwiftPM.appending(component: "config")
83+
fileprivate var dotSwiftPMConfigurationDirectory: AbsolutePath {
84+
return self.dotSwiftPM.appending(component: "configuration")
85+
}
86+
}
87+
88+
extension FileSystem {
89+
public func getOrCreateSwiftPMConfigurationDirectory(observabilityScope: ObservabilityScope?) throws -> AbsolutePath {
90+
let idiomaticConfigurationDirectory = self.swiftPMConfigurationDirectory
91+
92+
// temporary 5.6, remove on next version: transition from previous configuration location
93+
if !self.exists(idiomaticConfigurationDirectory) {
94+
try self.createDirectory(idiomaticConfigurationDirectory, recursive: true)
95+
}
96+
97+
// in the case where ~/.swiftpm/configuration is not the idiomatic location (eg on macOS where its /Users/<user>/Library/org.swift.swiftpm/configuration)
98+
if idiomaticConfigurationDirectory != self.dotSwiftPMConfigurationDirectory {
99+
// copy the configuration files from old location (eg /Users/<user>/Library/org.swift.swiftpm) to new one (eg /Users/<user>/Library/org.swift.swiftpm/configuration)
100+
// but leave them there for backwards compatibility (eg older xcode)
101+
let oldConfigDirectory = idiomaticConfigurationDirectory.parentDirectory
102+
if self.exists(oldConfigDirectory, followSymlink: false) && self.isDirectory(oldConfigDirectory) {
103+
let configurationFiles = try self.getDirectoryContents(oldConfigDirectory)
104+
.map{ oldConfigDirectory.appending(component: $0) }
105+
.filter{ self.isFile($0) && !self.isSymlink($0) && $0.extension != "lock"}
106+
for file in configurationFiles {
107+
let destination = idiomaticConfigurationDirectory.appending(component: file.basename)
108+
observabilityScope?.emit(warning: "Usage of \(file) has been deprecated. Please delete it and use the new \(destination) instead.")
109+
if !self.exists(destination) {
110+
try self.copy(from: file, to: destination)
111+
}
112+
}
113+
}
114+
// in the case where ~/.swiftpm/configuration is the idiomatic location (eg on Linux)
115+
} else {
116+
// copy the configuration files from old location (~/.swiftpm/config) to new one (~/.swiftpm/configuration)
117+
// but leave them there for backwards compatibility (eg older toolchain)
118+
let oldConfigDirectory = self.dotSwiftPM.appending(component: "config")
119+
if self.exists(oldConfigDirectory, followSymlink: false) && self.isDirectory(oldConfigDirectory) {
120+
let configurationFiles = try self.getDirectoryContents(oldConfigDirectory)
121+
.map{ oldConfigDirectory.appending(component: $0) }
122+
.filter{ self.isFile($0) && !self.isSymlink($0) && $0.extension != "lock"}
123+
for file in configurationFiles {
124+
let destination = idiomaticConfigurationDirectory.appending(component: file.basename)
125+
observabilityScope?.emit(warning: "Usage of \(file) has been deprecated. Please delete it and use the new \(destination) instead.")
126+
if !self.exists(destination) {
127+
try self.copy(from: file, to: destination)
128+
}
129+
}
130+
}
131+
}
132+
// ~temporary 5.6 migration
133+
134+
// Create idiomatic if necessary
135+
if !self.exists(idiomaticConfigurationDirectory) {
136+
try self.createDirectory(idiomaticConfigurationDirectory, recursive: true)
137+
}
138+
// Create ~/.swiftpm if necessary
139+
if !self.exists(self.dotSwiftPM) {
140+
try self.createDirectory(self.dotSwiftPM, recursive: true)
141+
}
142+
// Create ~/.swiftpm/configuration symlink if necessary
143+
if !self.exists(self.dotSwiftPMConfigurationDirectory, followSymlink: false) {
144+
try self.createSymbolicLink(dotSwiftPMConfigurationDirectory, pointingAt: idiomaticConfigurationDirectory, relative: false)
145+
}
146+
147+
return idiomaticConfigurationDirectory
90148
}
91149
}
92150

151+
// MARK: - security
152+
93153
extension FileSystem {
94-
public func getOrCreateSwiftPMConfigDirectory() throws -> AbsolutePath {
95-
let idiomaticConfigDirectory = self.swiftPMConfigDirectory
154+
/// SwiftPM security directory under user's security directory (if exists)
155+
public var swiftPMSecurityDirectory: AbsolutePath {
156+
if let path = self.idiomaticSwiftPMDirectory {
157+
return path.appending(component: "security")
158+
} else {
159+
return self.dotSwiftPMSecurityDirectory
160+
}
161+
}
96162

97-
// temporary 5.5, remove on next version: transition from ~/.swiftpm/config to idiomatic location + symbolic link
98-
if idiomaticConfigDirectory != self.dotSwiftPMConfigDirectory &&
99-
self.exists(self.dotSwiftPMConfigDirectory) && self.isDirectory(self.dotSwiftPMConfigDirectory) &&
100-
!self.exists(idiomaticConfigDirectory) {
101-
print("transitioning \(self.dotSwiftPMConfigDirectory) to \(idiomaticConfigDirectory)")
102-
try self.move(from: self.dotSwiftPMConfigDirectory, to: idiomaticConfigDirectory)
163+
fileprivate var dotSwiftPMSecurityDirectory: AbsolutePath {
164+
return self.dotSwiftPM.appending(component: "security")
165+
}
166+
}
167+
168+
extension FileSystem {
169+
public func getOrCreateSwiftPMSecurityDirectory() throws -> AbsolutePath {
170+
let idiomaticSecurityDirectory = self.swiftPMSecurityDirectory
171+
172+
// temporary 5.6, remove on next version: transition from ~/.swiftpm/security to idiomatic location + symbolic link
173+
if idiomaticSecurityDirectory != self.dotSwiftPMSecurityDirectory &&
174+
self.exists(self.dotSwiftPMSecurityDirectory) &&
175+
self.isDirectory(self.dotSwiftPMSecurityDirectory) {
176+
try self.removeFileTree(self.dotSwiftPMSecurityDirectory)
103177
}
178+
// ~temporary 5.6 migration
104179

105180
// Create idiomatic if necessary
106-
if !self.exists(idiomaticConfigDirectory) {
107-
try self.createDirectory(idiomaticConfigDirectory, recursive: true)
181+
if !self.exists(idiomaticSecurityDirectory) {
182+
try self.createDirectory(idiomaticSecurityDirectory, recursive: true)
108183
}
109184
// Create ~/.swiftpm if necessary
110185
if !self.exists(self.dotSwiftPM) {
111186
try self.createDirectory(self.dotSwiftPM, recursive: true)
112187
}
113-
// Create ~/.swiftpm/config symlink if necessary
114-
if !self.exists(self.dotSwiftPMConfigDirectory, followSymlink: false) {
115-
try self.createSymbolicLink(dotSwiftPMConfigDirectory, pointingAt: idiomaticConfigDirectory, relative: false)
188+
// Create ~/.swiftpm/security symlink if necessary
189+
if !self.exists(self.dotSwiftPMSecurityDirectory, followSymlink: false) {
190+
try self.createSymbolicLink(dotSwiftPMSecurityDirectory, pointingAt: idiomaticSecurityDirectory, relative: false)
116191
}
117-
return idiomaticConfigDirectory
192+
return idiomaticSecurityDirectory
118193
}
119194
}
120195

Sources/Commands/Options.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ public struct SwiftToolOptions: ParsableArguments {
149149
@Option(help: "Specify the shared configuration directory")
150150
var configPath: AbsolutePath?
151151

152+
@Option(help: "Specify the shared security directory")
153+
var securityPath: AbsolutePath?
154+
152155
/// Disables repository caching.
153156
@Flag(name: .customLong("repository-cache"), inversion: .prefixedEnableDisable, help: "Use a shared cache when fetching repositories")
154157
var useRepositoriesCache: Bool = true

Sources/Commands/SwiftTool.swift

Lines changed: 81 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,15 @@ public class SwiftTool {
294294
/// Path to the build directory.
295295
let buildPath: AbsolutePath
296296

297+
/// Path to the shared security directory
298+
let sharedSecurityDirectory: AbsolutePath?
299+
300+
/// Path to the shared cache directory
301+
let sharedCacheDirectory: AbsolutePath?
302+
303+
/// Path to the shared configuration directory
304+
let sharedConfigurationDirectory: AbsolutePath?
305+
297306
/// The process set to hold the launched processes. These will be terminated on any signal
298307
/// received by the swift tools.
299308
let processSet: ProcessSet
@@ -333,7 +342,7 @@ public class SwiftTool {
333342
self.observabilityScope.emit(error: "couldn't determine the current working directory")
334343
throw ExitCode.failure
335344
}
336-
originalWorkingDirectory = cwd
345+
self.originalWorkingDirectory = cwd
337346

338347
do {
339348
try Self.postprocessArgParserResult(options: options, observabilityScope: self.observabilityScope)
@@ -416,6 +425,11 @@ public class SwiftTool {
416425
customBuildPath ??
417426
(packageRoot ?? cwd).appending(component: ".build")
418427

428+
// make sure common directories are created
429+
self.sharedSecurityDirectory = try getSharedSecurityDirectory(options: self.options, observabilityScope: self.observabilityScope)
430+
self.sharedConfigurationDirectory = try getSharedConfigurationDirectory(options: self.options, observabilityScope: self.observabilityScope)
431+
self.sharedCacheDirectory = try getSharedCacheDirectory(options: self.options, observabilityScope: self.observabilityScope)
432+
419433
// set verbosity globals.
420434
// TODO: get rid of this global settings in TSC
421435
switch self.logLevel {
@@ -492,7 +506,7 @@ public class SwiftTool {
492506
}
493507

494508
func getMirrorsConfig(sharedConfigurationDirectory: AbsolutePath? = nil) throws -> Workspace.Configuration.Mirrors {
495-
let sharedConfigurationDirectory = try sharedConfigurationDirectory ?? self.getSharedConfigurationDirectory()
509+
let sharedConfigurationDirectory = sharedConfigurationDirectory ?? self.sharedConfigurationDirectory
496510
let sharedMirrorFile = sharedConfigurationDirectory.map { Workspace.DefaultLocations.mirrorsConfigurationFile(at: $0) }
497511
return try .init(
498512
localMirrorFile: self.mirrorsConfigFile(),
@@ -537,7 +551,7 @@ public class SwiftTool {
537551
func getRegistriesConfig(sharedConfigurationDirectory: AbsolutePath? = nil) throws -> Workspace.Configuration.Registries {
538552
let localRegistriesFile = try Workspace.DefaultLocations.registriesConfigurationFile(forRootPackage: self.getPackageRoot())
539553

540-
let sharedConfigurationDirectory = try sharedConfigurationDirectory ?? self.getSharedConfigurationDirectory()
554+
let sharedConfigurationDirectory = sharedConfigurationDirectory ?? self.sharedConfigurationDirectory
541555
let sharedRegistriesFile = sharedConfigurationDirectory.map {
542556
Workspace.DefaultLocations.registriesConfigurationFile(at: $0)
543557
}
@@ -607,56 +621,6 @@ public class SwiftTool {
607621
return providers
608622
}
609623

610-
private func getSharedCacheDirectory() throws -> AbsolutePath? {
611-
if let explicitCachePath = options.cachePath {
612-
// Create the explicit cache path if necessary
613-
if !localFileSystem.exists(explicitCachePath) {
614-
try localFileSystem.createDirectory(explicitCachePath, recursive: true)
615-
}
616-
return explicitCachePath
617-
}
618-
619-
do {
620-
return try localFileSystem.getOrCreateSwiftPMCacheDirectory()
621-
} catch {
622-
self.observabilityScope.emit(warning: "Failed creating default cache location, \(error)")
623-
return .none
624-
}
625-
}
626-
627-
private func getSharedConfigurationDirectory() throws -> AbsolutePath? {
628-
if let explicitConfigPath = options.configPath {
629-
// Create the explicit config path if necessary
630-
if !localFileSystem.exists(explicitConfigPath) {
631-
try localFileSystem.createDirectory(explicitConfigPath, recursive: true)
632-
}
633-
return explicitConfigPath
634-
}
635-
636-
do {
637-
return try localFileSystem.getOrCreateSwiftPMConfigDirectory()
638-
} catch {
639-
self.observabilityScope.emit(warning: "Failed creating default configuration location, \(error)")
640-
return .none
641-
}
642-
}
643-
644-
private func getSharedSecurityDirectory() throws -> AbsolutePath? {
645-
do {
646-
let fileSystem = localFileSystem
647-
let sharedSecurityDirectory = fileSystem.swiftPMSecurityDirectory
648-
if !fileSystem.exists(sharedSecurityDirectory) {
649-
try fileSystem.createDirectory(sharedSecurityDirectory, recursive: true)
650-
}
651-
// And make sure we can write files (locking the directory writes a lock file)
652-
try fileSystem.withLock(on: sharedSecurityDirectory, type: .exclusive) { }
653-
return sharedSecurityDirectory
654-
} catch {
655-
self.observabilityScope.emit(warning: "Failed creating shared security directory: \(error)")
656-
return .none
657-
}
658-
}
659-
660624
/// Returns the currently active workspace.
661625
func getActiveWorkspace() throws -> Workspace {
662626
if let workspace = _workspace {
@@ -665,28 +629,25 @@ public class SwiftTool {
665629

666630
let delegate = ToolWorkspaceDelegate(self.outputStream, logLevel: self.logLevel, observabilityScope: self.observabilityScope)
667631
let provider = GitRepositoryProvider(processSet: processSet)
668-
let sharedSecurityDirectory = try self.getSharedSecurityDirectory()
669-
let sharedCacheDirectory = try self.getSharedCacheDirectory()
670-
let sharedConfigurationDirectory = try self.getSharedConfigurationDirectory()
671632
let isXcodeBuildSystemEnabled = self.options.buildSystem == .xcode
672633
let workspace = try Workspace(
673634
fileSystem: localFileSystem,
674635
location: .init(
675-
workingDirectory: buildPath,
636+
workingDirectory: self.buildPath,
676637
editsDirectory: self.editsDirectory(),
677638
resolvedVersionsFile: self.resolvedVersionsFile(),
678-
sharedSecurityDirectory: sharedSecurityDirectory,
679-
sharedCacheDirectory: sharedCacheDirectory,
680-
sharedConfigurationDirectory: sharedConfigurationDirectory
639+
sharedSecurityDirectory: self.sharedSecurityDirectory,
640+
sharedCacheDirectory: self.sharedCacheDirectory,
641+
sharedConfigurationDirectory: self.sharedConfigurationDirectory
681642
),
682-
mirrors: self.getMirrorsConfig(sharedConfigurationDirectory: sharedConfigurationDirectory).mirrors,
683-
registries: try self.getRegistriesConfig(sharedConfigurationDirectory: sharedConfigurationDirectory).configuration,
643+
mirrors: self.getMirrorsConfig(sharedConfigurationDirectory: self.sharedConfigurationDirectory).mirrors,
644+
registries: try self.getRegistriesConfig(sharedConfigurationDirectory: self.sharedConfigurationDirectory).configuration,
684645
authorizationProvider: self.getAuthorizationProvider(),
685646
customManifestLoader: self.getManifestLoader(), // FIXME: doe we really need to customize it?
686647
customRepositoryProvider: provider, // FIXME: doe we really need to customize it?
687648
additionalFileRules: isXcodeBuildSystemEnabled ? FileRuleDescription.xcbuildFileTypes : FileRuleDescription.swiftpmFileTypes,
688-
resolverUpdateEnabled: !options.skipDependencyUpdate,
689-
resolverPrefetchingEnabled: options.shouldEnableResolverPrefetching,
649+
resolverUpdateEnabled: !self.options.skipDependencyUpdate,
650+
resolverPrefetchingEnabled: self.options.shouldEnableResolverPrefetching,
690651
resolverFingerprintCheckingMode: self.options.resolverFingerprintCheckingMode,
691652
sharedRepositoriesCacheEnabled: self.options.useRepositoriesCache,
692653
delegate: delegate
@@ -1011,7 +972,7 @@ public class SwiftTool {
1011972
case (false, .local):
1012973
cachePath = self.buildPath
1013974
case (false, .shared):
1014-
cachePath = try self.getSharedCacheDirectory().map{ Workspace.DefaultLocations.manifestsDirectory(at: $0) }
975+
cachePath = self.sharedCacheDirectory.map{ Workspace.DefaultLocations.manifestsDirectory(at: $0) }
1015976
}
1016977

1017978
var extraManifestFlags = self.options.manifestFlags
@@ -1062,6 +1023,61 @@ private func getEnvBuildPath(workingDir: AbsolutePath) -> AbsolutePath? {
10621023
return AbsolutePath(env, relativeTo: workingDir)
10631024
}
10641025

1026+
1027+
private func getSharedSecurityDirectory(options: SwiftToolOptions, observabilityScope: ObservabilityScope) throws -> AbsolutePath? {
1028+
if let explicitSecurityPath = options.securityPath {
1029+
// Create the explicit security path if necessary
1030+
if !localFileSystem.exists(explicitSecurityPath) {
1031+
try localFileSystem.createDirectory(explicitSecurityPath, recursive: true)
1032+
}
1033+
return explicitSecurityPath
1034+
}
1035+
1036+
do {
1037+
let sharedSecurityDirectory = try localFileSystem.getOrCreateSwiftPMSecurityDirectory()
1038+
// And make sure we can write files (locking the directory writes a lock file)
1039+
try localFileSystem.withLock(on: sharedSecurityDirectory, type: .exclusive) { }
1040+
return sharedSecurityDirectory
1041+
} catch {
1042+
observabilityScope.emit(warning: "Failed creating default security location, \(error)")
1043+
return .none
1044+
}
1045+
}
1046+
1047+
private func getSharedConfigurationDirectory(options: SwiftToolOptions, observabilityScope: ObservabilityScope) throws -> AbsolutePath? {
1048+
if let explicitConfigPath = options.configPath {
1049+
// Create the explicit config path if necessary
1050+
if !localFileSystem.exists(explicitConfigPath) {
1051+
try localFileSystem.createDirectory(explicitConfigPath, recursive: true)
1052+
}
1053+
return explicitConfigPath
1054+
}
1055+
1056+
do {
1057+
return try localFileSystem.getOrCreateSwiftPMConfigurationDirectory(observabilityScope: observabilityScope)
1058+
} catch {
1059+
observabilityScope.emit(warning: "Failed creating default configuration location, \(error)")
1060+
return .none
1061+
}
1062+
}
1063+
1064+
private func getSharedCacheDirectory(options: SwiftToolOptions, observabilityScope: ObservabilityScope) throws -> AbsolutePath? {
1065+
if let explicitCachePath = options.cachePath {
1066+
// Create the explicit cache path if necessary
1067+
if !localFileSystem.exists(explicitCachePath) {
1068+
try localFileSystem.createDirectory(explicitCachePath, recursive: true)
1069+
}
1070+
return explicitCachePath
1071+
}
1072+
1073+
do {
1074+
return try localFileSystem.getOrCreateSwiftPMCacheDirectory()
1075+
} catch {
1076+
observabilityScope.emit(warning: "Failed creating default cache location, \(error)")
1077+
return .none
1078+
}
1079+
}
1080+
10651081
/// A wrapper to hold the build system so we can use it inside
10661082
/// the int. handler without requiring to initialize it.
10671083
final class BuildSystemRef {

0 commit comments

Comments
 (0)