diff --git a/Sources/Basics/FileSystem+Extensions.swift b/Sources/Basics/FileSystem+Extensions.swift index 53f3612b1bd..f421c09f78c 100644 --- a/Sources/Basics/FileSystem+Extensions.swift +++ b/Sources/Basics/FileSystem+Extensions.swift @@ -21,10 +21,9 @@ extension FileSystem { public var dotSwiftPM: AbsolutePath { self.homeDirectory.appending(component: ".swiftpm") } - - /// SwiftPM security directory - public var swiftPMSecurityDirectory: AbsolutePath { - self.dotSwiftPM.appending(component: "security") + + fileprivate var idiomaticSwiftPMDirectory: AbsolutePath? { + return FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first.flatMap { AbsolutePath($0.path) }?.appending(component: "org.swift.swiftpm") } } @@ -69,52 +68,128 @@ extension FileSystem { } } -// MARK: - config +// MARK: - configuration extension FileSystem { - private var idiomaticUserConfigDirectory: AbsolutePath? { - return FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first.flatMap { AbsolutePath($0.path) } - } - /// SwiftPM config directory under user's config directory (if exists) - public var swiftPMConfigDirectory: AbsolutePath { - if let path = self.idiomaticUserConfigDirectory { - return path.appending(component: "org.swift.swiftpm") + public var swiftPMConfigurationDirectory: AbsolutePath { + if let path = self.idiomaticSwiftPMDirectory { + return path.appending(component: "configuration") } else { - return self.dotSwiftPMConfigDirectory + return self.dotSwiftPMConfigurationDirectory } } - fileprivate var dotSwiftPMConfigDirectory: AbsolutePath { - return self.dotSwiftPM.appending(component: "config") + fileprivate var dotSwiftPMConfigurationDirectory: AbsolutePath { + return self.dotSwiftPM.appending(component: "configuration") + } +} + +extension FileSystem { + public func getOrCreateSwiftPMConfigurationDirectory(observabilityScope: ObservabilityScope?) throws -> AbsolutePath { + let idiomaticConfigurationDirectory = self.swiftPMConfigurationDirectory + + // temporary 5.6, remove on next version: transition from previous configuration location + if !self.exists(idiomaticConfigurationDirectory) { + try self.createDirectory(idiomaticConfigurationDirectory, recursive: true) + } + + // in the case where ~/.swiftpm/configuration is not the idiomatic location (eg on macOS where its /Users//Library/org.swift.swiftpm/configuration) + if idiomaticConfigurationDirectory != self.dotSwiftPMConfigurationDirectory { + // copy the configuration files from old location (eg /Users//Library/org.swift.swiftpm) to new one (eg /Users//Library/org.swift.swiftpm/configuration) + // but leave them there for backwards compatibility (eg older xcode) + let oldConfigDirectory = idiomaticConfigurationDirectory.parentDirectory + if self.exists(oldConfigDirectory, followSymlink: false) && self.isDirectory(oldConfigDirectory) { + let configurationFiles = try self.getDirectoryContents(oldConfigDirectory) + .map{ oldConfigDirectory.appending(component: $0) } + .filter{ self.isFile($0) && !self.isSymlink($0) && $0.extension != "lock"} + for file in configurationFiles { + let destination = idiomaticConfigurationDirectory.appending(component: file.basename) + observabilityScope?.emit(warning: "Usage of \(file) has been deprecated. Please delete it and use the new \(destination) instead.") + if !self.exists(destination) { + try self.copy(from: file, to: destination) + } + } + } + // in the case where ~/.swiftpm/configuration is the idiomatic location (eg on Linux) + } else { + // copy the configuration files from old location (~/.swiftpm/config) to new one (~/.swiftpm/configuration) + // but leave them there for backwards compatibility (eg older toolchain) + let oldConfigDirectory = self.dotSwiftPM.appending(component: "config") + if self.exists(oldConfigDirectory, followSymlink: false) && self.isDirectory(oldConfigDirectory) { + let configurationFiles = try self.getDirectoryContents(oldConfigDirectory) + .map{ oldConfigDirectory.appending(component: $0) } + .filter{ self.isFile($0) && !self.isSymlink($0) && $0.extension != "lock"} + for file in configurationFiles { + let destination = idiomaticConfigurationDirectory.appending(component: file.basename) + observabilityScope?.emit(warning: "Usage of \(file) has been deprecated. Please delete it and use the new \(destination) instead.") + if !self.exists(destination) { + try self.copy(from: file, to: destination) + } + } + } + } + // ~temporary 5.6 migration + + // Create idiomatic if necessary + if !self.exists(idiomaticConfigurationDirectory) { + try self.createDirectory(idiomaticConfigurationDirectory, recursive: true) + } + // Create ~/.swiftpm if necessary + if !self.exists(self.dotSwiftPM) { + try self.createDirectory(self.dotSwiftPM, recursive: true) + } + // Create ~/.swiftpm/configuration symlink if necessary + if !self.exists(self.dotSwiftPMConfigurationDirectory, followSymlink: false) { + try self.createSymbolicLink(dotSwiftPMConfigurationDirectory, pointingAt: idiomaticConfigurationDirectory, relative: false) + } + + return idiomaticConfigurationDirectory } } +// MARK: - security + extension FileSystem { - public func getOrCreateSwiftPMConfigDirectory() throws -> AbsolutePath { - let idiomaticConfigDirectory = self.swiftPMConfigDirectory + /// SwiftPM security directory under user's security directory (if exists) + public var swiftPMSecurityDirectory: AbsolutePath { + if let path = self.idiomaticSwiftPMDirectory { + return path.appending(component: "security") + } else { + return self.dotSwiftPMSecurityDirectory + } + } - // temporary 5.5, remove on next version: transition from ~/.swiftpm/config to idiomatic location + symbolic link - if idiomaticConfigDirectory != self.dotSwiftPMConfigDirectory && - self.exists(self.dotSwiftPMConfigDirectory) && self.isDirectory(self.dotSwiftPMConfigDirectory) && - !self.exists(idiomaticConfigDirectory) { - print("transitioning \(self.dotSwiftPMConfigDirectory) to \(idiomaticConfigDirectory)") - try self.move(from: self.dotSwiftPMConfigDirectory, to: idiomaticConfigDirectory) + fileprivate var dotSwiftPMSecurityDirectory: AbsolutePath { + return self.dotSwiftPM.appending(component: "security") + } +} + +extension FileSystem { + public func getOrCreateSwiftPMSecurityDirectory() throws -> AbsolutePath { + let idiomaticSecurityDirectory = self.swiftPMSecurityDirectory + + // temporary 5.6, remove on next version: transition from ~/.swiftpm/security to idiomatic location + symbolic link + if idiomaticSecurityDirectory != self.dotSwiftPMSecurityDirectory && + self.exists(self.dotSwiftPMSecurityDirectory) && + self.isDirectory(self.dotSwiftPMSecurityDirectory) { + try self.removeFileTree(self.dotSwiftPMSecurityDirectory) } + // ~temporary 5.6 migration // Create idiomatic if necessary - if !self.exists(idiomaticConfigDirectory) { - try self.createDirectory(idiomaticConfigDirectory, recursive: true) + if !self.exists(idiomaticSecurityDirectory) { + try self.createDirectory(idiomaticSecurityDirectory, recursive: true) } // Create ~/.swiftpm if necessary if !self.exists(self.dotSwiftPM) { try self.createDirectory(self.dotSwiftPM, recursive: true) } - // Create ~/.swiftpm/config symlink if necessary - if !self.exists(self.dotSwiftPMConfigDirectory, followSymlink: false) { - try self.createSymbolicLink(dotSwiftPMConfigDirectory, pointingAt: idiomaticConfigDirectory, relative: false) + // Create ~/.swiftpm/security symlink if necessary + if !self.exists(self.dotSwiftPMSecurityDirectory, followSymlink: false) { + try self.createSymbolicLink(dotSwiftPMSecurityDirectory, pointingAt: idiomaticSecurityDirectory, relative: false) } - return idiomaticConfigDirectory + return idiomaticSecurityDirectory } } diff --git a/Sources/Commands/Options.swift b/Sources/Commands/Options.swift index b98ee381bf0..efa61a09508 100644 --- a/Sources/Commands/Options.swift +++ b/Sources/Commands/Options.swift @@ -149,6 +149,9 @@ public struct SwiftToolOptions: ParsableArguments { @Option(help: "Specify the shared configuration directory") var configPath: AbsolutePath? + @Option(help: "Specify the shared security directory") + var securityPath: AbsolutePath? + /// Disables repository caching. @Flag(name: .customLong("repository-cache"), inversion: .prefixedEnableDisable, help: "Use a shared cache when fetching repositories") var useRepositoriesCache: Bool = true diff --git a/Sources/Commands/SwiftTool.swift b/Sources/Commands/SwiftTool.swift index fb69433c436..20abd5400dc 100644 --- a/Sources/Commands/SwiftTool.swift +++ b/Sources/Commands/SwiftTool.swift @@ -294,6 +294,15 @@ public class SwiftTool { /// Path to the build directory. let buildPath: AbsolutePath + /// Path to the shared security directory + let sharedSecurityDirectory: AbsolutePath? + + /// Path to the shared cache directory + let sharedCacheDirectory: AbsolutePath? + + /// Path to the shared configuration directory + let sharedConfigurationDirectory: AbsolutePath? + /// The process set to hold the launched processes. These will be terminated on any signal /// received by the swift tools. let processSet: ProcessSet @@ -333,7 +342,7 @@ public class SwiftTool { self.observabilityScope.emit(error: "couldn't determine the current working directory") throw ExitCode.failure } - originalWorkingDirectory = cwd + self.originalWorkingDirectory = cwd do { try Self.postprocessArgParserResult(options: options, observabilityScope: self.observabilityScope) @@ -416,6 +425,11 @@ public class SwiftTool { customBuildPath ?? (packageRoot ?? cwd).appending(component: ".build") + // make sure common directories are created + self.sharedSecurityDirectory = try getSharedSecurityDirectory(options: self.options, observabilityScope: self.observabilityScope) + self.sharedConfigurationDirectory = try getSharedConfigurationDirectory(options: self.options, observabilityScope: self.observabilityScope) + self.sharedCacheDirectory = try getSharedCacheDirectory(options: self.options, observabilityScope: self.observabilityScope) + // set verbosity globals. // TODO: get rid of this global settings in TSC switch self.logLevel { @@ -492,7 +506,7 @@ public class SwiftTool { } func getMirrorsConfig(sharedConfigurationDirectory: AbsolutePath? = nil) throws -> Workspace.Configuration.Mirrors { - let sharedConfigurationDirectory = try sharedConfigurationDirectory ?? self.getSharedConfigurationDirectory() + let sharedConfigurationDirectory = sharedConfigurationDirectory ?? self.sharedConfigurationDirectory let sharedMirrorFile = sharedConfigurationDirectory.map { Workspace.DefaultLocations.mirrorsConfigurationFile(at: $0) } return try .init( localMirrorFile: self.mirrorsConfigFile(), @@ -537,7 +551,7 @@ public class SwiftTool { func getRegistriesConfig(sharedConfigurationDirectory: AbsolutePath? = nil) throws -> Workspace.Configuration.Registries { let localRegistriesFile = try Workspace.DefaultLocations.registriesConfigurationFile(forRootPackage: self.getPackageRoot()) - let sharedConfigurationDirectory = try sharedConfigurationDirectory ?? self.getSharedConfigurationDirectory() + let sharedConfigurationDirectory = sharedConfigurationDirectory ?? self.sharedConfigurationDirectory let sharedRegistriesFile = sharedConfigurationDirectory.map { Workspace.DefaultLocations.registriesConfigurationFile(at: $0) } @@ -607,56 +621,6 @@ public class SwiftTool { return providers } - private func getSharedCacheDirectory() throws -> AbsolutePath? { - if let explicitCachePath = options.cachePath { - // Create the explicit cache path if necessary - if !localFileSystem.exists(explicitCachePath) { - try localFileSystem.createDirectory(explicitCachePath, recursive: true) - } - return explicitCachePath - } - - do { - return try localFileSystem.getOrCreateSwiftPMCacheDirectory() - } catch { - self.observabilityScope.emit(warning: "Failed creating default cache location, \(error)") - return .none - } - } - - private func getSharedConfigurationDirectory() throws -> AbsolutePath? { - if let explicitConfigPath = options.configPath { - // Create the explicit config path if necessary - if !localFileSystem.exists(explicitConfigPath) { - try localFileSystem.createDirectory(explicitConfigPath, recursive: true) - } - return explicitConfigPath - } - - do { - return try localFileSystem.getOrCreateSwiftPMConfigDirectory() - } catch { - self.observabilityScope.emit(warning: "Failed creating default configuration location, \(error)") - return .none - } - } - - private func getSharedSecurityDirectory() throws -> AbsolutePath? { - do { - let fileSystem = localFileSystem - let sharedSecurityDirectory = fileSystem.swiftPMSecurityDirectory - if !fileSystem.exists(sharedSecurityDirectory) { - try fileSystem.createDirectory(sharedSecurityDirectory, recursive: true) - } - // And make sure we can write files (locking the directory writes a lock file) - try fileSystem.withLock(on: sharedSecurityDirectory, type: .exclusive) { } - return sharedSecurityDirectory - } catch { - self.observabilityScope.emit(warning: "Failed creating shared security directory: \(error)") - return .none - } - } - /// Returns the currently active workspace. func getActiveWorkspace() throws -> Workspace { if let workspace = _workspace { @@ -665,28 +629,25 @@ public class SwiftTool { let delegate = ToolWorkspaceDelegate(self.outputStream, logLevel: self.logLevel, observabilityScope: self.observabilityScope) let provider = GitRepositoryProvider(processSet: processSet) - let sharedSecurityDirectory = try self.getSharedSecurityDirectory() - let sharedCacheDirectory = try self.getSharedCacheDirectory() - let sharedConfigurationDirectory = try self.getSharedConfigurationDirectory() let isXcodeBuildSystemEnabled = self.options.buildSystem == .xcode let workspace = try Workspace( fileSystem: localFileSystem, location: .init( - workingDirectory: buildPath, + workingDirectory: self.buildPath, editsDirectory: self.editsDirectory(), resolvedVersionsFile: self.resolvedVersionsFile(), - sharedSecurityDirectory: sharedSecurityDirectory, - sharedCacheDirectory: sharedCacheDirectory, - sharedConfigurationDirectory: sharedConfigurationDirectory + sharedSecurityDirectory: self.sharedSecurityDirectory, + sharedCacheDirectory: self.sharedCacheDirectory, + sharedConfigurationDirectory: self.sharedConfigurationDirectory ), - mirrors: self.getMirrorsConfig(sharedConfigurationDirectory: sharedConfigurationDirectory).mirrors, - registries: try self.getRegistriesConfig(sharedConfigurationDirectory: sharedConfigurationDirectory).configuration, + mirrors: self.getMirrorsConfig(sharedConfigurationDirectory: self.sharedConfigurationDirectory).mirrors, + registries: try self.getRegistriesConfig(sharedConfigurationDirectory: self.sharedConfigurationDirectory).configuration, authorizationProvider: self.getAuthorizationProvider(), customManifestLoader: self.getManifestLoader(), // FIXME: doe we really need to customize it? customRepositoryProvider: provider, // FIXME: doe we really need to customize it? additionalFileRules: isXcodeBuildSystemEnabled ? FileRuleDescription.xcbuildFileTypes : FileRuleDescription.swiftpmFileTypes, - resolverUpdateEnabled: !options.skipDependencyUpdate, - resolverPrefetchingEnabled: options.shouldEnableResolverPrefetching, + resolverUpdateEnabled: !self.options.skipDependencyUpdate, + resolverPrefetchingEnabled: self.options.shouldEnableResolverPrefetching, resolverFingerprintCheckingMode: self.options.resolverFingerprintCheckingMode, sharedRepositoriesCacheEnabled: self.options.useRepositoriesCache, delegate: delegate @@ -1011,7 +972,7 @@ public class SwiftTool { case (false, .local): cachePath = self.buildPath case (false, .shared): - cachePath = try self.getSharedCacheDirectory().map{ Workspace.DefaultLocations.manifestsDirectory(at: $0) } + cachePath = self.sharedCacheDirectory.map{ Workspace.DefaultLocations.manifestsDirectory(at: $0) } } var extraManifestFlags = self.options.manifestFlags @@ -1062,6 +1023,61 @@ private func getEnvBuildPath(workingDir: AbsolutePath) -> AbsolutePath? { return AbsolutePath(env, relativeTo: workingDir) } + +private func getSharedSecurityDirectory(options: SwiftToolOptions, observabilityScope: ObservabilityScope) throws -> AbsolutePath? { + if let explicitSecurityPath = options.securityPath { + // Create the explicit security path if necessary + if !localFileSystem.exists(explicitSecurityPath) { + try localFileSystem.createDirectory(explicitSecurityPath, recursive: true) + } + return explicitSecurityPath + } + + do { + let sharedSecurityDirectory = try localFileSystem.getOrCreateSwiftPMSecurityDirectory() + // And make sure we can write files (locking the directory writes a lock file) + try localFileSystem.withLock(on: sharedSecurityDirectory, type: .exclusive) { } + return sharedSecurityDirectory + } catch { + observabilityScope.emit(warning: "Failed creating default security location, \(error)") + return .none + } +} + +private func getSharedConfigurationDirectory(options: SwiftToolOptions, observabilityScope: ObservabilityScope) throws -> AbsolutePath? { + if let explicitConfigPath = options.configPath { + // Create the explicit config path if necessary + if !localFileSystem.exists(explicitConfigPath) { + try localFileSystem.createDirectory(explicitConfigPath, recursive: true) + } + return explicitConfigPath + } + + do { + return try localFileSystem.getOrCreateSwiftPMConfigurationDirectory(observabilityScope: observabilityScope) + } catch { + observabilityScope.emit(warning: "Failed creating default configuration location, \(error)") + return .none + } +} + +private func getSharedCacheDirectory(options: SwiftToolOptions, observabilityScope: ObservabilityScope) throws -> AbsolutePath? { + if let explicitCachePath = options.cachePath { + // Create the explicit cache path if necessary + if !localFileSystem.exists(explicitCachePath) { + try localFileSystem.createDirectory(explicitCachePath, recursive: true) + } + return explicitCachePath + } + + do { + return try localFileSystem.getOrCreateSwiftPMCacheDirectory() + } catch { + observabilityScope.emit(warning: "Failed creating default cache location, \(error)") + return .none + } +} + /// A wrapper to hold the build system so we can use it inside /// the int. handler without requiring to initialize it. final class BuildSystemRef { diff --git a/Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift b/Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift index 24c3bf9037f..f181f5753c2 100644 --- a/Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift +++ b/Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift @@ -55,7 +55,7 @@ struct JSONPackageCollectionProvider: PackageCollectionProvider { self.decoder = JSONDecoder.makeWithDefaults() self.validator = JSONModel.Validator(configuration: configuration.validator) self.signatureValidator = signatureValidator ?? PackageCollectionSigning( - trustedRootCertsDir: configuration.trustedRootCertsDir ?? fileSystem.swiftPMConfigDirectory.appending(component: "trust-root-certs").asURL, + trustedRootCertsDir: configuration.trustedRootCertsDir ?? fileSystem.swiftPMConfigurationDirectory.appending(component: "trust-root-certs").asURL, additionalTrustedRootCerts: sourceCertPolicy.allRootCerts.map { Array($0) }, observabilityScope: observabilityScope, callbackQueue: .sharedConcurrent diff --git a/Sources/PackageCollections/Storage/FilePackageCollectionsSourcesStorage.swift b/Sources/PackageCollections/Storage/FilePackageCollectionsSourcesStorage.swift index bfcb4af420e..e3122fd4813 100644 --- a/Sources/PackageCollections/Storage/FilePackageCollectionsSourcesStorage.swift +++ b/Sources/PackageCollections/Storage/FilePackageCollectionsSourcesStorage.swift @@ -26,7 +26,7 @@ struct FilePackageCollectionsSourcesStorage: PackageCollectionsSourcesStorage { init(fileSystem: FileSystem = localFileSystem, path: AbsolutePath? = nil) { self.fileSystem = fileSystem - self.path = path ?? fileSystem.swiftPMConfigDirectory.appending(component: "collections.json") + self.path = path ?? fileSystem.swiftPMConfigurationDirectory.appending(component: "collections.json") self.encoder = JSONEncoder.makeWithDefaults() self.decoder = JSONDecoder.makeWithDefaults() } diff --git a/Sources/SPMTestSupport/MockWorkspace.swift b/Sources/SPMTestSupport/MockWorkspace.swift index afd29ba67d0..c020affeb83 100644 --- a/Sources/SPMTestSupport/MockWorkspace.swift +++ b/Sources/SPMTestSupport/MockWorkspace.swift @@ -241,7 +241,7 @@ public final class MockWorkspace { resolvedVersionsFile: self.sandbox.appending(component: "Package.resolved"), sharedSecurityDirectory: self.fileSystem.swiftPMSecurityDirectory, sharedCacheDirectory: self.fileSystem.swiftPMCacheDirectory, - sharedConfigurationDirectory: self.fileSystem.swiftPMConfigDirectory + sharedConfigurationDirectory: self.fileSystem.swiftPMConfigurationDirectory ), mirrors: self.mirrors, customToolsVersion: self.toolsVersion, diff --git a/Sources/Workspace/WorkspaceConfiguration.swift b/Sources/Workspace/WorkspaceConfiguration.swift index a4bc3b9cce4..e717268be9e 100644 --- a/Sources/Workspace/WorkspaceConfiguration.swift +++ b/Sources/Workspace/WorkspaceConfiguration.swift @@ -126,7 +126,7 @@ extension Workspace { resolvedVersionsFile: DefaultLocations.resolvedVersionsFile(forRootPackage: rootPath), sharedSecurityDirectory: fileSystem.swiftPMSecurityDirectory, sharedCacheDirectory: fileSystem.swiftPMCacheDirectory, - sharedConfigurationDirectory: fileSystem.swiftPMConfigDirectory + sharedConfigurationDirectory: fileSystem.swiftPMConfigurationDirectory ) } } diff --git a/Tests/FunctionalTests/MiscellaneousTests.swift b/Tests/FunctionalTests/MiscellaneousTests.swift index e8ef4305b34..644fb993d9c 100644 --- a/Tests/FunctionalTests/MiscellaneousTests.swift +++ b/Tests/FunctionalTests/MiscellaneousTests.swift @@ -415,7 +415,7 @@ class MiscellaneousTestCase: XCTestCase { XCTAssert(output.contains("does not exist"), "Error from git was not propagated to process output: \(output)") } } - + func testLocalPackageUsedAsURL() throws { fixture(name: "Miscellaneous/LocalPackageAsURL", createGitRepo: false) { prefix in // This fixture has a setup that is trying to use a local package @@ -431,7 +431,7 @@ class MiscellaneousTestCase: XCTestCase { XCTAssert(output.contains("Cannot clone from local directory"), "Didn't find expected output: \(output)") } } - + func testUnicode() { #if !os(Linux) && !os(Android) // TODO: - Linux has trouble with this and needs investigation. fixture(name: "Miscellaneous/Unicode") { prefix in @@ -505,7 +505,7 @@ class MiscellaneousTestCase: XCTestCase { XCTAssertMatch(stderr, .contains("warning: '--generate-linuxmain' option is deprecated")) } } - + func testGenerateLinuxMain() { #if os(macOS) fixture(name: "Miscellaneous/TestDiscovery/Simple") { path in @@ -552,13 +552,13 @@ class MiscellaneousTestCase: XCTestCase { } #endif } - + func testTestsCanLinkAgainstExecutable() throws { // Check if the host compiler supports the '-entry-point-function-name' flag. #if swift(<5.5) try XCTSkipIf(true, "skipping because host compiler doesn't support '-entry-point-function-name'") #endif - + fixture(name: "Miscellaneous/TestableExe") { prefix in do { let (stdout, _) = try executeSwiftTest(prefix) @@ -648,6 +648,20 @@ class MiscellaneousTestCase: XCTestCase { try SwiftPMProduct.SwiftBuild.execute(["--cache-path", customCachePath.pathString], packagePath: path) XCTAssertDirectoryExists(customCachePath) } + + // `FileSystem` does not support `chmod` on Linux + #if os(macOS) + fixture(name: "Miscellaneous/Simple") { path in + try localFileSystem.chmod(.userUnWritable, path: path) + let customCachePath = path.appending(components: "custom", "cache") + XCTAssertNoSuchPath(customCachePath) + let result = try SwiftPMProduct.SwiftBuild.executeProcess(["--cache-path", customCachePath.pathString], packagePath: path) + XCTAssert(result.exitStatus != .terminated(code: 0)) + let output = try result.utf8stderrOutput() + XCTAssert(output.contains("error: You don’t have permission"), "expected permissions error") + XCTAssertNoSuchPath(customCachePath) + } + #endif } func testCustomConfigPath() { @@ -657,5 +671,42 @@ class MiscellaneousTestCase: XCTestCase { try SwiftPMProduct.SwiftBuild.execute(["--config-path", customConfigPath.pathString], packagePath: path) XCTAssertDirectoryExists(customConfigPath) } + + // `FileSystem` does not support `chmod` on Linux + #if os(macOS) + fixture(name: "Miscellaneous/Simple") { path in + try localFileSystem.chmod(.userUnWritable, path: path) + let customConfigPath = path.appending(components: "custom", "config") + XCTAssertNoSuchPath(customConfigPath) + let result = try SwiftPMProduct.SwiftBuild.executeProcess(["--config-path", customConfigPath.pathString], packagePath: path) + XCTAssert(result.exitStatus != .terminated(code: 0)) + let output = try result.utf8stderrOutput() + XCTAssert(output.contains("error: You don’t have permission"), "expected permissions error") + XCTAssertNoSuchPath(customConfigPath) + } + #endif + } + + func testCustomSecurityPath() { + fixture(name: "Miscellaneous/Simple") { path in + let customSecurityPath = path.appending(components: "custom", "security") + XCTAssertNoSuchPath(customSecurityPath) + try SwiftPMProduct.SwiftBuild.execute(["--security-path", customSecurityPath.pathString], packagePath: path) + XCTAssertDirectoryExists(customSecurityPath) + } + + // `FileSystem` does not support `chmod` on Linux + #if os(macOS) + fixture(name: "Miscellaneous/Simple") { path in + try localFileSystem.chmod(.userUnWritable, path: path) + let customSecurityPath = path.appending(components: "custom", "security") + XCTAssertNoSuchPath(customSecurityPath) + let result = try SwiftPMProduct.SwiftBuild.executeProcess(["--security-path", customSecurityPath.pathString], packagePath: path) + XCTAssert(result.exitStatus != .terminated(code: 0)) + let output = try result.utf8stderrOutput() + XCTAssert(output.contains("error: You don’t have permission"), "expected permissions error") + XCTAssertNoSuchPath(customSecurityPath) + } + #endif } } diff --git a/Utilities/Docker/docker-compose.2004.55.yaml b/Utilities/Docker/docker-compose.2004.55.yaml index 13be333b9b4..ed02fc5f485 100644 --- a/Utilities/Docker/docker-compose.2004.55.yaml +++ b/Utilities/Docker/docker-compose.2004.55.yaml @@ -16,7 +16,6 @@ services: args: ubuntu_version: "focal" swift_version: "5.5" - base_image: "swiftlang/swift:nightly-5.5-focal" build: image: swift-package-manager:20.04-5.5 diff --git a/Utilities/Docker/docker-compose.yaml b/Utilities/Docker/docker-compose.yaml index e70fb344767..201473a85eb 100644 --- a/Utilities/Docker/docker-compose.yaml +++ b/Utilities/Docker/docker-compose.yaml @@ -27,7 +27,9 @@ services: - ~/.ssh:/root/.ssh - ~/.cache:/root/.cache - ~/.swiftpm/cache:/root/.swiftpm/cache - - ~/.swiftpm/config:/root/.swiftpm/config + - ~/.swiftpm/configuration:/root/.swiftpm/config # old location, remove after 5.6 + - ~/.swiftpm/configuration:/root/.swiftpm/configuration + - ~/.swiftpm/security:/root/.swiftpm/security # swift-package-manager code - ../..:/code/swift-package-manager:z # bootstrap script requires dependencies to be pre-fetched and in a specific place