Skip to content

Commit 82fdc32

Browse files
committed
Add swift-package-registry command
1 parent f892dc6 commit 82fdc32

File tree

7 files changed

+419
-0
lines changed

7 files changed

+419
-0
lines changed

Package.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ let package = Package(
240240
/** Interacts with package collections */
241241
name: "swift-package-collection",
242242
dependencies: ["Commands"]),
243+
.target(
244+
/** Interacts with package collections */
245+
name: "swift-package-registry",
246+
dependencies: ["Commands"]),
243247
.target(
244248
/** Shim tool to find test names on OS X */
245249
name: "swiftpm-xctest-helper",

Sources/Commands/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ add_library(Commands
1616
show-dependencies.swift
1717
SwiftBuildTool.swift
1818
SwiftPackageCollectionsTool.swift
19+
SwiftPackageRegistryTool.swift
1920
SwiftPackageTool.swift
2021
SwiftRunTool.swift
2122
SwiftTestTool.swift
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import ArgumentParser
12+
import Basics
13+
import TSCBasic
14+
import SPMBuildCore
15+
import Build
16+
import PackageModel
17+
import PackageLoading
18+
import PackageGraph
19+
import SourceControl
20+
import TSCUtility
21+
import XCBuildSupport
22+
import Workspace
23+
import Foundation
24+
import PackageRegistry
25+
26+
private enum RegistryConfigurationError: Swift.Error {
27+
case missingScope(String? = nil)
28+
case invalidURL(String)
29+
}
30+
31+
extension RegistryConfigurationError: CustomStringConvertible {
32+
var description: String {
33+
switch self {
34+
case .missingScope(let scope?):
35+
return "no existing entry for scope: \(scope)"
36+
case .missingScope:
37+
return "no existing entry for default scope"
38+
case .invalidURL(let url):
39+
return "invalid URL: \(url)"
40+
}
41+
}
42+
}
43+
44+
public struct SwiftPackageRegistryTool: ParsableCommand {
45+
public static var configuration = CommandConfiguration(
46+
commandName: "package-registry",
47+
_superCommandName: "swift",
48+
abstract: "Perform operations on Swift packages",
49+
discussion: "SEE ALSO: swift package",
50+
version: SwiftVersion.currentVersion.completeDisplayString,
51+
subcommands: [
52+
Set.self,
53+
Unset.self
54+
],
55+
helpNames: [.short, .long, .customLong("help", withSingleDash: true)])
56+
57+
@OptionGroup()
58+
var swiftOptions: SwiftToolOptions
59+
60+
public init() {}
61+
62+
struct Set: SwiftCommand {
63+
static let configuration = CommandConfiguration(
64+
abstract: "Set a custom registry")
65+
66+
@OptionGroup(_hiddenFromHelp: true)
67+
var swiftOptions: SwiftToolOptions
68+
69+
@Flag(help: "Apply settings to all projects for this user")
70+
var global: Bool = false
71+
72+
@Option(help: "Associate the registry with a given scope")
73+
var scope: String?
74+
75+
@Option(help: "Specify a user name for the remote machine")
76+
var login: String?
77+
78+
@Option(help: "Supply a password for the remote machine")
79+
var password: String?
80+
81+
@Argument(help: "The registry URL")
82+
var url: String
83+
84+
func run(_ swiftTool: SwiftTool) throws {
85+
guard let url = URL(string: url),
86+
url.scheme == "https"
87+
else {
88+
throw RegistryConfigurationError.invalidURL(url)
89+
}
90+
91+
// TODO: Require login if password is specified
92+
93+
let path = try swiftTool.getRegistryConfigurationPath(fileSystem: localFileSystem, global: global)
94+
95+
var configuration = RegistryConfiguration()
96+
if localFileSystem.exists(path) {
97+
configuration = try RegistryConfiguration.readFromJSONFile(in: localFileSystem, at: path)
98+
}
99+
100+
if let scope = scope {
101+
configuration.scopedRegistries[scope] = .init(url: url)
102+
} else {
103+
configuration.defaultRegistry = .init(url: url)
104+
}
105+
106+
try configuration.writeToJSONFile(in: localFileSystem, at: path)
107+
108+
// TODO: Add login and password to .netrc
109+
}
110+
}
111+
112+
struct Unset: SwiftCommand {
113+
static let configuration = CommandConfiguration(
114+
abstract: "Remove a configured registry")
115+
116+
@OptionGroup(_hiddenFromHelp: true)
117+
var swiftOptions: SwiftToolOptions
118+
119+
@Flag(help: "Apply settings to all projects for this user")
120+
var global: Bool = false
121+
122+
@Option(help: "Associate the registry with a given scope")
123+
var scope: String?
124+
125+
func run(_ swiftTool: SwiftTool) throws {
126+
let path = try swiftTool.getRegistryConfigurationPath(fileSystem: localFileSystem, global: global)
127+
var configuration = try RegistryConfiguration.readFromJSONFile(in: localFileSystem, at: path)
128+
129+
if let scope = scope {
130+
guard let _ = configuration.scopedRegistries[scope] else {
131+
throw RegistryConfigurationError.missingScope(scope)
132+
}
133+
configuration.scopedRegistries.removeValue(forKey: scope)
134+
} else {
135+
guard let _ = configuration.defaultRegistry else {
136+
throw RegistryConfigurationError.missingScope()
137+
}
138+
configuration.defaultRegistry = nil
139+
}
140+
141+
try configuration.writeToJSONFile(in: localFileSystem, at: path)
142+
}
143+
}
144+
}
145+
146+
// MARK: -
147+
148+
private extension Decodable {
149+
static func readFromJSONFile(in fileSystem: FileSystem, at path: AbsolutePath) throws -> Self {
150+
let content = try fileSystem.readFileContents(path)
151+
152+
let decoder = JSONDecoder()
153+
154+
return try decoder.decode(Self.self, from: Data(content.contents))
155+
}
156+
}
157+
158+
private extension Encodable {
159+
func writeToJSONFile(in fileSystem: FileSystem, at path: AbsolutePath) throws {
160+
let encoder = JSONEncoder()
161+
encoder.outputFormatting = [.sortedKeys, .prettyPrinted, .withoutEscapingSlashes]
162+
163+
let data = try encoder.encode(self)
164+
165+
try fileSystem.writeFileContents(path, bytes: ByteString(data), atomically: true)
166+
}
167+
}
168+
169+
private extension SwiftTool {
170+
func getRegistryConfigurationPath(fileSystem: FileSystem = localFileSystem, global: Bool) throws -> AbsolutePath {
171+
let filename = "registries.json"
172+
if global {
173+
return try fileSystem.getOrCreateSwiftPMConfigDirectory().appending(component: filename)
174+
} else {
175+
let directory = try configFilePath()
176+
if !fileSystem.exists(directory) {
177+
try fileSystem.createDirectory(directory, recursive: true)
178+
}
179+
180+
return directory.appending(component: filename)
181+
}
182+
}
183+
}

Sources/SPMTestSupport/SwiftPMProduct.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import TSCBasic
1515
public enum SwiftPMProduct: Product {
1616
case SwiftBuild
1717
case SwiftPackage
18+
case SwiftPackageRegistry
1819
case SwiftTest
1920
case SwiftRun
2021
case XCTestHelper
@@ -26,6 +27,8 @@ public enum SwiftPMProduct: Product {
2627
return RelativePath("swift-build")
2728
case .SwiftPackage:
2829
return RelativePath("swift-package")
30+
case .SwiftPackageRegistry:
31+
return RelativePath("swift-package-registry")
2932
case .SwiftTest:
3033
return RelativePath("swift-test")
3134
case .SwiftRun:
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# This source file is part of the Swift.org open source project
2+
#
3+
# Copyright (c) 2021 Apple Inc. and the Swift project authors
4+
# Licensed under Apache License v2.0 with Runtime Library Exception
5+
#
6+
# See http://swift.org/LICENSE.txt for license information
7+
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
8+
9+
add_executable(swift-package-registry
10+
main.swift)
11+
target_link_libraries(swift-package-registry PRIVATE
12+
Commands)
13+
14+
if(USE_CMAKE_INSTALL)
15+
install(TARGETS swift-package-registry
16+
RUNTIME DESTINATION bin)
17+
endif()
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2021 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import Commands
12+
13+
SwiftPackageRegistryTool.main()

0 commit comments

Comments
 (0)