Skip to content

Instantly share code, notes, and snippets.

@yunano
Last active September 6, 2015 16:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yunano/e9aaaddaf2f33b25ae76 to your computer and use it in GitHub Desktop.
Save yunano/e9aaaddaf2f33b25ae76 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
# ansible_best_practice.sh ver.20150907
#
# Copyright (c) 2015 yunano
# This software is released under the MIT License.
# http://opensource.org/licenses/mit-license.php
export MARKER='%'
export FROM='.'
export TO=''
export SHADOW='s'
export DIR_FALLBACK='c'
export ROLES='/roles'
export START=2
export DIV_PATTERN='^--- # '
export ALL=0
export DELETE=0
export DIVIDE=1
export GATHER=0
export GATHER_HIDDEN=0
export VERBOSE=0
usage() {
cat << EOD
Usage: bash $0 [Options]
Options:
-m MARKER marker indicate copy/link files started with MARKER
also treated as pseudo directory seperator
in file names (default: %)
-f FROM_DIR from-directory copy/link from FROM_DIR (default: .)
-t TO_DIR to-directory copy/link to TO_DIR
-s symbolic-link make symbolic links (default)
-H hard-link-fallback-copy
make hard links for files,
copy for directories if with -a
-S hard-link-fallback-symbolic
make hard links for files,
make symbolic links for directories if with -a
-c copy copy files
-p project-root deal FROM_DIR and TO_DIR as the project root
directory (default)
-r roles deal FROM_DIR and TO_DIR as the roles directory
-i individual-role deal FROM_DIR and TO_DIR as the individual role
directory
-a all-files also copy/link other than files indicated
by MARKER
this option is ignored if FROM_DIR == TO_DIR
-d delete-previous delete TO_DIR/roles/ before copy/link
if specified with -a, delete TO_DIR/*
if specified -g, delete TO_DIR/MARKER*
if specified with -r or -i, delete TO_DIR/*/
-D disable-divide disable yaml division
-g gather-mode gather mode aka reverse mode (implies -d)
-G gather-mode-including-hidden-directories
gather including hidden directories
-v verbose increase verbosity
-h help show this help
EOD
exit 2
}
while getopts ':m:f:t:sHScpriadDgGvh' opt
do
case $opt in
m)
MARKER=$OPTARG
;;
f)
FROM=$OPTARG
;;
t)
TO=$OPTARG
;;
s)
SHADOW='s'
;;
H)
SHADOW='h'
;;
S)
SHADOW='h'
DIR_FALLBACK='s'
;;
c)
SHADOW='c'
;;
p)
;;
r)
ROLES=''
;;
i)
ROLES=''
START=1
;;
a)
ALL=1
;;
d)
DELETE=1
;;
D)
DIVIDE=0
;;
g)
GATHER=1
DELETE=1
;;
G)
GATHER=1
GATHER_HIDDEN=1
DELETE=1
;;
v)
VERBOSE=1
;;
h | : | \?)
usage
break
;;
esac
done
make_shadow() {
file="${1##*/}"
if [ "$file" == "$MARKER" ]; then
[ $VERBOSE -eq 1 ] && echo "File '$MARKER' has been ignored"
return
fi
roles_dir="$TO$ROLES"
to_file_rel=$(awk -v name="$file" -v delim=$MARKER -v start=$START 'BEGIN { num = split(name, arr, delim); if (arr[num] == "" || arr[num] == ".yml") { arr[num] = "main.yml" }; if (num==start) { sub("\\.yml$", "", arr[start]); print "/" arr[start] "/tasks/main.yml" } else if (num==start+1) { if (arr[start] != "") { arr[start] = "/" arr[start] }; if (arr[num] ~ /\.yml$/) { print arr[start] "/tasks/" arr[num] } else { print arr[start] "/" arr[num] } } else if (num>=start+2) { str = ""; for (i=2; i<=num; i++) { str = str "/" arr[i] }; print str } }')
if ([ $DIVIDE -eq 1 ] && echo "$to_file_rel" | grep -q ".yml$" && grep -q "$DIV_PATTERN" "$1"); then
[ $START -eq 1 ] && role_dir="$roles_dir" || role_dir="$roles_dir/$(echo $to_file_rel | awk -F / '{ print $2 }')"
div_file=""
div_file_tmp=""
while IFS= read -r line
do
echo "$line" | grep -q "$DIV_PATTERN" && div_file_tmp="$role_dir/$(echo "$line" | sed 's/^.* //' | tr '\r\n' '\n')"
if [ "$div_file" != "$div_file_tmp" ]; then
div_file="$div_file_tmp"
mkdir -p "${div_file%/*}"
chmod u+rwx "${div_file%/*}"
: > "$div_file"
chmod u+rw "$div_file"
elif [ -n "$div_file" ]; then
echo "$line" >> "$div_file"
fi
done < "$1"
else
to_file="$roles_dir$to_file_rel"
mkdir -p "${to_file%/*}"
chmod u+rwx "${to_file%/*}"
[ $VERBOSE -eq 1 ] && echo "$1 -> $to_file"
if [ $2 -eq 1 ]; then
cp -P "$1" "$to_file"
elif [ $SHADOW == 'c' ]; then
cp "$1" "$to_file"
elif [ $SHADOW == 'h' ]; then
ln -f "$1" "$to_file"
else
ln -sf "$1" "$to_file"
fi
fi
}
export -f make_shadow
gather_shadow() {
from_file_rel="${1#$FROM$ROLES}"
if [ $START -eq 1 ]; then
role=""
role_rel="$from_file_rel"
else
role="$(echo $from_file_rel | awk -F / '{ print $2 }')"
role_rel="${from_file_rel#/$role}"
fi
if ([ $DIVIDE -eq 1 ] && echo "$role_rel" | grep -q ".yml$"); then
[ $VERBOSE -eq 1 ] && echo "Gather: $1 ->> $TO/$MARKER$role.yml"
echo "--- # ${role_rel#/}" >> "$TO/$MARKER$role.yml"
cat "$1" | awk '1' >> "$TO/$MARKER$role.yml"
else
to_file="$TO/$MARKER$role$(awk -v path="$role_rel" -v delim=$MARKER -v start=$START 'BEGIN { sub("/main\\.yml$", "/.yml", path); sub("^/tasks/", "/", path); sub("^/\\.yml$", ".yml", path); if (start==1) { sub("^/", "", path) }; gsub("/", delim, path); print path }')"
[ $VERBOSE -eq 1 ] && echo "$1 -> $to_file"
if [ $2 -eq 1 ]; then
cp -P "$1" "$to_file"
elif [ $SHADOW == 'c' ]; then
cp "$1" "$to_file"
elif [ $SHADOW == 'h' ]; then
ln -f "$1" "$to_file"
else
ln -sf "$1" "$to_file"
fi
fi
}
export -f gather_shadow
if [ -z "$MARKER" ]; then
echo "MARKER is empty" 1>&2
usage
fi
if [ ! -d "$FROM" ]; then
echo "Directory '$FROM' does not exist" 1>&2
usage
fi
if [ -z "$TO" ]; then
echo "Destination directory is empty" 1>&2
usage
fi
mkdir -p "$TO"
chmod u+rwx "$TO"
FROM=$(cd "$FROM"; pwd)
TO=$(cd "$TO"; pwd)
[ "$FROM" == "$TO" ] && ALL=0
[ $VERBOSE -eq 1 ] && echo "<< Copy/Link from '$FROM' to '$TO' >>"
if [ $DELETE -eq 1 ]; then
if [ $ALL -eq 1 ]; then
[ $VERBOSE -eq 1 ] && echo "Delete '$TO/*'"
find "$TO" -mindepth 1 -maxdepth 1 -print0 | xargs -0 -I ^ rm -rf "^"
elif [ $GATHER -eq 1 ]; then
[ $VERBOSE -eq 1 ] && echo "Delete '$TO/$MARKER*'"
find "$TO" -maxdepth 1 -name "${MARKER}* -type f" -print0 | xargs -0 -I ^ rm -f "^"
elif [ "$ROLES" == '/roles' ]; then
[ $VERBOSE -eq 1 ] && echo "Delete '$TO/roles'"
rm -rf "$TO/roles"
else
[ $VERBOSE -eq 1 ] && echo "Delete '$TO/*/'"
find "$TO" -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 -I ^ rm -rf "^"
fi
fi
if [ $ALL -eq 1 ]; then
[ $VERBOSE -eq 1 ] && echo "Copy/Link '$FROM/*' to '$TO'"
if [ $SHADOW == 'c' ]; then
find "$FROM" -mindepth 1 -type f | while IFS= read -r file
do
[ "$file" == "${file#$FROM/$MARKER}" ] && cp "$file" "$TO"
done
find "$FROM" -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 -I ^ cp -RP "^" "$TO"
elif [ $SHADOW == 'h' ]; then
find "$FROM" -mindepth 1 -type f | while IFS= read -r file
do
[ "$file" == "${file#$FROM/$MARKER}" ] && ln -f "$file" "$TO"
done
if [ $DIR_FALLBACK == 's' ]; then
find "$FROM" -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 -I ^ ln -sf "^" "$TO"
else
find "$FROM" -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 -I ^ cp -RP "^" "$TO"
fi
else
find "$FROM" -mindepth 1 -type f | while IFS= read -r file
do
[ "$file" == "${file#$FROM/$MARKER}" ] && ln -sf "$file" "$TO"
done
find "$FROM" -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 -I ^ ln -sf "^" "$TO"
fi
find "$FROM" -mindepth 1 -maxdepth 1 -type l | while IFS= read -r slink
do
if ([ -d "$slink" ] || echo "$slink" | fgrep -vq "$FROM/$MARKER"); then
cp -RP "$slink" "$TO"
fi
done
chmod -R u+rw "$TO"
find "$TO" -mindepth 1 -type d -print0 | xargs -0 -I ^ chmod u+x "^"
fi
if [ $GATHER -eq 0 ]; then
find "$FROM" -maxdepth 1 -type f -print0 -name "${MARKER}*" | xargs -0 -I ^ bash -c 'make_shadow "^" 0'
find "$FROM" -mindepth 1 -maxdepth 1 -type l | while IFS= read -r slink
do
([ -f "$slink" ] && echo "$slink" | fgrep -q "$FROM/$MARKER") && make_shadow "$slink" 1
done
else
find "$FROM$ROLES" -mindepth 1 -maxdepth 1 -type d | while IFS= read -r role
do
: > "$TO/$MARKER${role#$FROM$ROLES/}.yml"
done
find "$FROM$ROLES" -mindepth 2 -type f | while IFS= read -r file
do
[ $GATHER_HIDDEN -eq 1 -o "$file" = "${file#$FROM$ROLES/.}" ] && gather_shadow "$file" 0
done
find "$FROM$ROLES" -mindepth 2 -type l | while IFS= read -r slink
do
[ -f "$slink" ] && [ $GATHER_HIDDEN -eq 1 -o "$slink" = "${file#$FROM$ROLES/.}" ] && gather_shadow "$slink" 1
done
fi
chmod -R u+rw "$TO"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment