@@ -71,10 +71,21 @@ struct Install: SwiftlyCommand {
7171 ) )
7272 var postInstallFile : FilePath ?
7373
74+ @Option (
75+ help: ArgumentHelp (
76+ " A file path where progress information will be written in JSONL format " ,
77+ discussion: """
78+ Progress information will be appended to this file as JSON objects, one per line.
79+ Each progress entry contains timestamp, progress percentage, and a descriptive message.
80+ The file must be writable, else an error will be thrown.
81+ """
82+ ) )
83+ var progressFile : FilePath ?
84+
7485 @OptionGroup var root : GlobalOptions
7586
7687 private enum CodingKeys : String , CodingKey {
77- case version, use, verify, postInstallFile, root
88+ case version, use, verify, postInstallFile, root, progressFile
7889 }
7990
8091 mutating func run( ) async throws {
@@ -93,7 +104,9 @@ struct Install: SwiftlyCommand {
93104 try await validateLinked ( ctx)
94105
95106 var config = try await Config . load ( ctx)
96- let toolchainVersion = try await Self . determineToolchainVersion ( ctx, version: self . version, config: & config)
107+ let toolchainVersion = try await Self . determineToolchainVersion (
108+ ctx, version: self . version, config: & config
109+ )
97110
98111 let ( postInstallScript, pathChanged) = try await Self . execute (
99112 ctx,
@@ -102,7 +115,8 @@ struct Install: SwiftlyCommand {
102115 useInstalledToolchain: self . use,
103116 verifySignature: self . verify,
104117 verbose: self . root. verbose,
105- assumeYes: self . root. assumeYes
118+ assumeYes: self . root. assumeYes,
119+ progressFile: self . progressFile
106120 )
107121
108122 let shell =
@@ -192,8 +206,9 @@ struct Install: SwiftlyCommand {
192206 await ctx. message ( " Setting up toolchain proxies... " )
193207 }
194208
195- let proxiesToCreate = Set ( toolchainBinDirContents) . subtracting ( swiftlyBinDirContents) . union (
196- overwrite)
209+ let proxiesToCreate = Set ( toolchainBinDirContents) . subtracting ( swiftlyBinDirContents)
210+ . union (
211+ overwrite)
197212
198213 for p in proxiesToCreate {
199214 let proxy = Swiftly . currentPlatform. swiftlyBinDir ( ctx) / p
@@ -248,7 +263,8 @@ struct Install: SwiftlyCommand {
248263 useInstalledToolchain: Bool,
249264 verifySignature: Bool,
250265 verbose: Bool,
251- assumeYes: Bool
266+ assumeYes: Bool,
267+ progressFile: FilePath? = nil
252268 ) async throws -> ( postInstall: String? , pathChanged: Bool) {
253269 guard !config. installedToolchains. contains ( version) else {
254270 await ctx. message ( " \( version) is already installed. " )
@@ -258,10 +274,11 @@ struct Install: SwiftlyCommand {
258274 // Ensure the system is set up correctly before downloading it. Problems that prevent installation
259275 // will throw, while problems that prevent use of the toolchain will be written out as a post install
260276 // script for the user to run afterwards.
261- let postInstallScript = try await Swiftly . currentPlatform. verifySystemPrerequisitesForInstall (
262- ctx, platformName: config. platform. name, version: version,
263- requireSignatureValidation: verifySignature
264- )
277+ let postInstallScript = try await Swiftly . currentPlatform
278+ . verifySystemPrerequisitesForInstall (
279+ ctx, platformName: config. platform. name, version: version,
280+ requireSignatureValidation: verifySignature
281+ )
265282
266283 await ctx. message ( " Installing \( version) " )
267284
@@ -296,10 +313,17 @@ struct Install: SwiftlyCommand {
296313 }
297314 }
298315
299- let animation = PercentProgressAnimation (
300- stream: stdoutStream,
301- header: " Downloading \( version) "
302- )
316+ let animation : ProgressAnimationProtocol =
317+ if let progressFile
318+ {
319+ try JsonFileProgressReporter ( ctx, filePath: progressFile)
320+ } else {
321+ PercentProgressAnimation ( stream: stdoutStream, header: " Downloading \( version) " )
322+ }
323+
324+ defer {
325+ try ? ( animation as? JsonFileProgressReporter ) ? . close ( )
326+ }
303327
304328 var lastUpdate = Date ( )
305329
@@ -315,7 +339,9 @@ struct Install: SwiftlyCommand {
315339 reportProgress: { progress in
316340 let now = Date ( )
317341
318- guard lastUpdate. distance ( to: now) > 0.25 || progress. receivedBytes == progress. totalBytes
342+ guard
343+ lastUpdate. distance ( to: now) > 0.25
344+ || progress. receivedBytes == progress. totalBytes
319345 else {
320346 return
321347 }
@@ -334,7 +360,8 @@ struct Install: SwiftlyCommand {
334360 }
335361 )
336362 } catch let notFound as DownloadNotFoundError {
337- throw SwiftlyError ( message: " \( version) does not exist at URL \( notFound. url) , exiting " )
363+ throw SwiftlyError (
364+ message: " \( version) does not exist at URL \( notFound. url) , exiting " )
338365 } catch {
339366 animation. complete ( success: false )
340367 throw error
@@ -401,7 +428,9 @@ struct Install: SwiftlyCommand {
401428 }
402429
403430 /// Utilize the swift.org API along with the provided selector to select a toolchain for install.
404- public static func resolve( _ ctx: SwiftlyCoreContext, config: Config, selector: ToolchainSelector)
431+ public static func resolve(
432+ _ ctx: SwiftlyCoreContext, config: Config, selector: ToolchainSelector
433+ )
405434 async throws -> ToolchainVersion
406435 {
407436 switch selector {
@@ -426,7 +455,8 @@ struct Install: SwiftlyCommand {
426455 }
427456
428457 if let patch {
429- return . stable( ToolchainVersion . StableRelease ( major: major, minor: minor, patch: patch) )
458+ return . stable(
459+ ToolchainVersion . StableRelease ( major: major, minor: minor, patch: patch) )
430460 }
431461
432462 await ctx. message ( " Fetching the latest stable Swift \( major) . \( minor) release... " )
0 commit comments