Skip to content

Instantly share code, notes, and snippets.

@danyg
Last active January 5, 2023 15:56
Show Gist options
  • Save danyg/670c1aa0677b0ee50e834f63bb388be5 to your computer and use it in GitHub Desktop.
Save danyg/670c1aa0677b0ee50e834f63bb388be5 to your computer and use it in GitHub Desktop.
RUN your checks in parallel

Run your checks in parallel

This script allows you to

  • Do all the CI checks in parallel
  • Give it check a name (and emojis 😜)
  • Only show stdout and sterr of the first one to fail
  • Stops the build on the first check failed
  • Known which step is being checked (check Current Job perform explanation to reduce anxiety)

How to use

  1. Copy checks.sh and enqueJobs.sh to your project
  2. Give execute permisions to those files chmod +x
  3. Remove examples from checks.sh
  4. Add enqueJob "[full commands with arguments inside double quotes]" "[Emojis and Name of the Step with spaces inside double quotes]"
  5. Repeat point 4 for each step you need to do
  6. Execute it, add it to your CI pipeline, and enjoy!

Current Job perform explanation

As all the checks run in the background and therefore in parallel, the script start waiting until the first job finish, then the second, etc...

The first one might take longer than the second, so in the output, you will see Waiting for Job 1 ... but the second is done already, then you will see Waiting for Job 2 ... and instantly you'll see Job 2 Done!

The idea of showing which process is being Waited is just to give some sort of output that the thing is doing something and show some progress to reduce anxiety, if not the whole thing would not output anything until the whole checks are done or something had failed.

#!/bin/bash
source "$(dirname $0)/enqueJobs.sh"
export CI=1
enqueJob "npm run feature-flag:stale-check" "🚩 Feature Flag Checks"
enqueJob "npm run test:ci -- --colors" "πŸ§ͺ Unit-Tests"
enqueJob "npm run build" "πŸ“¦ Typescript Compilation"
enqueJob "npm run lint" "πŸ” Lint"
enqueJob "npm run cy:run-ct" "πŸ§ͺ Cypress Components Tests"
displayAndExitOnEnquedJobs
#!/bin/sh
# -- [ INIT ]------------------------------------------------------------------
NO_OK="❌"
OK="βœ…"
pids=()
jobNames=()
stdoutsFiles=()
pidsFinished=()
pidsFailed=()
startTimes=()
function cleanup {
for file in "${stdoutsFiles[@]}"; do
rm $file
done
}
function enqueJob {
jobNames+=("${2}")
tmpfile=$(mktemp)
stdoutsFiles+=($tmpfile)
pidsFinished+=(false)
pidsFailed+=(false)
startTimes+=(`date +%s`)
$1 >>$tmpfile 2>>$tmpfile &
pids+=($!)
}
# -- [ PARALLEL CHECKS AND ERROR OUTPUT ]--------------------------------------
function displayAndExitOnEnquedJobs {
echo ""
for i in "${!jobNames[@]}"; do
pid=${pids[$i]}
jobName=${jobNames[$i]}
echo "Enqueued: ${pid} | ${jobName}"
done
echo -e "\nProcessing only output of errored jobs will be shown...\n"
error=false
errorJobName=''
errorJob=-1
loadingFrames=(πŸŒ‘ 🌘 πŸŒ— πŸŒ– πŸŒ• πŸŒ” πŸŒ“ πŸŒ’)
loadingFrames2=("\033[1;31m.\033[1;32m.\033[1;33m.\033[0m" "\033[1;32m.\033[1;33m.\033[1;34m.\033[0m" "\033[1;33m.\033[1;34m.\033[1;35m.\033[0m" "\033[1;34m.\033[1;35m.\033[1;36m.\033[0m" "\033[1;35m.\033[1;36m.\033[1;31m.\033[0m" "\033[1;36m.\033[1;31m.\033[1;32m.\033[0m")
l1=0
l2=0
jobsDone=0
echo -e "\033[?25l\033[s" # save cursor position and hide cursor
while [ ! $jobsDone -eq ${#pids[@]} ]; do
l1=$(($l1 + 1))
if [ $(expr $l1 % ${#loadingFrames[@]}) -eq 0 ]; then
l1=0
fi
l2=$(($l2 + 1))
if [ $(expr $l2 % ${#loadingFrames2[@]}) -eq 0 ]; then
l2=0
fi
echo -e "\n\033[u"
# print the list of task
for i in "${!pids[@]}"; do # for each task
icon=${loadingFrames[$l1]}
dots=${loadingFrames2[$l2]}
jobColor="0;37"
pid=${pids[$i]}
jobName=${jobNames[$i]}
pidFinished=${pidsFinished[$i]}
elapsedTime='?'
isOpen=$(kill -0 $pid 2>&1)
S="WAIT "
if ! $pidFinished; then
startTime=${startTimes[$i]}
elapsedTimeSeconds=$((`date +%s`-startTime))
elapsedTime=`date -ur ${elapsedTimeSeconds} +%T`
elapsedTimeLabel="${elapsedTime}"
else
elapsedTimeLabel="${startTimes[$i]}"
fi
if $pidFinished; then
if ${pidsFailed[$i]}; then
icon=$NO_OK
jobColor="1;31"
else
icon=$OK
jobColor="1;32"
fi
dots=" "
elif [[ ! -z "$isOpen" ]]; then # if pid is closed
# if ! $pidFinished; then
pidsFinished[$i]=true
jobsDone=$(($jobsDone + 1))
startTimes[$i]=$elapsedTime
elapsedTimeLabel="${startTimes[$i]}"
# fi
if ! wait $pid; then # check exit status code if is bad
S="ERROR "
icon=$NO_OK # anounce error
jobColor="1;31"
error=true
errorJobName="${jobName}"
errorJob=$i
pidsFailed[$i]=true
else
S="SUCCESS"
jobColor="1;32"
icon=$OK
dots=" "
fi
fi
echo -e "$icon ${elapsedTimeLabel} \033[${jobColor}m${jobName}\033[0m ${dots} "
# echo -e "\r\033[1;36m$icon\033[0m \033[1;37m${jobName}\033[0m ${jobsDone}/$(($i + 1))/${#pids[@]} $S $pidFinished ${dots} "
done
sleep 0.08
if $error; then
break
fi
done
echo -e "\033[?25h" # show cursor
if $error; then
echo -e "\n\033[1;31m# Error detected in Job \033[1;37m${errorJobName}\033[1;31m !!!\033[0m\n"
for i in "${!pids[@]}"; do
pid=${pids[$i]}
jobName=${jobNames[$i]}
echo "Stopping ${jobName} @ ${pid}"
shh=$(kill -9 -- $pid >/dev/null 2>/dev/null) # kills process tree so no extra stdout outputs or open processes
done
echo -e "\n${errorJobName} Output:\n"
stdoutFile=${stdoutsFiles[$errorJob]}
cat $stdoutFile
echo -e "\033[0;36m\n"
echo "################################################################################"
echo "#"
echo -e "# \033[0;33mFailure on: \033[1;37m${errorJobName}\033[0;36m"
echo "#"
echo "################################################################################"
echo -e "\033[0m\n"
cleanup
exit -1
fi
for i in "${!pids[@]}"; do
pid=${pids[$i]}
kill -9 -- $pid >/dev/null 2>&1 # kills process tree so no extra stdout outputs or open processes
done
cleanup
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment