Add jinja processing to alt command

With the new functionality, when the 'alt' command is called (or automatically
triggered), any file with a name ending in '##yadm_tmpl' is treated as a jinja
template. The template is processed by envtpl and the result is written to a
file without the '##yadm_tmpl' name. The variables passed into the template
processing are

  YADM_CLASS
  YADM_OS
  YADM_HOSTNAME
  YADM_USER

These variables are set according to the normal rules for
CLASS, OS, HOSTNAME, and USER during the alt processing.
This commit is contained in:
Jan Schulz 2017-03-25 19:26:10 +01:00
parent 5678e383d8
commit a479b70d8a
4 changed files with 233 additions and 16 deletions

View File

@ -0,0 +1,151 @@
load common
load_fixtures
status=;output=; #; populated by bats run()
IN_REPO=(alt* "dir one")
export TEST_TREE_WITH_ALT=1
setup() {
destroy_tmp
build_repo "${IN_REPO[@]}"
}
function test_alt() {
local alt_type="$1"
local test_overwrite="$2"
local auto_alt="$3"
#; detemine test parameters
case $alt_type in
base)
real_name="alt-jinja"
file_content_match="-${T_SYS}-${T_HOST}-${T_USER}"
;;
override_all)
real_name="alt-jinja"
file_content_match="custom_class-custom_system-custom_host-custom_user"
;;
esac
if [ "$test_overwrite" = "true" ] ; then
#; create incorrect links (to overwrite)
echo "BAD_CONTENT" "$T_DIR_WORK/$real_name"
else
#; verify real file doesn't already exist
if [ -e "$T_DIR_WORK/$real_name" ] ; then
echo "ERROR: real file already exists before running yadm"
return 1
fi
fi
#; configure yadm.auto_alt=false
if [ "$auto_alt" = "false" ]; then
git config --file="$T_YADM_CONFIG" yadm.auto-alt false
fi
#; run yadm (alt or status)
if [ -z "$auto_alt" ]; then
run "${T_YADM_Y[@]}" alt
#; validate status and output
if [ "$status" != 0 ] || [[ ! "$output" =~ Creating.+$real_name ]]; then
echo "OUTPUT:$output"
echo "ERROR: Could not confirm status and output of alt command"
return 1;
fi
else
#; running any passed through Git command should trigger auto-alt
run "${T_YADM_Y[@]}" status
if [ -n "$auto_alt" ] && [[ "$output" =~ Creating.+$real_name ]]; then
echo "ERROR: Reporting of jinja processing should not happen"
return 1
fi
fi
#; validate link content
if [[ "$alt_type" =~ none ]] || [ "$auto_alt" = "false" ]; then
#; no real file should be present
if [ -L "$T_DIR_WORK/$real_name" ] ; then
echo "ERROR: Real file should not exist"
return 1
fi
else
#; correct real file should be present
local file_content
file_content=$(cat "$T_DIR_WORK/$real_name")
if [ "$file_content" != "$file_content_match" ]; then
echo "file_content: ${file_content}"
echo "expected_content: ${file_content_match}"
echo "ERROR: Link content is not correct"
return 1
fi
fi
}
@test "Command 'alt' (select jinja)" {
echo "
When the command 'alt' is provided
and file matches ##yadm_tmpl
Report jinja template processing
Verify that the correct content is written
Exit with 0
"
test_alt 'base' 'false' ''
}
@test "Command 'auto-alt' (enabled)" {
echo "
When a command possibly changes the repo
and auto-alt is configured true
and file matches ##yadm_tmpl
automatically process alternates
report no linking (not loud)
Verify that the correct content is written
"
test_alt 'base' 'false' 'true'
}
@test "Command 'auto-alt' (disabled)" {
echo "
When a command possibly changes the repo
and auto-alt is configured false
and file matches ##yadm_tmpl
Report no jinja template processing
Verify no content
"
test_alt 'base' 'false' 'false'
}
@test "Command 'alt' (overwrite existing content)" {
echo "
When the command 'alt' is provided
and file matches ##yadm_tmpl
and the real file exists, and is wrong
Report jinja template processing
Verify that the correct content is written
Exit with 0
"
test_alt 'base' 'true' ''
}
@test "Command 'alt' (overwritten settings)" {
echo "
When the command 'alt' is provided
and file matches ##yadm_tmpl
after setting local.*
Report jinja template processing
Verify that the correct content is written
Exit with 0
"
GIT_DIR="$T_DIR_REPO" git config local.os custom_system
GIT_DIR="$T_DIR_REPO" git config local.user custom_user
GIT_DIR="$T_DIR_REPO" git config local.host custom_host
GIT_DIR="$T_DIR_REPO" git config local.class custom_class
test_alt 'override_all' 'false' ''
}

View File

