Allow storing alternates elsewhere (#90)

This change allows alternates to be stored in "$YADM_DIR/alt". The
correct path within the work tree will be symlinked.

Storing alternates within the work tree is still allowed. Both locations
will be considered when choosing an appropriate alternate file.
This commit is contained in:
Tim Byrne 2019-10-10 08:23:36 -05:00
parent aeb6a54ad7
commit 4ea3ed9e2a
No known key found for this signature in database
GPG Key ID: 14DB4FC2465A4B12
4 changed files with 76 additions and 33 deletions

View File

@ -9,6 +9,7 @@ TEST_PATHS = [utils.ALT_FILE1, utils.ALT_FILE2, utils.ALT_DIR]
@pytest.mark.usefixtures('ds1_copy')
@pytest.mark.parametrize('yadm_alt', [True, False], ids=['alt', 'worktree'])
@pytest.mark.parametrize(
'tracked,encrypt,exclude', [
(False, False, False),
@ -17,32 +18,39 @@ TEST_PATHS = [utils.ALT_FILE1, utils.ALT_FILE2, utils.ALT_DIR]
(False, True, True),
], ids=['untracked', 'tracked', 'encrypted', 'excluded'])
def test_alt_source(
runner, yadm_y, paths,
tracked, encrypt, exclude):
runner, paths,
tracked, encrypt, exclude,
yadm_alt):
"""Test yadm alt operates on all expected sources of alternates"""
yadm_dir = setup_standard_yadm_dir(paths)
utils.create_alt_files(
paths, '##default', tracked=tracked, encrypt=encrypt, exclude=exclude)
run = runner(yadm_y('alt'))
paths, '##default', tracked=tracked, encrypt=encrypt, exclude=exclude,
yadm_alt=yadm_alt, yadm_dir=yadm_dir)
run = runner([paths.pgm, '-Y', yadm_dir, 'alt'])
assert run.success
assert run.err == ''
linked = utils.parse_alt_output(run.out)
basepath = yadm_dir.join('alt') if yadm_alt else paths.work
for link_path in TEST_PATHS:
source_file = link_path + '##default'
source_file_content = link_path + '##default'
source_file = basepath.join(source_file_content)
link_file = paths.work.join(link_path)
if tracked or (encrypt and not exclude):
assert paths.work.join(link_path).islink()
target = py.path.local(paths.work.join(link_path).readlink())
assert link_file.islink()
target = py.path.local(link_file.readlink())
if target.isfile():
assert paths.work.join(link_path).read() == source_file
assert str(paths.work.join(source_file)) in linked
assert link_file.read() == source_file_content
assert str(source_file) in linked
else:
assert paths.work.join(link_path).join(
utils.CONTAINED).read() == source_file
assert str(paths.work.join(source_file)) in linked
assert link_file.join(
utils.CONTAINED).read() == source_file_content
assert str(source_file) in linked
else:
assert not paths.work.join(link_path).exists()
assert str(paths.work.join(source_file)) not in linked
assert not link_file.exists()
assert str(source_file) not in linked
@pytest.mark.usefixtures('ds1_copy')
@ -55,9 +63,10 @@ def test_alt_source(
'##u.$tst_user', '##user.$tst_user',
])
def test_alt_conditions(
runner, yadm_y, paths,
runner, paths,
tst_sys, tst_distro, tst_host, tst_user, suffix):
"""Test conditions supported by yadm alt"""
yadm_dir = setup_standard_yadm_dir(paths)
# set the class
tst_class = 'testclass'
@ -72,7 +81,7 @@ def test_alt_conditions(
)
utils.create_alt_files(paths, suffix)
run = runner(yadm_y('alt'))
run = runner([paths.pgm, '-Y', yadm_dir, 'alt'])
assert run.success
assert run.err == ''
linked = utils.parse_alt_output(run.out)
@ -94,12 +103,13 @@ def test_alt_conditions(
@pytest.mark.parametrize('kind', ['builtin', '', 'envtpl', 'j2cli', 'j2'])
@pytest.mark.parametrize('label', ['t', 'template', 'yadm', ])
def test_alt_templates(
runner, yadm_y, paths, kind, label):
runner, paths, kind, label):
"""Test templates supported by yadm alt"""
yadm_dir = setup_standard_yadm_dir(paths)
suffix = f'##{label}.{kind}'
utils.create_alt_files(paths, suffix)
run = runner(yadm_y('alt'))
run = runner([paths.pgm, '-Y', yadm_dir, 'alt'])
assert run.success
assert run.err == ''
created = utils.parse_alt_output(run.out, linked=False)
@ -220,3 +230,11 @@ def test_template_overwrite_symlink(runner, yadm_y, paths, tst_sys):
assert not link.islink()
assert target.read().strip() == 'target'
assert link.read().strip() == 'test-data'
def setup_standard_yadm_dir(paths):
"""Configure a yadm home within the work tree"""
std_yadm_dir = paths.work.mkdir('.config').mkdir('yadm')
std_yadm_dir.join('repo.git').mksymlinkto(paths.repo, absolute=1)
std_yadm_dir.join('encrypt').mksymlinkto(paths.encrypt, absolute=1)
return std_yadm_dir

View File

