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:
parent
aeb6a54ad7
commit
4ea3ed9e2a
4 changed files with 76 additions and 33 deletions
|
@ -9,6 +9,7 @@ TEST_PATHS = [utils.ALT_FILE1, utils.ALT_FILE2, utils.ALT_DIR]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('ds1_copy')
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
|
@pytest.mark.parametrize('yadm_alt', [True, False], ids=['alt', 'worktree'])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'tracked,encrypt,exclude', [
|
'tracked,encrypt,exclude', [
|
||||||
(False, False, False),
|
(False, False, False),
|
||||||
|
@ -17,32 +18,39 @@ TEST_PATHS = [utils.ALT_FILE1, utils.ALT_FILE2, utils.ALT_DIR]
|
||||||
(False, True, True),
|
(False, True, True),
|
||||||
], ids=['untracked', 'tracked', 'encrypted', 'excluded'])
|
], ids=['untracked', 'tracked', 'encrypted', 'excluded'])
|
||||||
def test_alt_source(
|
def test_alt_source(
|
||||||
runner, yadm_y, paths,
|
runner, paths,
|
||||||
tracked, encrypt, exclude):
|
tracked, encrypt, exclude,
|
||||||
|
yadm_alt):
|
||||||
"""Test yadm alt operates on all expected sources of alternates"""
|
"""Test yadm alt operates on all expected sources of alternates"""
|
||||||
|
yadm_dir = setup_standard_yadm_dir(paths)
|
||||||
|
|
||||||
utils.create_alt_files(
|
utils.create_alt_files(
|
||||||
paths, '##default', tracked=tracked, encrypt=encrypt, exclude=exclude)
|
paths, '##default', tracked=tracked, encrypt=encrypt, exclude=exclude,
|
||||||
run = runner(yadm_y('alt'))
|
yadm_alt=yadm_alt, yadm_dir=yadm_dir)
|
||||||
|
run = runner([paths.pgm, '-Y', yadm_dir, 'alt'])
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ''
|
assert run.err == ''
|
||||||
linked = utils.parse_alt_output(run.out)
|
linked = utils.parse_alt_output(run.out)
|
||||||
|
|
||||||
|
basepath = yadm_dir.join('alt') if yadm_alt else paths.work
|
||||||
|
|
||||||
for link_path in TEST_PATHS:
|
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):
|
if tracked or (encrypt and not exclude):
|
||||||
assert paths.work.join(link_path).islink()
|
assert link_file.islink()
|
||||||
target = py.path.local(paths.work.join(link_path).readlink())
|
target = py.path.local(link_file.readlink())
|
||||||
if target.isfile():
|
if target.isfile():
|
||||||
assert paths.work.join(link_path).read() == source_file
|
assert link_file.read() == source_file_content
|
||||||
assert str(paths.work.join(source_file)) in linked
|
assert str(source_file) in linked
|
||||||
else:
|
else:
|
||||||
assert paths.work.join(link_path).join(
|
assert link_file.join(
|
||||||
utils.CONTAINED).read() == source_file
|
utils.CONTAINED).read() == source_file_content
|
||||||
assert str(paths.work.join(source_file)) in linked
|
assert str(source_file) in linked
|
||||||
else:
|
else:
|
||||||
assert not paths.work.join(link_path).exists()
|
assert not link_file.exists()
|
||||||
assert str(paths.work.join(source_file)) not in linked
|
assert str(source_file) not in linked
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('ds1_copy')
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
|
@ -55,9 +63,10 @@ def test_alt_source(
|
||||||
'##u.$tst_user', '##user.$tst_user',
|
'##u.$tst_user', '##user.$tst_user',
|
||||||
])
|
])
|
||||||
def test_alt_conditions(
|
def test_alt_conditions(
|
||||||
runner, yadm_y, paths,
|
runner, paths,
|
||||||
tst_sys, tst_distro, tst_host, tst_user, suffix):
|
tst_sys, tst_distro, tst_host, tst_user, suffix):
|
||||||
"""Test conditions supported by yadm alt"""
|
"""Test conditions supported by yadm alt"""
|
||||||
|
yadm_dir = setup_standard_yadm_dir(paths)
|
||||||
|
|
||||||
# set the class
|
# set the class
|
||||||
tst_class = 'testclass'
|
tst_class = 'testclass'
|
||||||
|
@ -72,7 +81,7 @@ def test_alt_conditions(
|
||||||
)
|
)
|
||||||
|
|
||||||
utils.create_alt_files(paths, suffix)
|
utils.create_alt_files(paths, suffix)
|
||||||
run = runner(yadm_y('alt'))
|
run = runner([paths.pgm, '-Y', yadm_dir, 'alt'])
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ''
|
assert run.err == ''
|
||||||
linked = utils.parse_alt_output(run.out)
|
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('kind', ['builtin', '', 'envtpl', 'j2cli', 'j2'])
|
||||||
@pytest.mark.parametrize('label', ['t', 'template', 'yadm', ])
|
@pytest.mark.parametrize('label', ['t', 'template', 'yadm', ])
|
||||||
def test_alt_templates(
|
def test_alt_templates(
|
||||||
runner, yadm_y, paths, kind, label):
|
runner, paths, kind, label):
|
||||||
"""Test templates supported by yadm alt"""
|
"""Test templates supported by yadm alt"""
|
||||||
|
yadm_dir = setup_standard_yadm_dir(paths)
|
||||||
|
|
||||||
suffix = f'##{label}.{kind}'
|
suffix = f'##{label}.{kind}'
|
||||||
utils.create_alt_files(paths, suffix)
|
utils.create_alt_files(paths, suffix)
|
||||||
run = runner(yadm_y('alt'))
|
run = runner([paths.pgm, '-Y', yadm_dir, 'alt'])
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ''
|
assert run.err == ''
|
||||||
created = utils.parse_alt_output(run.out, linked=False)
|
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 not link.islink()
|
||||||
assert target.read().strip() == 'target'
|
assert target.read().strip() == 'target'
|
||||||
assert link.read().strip() == 'test-data'
|
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
|
||||||
|
|
|
@ -32,7 +32,8 @@ def set_local(paths, variable, value):
|
||||||
def create_alt_files(paths, suffix,
|
def create_alt_files(paths, suffix,
|
||||||
preserve=False, tracked=True,
|
preserve=False, tracked=True,
|
||||||
encrypt=False, exclude=False,
|
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
|
"""Create new files, and add to the repo
|
||||||
|
|
||||||
This is used for testing alternate files. In each case, a suffix is
|
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.
|
repo handling are dependent upon the function arguments.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
basepath = yadm_dir.join('alt') if yadm_alt else paths.work
|
||||||
|
|
||||||
if not preserve:
|
if not preserve:
|
||||||
for remove_path in (ALT_FILE1, ALT_FILE2, ALT_DIR):
|
for remove_path in (ALT_FILE1, ALT_FILE2, ALT_DIR):
|
||||||
if paths.work.join(remove_path).exists():
|
if basepath.join(remove_path).exists():
|
||||||
paths.work.join(remove_path).remove(rec=1, ignore_errors=True)
|
basepath.join(remove_path).remove(rec=1, ignore_errors=True)
|
||||||
assert not paths.work.join(remove_path).exists()
|
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_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_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)
|
new_dir.write(ALT_DIR + suffix, ensure=True)
|
||||||
|
|
||||||
# Do not test directory support for jinja alternates
|
# 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)
|
test_path.write('\n' + content, mode='a', ensure=True)
|
||||||
assert test_path.exists()
|
assert test_path.exists()
|
||||||
|
|
||||||
_create_includefiles(includefile, paths, test_paths)
|
_create_includefiles(includefile, test_paths, basepath)
|
||||||
_create_tracked(tracked, test_paths, paths)
|
_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):
|
def parse_alt_output(output, linked=True):
|
||||||
|
@ -86,10 +91,10 @@ def parse_alt_output(output, linked=True):
|
||||||
return parsed_list.values()
|
return parsed_list.values()
|
||||||
|
|
||||||
|
|
||||||
def _create_includefiles(includefile, paths, test_paths):
|
def _create_includefiles(includefile, test_paths, basepath):
|
||||||
if includefile:
|
if includefile:
|
||||||
for dpath in INCLUDE_DIRS:
|
for dpath in INCLUDE_DIRS:
|
||||||
incfile = paths.work.join(dpath + '/' + INCLUDE_FILE)
|
incfile = basepath.join(dpath + '/' + INCLUDE_FILE)
|
||||||
incfile.write(INCLUDE_CONTENT, ensure=True)
|
incfile.write(INCLUDE_CONTENT, ensure=True)
|
||||||
test_paths += [incfile]
|
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"')
|
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:
|
if encrypt:
|
||||||
for encrypt_name in test_names:
|
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:
|
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
7
yadm
|
@ -33,6 +33,7 @@ YADM_ENCRYPT="encrypt"
|
||||||
YADM_ARCHIVE="files.gpg"
|
YADM_ARCHIVE="files.gpg"
|
||||||
YADM_BOOTSTRAP="bootstrap"
|
YADM_BOOTSTRAP="bootstrap"
|
||||||
YADM_HOOKS="hooks"
|
YADM_HOOKS="hooks"
|
||||||
|
YADM_ALT="alt"
|
||||||
|
|
||||||
HOOK_COMMAND=""
|
HOOK_COMMAND=""
|
||||||
FULL_COMMAND=""
|
FULL_COMMAND=""
|
||||||
|
@ -137,6 +138,11 @@ function score_file() {
|
||||||
target="$1"
|
target="$1"
|
||||||
filename="${target%%##*}"
|
filename="${target%%##*}"
|
||||||
conditions="${target#*##}"
|
conditions="${target#*##}"
|
||||||
|
|
||||||
|
if [ "${filename#$YADM_ALT/}" != "${filename}" ]; then
|
||||||
|
filename="${YADM_WORK}/${filename#$YADM_ALT/}"
|
||||||
|
fi
|
||||||
|
|
||||||
score=0
|
score=0
|
||||||
IFS=',' read -ra fields <<< "$conditions"
|
IFS=',' read -ra fields <<< "$conditions"
|
||||||
for field in "${fields[@]}"; do
|
for field in "${fields[@]}"; do
|
||||||
|
@ -1223,6 +1229,7 @@ function configure_paths() {
|
||||||
YADM_ARCHIVE="$YADM_DIR/$YADM_ARCHIVE"
|
YADM_ARCHIVE="$YADM_DIR/$YADM_ARCHIVE"
|
||||||
YADM_BOOTSTRAP="$YADM_DIR/$YADM_BOOTSTRAP"
|
YADM_BOOTSTRAP="$YADM_DIR/$YADM_BOOTSTRAP"
|
||||||
YADM_HOOKS="$YADM_DIR/$YADM_HOOKS"
|
YADM_HOOKS="$YADM_DIR/$YADM_HOOKS"
|
||||||
|
YADM_ALT="$YADM_DIR/$YADM_ALT"
|
||||||
|
|
||||||
# independent overrides for paths
|
# independent overrides for paths
|
||||||
if [ -n "$YADM_OVERRIDE_REPO" ]; then
|
if [ -n "$YADM_OVERRIDE_REPO" ]; then
|
||||||
|
|
13
yadm.1
13
yadm.1
|
@ -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
|
Files with more conditions will always be favored. Any invalid condition will
|
||||||
disqualify that file completely.
|
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
|
Alternate linking may best be demonstrated by example. Assume the following
|
||||||
files are managed by yadm's repository:
|
files are managed by yadm's repository:
|
||||||
|
|
||||||
|
@ -771,7 +777,7 @@ Otherwise it will be
|
||||||
.IR "$HOME/.config/yadm" .
|
.IR "$HOME/.config/yadm" .
|
||||||
|
|
||||||
The following are the default paths yadm uses for its own data.
|
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.
|
See the OPTIONS section for details.
|
||||||
.TP
|
.TP
|
||||||
.I $HOME/.config/yadm
|
.I $HOME/.config/yadm
|
||||||
|
@ -781,6 +787,11 @@ directory.
|
||||||
.I $YADM_DIR/config
|
.I $YADM_DIR/config
|
||||||
Configuration file for yadm.
|
Configuration file for yadm.
|
||||||
.TP
|
.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
|
.I $YADM_DIR/repo.git
|
||||||
Git repository used by yadm.
|
Git repository used by yadm.
|
||||||
.TP
|
.TP
|
||||||
|
|
Loading…
Reference in a new issue