Skip to content

Commit 8a5c51d

Browse files
authored
Update command-line interface for .netrc file handling (#3763)
* Use --enable-netrc / --disable-netrc inversion flags * Deprecate --netrc-optional flag * Emit warnings when specifying deprecated --netrc and --netrc-optional flags
1 parent 9f85874 commit 8a5c51d

File tree

3 files changed

+91
-62
lines changed

3 files changed

+91
-62
lines changed

Sources/Commands/Options.swift

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -314,21 +314,26 @@ public struct SwiftToolOptions: ParsableArguments {
314314
// Force the Xcode build system if we want to build more than one arch.
315315
archs.count > 1 ? .xcode : _buildSystem
316316
}
317+
318+
/// Whether to load .netrc files for authenticating with remote servers
319+
/// when downloading binary artifacts or communicating with a registry.
320+
@Flag(inversion: .prefixedEnableDisable,
321+
exclusivity: .exclusive,
322+
help: "Load credentials from a .netrc file")
323+
var netrc: Bool = true
317324

318-
/// Tells `Workspace` to attempt to locate .netrc file at `NSHomeDirectory`.
319-
@Flag()
320-
var netrc: Bool = false
321-
322-
/// Similar to `--netrc`, but this option makes the .netrc usage optional and not mandatory as with the `--netrc` option.
323-
@Flag(name: .customLong("netrc-optional"))
324-
var netrcOptional: Bool = false
325-
326-
/// The path to the netrc file which should be use for authentication when downloading binary target artifacts.
327-
/// Similar to `--netrc`, except that you also provide the path to the actual file to use.
328-
/// This is useful when you want to provide the information in another directory or with another file name.
329-
/// - important: Respects `--netrcOptional` option.
330-
@Option(name: .customLong("netrc-file"), completion: .file())
325+
/// The path to the .netrc file used when `netrc` is `true`.
326+
@Option(
327+
name: .customLong("netrc-file"),
328+
help: "Specify the .netrc file path.",
329+
completion: .file())
331330
var netrcFilePath: AbsolutePath?
331+
332+
@Flag(name: .customLong("netrc"), help: .hidden)
333+
var _deprecated_netrc: Bool = false
334+
335+
@Flag(name: .customLong("netrc-optional"), help: .hidden)
336+
var _deprecated_netrcOptional: Bool = false
332337

333338
public init() {}
334339
}

Sources/Commands/SwiftTool.swift

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,18 @@ public class SwiftTool {
422422
if options.shouldDisableManifestCaching {
423423
diagnostics.emit(warning: "'--disable-package-manifest-caching' option is deprecated; use '--manifest-caching' instead")
424424
}
425+
426+
if let _ = options.netrcFilePath, options.netrc == false {
427+
diagnostics.emit(.mutuallyExclusiveArgumentsError(arguments: ["--disable-netrc", "--netrc-file"]))
428+
}
429+
430+
if options._deprecated_netrc {
431+
diagnostics.emit(warning: "'--netrc' option is deprecated; .netrc files are located by default")
432+
}
433+
434+
if options._deprecated_netrcOptional {
435+
diagnostics.emit(warning: "'--netrc-optional' option is deprecated; .netrc files are located by default")
436+
}
425437
}
426438

