2727#include < vix/utils/Env.hpp>
2828#include < vix/cli/util/Semver.hpp>
2929#include < vix/cli/util/GitProgress.hpp>
30- #include < vix/cli/util/NetworkProgress.hpp>
3130#include < vix/cli/util/ProjectMutation.hpp>
3231
3332#include < nlohmann/json.hpp>
@@ -464,11 +463,24 @@ namespace vix::commands
464463 std::vector<std::string> args,
465464 const fs::path &cwd);
466465
466+ static bool install_progress_is_tty ()
467+ {
468+ #ifdef _WIN32
469+ if (_isatty (_fileno (stdout)) == 0 )
470+ return false ;
471+ #else
472+ if (::isatty (STDOUT_FILENO ) == 0 )
473+ return false ;
474+ #endif
475+ const char *term = std::getenv (" TERM" );
476+ return (!term || std::string_view (term) != " dumb" ) && std::getenv (" NO_COLOR" ) == nullptr ;
477+ }
478+
467479 class GitInstallProgress
468480 {
469481 public:
470482 GitInstallProgress (std::string dependency, std::size_t packageIndex, std::size_t packageCount)
471- : dependency_(std::move(dependency)), packageIndex_(packageIndex), packageCount_(packageCount), parser_([this ](const auto &event)
483+ : dependency_(std::move(dependency)), packageIndex_(packageIndex), packageCount_(packageCount), started_(std::chrono::steady_clock::now()), tty_(install_progress_is_tty()), parser_([this ](const auto &event)
472484 { render (event); }) {}
473485
474486 void push (std::string_view chunk) { parser_.push (chunk); }
@@ -484,30 +496,50 @@ namespace vix::commands
484496 void finish ()
485497 {
486498 parser_.finish ();
487- progress_.stop ();
499+ if (visible_)
500+ {
501+ std::cout << " \r\033 [2K" << std::flush;
502+ visible_ = false ;
503+ }
488504 }
489505
490506 private:
491507 void render (const vix::cli::util::GitProgressEvent &event)
492508 {
509+ // CI output must remain useful without terminal control sequences.
510+ const auto now = std::chrono::steady_clock::now ();
511+ // The first measurable Git event is deliberately not delayed. The
512+ // old delay made a real remote operation look hung.
513+ if (visible_ && event.phase == lastPhase_ && now - lastRender_ < std::chrono::milliseconds (100 ))
514+ return ;
493515 std::ostringstream line;
494- line << event. phase ;
516+ line << " " << CYAN << " • " << RESET << " " << CYAN << BOLD << dependency_ << RESET ;
495517 if (packageCount_ > 1 )
496- line << " " << dependency_ << " (" << (packageIndex_ + 1 ) << " /" << packageCount_ << " packages)" ;
497- else
498- line << " " << dependency_;
518+ line << " " << GRAY << " (" << (packageIndex_ + 1 ) << " /" << packageCount_ << " packages)" << RESET ;
519+ line << " " << GRAY << event.phase ;
499520 if (event.percent )
500521 line << " " << *event.percent << " %" ;
501522 if (!event.transferred .empty ())
502523 line << " " << event.transferred ;
503524 if (!event.speed .empty ())
504525 line << " " << event.speed ;
505- progress_.update (line.str ());
526+ line << RESET ;
527+ if (tty_)
528+ std::cout << " \r\033 [2K" << line.str () << std::flush;
529+ else
530+ std::cout << dependency_ << " : " << event.phase
531+ << (event.percent ? std::string (" " ) + std::to_string (*event.percent ) + " %" : " " ) << " \n "
532+ << std::flush;
533+ visible_ = true ;
534+ lastRender_ = now;
535+ lastPhase_ = event.phase ;
506536 }
507537 std::string dependency_;
508538 std::size_t packageIndex_{};
509539 std::size_t packageCount_{};
510- vix::cli::util::NetworkProgress progress_{dependency_};
540+ std::chrono::steady_clock::time_point started_, lastRender_{};
541+ bool tty_{false }, visible_{false };
542+ std::string lastPhase_;
511543 vix::cli::util::GitProgressParser parser_;
512544 };
513545
0 commit comments