@@ -84,31 +84,8 @@ struct Install: SwiftlyCommand {
8484 try validateSwiftly ( ctx)
8585
8686 var config = try Config . load ( ctx)
87+ let toolchainVersion = try await Self . determineToolchainVersion ( ctx, version: self . version, config: & config)
8788
88- var selector : ToolchainSelector
89-
90- if let version = self . version {
91- selector = try ToolchainSelector ( parsing: version)
92- } else {
93- if case let ( _, result) = try await selectToolchain ( ctx, config: & config) ,
94- case let . swiftVersionFile( _, sel, error) = result
95- {
96- if let sel = sel {
97- selector = sel
98- } else if let error = error {
99- throw error
100- } else {
101- throw SwiftlyError ( message: " Internal error selecting toolchain to install. " )
102- }
103- } else {
104- throw SwiftlyError (
105- message:
106- " Swiftly couldn't determine the toolchain version to install. Please set a version like this and try again: `swiftly install latest` "
107- )
108- }
109- }
110-
111- let toolchainVersion = try await Self . resolve ( ctx, config: config, selector: selector)
11289 let ( postInstallScript, pathChanged) = try await Self . execute (
11390 ctx,
11491 version: toolchainVersion,
@@ -158,6 +135,101 @@ struct Install: SwiftlyCommand {
158135 to: URL ( fileURLWithPath: postInstallFile) , options: . atomic
159136 )
160137 }
138+ }
139+
140+ public static func setupProxies(
141+ _ ctx: SwiftlyCoreContext ,
142+ version: ToolchainVersion ,
143+ verbose: Bool ,
144+ assumeYes: Bool
145+ ) async throws -> Bool {
146+ var pathChanged = false
147+
148+ // Create proxies if we have a location where we can point them
149+ if let proxyTo = try ? Swiftly . currentPlatform. findSwiftlyBin ( ctx) {
150+ // Ensure swiftly doesn't overwrite any existing executables without getting confirmation first.
151+ let swiftlyBinDir = Swiftly . currentPlatform. swiftlyBinDir ( ctx)
152+ let swiftlyBinDirContents =
153+ ( try ? FileManager . default. contentsOfDirectory ( atPath: swiftlyBinDir. path) ) ?? [ String] ( )
154+ let toolchainBinDir = Swiftly . currentPlatform. findToolchainBinDir ( ctx, version)
155+ let toolchainBinDirContents = try FileManager . default. contentsOfDirectory (
156+ atPath: toolchainBinDir. path)
157+
158+ let existingProxies = swiftlyBinDirContents. filter { bin in
159+ do {
160+ let linkTarget = try FileManager . default. destinationOfSymbolicLink (
161+ atPath: swiftlyBinDir. appendingPathComponent ( bin) . path)
162+ return linkTarget == proxyTo
163+ } catch { return false }
164+ }
165+
166+ let overwrite = Set ( toolchainBinDirContents) . subtracting ( existingProxies) . intersection (
167+ swiftlyBinDirContents)
168+ if !overwrite. isEmpty && !assumeYes {
169+ await ctx. print ( " The following existing executables will be overwritten: " )
170+
171+ for executable in overwrite {
172+ await ctx. print ( " \( swiftlyBinDir. appendingPathComponent ( executable) . path) " )
173+ }
174+
175+ guard await ctx. promptForConfirmation ( defaultBehavior: false ) else {
176+ throw SwiftlyError ( message: " Toolchain installation has been cancelled " )
177+ }
178+ }
179+
180+ if verbose {
181+ await ctx. print ( " Setting up toolchain proxies... " )
182+ }
183+
184+ let proxiesToCreate = Set ( toolchainBinDirContents) . subtracting ( swiftlyBinDirContents) . union (
185+ overwrite)
186+
187+ for p in proxiesToCreate {
188+ let proxy = Swiftly . currentPlatform. swiftlyBinDir ( ctx) . appendingPathComponent ( p)
189+
190+ if proxy. fileExists ( ) {
191+ try FileManager . default. removeItem ( at: proxy)
192+ }
193+
194+ try FileManager . default. createSymbolicLink (
195+ atPath: proxy. path,
196+ withDestinationPath: proxyTo
197+ )
198+
199+ pathChanged = true
200+ }
201+ }
202+ return pathChanged
203+ }
204+
205+ static func determineToolchainVersion(
206+ _ ctx: SwiftlyCoreContext ,
207+ version: String ? ,
208+ config: inout Config
209+ ) async throws -> ToolchainVersion {
210+ let selector : ToolchainSelector
211+
212+ if let version = version {
213+ selector = try ToolchainSelector ( parsing: version)
214+ } else {
215+ if case let ( _, result) = try await selectToolchain ( ctx, config: & config) ,
216+ case let . swiftVersionFile( _, sel, error) = result {
217+ if let sel = sel {
218+ selector = sel
219+ } else if let error = error {
220+ throw error
221+ } else {
222+ throw SwiftlyError ( message: " Internal error selecting toolchain to install. " )
223+ }
224+ } else {
225+ throw SwiftlyError (
226+ message:
227+ " Swiftly couldn't determine the toolchain version to install. Please set a version like this and try again: `swiftly install latest` "
228+ )
229+ }
230+ }
231+
232+ return try await Self . resolve ( ctx, config: config, selector: selector)
161233 }
162234
163235 public static func execute(
@@ -274,62 +346,12 @@ struct Install: SwiftlyCommand {
274346
275347 try await Swiftly . currentPlatform. install ( ctx, from: tmpFile, version: version, verbose: verbose)
276348
277- var pathChanged = false
278-
279- // Create proxies if we have a location where we can point them
280- if let proxyTo = try ? Swiftly . currentPlatform. findSwiftlyBin ( ctx) {
281- // Ensure swiftly doesn't overwrite any existing executables without getting confirmation first.
282- let swiftlyBinDir = Swiftly . currentPlatform. swiftlyBinDir ( ctx)
283- let swiftlyBinDirContents =
284- ( try ? FileManager . default. contentsOfDirectory ( atPath: swiftlyBinDir. path) ) ?? [ String] ( )
285- let toolchainBinDir = Swiftly . currentPlatform. findToolchainBinDir ( ctx, version)
286- let toolchainBinDirContents = try FileManager . default. contentsOfDirectory (
287- atPath: toolchainBinDir. path)
288-
289- let existingProxies = swiftlyBinDirContents. filter { bin in
290- do {
291- let linkTarget = try FileManager . default. destinationOfSymbolicLink (
292- atPath: swiftlyBinDir. appendingPathComponent ( bin) . path)
293- return linkTarget == proxyTo
294- } catch { return false }
295- }
296-
297- let overwrite = Set ( toolchainBinDirContents) . subtracting ( existingProxies) . intersection (
298- swiftlyBinDirContents)
299- if !overwrite. isEmpty && !assumeYes {
300- await ctx. print ( " The following existing executables will be overwritten: " )
301-
302- for executable in overwrite {
303- await ctx. print ( " \( swiftlyBinDir. appendingPathComponent ( executable) . path) " )
304- }
305-
306- guard await ctx. promptForConfirmation ( defaultBehavior: false ) else {
307- throw SwiftlyError ( message: " Toolchain installation has been cancelled " )
308- }
309- }
310-
311- if verbose {
312- await ctx. print ( " Setting up toolchain proxies... " )
313- }
314-
315- let proxiesToCreate = Set ( toolchainBinDirContents) . subtracting ( swiftlyBinDirContents) . union (
316- overwrite)
317-
318- for p in proxiesToCreate {
319- let proxy = Swiftly . currentPlatform. swiftlyBinDir ( ctx) . appendingPathComponent ( p)
320-
321- if proxy. fileExists ( ) {
322- try FileManager . default. removeItem ( at: proxy)
323- }
324-
325- try FileManager . default. createSymbolicLink (
326- atPath: proxy. path,
327- withDestinationPath: proxyTo
328- )
329-
330- pathChanged = true
331- }
332- }
349+ let pathChanged = try await Self . setupProxies (
350+ ctx,
351+ version: version,
352+ verbose: verbose,
353+ assumeYes: assumeYes
354+ )
333355
334356 config. installedToolchains. insert ( version)
335357
0 commit comments