55
66set -euo pipefail
77
8+ # Exit cleanly on SIGPIPE (e.g., mars clone | grep, mars status | head)
9+ trap ' exit 0' PIPE
10+
811MARS_VERSION=" 0.1.1"
912
1013
@@ -289,6 +292,11 @@ ui_spinner_start() {
289292 local message=" $1 "
290293 _SPINNER_MSG=" $message "
291294
295+ # Skip spinner animation when stdout is not a terminal (piped/redirected)
296+ if [[ ! -t 1 ]]; then
297+ return
298+ fi
299+
292300 (
293301 local i=0
294302 local frame_count=${# S_SPINNER_FRAMES[@]}
@@ -314,8 +322,10 @@ ui_spinner_stop() {
314322 fi
315323 _SPINNER_PID=" "
316324
317- # Clear spinner line
318- printf ' \r\033[K'
325+ # Clear spinner line only when connected to a terminal
326+ if [[ -t 1 ]]; then
327+ printf ' \r\033[K'
328+ fi
319329
320330 # Show final message if provided
321331 if [[ -n " $final_message " ]]; then
@@ -332,7 +342,9 @@ ui_spinner_error() {
332342 fi
333343 _SPINNER_PID=" "
334344
335- printf ' \r\033[K'
345+ if [[ -t 1 ]]; then
346+ printf ' \r\033[K'
347+ fi
336348 ui_step_error " $message "
337349}
338350
@@ -1140,10 +1152,7 @@ EOF
11401152
11411153# === lib/commands/clone.sh ===
11421154# Mars CLI - clone command
1143- # Clone configured repositories with parallel execution
1144-
1145- # Maximum concurrent clone jobs
1146- CLONE_PARALLEL_LIMIT= 4
1155+ # Clone configured repositories with per-repo progress
11471156
11481157cmd_clone () {
11491158 local tag=" "
@@ -1184,142 +1193,75 @@ cmd_clone() {
11841193 return 1
11851194 fi
11861195
1187- # Count repos
1196+ # Count total repos
11881197 local total=0
1189- local to_clone=()
1190- local already_cloned=()
1191-
11921198 while IFS= read -r repo; do
11931199 [[ -z " $repo " ]] && continue
11941200 total=$(( total + 1 ))
1195-
1196- local path
1197- path=$( yaml_get_path " $repo " )
1198- local full_path=" $MARS_REPOS_DIR /$path "
1199-
1200- if [[ -d " $full_path " ]] && [[ $force -eq 0 ]]; then
1201- already_cloned+=(" $repo " )
1202- else
1203- to_clone+=(" $repo " )
1204- fi
12051201 done <<< " $repos"
12061202
1207- # Report already cloned
1208- for repo in " ${already_cloned[@]} " ; do
1209- local path
1210- path=$( yaml_get_path " $repo " )
1211- ui_step_done " Already cloned:" " $path "
1212- done
1213-
1214- if [[ ${# to_clone[@]} -eq 0 ]]; then
1215- ui_outro " All repositories already cloned"
1216- return 0
1217- fi
1218-
1219- ui_bar_line
1220- ui_info " Cloning ${# to_clone[@]} of $total repositories..."
1221- ui_bar_line
1222-
1223- # Clone with parallelism
1224- local pids=()
1225- local repo_for_pid=()
1203+ local current=0
12261204 local success_count=0
1205+ local skip_count=0
12271206 local fail_count=0
1228- local failed_repos=()
12291207
1230- for repo in " ${to_clone[@]} " ; do
1208+ # Clone each repo with spinner feedback
1209+ while IFS= read -r repo; do
1210+ [[ -z " $repo " ]] && continue
1211+ current=$(( current + 1 ))
1212+
12311213 local url
12321214 url=$( yaml_get_url " $repo " )
12331215 local path
12341216 path=$( yaml_get_path " $repo " )
12351217 local full_path=" $MARS_REPOS_DIR /$path "
12361218
1219+ # Already cloned?
1220+ if [[ -d " $full_path " ]] && [[ $force -eq 0 ]]; then
1221+ ui_step_done " Already cloned:" " $path "
1222+ skip_count=$(( skip_count + 1 ))
1223+ continue
1224+ fi
1225+
12371226 # Remove existing directory if force
12381227 if [[ -d " $full_path " ]] && [[ $force -eq 1 ]]; then
12391228 rm -rf " $full_path "
12401229 fi
12411230
1242- # Wait if at parallel limit
1243- while [[ ${# pids[@]} -ge $CLONE_PARALLEL_LIMIT ]]; do
1244- _clone_wait_one
1245- done
1231+ # Show spinner while cloning
1232+ ui_spinner_start " Cloning $path ... ($current /$total )"
12461233
1247- # Start clone in background
1248- (
1249- if git clone --quiet " $url " " $full_path " 2> /dev/null; then
1250- exit 0
1251- else
1252- exit 1
1234+ local clone_err
1235+ if clone_err=$( git clone --quiet " $url " " $full_path " 2>&1 ) ; then
1236+ ui_spinner_stop
1237+ ui_step_done " Cloned:" " $path "
1238+ success_count=$(( success_count + 1 ))
1239+ else
1240+ ui_spinner_error " Failed to clone: $path "
1241+ if [[ -n " $clone_err " ]]; then
1242+ ui_info " $( ui_dim " $clone_err " ) "
12531243 fi
1254- ) &
1255-
1256- pids+=($! )
1257- repo_for_pid+=(" $repo " )
1258- done
1259-
1260- # Wait for remaining jobs
1261- while [[ ${# pids[@]} -gt 0 ]]; do
1262- _clone_wait_one
1263- done
1244+ fail_count=$(( fail_count + 1 ))
1245+ fi
1246+ done <<< " $repos"
12641247
12651248 # Summary
12661249 ui_bar_line
12671250
12681251 if [[ $fail_count -eq 0 ]]; then
1269- ui_outro " Cloned $success_count repositories successfully"
1252+ local msg=" Cloned $success_count repositories successfully"
1253+ if [[ $skip_count -gt 0 ]]; then
1254+ msg=" $msg , $skip_count already cloned"
1255+ fi
1256+ ui_outro " $msg "
12701257 else
1271- for repo in " ${failed_repos[@]} " ; do
1272- local path
1273- path=$( yaml_get_path " $repo " )
1274- ui_step_error " Failed: $path "
1275- done
12761258 ui_outro_cancel " Cloned $success_count , failed $fail_count "
12771259 return 1
12781260 fi
12791261
12801262 return 0
12811263}
12821264
1283- # Helper to wait for one clone job
1284- _clone_wait_one () {
1285- if [[ ${# pids[@]} -eq 0 ]]; then
1286- return
1287- fi
1288-
1289- # Wait for any process to complete
1290- local pid
1291- for i in " ${! pids[@]} " ; do
1292- pid=" ${pids[$i]} "
1293- if ! kill -0 " $pid " 2> /dev/null; then
1294- # Process finished
1295- wait " $pid "
1296- local exit_code=$?
1297- local repo=" ${repo_for_pid[$i]} "
1298- local path
1299- path=$( yaml_get_path " $repo " )
1300-
1301- if [[ $exit_code -eq 0 ]]; then
1302- ui_step_done " Cloned:" " $path "
1303- success_count=$(( success_count + 1 ))
1304- else
1305- ui_step_error " Failed to clone: $path "
1306- fail_count=$(( fail_count + 1 ))
1307- failed_repos+=(" $repo " )
1308- fi
1309-
1310- # Remove from arrays
1311- unset ' pids[i]'
1312- unset ' repo_for_pid[i]'
1313- pids=(" ${pids[@]} " )
1314- repo_for_pid=(" ${repo_for_pid[@]} " )
1315- return
1316- fi
1317- done
1318-
1319- # If all still running, sleep briefly
1320- sleep 0.1
1321- }
1322-
13231265# === lib/commands/status.sh ===
13241266# Mars CLI - status command
13251267# Show git status across all repositories
@@ -1767,9 +1709,13 @@ cmd_exec() {
17671709 return 1
17681710 ;;
17691711 * )
1770- # Everything else is the command
1771- command=" $* "
1772- break
1712+ if [[ -z " $command " ]]; then
1713+ command=" $1 "
1714+ else
1715+ ui_step_error " Unexpected argument: $1 "
1716+ return 1
1717+ fi
1718+ shift
17731719 ;;
17741720 esac
17751721 done
0 commit comments