Переглянути джерело

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.
Tim Byrne 5 роки тому
батько
коміт
4ea3ed9e2a
4 змінених файлів з 76 додано та 33 видалено
  1. 36 18
      test/test_alt.py
  2. 21 14
      test/utils.py
  3. 7 0
      yadm
  4. 12 1
      yadm.1

+ 36 - 18
test/test_alt.py

@@ -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

+ 21 - 14
test/utils.py

@@ -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 - 0
yadm

@@ -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

+ 12 - 1
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
 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