@ -202,8 +202,10 @@ function create_worktree() {
make_parents "$DIR_WORKTREE/$f"
echo "$f" > "$DIR_WORKTREE/$f"
done
echo "{{ YADM_CLASS }}-{{ YADM_OS }}-{{ YADM_HOSTNAME }}-{{ YADM_USER }}" > "$DIR_WORKTREE/alt-jinja##yadm_tmpl"
fi
if [ ! -z "$TEST_TREE_WITH_WILD" ] ; then
#; wildcard test data - yes this is a big mess :(
#; none

56
yadm
View File

@ -114,30 +114,32 @@ function alt() {
require_repo
match_class="$(config local.class)"
if [ -z "$match_class" ] ; then
local_class="$(config local.class)"
if [ -z "$local_class" ] ; then
match_class="%"
else
match_class="$local_class"
fi
match_class="(%|$match_class)"
match_system="$(config local.os)"
if [ -z "$match_system" ] ; then
match_system=$(uname -s)
local_system="$(config local.os)"
if [ -z "$local_system" ] ; then
local_system=$(uname -s)
fi
match_system="(%|$match_system)"
match_system="(%|$local_system)"
match_host="$(config local.host)"
if [ -z "$match_host" ] ; then
match_host=$(hostname)
match_host=${match_host%%.*} #; trim any domain from hostname
local_host="$(config local.host)"
if [ -z "$local_host" ] ; then
local_host=$(hostname)
local_host=${local_host%%.*} #; trim any domain from hostname
fi
match_host="(%|$match_host)"
match_host="(%|$local_host)"
match_user="$(config local.user)"
if [ -z "$match_user" ] ; then
match_user=$(id -u -n)
local_user="$(config local.user)"
if [ -z "$local_user" ] ; then
local_user=$(id -u -n)
fi
match_user="(%|$match_user)"
match_user="(%|$local_user)"
#; regex for matching "<file>##CLASS.SYSTEM.HOSTNAME.USER"
match1="^(.+)##(()|$match_system|$match_system\.$match_host|$match_system\.$match_host\.$match_user)$"
@ -193,6 +195,30 @@ function alt() {
done
done
#; loop over all "tracked" files
#; for every file which is a *##yadm_tmpl create a real file
local IFS=$'\n'
local match="^(.+)##yadm_tmpl"
local envtpl_bin=$(which envtpl)
for tracked_file in $("$GIT_PROGRAM" ls-files | sort) $(cat "$YADM_ENCRYPT" 2>/dev/null); do
tracked_file="$YADM_WORK/$tracked_file"
if [ -e "$tracked_file" ] ; then
if [[ $tracked_file =~ $match ]] ; then
if [[ -z "$envtpl_bin" ]]; then
debug "'envtpl' (pip install envtpl) not available, not creating $real_file from template $tracked_file"
[ -n "$loud" ] && echo "'envtpl' (pip install envtpl) not available, not creating $real_file from template $tracked_file"
else
real_file="${BASH_REMATCH[1]}"
debug "Creating $real_file from template $tracked_file"
[ -n "$loud" ] && echo "Creating $real_file from template $tracked_file"
YADM_CLASS="$local_class" YADM_OS="$local_system" \
YADM_HOSTNAME="$local_host" YADM_USER="$local_user" \
$envtpl_bin < $tracked_file > $real_file
fi
fi
fi
done
}
function bootstrap() {

40
yadm.1
View File

@ -89,7 +89,8 @@ Instead use the
command (see below).
.TP
.B alt
Create symbolic links for any managed files matching the naming rules describe in the ALTERNATES section.
Create symbolic links and process jinja templates for any managed files matching the naming rules describe
in the ALTERNATES section.
It is usually unnecessary to run this command, as
.B yadm
automatically processes alternates by default.
@ -473,6 +474,43 @@ using the configuration options
and
.BR local.user .
If
.BR envtpl
(
.BR pip\ install\ envtpl
) is available, you can also create
.B jinja
templates (http://jinja.pocoo.org/) which will transformed into real files.
.B yadm
will treat files ending in
##yadm_tmpl
as jinja templates. During processing, the following variables are
set according to the above rules:
YADM_CLASS
YADM_OS
YADM_HOSTNAME
YADM_USER
E.g. a file 'whatever##yadm_tmpl' with the following content
{% if YADM_USER == 'harvey' -%}
config={{YADM_CLASS}}-{{ YADM_OS }}
{% else -%}
config=dev-whatever
{% endif -%}
would output a file with the follwing content, if the username would be 'harvey'
config=work-Linux
and the following otherwise:
config=dev-whatever
.SH ENCRYPTION
It can be useful to manage confidential files, like SSH or GPG keys, across
multiple systems. However, doing so would put plain text data into a Git