@@ -71,10 +71,20 @@ 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+ """
81+ ) )
82+ var progressFile : FilePath ?
83+
7484 @OptionGroup var root : GlobalOptions
7585
7686 private enum CodingKeys : String , CodingKey {
77- case version, use, verify, postInstallFile, root
87+ case version, use, verify, postInstallFile, root, progressFile
7888 }
7989
8090 mutating func run( ) async throws {
@@ -93,7 +103,9 @@ struct Install: SwiftlyCommand {
93103 try await validateLinked ( ctx)
94104
95105 var config = try await Config . load ( ctx)
96- let toolchainVersion = try await Self . determineToolchainVersion ( ctx, version: self . version, config: & config)
106+ let toolchainVersion = try await Self . determineToolchainVersion (
107+ ctx, version: self . version, config: & config
108+ )
97109
98110 let ( postInstallScript, pathChanged) = try await Self . execute (
99111 ctx,
@@ -102,7 +114,8 @@ struct Install: SwiftlyCommand {
102114 useInstalledToolchain: self . use,
103115 verifySignature: self . verify,
104116 verbose: self . root. verbose,
105- assumeYes: self . root. assumeYes
117+ assumeYes: self . root. assumeYes,
118+ progressFile: self . progressFile
106119 )
107120
108121 let shell =
@@ -192,8 +205,9 @@ struct Install: SwiftlyCommand {
192205 await ctx. message ( " Setting up toolchain proxies... " )
193206 }
194207
195- let proxiesToCreate = Set ( toolchainBinDirContents) . subtracting ( swiftlyBinDirContents) . union (
196- overwrite)
208+ let proxiesToCreate = Set ( toolchainBinDirContents) . subtracting ( swiftlyBinDirContents)
209+ . union (
210+ overwrite)
197211
198212 for p in proxiesToCreate {
199213 let proxy = Swiftly . currentPlatform. swiftlyBinDir ( ctx) / p
@@ -248,7 +262,8 @@ struct Install: SwiftlyCommand {
248262 useInstalledToolchain: Bool,
249263 verifySignature: Bool,
250264 verbose: Bool,
251- assumeYes: Bool
265+ assumeYes: Bool,
266+ progressFile: FilePath? = nil
252267 ) async throws -> ( postInstall: String? , pathChanged: Bool) {
253268 guard !config. installedToolchains. contains ( version) else {
254269 await ctx. message ( " \( version) is already installed. " )
@@ -258,10 +273,11 @@ struct Install: SwiftlyCommand {
258273 // Ensure the system is set up correctly before downloading it. Problems that prevent installation
259274 // will throw, while problems that prevent use of the toolchain will be written out as a post install
260275 // 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- )
276+ let postInstallScript = try await Swiftly . currentPlatform
277+ . verifySystemPrerequisitesForInstall (
278+ ctx, platformName: config. platform. name, version: version,
279+ requireSignatureValidation: verifySignature
280+ )
265281
266282 await ctx. message ( " Installing \( version) " )
267283
@@ -296,10 +312,13 @@ struct Install: SwiftlyCommand {
296312 }
297313 }
298314
299- let animation = PercentProgressAnimation (
300- stream: stdoutStream,
301- header: " Downloading \( version) "
302- )
315+ let animation : ProgressAnimationProtocol =
316+ progressFile != nil
317+ ? JsonFileProgressReporter ( filePath: progressFile!)
318+ : PercentProgressAnimation (
319+ stream: stdoutStream,
320+ header: " Downloading \( version) "
321+ )
303322
304323 var lastUpdate = Date ( )
305324
@@ -315,7 +334,9 @@ struct Install: SwiftlyCommand {
315334 reportProgress: { progress in
316335 let now = Date ( )
317336
318- guard lastUpdate. distance ( to: now) > 0.25 || progress. receivedBytes == progress. totalBytes
337+ guard
338+ lastUpdate. distance ( to: now) > 0.25
339+ || progress. receivedBytes == progress. totalBytes
319340 else {
320341 return
321342 }
@@ -334,7 +355,8 @@ struct Install: SwiftlyCommand {
334355 }
335356 )
336357 } catch let notFound as DownloadNotFoundError {
337- throw SwiftlyError ( message: " \( version) does not exist at URL \( notFound. url) , exiting " )
358+ throw SwiftlyError (
359+ message: " \( version) does not exist at URL \( notFound. url) , exiting " )
338360 } catch {
339361 animation. complete ( success: false )
340362 throw error
@@ -401,7 +423,9 @@ struct Install: SwiftlyCommand {
401423 }
402424
403425 /// 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)
426+ public static func resolve(
427+ _ ctx: SwiftlyCoreContext, config: Config, selector: ToolchainSelector
428+ )
405429 async throws -> ToolchainVersion
406430 {
407431 switch selector {
@@ -426,7 +450,8 @@ struct Install: SwiftlyCommand {
426450 }
427451
428452 if let patch {
429- return . stable( ToolchainVersion . StableRelease ( major: major, minor: minor, patch: patch) )
453+ return . stable(
454+ ToolchainVersion . StableRelease ( major: major, minor: minor, patch: patch) )
430455 }
431456
432457 await ctx. message ( " Fetching the latest stable Swift \( major) . \( minor) release... " )
0 commit comments