Created
December 4, 2019 20:15
-
-
Save tomdavidson/f7d196d8e7c1f20698e48705f42c8dc1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
help() { | |
cat <<EOF | |
Simple monorepo lifecycle/pipeline tool for running one or more commands on one | |
or more directories that have diffs compared to an ansector. The primary | |
use case is for selective CI jobs within a trunk based workflow. | |
Takes two arguments, <glob> <command>. The command is invoked from each | |
directory context matching the glob. | |
Usage: | |
lolaus "./tests/* :(top,exclude)**requirements.txt" ls | |
lolaus "**" pwd | |
lolaus "*/*/package.json" npm test & lolaus "*/*/requirements.txt" "python test.py" | |
lolaus "apps/*/index.js" | |
lolaus "**" ls target-branch other-branch | |
EOF | |
exit 1 | |
} | |
# lolaus <cmd> [glob] [target_ref] [source_ref] | |
function main() { | |
[[ $1 == "-h" || $1 == "--help" ]] && help | |
local GLOB | |
GLOB=${1:-'**'} | |
local CMD | |
CMD="$2" | |
local TARGET_REF | |
TARGET_REF=$(get-target-ref "$3") | |
[[ ${DEBUG} =~ ^(true|TRUE) ]] && echo target: $TARGET_REF | |
local SOURCE_REF | |
SOURCE_REF=$(get-source-ref "$4") | |
[[ ${DEBUG} =~ ^(true|TRUE) ]] && echo source: $SOURCE_REF | |
local CONCESTOR | |
CONCESTOR=$(get-concestor $TARGET_REF $SOURCE_REF) | |
[[ ${DEBUG} =~ ^(true|TRUE) ]] && echo concestor: $CONCESTOR | |
local DIFFS | |
DIFFS=$(get-diffs $CONCESTOR $SOURCE_REF) | |
[[ ${DEBUG} =~ ^(true|TRUE) ]] && echo diffs: $DIFFS | |
local DIRS | |
DIRS=$(files-to-dirs "$DIFFS") | |
[[ ${DEBUG} =~ ^(true|TRUE) ]] && echo dirs: $DIRS | |
[[ x"$2" == "x" ]] && echo "$DIRS" || cmd-runner "$CMD" "$DIRS" | |
} | |
# Invokes the provided command in each provided direcotry | |
# cmd-runner <cmd> <dirs> | |
cmd-runner() { | |
: ${1? "Please provide comands to run."} | |
: ${2? "Please provide directories to run in."} | |
local cmd | |
cmd="$1" | |
declare -a local dirs | |
dirs=($2) | |
for d in "${dirs[@]}"; do | |
[[ ! -d $d ]] && echo "cmd-runner only works on dirs, not: $d" && exit 1 | |
local cmds="cd $d; $cmd" | |
output="$(eval "$cmds" 2>&1)" | |
[[ x"$output" == "x" ]] && output="$cmd produced no output" | |
[[ $d == "$PWD" ]] && d='./' | |
relative=${d/$(echo "$PWD/")/} | |
echo -e "$(tput smso) $relative $(tput sgr0)\n$output\n" | |
done | |
} | |
# Returns list of sorted and unique containing directories from a list of files. | |
# files-to-dirs <files> | |
files-to-dirs() { | |
declare -a local files | |
files=($1) | |
local path | |
path=$(pwd) | |
declare -a local dirs | |
declare -a local sorted | |
for file in "${files[@]}"; do | |
if [ -n "${file##*/*}" ]; then | |
dirs+=("$path") | |
else | |
dirs+=("$path/${file%/*}") | |
fi | |
done | |
IFS=$'\n' sorted=($(sort -u <<<"${dirs[*]}")) | |
unset IFS | |
echo "${sorted[*]}" | |
} | |
# Determines the closest common ansester from two git refs with a default | |
# strategy of 'fork-point' | |
# get-concestor <LEFT_REF> <RIGHT_REF> [STRATEGY] | |
get-concestor() { | |
local left_ref=${1} | |
local right_ref=${2} | |
local strategy=${3:-'fp'} | |
local concestor | |
case ${strategy} in | |
fp) concestor=$(git merge-base --fork-point $left_ref $right_ref) ;; | |
esac | |
echo $concestor | |
} | |
# Returns a list of files with diffs between two git refs with optional glob. | |
# get-diffs <left_ref> <right_ref> [glob] | |
get-diffs() { | |
local left_ref=$1 | |
local right_ref=$2 | |
local glob=${3:-"**"} | |
local diffs | |
diffs=$(git diff --name-only $left_ref..$right_ref -- $glob) | |
echo $diffs | |
} | |
# Returns the verified git reference to target. Defaults to current ref. | |
# get-source-ref [branch name] | |
get-source-ref() { | |
local ref=$1 | |
if [ ! -z "$ref" ]; then | |
if ! git rev-parse --quiet --verify $ref &>/dev/null; then | |
echo "Source git ref, $ref, is not valid" | |
exit 1 | |
fi | |
else | |
ref=$(git symbolic-ref --short HEAD) | |
fi | |
echo $ref | |
} | |
# Returns the verified git reference to target. Default to 'next' then 'master' | |
# get-target-ref [branch name] | |
get-target-ref() { | |
local REF=$1 | |
if [ ! -z "$ref" ]; then | |
if ! git rev-parse --quiet --verify $ref &>/dev/null; then | |
echo "Targeted git ref, $ref, is not valid" | |
return 1 | |
fi | |
else | |
if git rev-parse --quiet --verify next &>/dev/null; then | |
ref='next' | |
elif git rev-parse --quiet --verify master &>/dev/null; then | |
ref='master' | |
else | |
return 1 | |
fi | |
fi | |
echo $ref | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment