Skip to content

Commit 7387fc7

Browse files
authored
Add support for Netrc for Downloader (#2833)
* working with custom netrc file location * Added support to TSC for using Netrc credentials for binary artifact access https://forums.swift.org/t/spm-support-basic-auth-for-non-git-binary-dependency-hosts/37878 * eliminated dependency on NSHomeDirectory * cleaned up cruft from support core copy * test case emits error on unsupported OS for --netrc-file option
1 parent 10fd75f commit 7387fc7

File tree

5 files changed

+65
-7
lines changed

5 files changed

+65
-7
lines changed

Sources/Commands/Options.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,5 +255,9 @@ public struct SwiftToolOptions: ParsableArguments {
255255
archs.count > 1 ? .xcode : _buildSystem
256256
}
257257

258+
/// The path to the netrc file which should be use for authentication when downloading binary target artifacts.
259+
@Option(name: .customLong("netrc-file"), completion: .file())
260+
var netrcFilePath: AbsolutePath?
261+
258262
public init() {}
259263
}

Sources/Commands/SwiftTool.swift

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -353,23 +353,36 @@ public class SwiftTool {
353353
verbosity = Verbosity(rawValue: options.verbosity)
354354
Process.verbose = verbosity != .concise
355355
}
356-
356+
357357
static func postprocessArgParserResult(options: SwiftToolOptions, diagnostics: DiagnosticsEngine) throws {
358358
if options.chdir != nil {
359359
diagnostics.emit(warning: "'--chdir/-C' option is deprecated; use '--package-path' instead")
360360
}
361-
361+
362362
if options.multirootPackageDataFile != nil {
363363
diagnostics.emit(.unsupportedFlag("--multiroot-data-file"))
364364
}
365-
365+
366366
if options.useExplicitModuleBuild && !options.useIntegratedSwiftDriver {
367367
diagnostics.emit(error: "'--experimental-explicit-module-build' option requires '--use-integrated-swift-driver'")
368368
}
369-
369+
370370
if !options.archs.isEmpty && options.customCompileTriple != nil {
371371
diagnostics.emit(.mutuallyExclusiveArgumentsError(arguments: ["--arch", "--triple"]))
372372
}
373+
374+
if options.netrcFilePath != nil {
375+
// --netrc-file option only supported on macOS >=10.13
376+
#if os(macOS)
377+
if #available(macOS 10.13, *) {
378+
// ok, check succeeds
379+
} else {
380+
diagnostics.emit(error: "'--netrc-file' option is only supported on macOS >=10.13")
381+
}
382+
#else
383+
diagnostics.emit(error: "'--netrc-file' option is only supported on macOS >=10.13")
384+
#endif
385+
}
373386
}
374387

375388
func editablesPath() throws -> AbsolutePath {
@@ -405,6 +418,10 @@ public class SwiftTool {
405418
private lazy var _swiftpmConfig: Result<SwiftPMConfig, Swift.Error> = {
406419
return Result(catching: { SwiftPMConfig(path: try configFilePath()) })
407420
}()
421+
422+
func resolvedNetrcFilePath() -> AbsolutePath? {
423+
return options.netrcFilePath
424+
}
408425

409426
/// Holds the currently active workspace.
410427
///
@@ -430,6 +447,7 @@ public class SwiftTool {
430447
delegate: delegate,
431448
config: try getSwiftPMConfig(),
432449
repositoryProvider: provider,
450+
netrcFilePath: resolvedNetrcFilePath(),
433451
isResolverPrefetchingEnabled: options.shouldEnableResolverPrefetching,
434452
skipUpdate: options.skipDependencyUpdate,
435453
enableResolverTrace: options.enableResolverTrace

Sources/SPMTestSupport/MockDownloader.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public class MockDownloader: Downloader {
4949
public func downloadFile(
5050
at url: Foundation.URL,
5151
to destinationPath: AbsolutePath,
52-
withAuthorizationProvider: AuthorizationProviding? = nil,
52+
withAuthorizationProvider authorizationProvider: AuthorizationProviding? = nil,
5353
progress: @escaping Downloader.Progress,
5454
completion: @escaping Downloader.Completion
5555
) {

Sources/Workspace/Workspace.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,8 @@ public class Workspace {
346346

347347
/// The downloader used for downloading binary artifacts.
348348
fileprivate let downloader: Downloader
349+
350+
fileprivate let netrcFilePath: AbsolutePath?
349351

350352
/// The downloader used for unarchiving binary artifacts.
351353
fileprivate let archiver: Archiver
@@ -396,6 +398,7 @@ public class Workspace {
396398
fileSystem: FileSystem = localFileSystem,
397399
repositoryProvider: RepositoryProvider = GitRepositoryProvider(),
398400
downloader: Downloader = FoundationDownloader(),
401+
netrcFilePath: AbsolutePath? = nil,
399402
archiver: Archiver = ZipArchiver(),
400403
checksumAlgorithm: HashAlgorithm = SHA256(),
401404
additionalFileRules: [FileRuleDescription] = [],
@@ -412,6 +415,7 @@ public class Workspace {
412415
self.currentToolsVersion = currentToolsVersion
413416
self.toolsVersionLoader = toolsVersionLoader
414417
self.downloader = downloader
418+
self.netrcFilePath = netrcFilePath
415419
self.archiver = archiver
416420
self.checksumAlgorithm = checksumAlgorithm
417421
self.isResolverPrefetchingEnabled = isResolverPrefetchingEnabled
@@ -1400,7 +1404,15 @@ extension Workspace {
14001404
private func download(_ artifacts: [ManagedArtifact], diagnostics: DiagnosticsEngine) {
14011405
let group = DispatchGroup()
14021406
let tempDiagnostics = DiagnosticsEngine()
1403-
1407+
1408+
var authProvider: AuthorizationProviding? = nil
1409+
#if os(macOS)
1410+
// Netrc feature currently only supported on macOS 10.13+ due to dependency
1411+
// on NSTextCheckingResult.range(with:)
1412+
if #available(macOS 10.13, *) {
1413+
authProvider = try? Netrc.load(fromFileAtPath: netrcFilePath).get()
1414+
}
1415+
#endif
14041416
for artifact in artifacts {
14051417
group.enter()
14061418

@@ -1419,10 +1431,12 @@ extension Workspace {
14191431

14201432
let parsedURL = URL(string: url)!
14211433
let archivePath = parentDirectory.appending(component: parsedURL.lastPathComponent)
1434+
1435+
14221436
downloader.downloadFile(
14231437
at: parsedURL,
14241438
to: archivePath,
1425-
withAuthorizationProvider: nil,
1439+
withAuthorizationProvider: authProvider,
14261440
progress: { bytesDownloaded, totalBytesToDownload in
14271441
self.delegate?.downloadingBinaryArtifact(
14281442
from: url,

Tests/CommandsTests/PackageToolTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,28 @@ final class PackageToolTests: XCTestCase {
4141
func testVersion() throws {
4242
XCTAssert(try execute(["--version"]).stdout.contains("Swift Package Manager"))
4343
}
44+
45+
func testNetrcFile() throws {
46+
func verifyUnsupportedOSThrows() {
47+
do {
48+
// should throw and be caught
49+
try execute(["update", "--netrc-file", "/Users/me/.hidden/.netrc"])
50+
XCTFail()
51+
} catch {
52+
XCTAssert(true)
53+
}
54+
}
55+
#if os(macOS)
56+
if #available(macOS 10.13, *) {
57+
// should succeed
58+
XCTAssert(try execute(["--netrc-file", "/Users/me/.hidden/.netrc"]).stdout.contains("USAGE: swift package"))
59+
} else {
60+
verifyUnsupportedOSThrows()
61+
}
62+
#else
63+
verifyUnsupportedOSThrows()
64+
#endif
65+
}
4466

4567
func testResolve() throws {
4668
fixture(name: "DependencyResolution/External/Simple") { prefix in

0 commit comments

Comments
 (0)