dominicegginton / spinner

Powerful Swift CLI Spinners
MIT License
45 stars 5 forks source link

Duration is not changing #48

Open SeRG1k17 opened 3 months ago

SeRG1k17 commented 3 months ago
let spinner = Spinner(.dots, message, format: "{S} {T} ⏱️ {D}")
spinner.start()

//example of command more then 1s:
shell.run("bundle exec pod install")
spinner.stop()

//Output:

✔ 🫛 Pods install ⏱️ 0s

I checked your code and it looks correct, you calculate the value between the start and the current time each time. However, the value does not change

func render() {
        var spinner = self.format.replacingOccurrences(of: "{S}", with: self.frame()).replacingOccurrences(of: "{T}", with: self.message)
        if let timestamp = self.timestamp {
            let duration = Now() - timestamp
            spinner = spinner.replacingOccurrences(of: "{D}", with: duration.timeString)
        }
        stream.write(string: "\r", terminator: "")
        stream.write(string: spinner, terminator: "")
    }

Thank you for good project!

dominicegginton commented 3 months ago

@SeRG1k17 thanks for reaching out. I will take a look into this ASAP and get back to you.

dominicegginton commented 3 months ago

Hey @SeRG1k17 I was not able to replicate the issue you are facing (please see: https://github.com/dominicegginton/spinner/blob/duration-debug/Sources/DebugDuration/main.swift where I have created an example to debug)

import Spinner
import Foundation

@discardableResult
func shell(_ args: String...) -> Int32 {
    let task = Process()
    task.launchPath = "/usr/bin/env"
    task.arguments = args
    task.launch()
    task.waitUntilExit()
    return task.terminationStatus
}

let message = "duration debugging - running sleep cmd"

let spinner = Spinner(.dots, message, format: "{S} {T} ⏱️ {D}")
spinner.start()
shell("sleep", "10") // do work
spinner.stop()

Running my example I got the expected result (including duration updates as time progresses): image

Please can you provide more information about the environment you are building on and the call to the function shell.run provided example? This may give me more ideas on what could be the problem here.

SeRG1k17 commented 2 months ago

I managed to get a time greater than 0 seconds, this happens when the command hangs and works for a very long time. It seems more than 1 minute, same on CI. I will try create a sample soon

//ShellService.swift
func run(
        _ command: String,
        message: String = "",
        silentOutput: Bool = false
    ) throws -> String {
        let spinner = Spinner(.dots, message, format: "{S} {T} ⏱️ {D}")
        spinner.start()

        let process = Process()

        process.arguments = ["-c", command]
        process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
        process.launchPath = "/bin/bash"

        let outputPipe = Pipe()
        process.standardOutput = outputPipe

        let errorPipe = Pipe()
        process.standardError = errorPipe

        var outputData = Data()
        var errorData = Data()
        let outputQueue = DispatchQueue(label: "process-output-queue")

        outputPipe.fileHandleForReading.readabilityHandler = { handler in
            outputQueue.async {
                outputData.append(handler.availableData)
                if !silentOutput {
                    self.logService.standartOutput(handler.availableData)
                }
            }
        }
        errorPipe.fileHandleForReading.readabilityHandler = { handler in
            outputQueue.async {
                errorData.append(handler.availableData)
                if !silentOutput {
                    self.logService.standartOutput(handler.availableData)
                }
            }
        }

        do {
            try process.run()
        } catch {
            spinner.error(error.localizedDescription)
//            logService.log(error.localizedDescription, logLevel: .debug)
            throw error
        }
        logService.log("command: '\(command)'".blue, logLevel: .debug)
        process.waitUntilExit()

        outputPipe.fileHandleForReading.readabilityHandler = nil
        errorPipe.fileHandleForReading.readabilityHandler = nil

        return try outputQueue.sync {
            if process.terminationStatus == EXIT_SUCCESS {
                spinner.success()
                return try outputData.string
            } else {
                spinner.error()
                if let errorString = try? errorData.string, !errorString.isEmpty {
                    throw Error.custom(errorString)
                } else {
                    throw Error.runtime(reason: process.terminationReason)
                }
            }
        }
    }
koznobikhin commented 2 months ago

Hello @dominicegginton, Looks like the issue reproduces only on Apple Silicon. I guess there is something wrong with DispatchTime.now there, so it would be great if you could change it to clock_gettime_nsec_np or something.

Screenshot 2024-08-29 at 18 41 27