427439
private func editsDirectory() throws -> AbsolutePath {
@@ -485,27 +497,22 @@ public class SwiftTool {
485497
return try self.getNetrcConfig()?.get()
486498
}
487499

488-
func getNetrcConfig() throws -> Workspace.Configuration.Netrc? {
489-
guard options.netrc || options.netrcFilePath != nil || options.netrcOptional else {
490-
return .none
491-
}
500+
func getNetrcConfig() -> Workspace.Configuration.Netrc? {
501+
guard options.netrc else { return nil }
492502

493-
let netrcFilePath = try self.netrcFilePath()
494-
return netrcFilePath.map { .init(path: $0, fileSystem: localFileSystem) }
495-
}
496-
497-
private func netrcFilePath() throws -> AbsolutePath? {
498-
let netrcFilePath = options.netrcFilePath ?? localFileSystem.homeDirectory.appending(component: ".netrc")
499-
guard localFileSystem.exists(netrcFilePath) else {
500-
if !options.netrcOptional {
501-
ObservabilitySystem.topScope.emit(error: "Cannot find mandatory .netrc file at \(netrcFilePath). To make .netrc file optional, use --netrc-optional flag.")
502-
throw ExitCode.failure
503-
} else {
504-
ObservabilitySystem.topScope.emit(warning: "Did not find optional .netrc file at \(netrcFilePath).")
505-
return .none
503+
if let configuredPath = options.netrcFilePath {
504+
guard localFileSystem.exists(configuredPath) else {
505+
ObservabilitySystem.topScope.emit(error: "Did not find .netrc file at \(configuredPath).")
506+
return nil
506507
}
508+
509+
return .init(path: configuredPath, fileSystem: localFileSystem)
510+
} else {
511+
let defaultPath = localFileSystem.homeDirectory.appending(component: ".netrc")
512+
guard localFileSystem.exists(defaultPath) else { return nil }
513+
514+
return .init(path: defaultPath, fileSystem: localFileSystem)
507515
}
508-
return netrcFilePath
509516
}
510517

511518
private func getSharedCacheDirectory() throws -> AbsolutePath? {

Tests/CommandsTests/PackageToolTests.swift

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,34 @@ final class PackageToolTests: XCTestCase {
5050
XCTAssertMatch(stdout, .contains("Swift Package Manager"))
5151
}
5252

53+
func testNetrc() throws {
54+
fixture(name: "DependencyResolution/External/XCFramework") { packageRoot in
55+
// --enable-netrc flag
56+
try self.execute(["resolve", "--enable-netrc"], packagePath: packageRoot)
57+
58+
// --disable-netrc flag
59+
try self.execute(["resolve", "--disable-netrc"], packagePath: packageRoot)
60+
61+
// --enable-netrc and --disable-netrc flags
62+
XCTAssertThrowsError(
63+
try self.execute(["resolve", "--enable-netrc", "--disable-netrc"], packagePath: packageRoot)
64+
) { error in
65+
XCTAssertMatch(String(describing: error), .contains("Value to be set with flag '--disable-netrc' had already been set with flag '--enable-netrc'"))
66+
}
67+
68+
// deprecated --netrc flag
69+
let stderr = try self.execute(["resolve", "--netrc"], packagePath: packageRoot).stderr
70+
XCTAssertMatch(stderr, .contains("'--netrc' option is deprecated"))
71+
}
72+
}
73+
74+
func testNetrcOptional() throws {
75+
fixture(name: "DependencyResolution/External/XCFramework") { packageRoot in
76+
let stderr = try self.execute(["resolve", "--netrc-optional"], packagePath: packageRoot).stderr
77+
XCTAssertMatch(stderr, .contains("'--netrc-optional' option is deprecated"))
78+
}
79+
}
80+
5381
func testNetrcFile() throws {
5482
fixture(name: "DependencyResolution/External/XCFramework") { packageRoot in
5583
let fs = localFileSystem
@@ -58,39 +86,28 @@ final class PackageToolTests: XCTestCase {
5886
stream <<< "machine mymachine.labkey.org login [email protected] password mypassword"
5987
}
6088

61-
do {
62-
// file at correct location
63-
try execute(["--netrc-file", netrcPath.pathString, "resolve"], packagePath: packageRoot)
64-
// file does not exist, but is optional
65-
let textOutput = try execute(["--netrc-file", "/foo", "--netrc-optional", "resolve"], packagePath: packageRoot).stderr
66-
XCTAssertMatch(textOutput, .contains("warning: Did not find optional .netrc file at /foo."))
67-
68-
// required file does not exist, will throw
69-
try execute(["--netrc-file", "/foo", "resolve"], packagePath: packageRoot)
70-
} catch {
71-
XCTAssertMatch(String(describing: error), .contains("Cannot find mandatory .netrc file at /foo"))
89+
// valid .netrc file path
90+
try execute(["resolve", "--netrc-file", netrcPath.pathString], packagePath: packageRoot)
91+
92+
// valid .netrc file path with --disable-netrc option
93+
XCTAssertThrowsError(
94+
try execute(["resolve", "--netrc-file", netrcPath.pathString, "--disable-netrc"], packagePath: packageRoot)
95+
) { error in
96+
XCTAssertMatch(String(describing: error), .contains("'--disable-netrc' and '--netrc-file' are mutually exclusive"))
7297
}
73-
}
7498

75-
fixture(name: "DependencyResolution/External/XCFramework") { packageRoot in
76-
do {
77-
// Developer machine may have .netrc file at NSHomeDirectory; modify test accordingly
78-
if localFileSystem.exists(localFileSystem.homeDirectory.appending(RelativePath(".netrc"))) {
79-
try execute(["--netrc", "resolve"], packagePath: packageRoot)
80-
} else {
81-
// file does not exist, but is optional
82-
let textOutput = try execute(["--netrc", "--netrc-optional", "resolve"], packagePath: packageRoot)
83-
XCTAssertMatch(textOutput.stderr, .contains("Did not find optional .netrc file at \(localFileSystem.homeDirectory)/.netrc."))
84-
85-
// file does not exist, but is optional
86-
let textOutput2 = try execute(["--netrc-optional", "resolve"], packagePath: packageRoot)
87-
XCTAssertMatch(textOutput2.stderr, .contains("Did not find optional .netrc file at \(localFileSystem.homeDirectory)/.netrc."))
88-
89-
// required file does not exist, will throw
90-
try execute(["--netrc", "resolve"], packagePath: packageRoot)
91-
}
92-
} catch {
93-
XCTAssertMatch(String(describing: error), .contains("Cannot find mandatory .netrc file at \(localFileSystem.homeDirectory)/.netrc"))
99+
// invalid .netrc file path
100+
XCTAssertThrowsError(
101+
try execute(["resolve", "--netrc-file", "/foo"], packagePath: packageRoot)
102+
) { error in
103+
XCTAssertMatch(String(describing: error), .contains("Did not find .netrc file at /foo."))
104+
}
105+
106+
// invalid .netrc file path with --disable-netrc option
107+
XCTAssertThrowsError(
108+
try execute(["resolve", "--netrc-file", "/foo", "--disable-netrc"], packagePath: packageRoot)
109+
) { error in
110+
XCTAssertMatch(String(describing: error), .contains("'--disable-netrc' and '--netrc-file' are mutually exclusive"))
94111
}
95112
}
96113
}

0 commit comments

Comments
 (0)