@ -32,7 +32,8 @@ def set_local(paths, variable, value):
def create_alt_files(paths, suffix,
preserve=False, tracked=True,
encrypt=False, exclude=False,
content=None, includefile=False):
content=None, includefile=False,
yadm_alt=False, yadm_dir=None):
"""Create new files, and add to the repo
This is used for testing alternate files. In each case, a suffix is
@ -40,17 +41,19 @@ def create_alt_files(paths, suffix,
repo handling are dependent upon the function arguments.
"""
basepath = yadm_dir.join('alt') if yadm_alt else paths.work
if not preserve:
for remove_path in (ALT_FILE1, ALT_FILE2, ALT_DIR):
if paths.work.join(remove_path).exists():
paths.work.join(remove_path).remove(rec=1, ignore_errors=True)
assert not paths.work.join(remove_path).exists()
if basepath.join(remove_path).exists():
basepath.join(remove_path).remove(rec=1, ignore_errors=True)
assert not basepath.join(remove_path).exists()
new_file1 = paths.work.join(ALT_FILE1 + suffix)
new_file1 = basepath.join(ALT_FILE1 + suffix)
new_file1.write(ALT_FILE1 + suffix, ensure=True)
new_file2 = paths.work.join(ALT_FILE2 + suffix)
new_file2 = basepath.join(ALT_FILE2 + suffix)
new_file2.write(ALT_FILE2 + suffix, ensure=True)
new_dir = paths.work.join(ALT_DIR + suffix).join(CONTAINED)
new_dir = basepath.join(ALT_DIR + suffix).join(CONTAINED)
new_dir.write(ALT_DIR + suffix, ensure=True)
# Do not test directory support for jinja alternates
@ -65,9 +68,11 @@ def create_alt_files(paths, suffix,
test_path.write('\n' + content, mode='a', ensure=True)
assert test_path.exists()
_create_includefiles(includefile, paths, test_paths)
_create_includefiles(includefile, test_paths, basepath)
_create_tracked(tracked, test_paths, paths)
_create_encrypt(encrypt, test_names, suffix, paths, exclude)
prefix = '.config/yadm/alt/' if yadm_alt else ''
_create_encrypt(encrypt, test_names, suffix, paths, exclude, prefix)
def parse_alt_output(output, linked=True):
@ -86,10 +91,10 @@ def parse_alt_output(output, linked=True):
return parsed_list.values()
def _create_includefiles(includefile, paths, test_paths):
def _create_includefiles(includefile, test_paths, basepath):
if includefile:
for dpath in INCLUDE_DIRS:
incfile = paths.work.join(dpath + '/' + INCLUDE_FILE)
incfile = basepath.join(dpath + '/' + INCLUDE_FILE)
incfile.write(INCLUDE_CONTENT, ensure=True)
test_paths += [incfile]
@ -101,9 +106,11 @@ def _create_tracked(tracked, test_paths, paths):
os.system(f'GIT_DIR={str(paths.repo)} git commit -m "Add test files"')
def _create_encrypt(encrypt, test_names, suffix, paths, exclude):
def _create_encrypt(encrypt, test_names, suffix, paths, exclude, prefix):
if encrypt:
for encrypt_name in test_names:
paths.encrypt.write(f'{encrypt_name + suffix}\n', mode='a')
paths.encrypt.write(
f'{prefix + encrypt_name + suffix}\n', mode='a')
if exclude:
paths.encrypt.write(f'!{encrypt_name + suffix}\n', mode='a')
paths.encrypt.write(
f'!{prefix + encrypt_name + suffix}\n', mode='a')

7
yadm
View File

@ -33,6 +33,7 @@ YADM_ENCRYPT="encrypt"
YADM_ARCHIVE="files.gpg"
YADM_BOOTSTRAP="bootstrap"
YADM_HOOKS="hooks"
YADM_ALT="alt"
HOOK_COMMAND=""
FULL_COMMAND=""
@ -137,6 +138,11 @@ function score_file() {
target="$1"
filename="${target%%##*}"
conditions="${target#*##}"
if [ "${filename#$YADM_ALT/}" != "${filename}" ]; then
filename="${YADM_WORK}/${filename#$YADM_ALT/}"
fi
score=0
IFS=',' read -ra fields <<< "$conditions"
for field in "${fields[@]}"; do
@ -1223,6 +1229,7 @@ function configure_paths() {
YADM_ARCHIVE="$YADM_DIR/$YADM_ARCHIVE"
YADM_BOOTSTRAP="$YADM_DIR/$YADM_BOOTSTRAP"
YADM_HOOKS="$YADM_DIR/$YADM_HOOKS"
YADM_ALT="$YADM_DIR/$YADM_ALT"
# independent overrides for paths
if [ -n "$YADM_OVERRIDE_REPO" ]; then

13
yadm.1
View File

@ -478,6 +478,12 @@ condition. The number of conditions is the next largest factor in scoring.
Files with more conditions will always be favored. Any invalid condition will
disqualify that file completely.
If you don't care to have all versions of alternates stored in the same
directory as the generated symlink, you can place them in the
.I $HOME/.config/yadm/alt
directory. The generated symlink or processed template will be created using
same relative path.
Alternate linking may best be demonstrated by example. Assume the following
files are managed by yadm's repository:
@ -771,7 +777,7 @@ Otherwise it will be
.IR "$HOME/.config/yadm" .
The following are the default paths yadm uses for its own data.
These paths can be altered using universal options.
Most of these paths can be altered using universal options.
See the OPTIONS section for details.
.TP
.I $HOME/.config/yadm
@ -781,6 +787,11 @@ directory.
.I $YADM_DIR/config
Configuration file for yadm.
.TP
.I $YADM_DIR/alt
This is a directory to keep "alternate files" without having them side-by-side
with the resulting symlink or processed template. Alternate files placed in
this directory will be created relative to $HOME instead.
.TP
.I $YADM_DIR/repo.git
Git repository used by yadm.
.TP