mirror of
https://github.com/TheLocehiliosan/yadm
synced 2025-06-02 15:43:59 +00:00
Release 3.4.0
* Improve and harden alt file regeneration (#466) * Fix "yadm config" in fish completion (#491) * Fix "yadm clone" when not run in "$YADM_WORK" (#513) * Output the actual paths in help message (#376) * Verify all alt conditions for templates (#478) * Ignore case in alt and default template conditions (#455, #456) * Fall back to ID for distro family if ID_LIKE is not available (#494) * Support overriding distro and distro family (#430) * Improve support for Bash 3 (the default version on macOS) * Make "yadm clone --recursive" work as expected (#517) * Don't include files multiple times in archive (#125) * Document YADM_HOOK_DATA and YADM_HOOK_DIR env variables (#343) * Support alt dirs with deeply nested tracked files (#495)
This commit is contained in:
commit
5648f8b337
124
.github/workflows/test.yml
vendored
124
.github/workflows/test.yml
vendored
@ -1,13 +1,129 @@
|
||||
---
|
||||
name: Tests
|
||||
|
||||
on: # yamllint disable-line rule:truthy
|
||||
- push
|
||||
- pull_request
|
||||
- workflow_dispatch
|
||||
|
||||
env:
|
||||
SC_VER: "0.10.0"
|
||||
ESH_VER: "0.3.2"
|
||||
|
||||
jobs:
|
||||
Tests:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-20.04
|
||||
- ubuntu-24.04
|
||||
- macos-13
|
||||
- macos-15
|
||||
- windows-2022
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Tests
|
||||
run: make test
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: Vampire/setup-wsl@v4
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
|
||||
- name: Install dependencies on Linux
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
expect \
|
||||
${{ matrix.os != 'ubuntu-20.04' && 'j2cli' || '' }}
|
||||
|
||||
- name: Install dependencies on macOS
|
||||
if: ${{ runner.os == 'macOS' }}
|
||||
run: |
|
||||
command -v expect || brew install expect
|
||||
|
||||
- name: Install dependencies on Windows (WSL)
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
shell: wsl-bash {0}
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
dos2unix \
|
||||
expect \
|
||||
gettext-base \
|
||||
git \
|
||||
gnupg \
|
||||
j2cli \
|
||||
lsb-release \
|
||||
man \
|
||||
python3-pip
|
||||
|
||||
- name: Prepare tools directory
|
||||
run: |
|
||||
mkdir "${{ runner.temp }}/tools"
|
||||
echo "${{ runner.temp }}/tools" >> "${{ github.path }}"
|
||||
|
||||
- name: Install shellcheck
|
||||
run: |
|
||||
cd "${{ runner.temp }}"
|
||||
|
||||
OS=${{ runner.os == 'macOS' && 'darwin' || 'linux' }}
|
||||
ARCH=${{ runner.arch == 'ARM64' && 'aarch64' || 'x86_64' }}
|
||||
|
||||
BASE_URL="https://github.com/koalaman/shellcheck/releases/download"
|
||||
SC="v$SC_VER/shellcheck-v$SC_VER.$OS.$ARCH.tar.xz"
|
||||
|
||||
curl -L "$BASE_URL/$SC" | tar Jx shellcheck-v$SC_VER/shellcheck
|
||||
mv shellcheck-v$SC_VER/shellcheck tools
|
||||
|
||||
- name: Install esh
|
||||
run: |
|
||||
cd "${{ runner.temp }}/tools"
|
||||
|
||||
BASE_URL="https://github.com/jirutka/esh/raw/refs/tags"
|
||||
curl -L -o esh "$BASE_URL/v$ESH_VER/esh"
|
||||
chmod +x esh
|
||||
|
||||
- name: Add old yadm versions # to test upgrades
|
||||
run: |
|
||||
for version in 1.12.0 2.5.0; do
|
||||
git fetch origin $version:refs/tags/$version
|
||||
git cat-file blob $version:yadm \
|
||||
> "${{ runner.temp }}/tools/yadm-$version"
|
||||
chmod +x "${{ runner.temp }}/tools/yadm-$version"
|
||||
done
|
||||
|
||||
- name: Set up Python 3.11
|
||||
if: ${{ runner.os == 'macOS' || matrix.os == 'ubuntu-20.04' }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
||||
- name: Install dependencies and run tests (Linux/macOS)
|
||||
if: ${{ runner.os != 'Windows' }}
|
||||
run: |
|
||||
git config --global user.email test@yadm.io
|
||||
git config --global user.name "Yadm Test"
|
||||
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install -r test/requirements.txt
|
||||
pytest -v --color=yes --basetemp="${{ runner.temp }}/pytest"
|
||||
|
||||
- name: Install dependencies and run tests (WSL)
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
shell: wsl-bash {0}
|
||||
run: |
|
||||
git config --global user.email test@yadm.io
|
||||
git config --global user.name "Yadm Test"
|
||||
git config --global protocol.file.allow always
|
||||
|
||||
dos2unix yadm.1 .github/workflows/*.yml test/pinentry-mock
|
||||
chmod +x test/pinentry-mock
|
||||
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install -r test/requirements.txt
|
||||
pytest -v --color=yes
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@
|
||||
.testyadm
|
||||
_site
|
||||
testenv
|
||||
__pycache__/
|
||||
|
15
CHANGES
15
CHANGES
@ -1,3 +1,18 @@
|
||||
3.4.0
|
||||
* Improve and harden alt file regeneration (#466)
|
||||
* Fix "yadm config" in fish completion (#491)
|
||||
* Fix "yadm clone" when not run in "$YADM_WORK" (#513)
|
||||
* Output the actual paths in help message (#376)
|
||||
* Verify all alt conditions for templates (#478)
|
||||
* Ignore case in alt and default template conditions (#455, #456)
|
||||
* Fall back to ID for distro family if ID_LIKE is not available (#494)
|
||||
* Support overriding distro and distro family (#430)
|
||||
* Improve support for Bash 3 (the default version on macOS)
|
||||
* Make "yadm clone --recursive" work as expected (#517)
|
||||
* Don't include files multiple times in archive (#125)
|
||||
* Document YADM_HOOK_DATA and YADM_HOOK_DIR env variables (#343)
|
||||
* Support alt dirs with deeply nested tracked files (#495)
|
||||
|
||||
3.3.0
|
||||
* Support nested ifs in default template (#436)
|
||||
* Support include and ifs in default template includes (#406)
|
||||
|
@ -3,8 +3,8 @@ CONTRIBUTORS
|
||||
Tim Byrne
|
||||
Erik Flodin
|
||||
Martin Zuther
|
||||
Jan Schulz
|
||||
Ross Smith II
|
||||
Jan Schulz
|
||||
Jonathan Daigle
|
||||
Luis López
|
||||
Tin Lai
|
||||
@ -15,11 +15,13 @@ James Clark
|
||||
Glenn Waters
|
||||
Nicolas signed-log FORMICHELLA
|
||||
Tomas Cernaj
|
||||
AVM.Martin
|
||||
Joshua Cold
|
||||
jonasc
|
||||
Nicolas stig124 FORMICHELLA
|
||||
Chad Wade Day, Jr
|
||||
Sébastien Gross
|
||||
Christof Warlich
|
||||
David Mandelberg
|
||||
Paulo Köch
|
||||
Oren Zipori
|
||||
@ -47,6 +49,7 @@ Tim Condit
|
||||
Thomas Luzat
|
||||
Russ Allbery
|
||||
Patrick Roddy
|
||||
heddxh
|
||||
dessert1
|
||||
Brayden Banks
|
||||
Alexandre GV
|
||||
|
4
Makefile
4
Makefile
@ -1,5 +1,5 @@
|
||||
PYTESTS = $(wildcard test/test_*.py)
|
||||
IMAGE = docker.io/yadm/testbed:2023-07-12
|
||||
IMAGE = docker.io/yadm/testbed:2024-11-11
|
||||
OCI = docker
|
||||
|
||||
.PHONY: all
|
||||
@ -176,7 +176,7 @@ man-ps:
|
||||
@groff -man -Tps ./yadm.1 > yadm.ps
|
||||
|
||||
yadm.md: yadm.1
|
||||
@groff -man -Tutf8 -Z ./yadm.1 | grotty -c | col -bx | sed 's/^[A-Z]/## &/g' | sed '/yadm(1)/d' > yadm.md
|
||||
@groff -man -Tutf8 -Z ./yadm.1 | grotty -c | col -bx | sed 's/^[A-Z]/## &/g' | sed '/YADM(1)/d' > yadm.md
|
||||
|
||||
.PHONY: contrib
|
||||
contrib: SHELL = /bin/bash
|
||||
|
@ -78,8 +78,8 @@ The star count helps others discover yadm.
|
||||
[master-badge]: https://img.shields.io/github/actions/workflow/status/yadm-dev/yadm/test.yml?branch=master
|
||||
[master-commits]: https://github.com/yadm-dev/yadm/commits/master
|
||||
[master-date]: https://img.shields.io/github/last-commit/yadm-dev/yadm/master.svg?label=master
|
||||
[obs-badge]: https://img.shields.io/badge/OBS-v3.3.0-blue
|
||||
[obs-link]: https://software.opensuse.org//download.html?project=home%3ATheLocehiliosan%3Ayadm&package=yadm
|
||||
[obs-badge]: https://img.shields.io/badge/OBS-v3.4.0-blue
|
||||
[obs-link]: https://software.opensuse.org/download.html?project=home%3ATheLocehiliosan%3Ayadm&package=yadm
|
||||
[releases-badge]: https://img.shields.io/github/tag/yadm-dev/yadm.svg?label=latest+release
|
||||
[releases-link]: https://github.com/yadm-dev/yadm/releases
|
||||
[transcrypt]: https://github.com/elasticdog/transcrypt
|
||||
|
16
bootstrap
16
bootstrap
@ -35,18 +35,20 @@ REPO_URL=""
|
||||
|
||||
function _private_yadm() {
|
||||
unset -f yadm
|
||||
if command -v yadm &> /dev/null; then
|
||||
if command -v yadm &>/dev/null; then
|
||||
echo "Found yadm installed locally, removing remote yadm() function"
|
||||
unset -f _private_yadm
|
||||
command yadm "$@"
|
||||
else
|
||||
function yadm() { _private_yadm "$@"; }; export -f yadm
|
||||
function yadm() { _private_yadm "$@"; }
|
||||
export -f yadm
|
||||
echo WARNING: Using yadm remotely. You should install yadm locally.
|
||||
curl -fsSL "$YADM_REPO/raw/$YADM_RELEASE/yadm" | bash -s -- "$@"
|
||||
fi
|
||||
}
|
||||
export -f _private_yadm
|
||||
function yadm() { _private_yadm "$@"; }; export -f yadm
|
||||
function yadm() { _private_yadm "$@"; }
|
||||
export -f yadm
|
||||
|
||||
# if being sourced, return here, otherwise continue processing
|
||||
return 2>/dev/null
|
||||
@ -57,7 +59,7 @@ function remote_yadm() {
|
||||
}
|
||||
|
||||
function ask_about_source() {
|
||||
if ! command -v yadm &> /dev/null; then
|
||||
if ! command -v yadm &>/dev/null; then
|
||||
echo
|
||||
echo "***************************************************"
|
||||
echo "yadm is NOT currently installed."
|
||||
@ -83,7 +85,7 @@ function build_url() {
|
||||
echo " 3. GitLab"
|
||||
echo " 4. Other"
|
||||
echo
|
||||
read -r -p "Where is your repo? (1/2/3/4) ->" choice < /dev/tty
|
||||
read -r -p "Where is your repo? (1/2/3/4) ->" choice </dev/tty
|
||||
case $choice in
|
||||
1)
|
||||
REPO_URL="https://github.com/"
|
||||
@ -97,7 +99,7 @@ function build_url() {
|
||||
*)
|
||||
echo
|
||||
echo Please specify the full URL of your dotfiles repo
|
||||
read -r -p "URL ->" choice < /dev/tty
|
||||
read -r -p "URL ->" choice </dev/tty
|
||||
REPO_URL="$choice"
|
||||
return
|
||||
;;
|
||||
@ -107,7 +109,7 @@ function build_url() {
|
||||
echo "Provide your user and repo separated by '/'"
|
||||
echo "For example: UserName/dotfiles"
|
||||
echo
|
||||
read -r -p "User/Repo ->" choice < /dev/tty
|
||||
read -r -p "User/Repo ->" choice </dev/tty
|
||||
[[ "$choice" =~ ^[^[:space:]]+/[^[:space:]]+$ ]] || {
|
||||
echo "Not formatted as USER/REPO"
|
||||
REPO_URL=
|
||||
|
@ -60,7 +60,7 @@ complete -x -c yadm -n '__fish_yadm_using_command introspect' -a (printf -- '%s\
|
||||
complete -x -c yadm -n '__fish_yadm_needs_command' -a 'gitconfig' -d 'Pass options to the git config command'
|
||||
complete -x -c yadm -n '__fish_yadm_needs_command' -a 'config' -d 'Configure a setting'
|
||||
for name in (yadm introspect configs)
|
||||
complete -x -c yadm -n '__fish_yadm_using_command config' -a '$name' -d 'yadm config'
|
||||
complete -x -c yadm -n '__fish_yadm_using_command config' -a $name -d 'yadm config'
|
||||
end
|
||||
|
||||
# yadm universial options
|
||||
|
@ -7,6 +7,7 @@ markers = [
|
||||
|
||||
[tool.pylint.design]
|
||||
max-args = 14
|
||||
max-positional-arguments = 10
|
||||
max-locals = 28
|
||||
max-attributes = 8
|
||||
max-statements = 65
|
||||
|
@ -1,8 +1,7 @@
|
||||
FROM ubuntu:23.04
|
||||
MAINTAINER Tim Byrne <sultan@locehilios.com>
|
||||
FROM ubuntu:24.10
|
||||
|
||||
# Shellcheck and esh versions
|
||||
ARG SC_VER=0.9.0
|
||||
ARG SC_VER=0.10.0
|
||||
ARG ESH_VER=0.3.2
|
||||
|
||||
# Install prerequisites and configure UTF-8 locale
|
||||
@ -14,6 +13,7 @@ RUN \
|
||||
expect \
|
||||
git \
|
||||
gnupg \
|
||||
j2cli \
|
||||
locales \
|
||||
lsb-release \
|
||||
make \
|
||||
@ -39,10 +39,9 @@ RUN cd /opt \
|
||||
&& rm -f shellcheck-v$SC_VER.linux.x86_64.tar.xz \
|
||||
&& ln -s /opt/shellcheck-v$SC_VER/shellcheck /usr/local/bin
|
||||
|
||||
# Upgrade pip3 and install requirements
|
||||
# Install requirements
|
||||
COPY test/requirements.txt /tmp/requirements.txt
|
||||
RUN python3 -m pip install --break-system-packages --upgrade pip setuptools \
|
||||
&& python3 -m pip install --break-system-packages --upgrade -r /tmp/requirements.txt \
|
||||
RUN python3 -m pip install --break-system-packages -r /tmp/requirements.txt \
|
||||
&& rm -f /tmp/requirements
|
||||
|
||||
# Install esh
|
||||
|
@ -9,7 +9,6 @@ import pwd
|
||||
import shutil
|
||||
from subprocess import PIPE, Popen
|
||||
|
||||
import py
|
||||
import pytest
|
||||
|
||||
|
||||
@ -26,37 +25,37 @@ def pytest_addoption(parser):
|
||||
@pytest.fixture(scope="session")
|
||||
def shellcheck_version():
|
||||
"""Version of shellcheck supported"""
|
||||
return "0.9.0"
|
||||
return "0.10.0"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def pylint_version():
|
||||
"""Version of pylint supported"""
|
||||
return "2.17.0"
|
||||
return "3.3.1"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def isort_version():
|
||||
"""Version of isort supported"""
|
||||
return "5.12.0"
|
||||
return "5.13.2"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def flake8_version():
|
||||
"""Version of flake8 supported"""
|
||||
return "6.0.0"
|
||||
return "7.1.1"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def black_version():
|
||||
"""Version of black supported"""
|
||||
return "23.1.0"
|
||||
return "24.10.0"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def yamllint_version():
|
||||
"""Version of yamllint supported"""
|
||||
return "1.30.0"
|
||||
return "1.35.1"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@ -82,19 +81,31 @@ def tst_distro(runner):
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def tst_distro_family(runner):
|
||||
def tst_distro_family():
|
||||
"""Test session's distro_family"""
|
||||
family = ""
|
||||
with contextlib.suppress(Exception):
|
||||
run = runner(command=["grep", "-oP", r"ID_LIKE=\K.+", "/etc/os-release"], report=False)
|
||||
family = run.out.strip()
|
||||
return family
|
||||
with open("/etc/os-release", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
if line.startswith("ID_LIKE="):
|
||||
family = line[8:]
|
||||
break
|
||||
if line.startswith("ID="):
|
||||
family = line[3:]
|
||||
# No break, only used as fallback in case ID_LIKE isn't found
|
||||
return family.replace('"', "").rstrip()
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def tst_sys():
|
||||
"""Test session's uname value"""
|
||||
return platform.system()
|
||||
system = platform.system()
|
||||
if system == "Linux":
|
||||
# Additional check for WSL
|
||||
with open("/proc/version", encoding="utf-8") as f:
|
||||
if "icrosoft" in f.read():
|
||||
system = "WSL"
|
||||
return system
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@ -140,6 +151,8 @@ def supported_configs():
|
||||
return [
|
||||
"local.arch",
|
||||
"local.class",
|
||||
"local.distro",
|
||||
"local.distro-family",
|
||||
"local.hostname",
|
||||
"local.os",
|
||||
"local.user",
|
||||
@ -246,7 +259,7 @@ class Runner:
|
||||
if not expect:
|
||||
return
|
||||
cmdline = " ".join([f'"{w}"' for w in self.command])
|
||||
expect_script = f"set timeout 2\nspawn {cmdline}\n"
|
||||
expect_script = f"set timeout 5\nspawn {cmdline}\n"
|
||||
for question, answer in expect:
|
||||
expect_script += "expect {\n" f'"{question}" {{send "{answer}\\r"}}\n' "timeout {close;exit 128}\n" "}\n"
|
||||
expect_script += "expect eof\n" "foreach {pid spawnid os_error_flag value} [wait] break\n" "exit $value"
|
||||
@ -575,17 +588,21 @@ def ds1(ds1_work_copy, paths, ds1_dset):
|
||||
def gnupg(tmpdir_factory, runner):
|
||||
"""Location of GNUPGHOME"""
|
||||
|
||||
def register_gpg_password(password):
|
||||
"""Publish a new GPG mock password"""
|
||||
py.path.local("/tmp/mock-password").write(password)
|
||||
|
||||
home = tmpdir_factory.mktemp("gnupghome")
|
||||
home.chmod(0o700)
|
||||
conf = home.join("gpg.conf")
|
||||
conf.write("no-secmem-warning\n")
|
||||
conf.chmod(0o600)
|
||||
agentconf = home.join("gpg-agent.conf")
|
||||
agentconf.write(f'pinentry-program {os.path.abspath("test/pinentry-mock")}\n' "max-cache-ttl 0\n")
|
||||
agentconf.write(
|
||||
f"""\
|
||||
pinentry-program {os.path.abspath("test/pinentry-mock")}
|
||||
max-cache-ttl 0
|
||||
browser-socket none
|
||||
extra-socket none
|
||||
disable-scdaemon
|
||||
"""
|
||||
)
|
||||
agentconf.chmod(0o600)
|
||||
data = collections.namedtuple("GNUPG", ["home", "pw"])
|
||||
env = os.environ.copy()
|
||||
@ -594,4 +611,12 @@ def gnupg(tmpdir_factory, runner):
|
||||
# this pre-populates std files in the GNUPGHOME
|
||||
runner(["gpg", "-k"], env=env)
|
||||
|
||||
return data(home, register_gpg_password)
|
||||
def register_gpg_password(password):
|
||||
"""Publish a new GPG mock password and flush cached passwords"""
|
||||
home.join("mock-password").write(password)
|
||||
runner(["gpgconf", "--reload", "gpg-agent"], env=env)
|
||||
|
||||
yield data(home, register_gpg_password)
|
||||
|
||||
runner(["gpgconf", "--kill", "gpg-agent"], env=env)
|
||||
runner(["gpgconf", "--remove-socketdir", "gpg-agent"], env=env)
|
||||
|
@ -6,10 +6,9 @@
|
||||
echo "OK Pleased to meet you"
|
||||
while read -r line; do
|
||||
if [[ $line =~ GETPIN ]]; then
|
||||
password="$(cat /tmp/mock-password 2>/dev/null)"
|
||||
password="$(cat "$GNUPGHOME/mock-password" 2>/dev/null)"
|
||||
if [ -n "$password" ]; then
|
||||
echo -n "D "
|
||||
echo "$password"
|
||||
echo "D $password"
|
||||
echo "OK";
|
||||
else
|
||||
echo "CANCEL";
|
||||
|
@ -1,8 +1,8 @@
|
||||
black==23.1.0
|
||||
black==24.10.0
|
||||
envtpl
|
||||
flake8==6.0.0
|
||||
isort==5.12.0
|
||||
flake8==7.1.1
|
||||
isort==5.13.2
|
||||
j2cli
|
||||
pylint==2.17.0
|
||||
pytest==7.2.2
|
||||
yamllint==1.30.0
|
||||
pylint==3.3.1
|
||||
pytest==8.3.3
|
||||
yamllint==1.35.1
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Test alt"""
|
||||
|
||||
import os
|
||||
import string
|
||||
|
||||
@ -169,6 +170,21 @@ def test_alt_templates(runner, paths, kind, label):
|
||||
assert str(paths.work.join(source_file)) in created
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("ds1_copy")
|
||||
def test_alt_template_with_condition(runner, paths, tst_arch):
|
||||
"""Test template with extra condition"""
|
||||
yadm_dir, yadm_data = setup_standard_yadm_dir(paths)
|
||||
|
||||
suffix = f"##template,arch.not{tst_arch}"
|
||||
utils.create_alt_files(paths, suffix)
|
||||
run = runner([paths.pgm, "-Y", yadm_dir, "--yadm-data", yadm_data, "alt"])
|
||||
assert run.success
|
||||
assert run.err == ""
|
||||
|
||||
created = utils.parse_alt_output(run.out, linked=False)
|
||||
assert len(created) == 0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("ds1_copy")
|
||||
@pytest.mark.parametrize("autoalt", [None, "true", "false"])
|
||||
def test_auto_alt(runner, yadm_cmd, paths, autoalt):
|
||||
|
@ -40,7 +40,8 @@ def test_alt_copy(runner, yadm_cmd, paths, tst_sys, setting, expect_link, pre_ex
|
||||
run = runner(yadm_cmd("alt"))
|
||||
assert run.success
|
||||
assert run.err == ""
|
||||
assert "Linking" in run.out
|
||||
action = "Copying" if setting is True else "Linking"
|
||||
assert action in run.out
|
||||
|
||||
assert alt_path.read() == expected_content
|
||||
assert alt_path.islink() == expect_link
|
||||
|
@ -108,6 +108,39 @@ def test_clone(runner, paths, yadm_cmd, repo_config, ds1, good_remote, repo_exis
|
||||
assert old_repo.exists()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("remote_with_submodules")
|
||||
@pytest.mark.parametrize("action", ["recursive", "recurse", "specific"])
|
||||
def test_clone_submodules(runner, paths, yadm_cmd, repo_config, action):
|
||||
"""Test clone operation with submodules"""
|
||||
|
||||
# clear out the work path
|
||||
paths.work.remove()
|
||||
paths.work.mkdir()
|
||||
|
||||
env = {
|
||||
"GIT_CONFIG_COUNT": "1",
|
||||
"GIT_CONFIG_KEY_0": "protocol.file.allow",
|
||||
"GIT_CONFIG_VALUE_0": "always",
|
||||
}
|
||||
|
||||
args = ["clone", "-w", paths.work]
|
||||
if action == "recursive":
|
||||
args += ["--recursive"]
|
||||
elif action == "recurse":
|
||||
args += ["--recurse-submodules"]
|
||||
elif action == "specific":
|
||||
args += ["--recurse-submodules=a", "--recurse-submodules=d1/c"]
|
||||
args += [f"file://{paths.remote}"]
|
||||
run = runner(command=yadm_cmd(*args), env=env)
|
||||
assert successful_clone(run, paths, repo_config)
|
||||
|
||||
for path in ("a", "b", "d1/c"):
|
||||
if action != "specific" or path != "b":
|
||||
assert paths.work.join(path).join(".git").exists()
|
||||
else:
|
||||
assert not paths.work.join(path).join(".git").exists()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("remote")
|
||||
@pytest.mark.parametrize(
|
||||
"bs_exists, bs_param, answer",
|
||||
@ -305,16 +338,38 @@ def remote(paths, ds1_repo_copy):
|
||||
"""Function scoped remote (based on ds1)"""
|
||||
# pylint: disable=unused-argument
|
||||
# This is ignored because
|
||||
# @pytest.mark.usefixtures('ds1_remote_copy')
|
||||
# @pytest.mark.usefixtures('ds1_repo_copy')
|
||||
# cannot be applied to another fixture.
|
||||
paths.remote.remove()
|
||||
paths.repo.move(paths.remote)
|
||||
|
||||
|
||||
def test_no_repo(
|
||||
runner,
|
||||
yadm_cmd,
|
||||
):
|
||||
@pytest.fixture()
|
||||
def remote_with_submodules(tmpdir_factory, runner, paths, remote, ds1_work_copy):
|
||||
"""Function scoped remote with submodules (based on ds1)"""
|
||||
# pylint: disable=unused-argument
|
||||
# This is ignored because
|
||||
# @pytest.mark.usefixtures('remote', 'ds1_work_copy')
|
||||
# cannot be applied to another fixture.
|
||||
submodule = tmpdir_factory.mktemp("submodule")
|
||||
paths.remote.copy(submodule)
|
||||
|
||||
env = os.environ.copy()
|
||||
env["GIT_DIR"] = str(paths.remote)
|
||||
|
||||
for path in ("a", "b", "d1/c"):
|
||||
run = runner(
|
||||
["git", "-C", paths.work, "-c", "protocol.file.allow=always", "submodule", "add", submodule, path],
|
||||
env=env,
|
||||
report=False,
|
||||
)
|
||||
assert run.success
|
||||
|
||||
run = runner(["git", "-C", paths.work, "commit", "-m", '"Add submodules"'], env=env, report=False)
|
||||
assert run.success
|
||||
|
||||
|
||||
def test_no_repo(runner, yadm_cmd):
|
||||
"""Test cloning without specifying a repo"""
|
||||
run = runner(command=yadm_cmd("clone", "-f"))
|
||||
assert run.failure
|
||||
@ -326,3 +381,31 @@ def test_no_repo(
|
||||
def verify_head(paths, branch):
|
||||
"""Assert the local repo has the correct head branch"""
|
||||
assert paths.repo.join("HEAD").read() == f"ref: refs/heads/{branch}\n"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("remote")
|
||||
def test_clone_subdirectory(runner, paths, yadm_cmd, repo_config):
|
||||
"""Test clone from sub-directory of YADM_WORK"""
|
||||
|
||||
# clear out the work path
|
||||
paths.work.remove()
|
||||
paths.work.mkdir()
|
||||
|
||||
# create sub-directory
|
||||
subdir = paths.work.mkdir("subdir")
|
||||
|
||||
# determine remote url
|
||||
remote_url = f"file://{paths.remote}"
|
||||
|
||||
# run the clone command
|
||||
args = ["clone", "-w", paths.work, remote_url]
|
||||
run = runner(command=yadm_cmd(*args), cwd=subdir)
|
||||
|
||||
# clone should succeed, and repo should be configured properly
|
||||
assert successful_clone(run, paths, repo_config)
|
||||
|
||||
# ensure that no changes found as this is a clean dotfiles clone
|
||||
run = runner(command=yadm_cmd("status", "-uno", "--porcelain"), cwd=subdir)
|
||||
assert run.success
|
||||
assert run.out == ""
|
||||
assert run.err == ""
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
import os
|
||||
import shlex
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
@ -219,7 +218,6 @@ def test_symmetric_decrypt(runner, yadm_cmd, paths, decrypt_targets, gnupg, doli
|
||||
|
||||
if bad_phrase:
|
||||
gnupg.pw("")
|
||||
time.sleep(1) # allow gpg-agent cache to expire
|
||||
else:
|
||||
gnupg.pw(PASSPHRASE)
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Test help"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
|
@ -20,7 +20,8 @@ def test_list(runner, yadm_cmd, paths, ds1, location):
|
||||
run_dir = paths.work
|
||||
elif location == "outside":
|
||||
run_dir = paths.work.join("..")
|
||||
elif location == "subdir":
|
||||
else:
|
||||
assert location == "subdir"
|
||||
# first directory with tracked data
|
||||
run_dir = paths.work.join(ds1.tracked_dirs[0])
|
||||
with run_dir.as_cwd():
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Syntax checks"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import pytest
|
||||
|
||||
@ -77,7 +78,11 @@ def test_yamllint(pytestconfig, runner, yamllint_version):
|
||||
|
||||
def test_man(runner):
|
||||
"""Check for warnings from man"""
|
||||
run = runner(command=["man.REAL", "--warnings", "./yadm.1"])
|
||||
if shutil.which("mandoc"):
|
||||
command = ["mandoc", "-T", "lint"]
|
||||
else:
|
||||
command = ["groff", "-ww", "-z"]
|
||||
run = runner(command=command + ["-man", "./yadm.1"])
|
||||
assert run.success
|
||||
assert run.out == ""
|
||||
assert run.err == ""
|
||||
assert "yadm - Yet Another Dotfiles Manager" in run.out
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: choose_template_cmd"""
|
||||
"""Unit tests: choose_template_processor"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@ -7,7 +8,7 @@ import pytest
|
||||
def test_kind_default(runner, yadm, awk, label):
|
||||
"""Test kind: default"""
|
||||
|
||||
expected = "template_default"
|
||||
expected = "default"
|
||||
awk_avail = "true"
|
||||
|
||||
if not awk:
|
||||
@ -19,8 +20,8 @@ def test_kind_default(runner, yadm, awk, label):
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
function awk_available {{ { awk_avail}; }}
|
||||
template="$(choose_template_cmd "{label}")"
|
||||
function awk_available {{ {awk_avail}; }}
|
||||
template="$(choose_template_processor "{label}")"
|
||||
echo "TEMPLATE:$template"
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
@ -42,17 +43,17 @@ def test_kind_j2cli_envtpl(runner, yadm, envtpl, j2cli, label):
|
||||
j2cli_avail = "true" if j2cli else "false"
|
||||
|
||||
if label in ("j2cli", "j2") and j2cli:
|
||||
expected = "template_j2cli"
|
||||
expected = "j2cli"
|
||||
elif label in ("envtpl", "j2") and envtpl:
|
||||
expected = "template_envtpl"
|
||||
expected = "envtpl"
|
||||
else:
|
||||
expected = ""
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
function envtpl_available {{ { envtpl_avail}; }}
|
||||
function j2cli_available {{ { j2cli_avail}; }}
|
||||
template="$(choose_template_cmd "{label}")"
|
||||
function envtpl_available {{ {envtpl_avail}; }}
|
||||
function j2cli_available {{ {j2cli_avail}; }}
|
||||
template="$(choose_template_processor "{label}")"
|
||||
echo "TEMPLATE:$template"
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
@ -89,7 +89,9 @@ def run_test(runner, paths, args, expected_matches, cwd=None):
|
||||
XDG_DATA_HOME=
|
||||
HOME="{HOME}" set_yadm_dirs
|
||||
configure_paths
|
||||
declare -p | grep -E '(YADM|GIT)_'
|
||||
for var in "${{!YADM_@}}" "${{!GIT_@}}"; do
|
||||
echo "$var=\\"${{!var}}\\""
|
||||
done
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script, cwd=cwd)
|
||||
assert run.success
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: copy_perms"""
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
@ -7,7 +8,7 @@ OCTAL = "7654"
|
||||
NON_OCTAL = "9876"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stat_broken", [True, False], ids=["normal", "stat broken"])
|
||||
@pytest.mark.parametrize("stat_broken", [False, True], ids=["normal", "stat broken"])
|
||||
def test_copy_perms(runner, yadm, tmpdir, stat_broken):
|
||||
"""Test function copy_perms"""
|
||||
src_mode = 0o754
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: exclude_encrypted"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: issue_legacy_path_warning"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
|
@ -97,6 +97,7 @@ def create_test_encrypt_data(paths):
|
||||
|
||||
# wildcards
|
||||
edata += "wild*\n"
|
||||
edata += "*card1\n" # matches same file as the one above
|
||||
paths.work.join("wildcard1").write("", ensure=True)
|
||||
paths.work.join("wildcard2").write("", ensure=True)
|
||||
expected.add("wildcard1")
|
||||
@ -105,7 +106,8 @@ def create_test_encrypt_data(paths):
|
||||
edata += "dirwild*\n"
|
||||
paths.work.join("dirwildcard/file1").write("", ensure=True)
|
||||
paths.work.join("dirwildcard/file2").write("", ensure=True)
|
||||
expected.add("dirwildcard")
|
||||
expected.add("dirwildcard/file1")
|
||||
expected.add("dirwildcard/file2")
|
||||
|
||||
# excludes
|
||||
edata += "exclude*\n"
|
||||
@ -186,9 +188,7 @@ def run_parse_encrypt(runner, paths, skip_parse=False, twice=False):
|
||||
YADM_WORK={paths.work}
|
||||
export YADM_WORK
|
||||
{parse_cmd}
|
||||
export ENCRYPT_INCLUDE_FILES
|
||||
export PARSE_ENCRYPT_SHORT
|
||||
env
|
||||
echo PARSE_ENCRYPT_SHORT=$PARSE_ENCRYPT_SHORT
|
||||
echo EIF_COUNT:${{#ENCRYPT_INCLUDE_FILES[@]}}
|
||||
for value in "${{ENCRYPT_INCLUDE_FILES[@]}}"; do
|
||||
echo "EIF:$value"
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: private_dirs"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: query_distro"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
|
@ -1,15 +1,18 @@
|
||||
"""Unit tests: query_distro_family"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize("condition", ["os-release", "os-release-quotes", "missing"])
|
||||
@pytest.mark.parametrize("condition", ["os-release", "os-release-quotes", "missing", "fallback"])
|
||||
def test_query_distro_family(runner, yadm, tmp_path, condition):
|
||||
"""Match ID_LIKE when present"""
|
||||
test_family = "testfamily"
|
||||
os_release = tmp_path.joinpath("os-release")
|
||||
if "os-release" in condition:
|
||||
quotes = '"' if "quotes" in condition else ""
|
||||
os_release.write_text(f"testing\nID_LIKE={quotes}{test_family}{quotes}\nfamily")
|
||||
os_release.write_text(f"testing\nID=test\nID_LIKE={quotes}{test_family}{quotes}\nfamily")
|
||||
elif condition == "fallback":
|
||||
os_release.write_text(f'testing\nID="{test_family}"\nfamily')
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
OS_RELEASE="{os_release}"
|
||||
@ -18,7 +21,7 @@ def test_query_distro_family(runner, yadm, tmp_path, condition):
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
assert run.err == ""
|
||||
if "os-release" in condition:
|
||||
assert run.out.rstrip() == test_family
|
||||
else:
|
||||
if condition == "missing":
|
||||
assert run.out.rstrip() == ""
|
||||
else:
|
||||
assert run.out.rstrip() == test_family
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: record_score"""
|
||||
|
||||
import pytest
|
||||
|
||||
INIT_VARS = """
|
||||
@ -10,7 +11,7 @@ INIT_VARS = """
|
||||
alt_scores=()
|
||||
alt_targets=()
|
||||
alt_sources=()
|
||||
alt_template_cmds=()
|
||||
alt_template_processors=()
|
||||
"""
|
||||
|
||||
REPORT_RESULTS = """
|
||||
@ -18,6 +19,7 @@ REPORT_RESULTS = """
|
||||
echo "SCORES:${alt_scores[@]}"
|
||||
echo "TARGETS:${alt_targets[@]}"
|
||||
echo "SOURCES:${alt_sources[@]}"
|
||||
echo "TEMPLATE_PROCESSORS:${alt_template_processors[@]}"
|
||||
"""
|
||||
|
||||
|
||||
@ -37,6 +39,7 @@ def test_dont_record_zeros(runner, yadm):
|
||||
assert "SCORES:\n" in run.out
|
||||
assert "TARGETS:\n" in run.out
|
||||
assert "SOURCES:\n" in run.out
|
||||
assert "TEMPLATE_PROCESSORS:\n" in run.out
|
||||
|
||||
|
||||
def test_new_scores(runner, yadm):
|
||||
@ -45,9 +48,9 @@ def test_new_scores(runner, yadm):
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
{INIT_VARS}
|
||||
record_score "1" "tgt_one" "src_one"
|
||||
record_score "2" "tgt_two" "src_two"
|
||||
record_score "4" "tgt_three" "src_three"
|
||||
record_score "1" "tgt_one" "src_one" ""
|
||||
record_score "2" "tgt_two" "src_two" ""
|
||||
record_score "4" "tgt_three" "src_three" ""
|
||||
{REPORT_RESULTS}
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
@ -57,6 +60,7 @@ def test_new_scores(runner, yadm):
|
||||
assert "SCORES:1 2 4\n" in run.out
|
||||
assert "TARGETS:tgt_one tgt_two tgt_three\n" in run.out
|
||||
assert "SOURCES:src_one src_two src_three\n" in run.out
|
||||
assert "TEMPLATE_PROCESSORS: \n" in run.out
|
||||
|
||||
|
||||
@pytest.mark.parametrize("difference", ["lower", "equal", "higher"])
|
||||
@ -80,7 +84,8 @@ def test_existing_scores(runner, yadm, difference):
|
||||
alt_scores=(2)
|
||||
alt_targets=("testtgt")
|
||||
alt_sources=("existing_src")
|
||||
record_score "{score}" "testtgt" "new_src"
|
||||
alt_template_processors=("")
|
||||
record_score "{score}" "testtgt" "new_src" ""
|
||||
{REPORT_RESULTS}
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
@ -90,6 +95,7 @@ def test_existing_scores(runner, yadm, difference):
|
||||
assert f"SCORES:{expected_score}\n" in run.out
|
||||
assert "TARGETS:testtgt\n" in run.out
|
||||
assert f"SOURCES:{expected_src}\n" in run.out
|
||||
assert "TEMPLATE_PROCESSORS:\n" in run.out
|
||||
|
||||
|
||||
def test_existing_template(runner, yadm):
|
||||
@ -100,9 +106,9 @@ def test_existing_template(runner, yadm):
|
||||
{INIT_VARS}
|
||||
alt_scores=(1)
|
||||
alt_targets=("testtgt")
|
||||
alt_sources=()
|
||||
alt_template_cmds=("existing_template")
|
||||
record_score "2" "testtgt" "new_src"
|
||||
alt_sources=("src")
|
||||
alt_template_processors=("existing_template")
|
||||
record_score "2" "testtgt" "new_src" ""
|
||||
{REPORT_RESULTS}
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
@ -111,7 +117,8 @@ def test_existing_template(runner, yadm):
|
||||
assert "SIZE:1\n" in run.out
|
||||
assert "SCORES:1\n" in run.out
|
||||
assert "TARGETS:testtgt\n" in run.out
|
||||
assert "SOURCES:\n" in run.out
|
||||
assert "SOURCES:src\n" in run.out
|
||||
assert "TEMPLATE_PROCESSORS:existing_template\n" in run.out
|
||||
|
||||
|
||||
def test_config_first(runner, yadm):
|
||||
@ -122,20 +129,61 @@ def test_config_first(runner, yadm):
|
||||
YADM_TEST=1 source {yadm}
|
||||
{INIT_VARS}
|
||||
YADM_CONFIG={config}
|
||||
record_score "1" "tgt_before" "src_before"
|
||||
record_template "tgt_tmp" "cmd_tmp" "src_tmp"
|
||||
record_score "2" "{config}" "src_config"
|
||||
record_score "3" "tgt_after" "src_after"
|
||||
record_score "1" "tgt_before" "src_before" ""
|
||||
record_score "1" "tgt_tmp" "src_tmp" "processor_tmp"
|
||||
record_score "2" "{config}" "src_config" ""
|
||||
record_score "3" "tgt_after" "src_after" ""
|
||||
{REPORT_RESULTS}
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
assert run.err == ""
|
||||
assert "SIZE:4\n" in run.out
|
||||
assert "SCORES:2 1 1 3\n" in run.out
|
||||
assert f"TARGETS:{config} tgt_before tgt_tmp tgt_after\n" in run.out
|
||||
assert "SOURCES:src_config src_before src_tmp src_after\n" in run.out
|
||||
assert "TEMPLATE_PROCESSORS: processor_tmp \n" in run.out
|
||||
|
||||
|
||||
def test_new_template(runner, yadm):
|
||||
"""Test new template"""
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
{INIT_VARS}
|
||||
record_score 0 "tgt_one" "src_one" "processor_one"
|
||||
record_score 0 "tgt_two" "src_two" "processor_two"
|
||||
record_score 0 "tgt_three" "src_three" "processor_three"
|
||||
{REPORT_RESULTS}
|
||||
echo "CMD_VALUE:${{alt_template_cmds[@]}}"
|
||||
echo "CMD_INDEX:${{!alt_template_cmds[@]}}"
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
assert run.err == ""
|
||||
assert "SIZE:3\n" in run.out
|
||||
assert "SCORES:2 1 3\n" in run.out
|
||||
assert f"TARGETS:{config} tgt_before tgt_tmp tgt_after\n" in run.out
|
||||
assert "SOURCES:src_config src_before src_tmp src_after\n" in run.out
|
||||
assert "CMD_VALUE:cmd_tmp\n" in run.out
|
||||
assert "CMD_INDEX:2\n" in run.out
|
||||
assert "SCORES:0 0 0\n" in run.out
|
||||
assert "TARGETS:tgt_one tgt_two tgt_three\n" in run.out
|
||||
assert "SOURCES:src_one src_two src_three\n" in run.out
|
||||
assert "TEMPLATE_PROCESSORS:processor_one processor_two processor_three\n" in run.out
|
||||
|
||||
|
||||
def test_overwrite_existing_template(runner, yadm):
|
||||
"""Overwrite existing templates"""
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
{INIT_VARS}
|
||||
alt_scores=(0)
|
||||
alt_targets=("testtgt")
|
||||
alt_template_processors=("existing_processor")
|
||||
alt_sources=("existing_src")
|
||||
record_score 0 "testtgt" "new_src" "new_processor"
|
||||
{REPORT_RESULTS}
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
assert run.err == ""
|
||||
assert "SIZE:1\n" in run.out
|
||||
assert "SCORES:0\n" in run.out
|
||||
assert "TARGETS:testtgt\n" in run.out
|
||||
assert "SOURCES:new_src\n" in run.out
|
||||
assert "TEMPLATE_PROCESSORS:new_processor\n" in run.out
|
||||
|
@ -1,55 +0,0 @@
|
||||
"""Unit tests: record_template"""
|
||||
|
||||
INIT_VARS = """
|
||||
alt_targets=()
|
||||
alt_template_cmds=()
|
||||
alt_sources=()
|
||||
"""
|
||||
|
||||
REPORT_RESULTS = """
|
||||
echo "SIZE:${#alt_targets[@]}"
|
||||
echo "TARGETS:${alt_targets[@]}"
|
||||
echo "CMDS:${alt_template_cmds[@]}"
|
||||
echo "SOURCES:${alt_sources[@]}"
|
||||
"""
|
||||
|
||||
|
||||
def test_new_template(runner, yadm):
|
||||
"""Test new template"""
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
{INIT_VARS}
|
||||
record_template "tgt_one" "cmd_one" "src_one"
|
||||
record_template "tgt_two" "cmd_two" "src_two"
|
||||
record_template "tgt_three" "cmd_three" "src_three"
|
||||
{REPORT_RESULTS}
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
assert run.err == ""
|
||||
assert "SIZE:3\n" in run.out
|
||||
assert "TARGETS:tgt_one tgt_two tgt_three\n" in run.out
|
||||
assert "CMDS:cmd_one cmd_two cmd_three\n" in run.out
|
||||
assert "SOURCES:src_one src_two src_three\n" in run.out
|
||||
|
||||
|
||||
def test_existing_template(runner, yadm):
|
||||
"""Overwrite existing templates"""
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
{INIT_VARS}
|
||||
alt_targets=("testtgt")
|
||||
alt_template_cmds=("existing_cmd")
|
||||
alt_sources=("existing_src")
|
||||
record_template "testtgt" "new_cmd" "new_src"
|
||||
{REPORT_RESULTS}
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
assert run.err == ""
|
||||
assert "SIZE:1\n" in run.out
|
||||
assert "TARGETS:testtgt\n" in run.out
|
||||
assert "CMDS:new_cmd\n" in run.out
|
||||
assert "SOURCES:new_src\n" in run.out
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: relative_path"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@ -10,11 +11,16 @@ import pytest
|
||||
("/A/B/C", "/A/B/C", ""),
|
||||
("/A/B/C", "/A/B/C/D", "D"),
|
||||
("/A/B/C", "/A/B/C/D/E", "D/E"),
|
||||
("/A/B/C", "/A/B/CD", "../CD"),
|
||||
("/A/B/C", "/A/BB/C", "../../BB/C"),
|
||||
("/A/B/C", "/A/B/D", "../D"),
|
||||
("/A/B/C", "/A/B/D/E", "../D/E"),
|
||||
("/A/B/C", "/A/D", "../../D"),
|
||||
("/A/B/C", "/A/D/E", "../../D/E"),
|
||||
("/A/B/C", "/D/E/F", "../../../D/E/F"),
|
||||
("/", "/A/B/C", "A/B/C"),
|
||||
("/A/B/C", "/", "../../.."),
|
||||
("/A/B B/C", "/A/C C/D", "../../C C/D"),
|
||||
],
|
||||
)
|
||||
def test_relative_path(runner, paths, base, full_path, expected):
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: remove_stale_links"""
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
@ -24,7 +25,7 @@ def test_remove_stale_links(runner, yadm, tmpdir, kind, linked):
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
possible_alts=({link})
|
||||
possible_alt_targets=({link})
|
||||
alt_linked=({alt_linked})
|
||||
function rm() {{ echo rm "$@"; }}
|
||||
remove_stale_links
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: report_invalid_alts"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: score_file"""
|
||||
|
||||
import pytest
|
||||
|
||||
CONDITION = {
|
||||
@ -53,42 +54,42 @@ def calculate_score(filename):
|
||||
if label in CONDITION["default"]["labels"]:
|
||||
score += 1000
|
||||
elif label in CONDITION["arch"]["labels"]:
|
||||
if value == "testarch":
|
||||
if value.lower() == "testarch":
|
||||
score += 1000 + CONDITION["arch"]["modifier"]
|
||||
else:
|
||||
score = 0
|
||||
break
|
||||
elif label in CONDITION["system"]["labels"]:
|
||||
if value == "testsystem":
|
||||
if value.lower() == "testsystem":
|
||||
score += 1000 + CONDITION["system"]["modifier"]
|
||||
else:
|
||||
score = 0
|
||||
break
|
||||
elif label in CONDITION["distro"]["labels"]:
|
||||
if value == "testdistro":
|
||||
if value.lower() == "testdistro":
|
||||
score += 1000 + CONDITION["distro"]["modifier"]
|
||||
else:
|
||||
score = 0
|
||||
break
|
||||
elif label in CONDITION["class"]["labels"]:
|
||||
if value == "testclass":
|
||||
if value.lower() == "testclass":
|
||||
score += 1000 + CONDITION["class"]["modifier"]
|
||||
else:
|
||||
score = 0
|
||||
break
|
||||
elif label in CONDITION["hostname"]["labels"]:
|
||||
if value == "testhost":
|
||||
if value.lower() == "testhost":
|
||||
score += 1000 + CONDITION["hostname"]["modifier"]
|
||||
else:
|
||||
score = 0
|
||||
break
|
||||
elif label in CONDITION["user"]["labels"]:
|
||||
if value == "testuser":
|
||||
if value.lower() == "testuser":
|
||||
score += 1000 + CONDITION["user"]["modifier"]
|
||||
else:
|
||||
score = 0
|
||||
break
|
||||
elif label in TEMPLATE_LABELS:
|
||||
elif label not in TEMPLATE_LABELS:
|
||||
score = 0
|
||||
break
|
||||
return score
|
||||
@ -104,12 +105,12 @@ def calculate_score(filename):
|
||||
def test_score_values(runner, yadm, default, arch, system, distro, cla, host, user):
|
||||
"""Test score results"""
|
||||
# pylint: disable=too-many-branches
|
||||
local_class = "testclass"
|
||||
local_arch = "testarch"
|
||||
local_system = "testsystem"
|
||||
local_distro = "testdistro"
|
||||
local_host = "testhost"
|
||||
local_user = "testuser"
|
||||
local_class = "testClass"
|
||||
local_arch = "testARch"
|
||||
local_system = "TESTsystem"
|
||||
local_distro = "testDISTro"
|
||||
local_host = "testHost"
|
||||
local_user = "testUser"
|
||||
filenames = {"filename##": 0}
|
||||
|
||||
if default:
|
||||
@ -189,7 +190,7 @@ def test_score_values(runner, yadm, default, arch, system, distro, cla, host, us
|
||||
expected = ""
|
||||
for filename, score in filenames.items():
|
||||
script += f"""
|
||||
score_file "{filename}"
|
||||
score_file "{filename}" "dest"
|
||||
echo "{filename}"
|
||||
echo "$score"
|
||||
"""
|
||||
@ -254,7 +255,7 @@ def test_score_values_templates(runner, yadm):
|
||||
expected = ""
|
||||
for filename, score in filenames.items():
|
||||
script += f"""
|
||||
score_file "{filename}"
|
||||
score_file "{filename}" "dest"
|
||||
echo "{filename}"
|
||||
echo "$score"
|
||||
"""
|
||||
@ -266,19 +267,19 @@ def test_score_values_templates(runner, yadm):
|
||||
assert run.out == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("cmd_generated", [True, False], ids=["supported-template", "unsupported-template"])
|
||||
def test_template_recording(runner, yadm, cmd_generated):
|
||||
"""Template should be recorded if choose_template_cmd outputs a command"""
|
||||
@pytest.mark.parametrize("processor_generated", [True, False], ids=["supported-template", "unsupported-template"])
|
||||
def test_template_recording(runner, yadm, processor_generated):
|
||||
"""Template should be recorded if choose_template_processor outputs a command"""
|
||||
|
||||
mock = "function choose_template_cmd() { return; }"
|
||||
mock = "function choose_template_processor() { return; }"
|
||||
expected = ""
|
||||
if cmd_generated:
|
||||
mock = 'function choose_template_cmd() { echo "test_cmd"; }'
|
||||
if processor_generated:
|
||||
mock = 'function choose_template_processor() { echo "test_processor"; }'
|
||||
expected = "template recorded"
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
function record_template() {{ echo "template recorded"; }}
|
||||
function record_score() {{ [ -n "$4" ] && echo "template recorded"; }}
|
||||
{mock}
|
||||
score_file "testfile##template.kind"
|
||||
"""
|
||||
@ -288,15 +289,15 @@ def test_template_recording(runner, yadm, cmd_generated):
|
||||
assert run.out.rstrip() == expected
|
||||
|
||||
|
||||
def test_underscores_in_distro_and_family(runner, yadm):
|
||||
"""Test replacing spaces in distro / distro_family with underscores"""
|
||||
def test_underscores_and_upper_case_in_distro_and_family(runner, yadm):
|
||||
"""Test replacing spaces with underscores and lowering case in distro / distro_family"""
|
||||
local_distro = "test distro"
|
||||
local_distro_family = "test family"
|
||||
filenames = {
|
||||
"filename##distro.test distro": 1004,
|
||||
"filename##distro.Test Distro": 1004,
|
||||
"filename##distro.test-distro": 0,
|
||||
"filename##distro.test_distro": 1004,
|
||||
"filename##distro_family.test family": 1008,
|
||||
"filename##distro_family.test FAMILY": 1008,
|
||||
"filename##distro_family.test-family": 0,
|
||||
"filename##distro_family.test_family": 1008,
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: set_local_alt_values"""
|
||||
|
||||
import pytest
|
||||
import utils
|
||||
|
||||
@ -12,6 +13,8 @@ import utils
|
||||
"os",
|
||||
"hostname",
|
||||
"user",
|
||||
"distro",
|
||||
"distro-family",
|
||||
],
|
||||
ids=[
|
||||
"no-override",
|
||||
@ -20,11 +23,15 @@ import utils
|
||||
"override-os",
|
||||
"override-hostname",
|
||||
"override-user",
|
||||
"override-distro",
|
||||
"override-distro-family",
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("ds1_copy")
|
||||
def test_set_local_alt_values(runner, yadm, paths, tst_arch, tst_sys, tst_host, tst_user, override):
|
||||
"""Use issue_legacy_path_warning"""
|
||||
def test_set_local_alt_values(
|
||||
runner, yadm, paths, tst_arch, tst_sys, tst_host, tst_user, tst_distro, tst_distro_family, override
|
||||
):
|
||||
"""Test handling of local alt values"""
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm} &&
|
||||
set_operating_system &&
|
||||
@ -33,8 +40,10 @@ def test_set_local_alt_values(runner, yadm, paths, tst_arch, tst_sys, tst_host,
|
||||
echo "class='$local_class'"
|
||||
echo "arch='$local_arch'"
|
||||
echo "os='$local_system'"
|
||||
echo "host='$local_host'"
|
||||
echo "hostname='$local_host'"
|
||||
echo "user='$local_user'"
|
||||
echo "distro='$local_distro'"
|
||||
echo "distro-family='$local_distro_family'"
|
||||
"""
|
||||
|
||||
if override == "class":
|
||||
@ -47,46 +56,18 @@ def test_set_local_alt_values(runner, yadm, paths, tst_arch, tst_sys, tst_host,
|
||||
assert run.success
|
||||
assert run.err == ""
|
||||
|
||||
if override == "class":
|
||||
assert "class='override'" in run.out
|
||||
else:
|
||||
assert "class=''" in run.out
|
||||
default_values = {
|
||||
"class": "",
|
||||
"arch": tst_arch,
|
||||
"os": tst_sys,
|
||||
"hostname": tst_host,
|
||||
"user": tst_user,
|
||||
"distro": tst_distro,
|
||||
"distro-family": tst_distro_family,
|
||||
}
|
||||
|
||||
if override == "arch":
|
||||
assert "arch='override'" in run.out
|
||||
else:
|
||||
assert f"arch='{tst_arch}'" in run.out
|
||||
|
||||
if override == "os":
|
||||
assert "os='override'" in run.out
|
||||
else:
|
||||
assert f"os='{tst_sys}'" in run.out
|
||||
|
||||
if override == "hostname":
|
||||
assert "host='override'" in run.out
|
||||
else:
|
||||
assert f"host='{tst_host}'" in run.out
|
||||
|
||||
if override == "user":
|
||||
assert "user='override'" in run.out
|
||||
else:
|
||||
assert f"user='{tst_user}'" in run.out
|
||||
|
||||
|
||||
def test_distro_and_family(runner, yadm):
|
||||
"""Assert that local_distro/local_distro_family are set"""
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
function config() {{ echo "$1"; }}
|
||||
function query_distro() {{ echo "testdistro"; }}
|
||||
function query_distro_family() {{ echo "testfamily"; }}
|
||||
set_local_alt_values
|
||||
echo "distro='$local_distro'"
|
||||
echo "distro_family='$local_distro_family'"
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
assert run.err == ""
|
||||
assert "distro='testdistro'" in run.out
|
||||
assert "distro_family='testfamily'" in run.out
|
||||
for key, value in default_values.items():
|
||||
if key == override:
|
||||
assert f"{key}='override'" in run.out
|
||||
else:
|
||||
assert f"{key}='{value}'" in run.out
|
||||
|
@ -36,5 +36,5 @@ def test_set_operating_system(runner, paths, tst_sys, proc_value, expected_os):
|
||||
assert run.success
|
||||
assert run.err == ""
|
||||
if expected_os == "uname":
|
||||
expected_os = tst_sys
|
||||
expected_os = tst_sys if tst_sys != "WSL" else "Linux"
|
||||
assert run.out.rstrip() == expected_os
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: set_yadm_dirs"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
|
@ -35,13 +35,13 @@ wrong class 1
|
||||
{{% if yadm.class != "wronglcass" %}}
|
||||
Included section from !=
|
||||
{{% endif\t\t %}}
|
||||
{{% if yadm.class == "{LOCAL_CLASS}" %}}
|
||||
{{% if yadm.class == "{LOCAL_CLASS.lower()}" %}}
|
||||
Included section for class = {{{{yadm.class}}}} ({{{{yadm.class}}}} repeated)
|
||||
Multiple lines
|
||||
{{% else %}}
|
||||
Should not be included...
|
||||
{{% endif %}}
|
||||
{{% if yadm.class == "{LOCAL_CLASS2}" %}}
|
||||
{{% if yadm.class == "{LOCAL_CLASS2.upper()}" %}}
|
||||
Included section for second class
|
||||
{{% endif %}}
|
||||
{{% if yadm.class == "wrongclass2" %}}
|
||||
@ -50,7 +50,7 @@ wrong class 2
|
||||
{{% if yadm.arch == "wrongarch1" %}}
|
||||
wrong arch 1
|
||||
{{% endif %}}
|
||||
{{% if yadm.arch == "{LOCAL_ARCH}" %}}
|
||||
{{% if yadm.arch == "{LOCAL_ARCH.title()}" %}}
|
||||
Included section for arch = {{{{yadm.arch}}}} ({{{{yadm.arch}}}} repeated)
|
||||
{{% endif %}}
|
||||
{{% if yadm.arch == "wrongarch2" %}}
|
||||
@ -59,7 +59,7 @@ wrong arch 2
|
||||
{{% if yadm.os == "wrongos1" %}}
|
||||
wrong os 1
|
||||
{{% endif %}}
|
||||
{{% if yadm.os == "{LOCAL_SYSTEM}" %}}
|
||||
{{% if yadm.os == "{LOCAL_SYSTEM.lower()}" %}}
|
||||
Included section for os = {{{{yadm.os}}}} ({{{{yadm.os}}}} repeated)
|
||||
{{% endif %}}
|
||||
{{% if yadm.os == "wrongos2" %}}
|
||||
@ -68,7 +68,7 @@ wrong os 2
|
||||
{{% if yadm.hostname == "wronghost1" %}}
|
||||
wrong host 1
|
||||
{{% endif %}}
|
||||
{{% if yadm.hostname == "{LOCAL_HOST}" %}}
|
||||
{{% if yadm.hostname == "{LOCAL_HOST.upper()}" %}}
|
||||
Included section for host = {{{{yadm.hostname}}}} ({{{{yadm.hostname}}}} again)
|
||||
{{% endif %}}
|
||||
{{% if yadm.hostname == "wronghost2" %}}
|
||||
@ -77,7 +77,7 @@ wrong host 2
|
||||
{{% if yadm.user == "wronguser1" %}}
|
||||
wrong user 1
|
||||
{{% endif %}}
|
||||
{{% if yadm.user == "{LOCAL_USER}" %}}
|
||||
{{% if yadm.user == "{LOCAL_USER.title()}" %}}
|
||||
Included section for user = {{{{yadm.user}}}} ({{{{yadm.user}}}} repeated)
|
||||
{{% endif %}}
|
||||
{{% if yadm.user == "wronguser2" %}}
|
||||
@ -86,7 +86,7 @@ wrong user 2
|
||||
{{% if yadm.distro == "wrongdistro1" %}}
|
||||
wrong distro 1
|
||||
{{% endif %}}
|
||||
{{% if yadm.distro == "{LOCAL_DISTRO}" %}}
|
||||
{{% if yadm.distro == "{LOCAL_DISTRO.lower()}" %}}
|
||||
Included section for distro = {{{{yadm.distro}}}} ({{{{yadm.distro}}}} again)
|
||||
{{% endif %}}
|
||||
{{% if yadm.distro == "wrongdistro2" %}}
|
||||
@ -95,14 +95,14 @@ wrong distro 2
|
||||
{{% if yadm.distro_family == "wrongfamily1" %}}
|
||||
wrong family 1
|
||||
{{% endif %}}
|
||||
{{% if yadm.distro_family == "{LOCAL_DISTRO_FAMILY}" %}}
|
||||
{{% if yadm.distro_family == "{LOCAL_DISTRO_FAMILY.upper()}" %}}
|
||||
Included section for distro_family = \
|
||||
{{{{yadm.distro_family}}}} ({{{{yadm.distro_family}}}} again)
|
||||
{{% endif %}}
|
||||
{{% if yadm.distro_family == "wrongfamily2" %}}
|
||||
wrong family 2
|
||||
{{% endif %}}
|
||||
{{% if env.VAR == "{ENV_VAR}" %}}
|
||||
{{% if env.VAR == "{ENV_VAR.title()}" %}}
|
||||
Included section for env.VAR = {{{{env.VAR}}}} ({{{{env.VAR}}}} again)
|
||||
{{% endif %}}
|
||||
{{% if env.VAR == "wrongenvvar" %}}
|
||||
@ -231,7 +231,7 @@ def test_template_default(runner, yadm, tmpdir):
|
||||
local_user="{LOCAL_USER}"
|
||||
local_distro="{LOCAL_DISTRO}"
|
||||
local_distro_family="{LOCAL_DISTRO_FAMILY}"
|
||||
template_default "{input_file}" "{output_file}"
|
||||
template default "{input_file}" "{output_file}"
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script, env={"VAR": ENV_VAR})
|
||||
assert run.success
|
||||
@ -251,7 +251,7 @@ def test_source(runner, yadm, tmpdir):
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
set_awk
|
||||
template_default "{input_file}" "{output_file}"
|
||||
template default "{input_file}" "{output_file}"
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
@ -285,7 +285,7 @@ def test_include(runner, yadm, tmpdir):
|
||||
set_awk
|
||||
local_class="{LOCAL_CLASS}"
|
||||
local_system="{LOCAL_SYSTEM}"
|
||||
template_default "{input_file}" "{output_file}"
|
||||
template default "{input_file}" "{output_file}"
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
@ -305,7 +305,7 @@ def test_nested_ifs(runner, yadm, tmpdir):
|
||||
YADM_TEST=1 source {yadm}
|
||||
set_awk
|
||||
local_user="me"
|
||||
template_default "{input_file}" "{output_file}"
|
||||
template default "{input_file}" "{output_file}"
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
@ -323,7 +323,7 @@ def test_env(runner, yadm, tmpdir):
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
set_awk
|
||||
template_default "{input_file}" "{output_file}"
|
||||
template default "{input_file}" "{output_file}"
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: template_esh"""
|
||||
|
||||
import os
|
||||
|
||||
FILE_MODE = 0o754
|
||||
@ -139,7 +140,7 @@ def test_template_esh(runner, yadm, tmpdir):
|
||||
local_user="{LOCAL_USER}"
|
||||
local_distro="{LOCAL_DISTRO}"
|
||||
local_distro_family="{LOCAL_DISTRO_FAMILY}"
|
||||
template_esh "{input_file}" "{output_file}"
|
||||
template esh "{input_file}" "{output_file}"
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
@ -158,7 +159,7 @@ def test_source(runner, yadm, tmpdir):
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
template_esh "{input_file}" "{output_file}"
|
||||
template esh "{input_file}" "{output_file}"
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: template_j2cli & template_envtpl"""
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
@ -145,7 +146,7 @@ def test_template_j2(runner, yadm, tmpdir, processor):
|
||||
local_user="{LOCAL_USER}"
|
||||
local_distro="{LOCAL_DISTRO}"
|
||||
local_distro_family="{LOCAL_DISTRO_FAMILY}"
|
||||
template_{processor} "{input_file}" "{output_file}"
|
||||
template {processor} "{input_file}" "{output_file}"
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
@ -165,7 +166,7 @@ def test_source(runner, yadm, tmpdir, processor):
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
template_{processor} "{input_file}" "{output_file}"
|
||||
template {processor} "{input_file}" "{output_file}"
|
||||
"""
|
||||
run = runner(command=["bash"], inp=script)
|
||||
assert run.success
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests: upgrade"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@ -62,11 +63,10 @@ def test_upgrade(tmpdir, runner, yadm, condition):
|
||||
function git() {{
|
||||
echo "$@"
|
||||
if [[ "$*" = *"submodule status" ]]; then
|
||||
{ 'echo " 1234567 mymodule (1.0)"'
|
||||
if condition == 'submodules' else ':' }
|
||||
{'echo " 1234567 mymodule (1.0)"' if condition == 'submodules' else ':'}
|
||||
fi
|
||||
if [[ "$*" = *ls-files* ]]; then
|
||||
return { 1 if condition == 'untracked' else 0 }
|
||||
return {1 if condition == 'untracked' else 0}
|
||||
fi
|
||||
return 0
|
||||
}}
|
||||
|
@ -19,13 +19,15 @@ import pytest
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("submodule", [False, True], ids=["no submodule", "with submodules"])
|
||||
def test_upgrade(tmpdir, runner, versions, submodule):
|
||||
def test_upgrade(tmpdir, runner, paths, versions, submodule):
|
||||
"""Upgrade tests"""
|
||||
# pylint: disable=too-many-statements
|
||||
home = tmpdir.mkdir("HOME")
|
||||
env = {"HOME": str(home)}
|
||||
runner(["git", "config", "--global", "init.defaultBranch", "master"], env=env)
|
||||
runner(["git", "config", "--global", "protocol.file.allow", "always"], env=env)
|
||||
runner(["git", "config", "--global", "user.email", "test@yadm.io"], env=env)
|
||||
runner(["git", "config", "--global", "user.name", "Yadm Test"], env=env)
|
||||
|
||||
if submodule:
|
||||
ext_repo = tmpdir.mkdir("ext_repo")
|
||||
@ -39,7 +41,7 @@ def test_upgrade(tmpdir, runner, versions, submodule):
|
||||
os.environ.pop("XDG_DATA_HOME", None)
|
||||
|
||||
def run_version(version, *args, check_stderr=True):
|
||||
yadm = f"yadm-{version}" if version else "/yadm/yadm"
|
||||
yadm = f"yadm-{version}" if version else paths.pgm
|
||||
run = runner([yadm, *args], shell=True, cwd=str(home), env=env)
|
||||
assert run.success
|
||||
if check_stderr:
|
||||
|
@ -12,7 +12,7 @@ ALT_DIR = "test alt/test alt dir"
|
||||
|
||||
# Directory based alternates must have a tracked contained file.
|
||||
# This will be the test contained file name
|
||||
CONTAINED = "contained_file"
|
||||
CONTAINED = "contained_dir/contained_file"
|
||||
|
||||
# These variables are used for making include files which will be processed
|
||||
# within jinja templates
|
||||
@ -84,7 +84,7 @@ def parse_alt_output(output, linked=True):
|
||||
"""Parse output of 'alt', and return list of linked files"""
|
||||
regex = r"Creating (.+) from template (.+)$"
|
||||
if linked:
|
||||
regex = r"Linking (.+) to (.+)$"
|
||||
regex = r"(?:Copy|Link)ing (.+) to (.+)$"
|
||||
parsed_list = {}
|
||||
for line in output.splitlines():
|
||||
match = re.match(regex, line)
|
||||
|
243
yadm.1
243
yadm.1
@ -1,5 +1,5 @@
|
||||
.\" vim: set spell so=8:
|
||||
.TH yadm 1 "8 November 2024" "3.3.0"
|
||||
.TH YADM 1 "February 9, 2025" "3.4.0"
|
||||
|
||||
.SH NAME
|
||||
|
||||
@ -15,55 +15,55 @@ yadm \- Yet Another Dotfiles Manager
|
||||
.I git-command-or-alias
|
||||
.RI [ options ]
|
||||
|
||||
.B yadm
|
||||
init
|
||||
.RB [ -f ]
|
||||
.RB [ -w
|
||||
.B yadm init
|
||||
.RB [ \-f ]
|
||||
.RB [ \-w
|
||||
.IR dir ]
|
||||
|
||||
.B yadm
|
||||
.RI clone " url
|
||||
.RB [ -f ]
|
||||
.RB [ -w
|
||||
.B yadm clone
|
||||
.I url
|
||||
.RB [ \-f ]
|
||||
.RB [ \-w
|
||||
.IR dir ]
|
||||
.RB [ -b
|
||||
.RB [ \-b
|
||||
.IR branch ]
|
||||
.RB [ --bootstrap ]
|
||||
.RB [ --no-bootstrap ]
|
||||
.RB [ \-\-bootstrap ]
|
||||
.RB [ \-\-no\-bootstrap ]
|
||||
|
||||
.B yadm
|
||||
.RI config " name
|
||||
.B yadm config
|
||||
.I name
|
||||
.RI [ value ]
|
||||
|
||||
.B yadm
|
||||
config
|
||||
.RB [ -e ]
|
||||
.B yadm config
|
||||
.RB [ \-e ]
|
||||
|
||||
.B yadm
|
||||
list
|
||||
.RB [ -a ]
|
||||
.B yadm list
|
||||
.RB [ \-a ]
|
||||
|
||||
.BR yadm " bootstrap
|
||||
.B yadm bootstrap
|
||||
|
||||
.BR yadm " encrypt
|
||||
.B yadm encrypt
|
||||
|
||||
.BR yadm " decrypt
|
||||
.RB [ -l ]
|
||||
.B yadm decrypt
|
||||
.RB [ \-l ]
|
||||
|
||||
.BR yadm " alt
|
||||
.B yadm alt
|
||||
|
||||
.BR yadm " perms
|
||||
.B yadm perms
|
||||
|
||||
.BR yadm " enter [ command ]
|
||||
.B yadm enter
|
||||
.RI [ command ]
|
||||
|
||||
.BR yadm " git-crypt [ options ]
|
||||
.B yadm git\-crypt
|
||||
.RI [ options ]
|
||||
|
||||
.BR yadm " transcrypt [ options ]
|
||||
.B yadm transcrypt
|
||||
.RI [ options ]
|
||||
|
||||
.BR yadm " upgrade
|
||||
.RB [ -f ]
|
||||
.B yadm upgrade
|
||||
.RB [ \-f ]
|
||||
|
||||
.BR yadm " introspect
|
||||
.B yadm introspect
|
||||
.I category
|
||||
|
||||
.SH DESCRIPTION
|
||||
@ -83,8 +83,7 @@ Any command not internally handled by yadm is passed through to
|
||||
.BR git (1).
|
||||
Git commands or aliases are invoked with the yadm managed repository.
|
||||
The working directory for Git commands will be the configured
|
||||
.IR work-tree " (usually
|
||||
.IR $HOME ).
|
||||
.IR work-tree \ (usually\ $HOME ).
|
||||
|
||||
Dotfiles are managed by using standard
|
||||
.B git
|
||||
@ -95,7 +94,7 @@ commands;
|
||||
.IR pull ,
|
||||
etc.
|
||||
|
||||
.RI The " config
|
||||
.RI The\ config
|
||||
command is not passed directly through.
|
||||
Instead use the
|
||||
.I gitconfig
|
||||
@ -114,7 +113,7 @@ Execute
|
||||
.I $HOME/.config/yadm/bootstrap
|
||||
if it exists.
|
||||
.TP
|
||||
.BI clone " url
|
||||
.BI clone \ url
|
||||
Clone a remote repository for tracking dotfiles.
|
||||
After the contents of the remote repository have been fetched, a "check out" of
|
||||
the remote HEAD branch is attempted.
|
||||
@ -130,15 +129,15 @@ By default,
|
||||
will be used as the
|
||||
.IR work-tree ,
|
||||
but this can be overridden with the
|
||||
.BR -w " option.
|
||||
.BR \-w \ option.
|
||||
yadm can be forced to overwrite an existing repository by providing the
|
||||
.BR -f " option.
|
||||
.BR \-f \ option.
|
||||
If you want to use a branch other than the remote HEAD branch
|
||||
you can specify it using the
|
||||
.BR -b " option.
|
||||
.BR \-b \ option.
|
||||
By default yadm will ask the user if the bootstrap program should be run (if it
|
||||
exists). The options
|
||||
.BR --bootstrap " or " --no-bootstrap
|
||||
.BR \-\-bootstrap " or " \-\-no\-bootstrap
|
||||
will either force the bootstrap to be run, or prevent it from being run,
|
||||
without prompting the user.
|
||||
.TP
|
||||
@ -153,10 +152,9 @@ See the CONFIGURATION section for more details.
|
||||
Decrypt all files stored in
|
||||
.IR $HOME/.local/share/yadm/archive .
|
||||
Files decrypted will be relative to the configured
|
||||
.IR work-tree " (usually
|
||||
.IR $HOME ).
|
||||
.IR work-tree \ (usually\ $HOME ).
|
||||
Using the
|
||||
.B -l
|
||||
.B \-l
|
||||
option will list the files stored without extracting them.
|
||||
.TP
|
||||
.B encrypt
|
||||
@ -191,12 +189,12 @@ Emacs Tramp and Magit can manage files by using this configuration:
|
||||
With this config, use (magit-status "/yadm::").
|
||||
.RE
|
||||
.TP
|
||||
.BI git-crypt " options
|
||||
.BI git-crypt \ options
|
||||
If git-crypt is installed, this command allows you to pass options directly to
|
||||
git-crypt, with the environment configured to use the yadm repository.
|
||||
|
||||
git-crypt enables transparent encryption and decryption of files in a git repository.
|
||||
You can read
|
||||
git-crypt enables transparent encryption and decryption of files in a git
|
||||
repository. You can read
|
||||
https://github.com/AGWA/git-crypt
|
||||
for details.
|
||||
.TP
|
||||
@ -232,17 +230,17 @@ By default,
|
||||
will be used as the
|
||||
.IR work-tree ,
|
||||
but this can be overridden with the
|
||||
.BR -w " option.
|
||||
.BR \-w \ option.
|
||||
yadm can be forced to overwrite an existing repository by providing the
|
||||
.BR -f " option.
|
||||
.BR \-f \ option.
|
||||
.TP
|
||||
.B list
|
||||
Print a list of files managed by yadm.
|
||||
.RB The " -a
|
||||
.RB The \ \-a
|
||||
option will cause all managed files to be listed.
|
||||
Otherwise, the list will only include files from the current directory or below.
|
||||
.TP
|
||||
.BI introspect " category
|
||||
.BI introspect \ category
|
||||
Report internal yadm data. Supported categories are
|
||||
.IR commands ,
|
||||
.IR configs ,
|
||||
@ -259,12 +257,12 @@ configuration
|
||||
.I yadm.auto-perms
|
||||
to "false".
|
||||
.TP
|
||||
.BI transcrypt " options
|
||||
.BI transcrypt \ options
|
||||
If transcrypt is installed, this command allows you to pass options directly to
|
||||
transcrypt, with the environment configured to use the yadm repository.
|
||||
|
||||
transcrypt enables transparent encryption and decryption of files in a git repository.
|
||||
You can read
|
||||
transcrypt enables transparent encryption and decryption of files in a git
|
||||
repository. You can read
|
||||
https://github.com/elasticdog/transcrypt
|
||||
for details.
|
||||
.TP
|
||||
@ -283,7 +281,7 @@ your submodules cannot be de-initialized, the upgrade will fail. The most
|
||||
common reason submodules will fail to de-initialize is because they have local
|
||||
modifications. If you are willing to lose the local modifications to those
|
||||
submodules, you can use the
|
||||
.B -f
|
||||
.B \-f
|
||||
option with the "upgrade" command to force the de-initialization.
|
||||
|
||||
After running "yadm upgrade", you should run "yadm status" to review changes
|
||||
@ -306,33 +304,33 @@ For example, the following alias could be used to override the repository
|
||||
directory.
|
||||
|
||||
.RS
|
||||
alias yadm='yadm --yadm-repo /alternate/path/to/repo'
|
||||
alias yadm='yadm \-\-yadm\-repo /alternate/path/to/repo'
|
||||
.RE
|
||||
|
||||
The following is the full list of universal options.
|
||||
Each option should be followed by a path.
|
||||
.TP
|
||||
.B -Y,--yadm-dir
|
||||
.B \-Y, \-\-yadm\-dir
|
||||
Override the yadm directory.
|
||||
yadm stores its configurations relative to this directory.
|
||||
.TP
|
||||
.B --yadm-data
|
||||
.B \-\-yadm\-data
|
||||
Override the yadm data directory.
|
||||
yadm stores its data relative to this directory.
|
||||
.TP
|
||||
.B --yadm-repo
|
||||
.B \-\-yadm\-repo
|
||||
Override the location of the yadm repository.
|
||||
.TP
|
||||
.B --yadm-config
|
||||
.B \-\-yadm\-config
|
||||
Override the location of the yadm configuration file.
|
||||
.TP
|
||||
.B --yadm-encrypt
|
||||
.B \-\-yadm\-encrypt
|
||||
Override the location of the yadm encryption configuration.
|
||||
.TP
|
||||
.B --yadm-archive
|
||||
.B \-\-yadm\-archive
|
||||
Override the location of the yadm encrypted files archive.
|
||||
.TP
|
||||
.B --yadm-bootstrap
|
||||
.B \-\-yadm\-bootstrap
|
||||
Override the location of the yadm bootstrap program.
|
||||
|
||||
.SH CONFIGURATION
|
||||
@ -377,7 +375,8 @@ manually to update permissions.
|
||||
This feature is enabled by default.
|
||||
.TP
|
||||
.B yadm.auto-private-dirs
|
||||
Disable the automatic creating of private directories described in the section PERMISSIONS.
|
||||
Disable the automatic creating of private directories described in the section
|
||||
PERMISSIONS.
|
||||
.TP
|
||||
.B yadm.cipher
|
||||
Configure which encryption system is used by the encrypt/decrypt commands.
|
||||
@ -426,8 +425,8 @@ Disable the permission changes to
|
||||
.IR $HOME/.ssh/* .
|
||||
This feature is enabled by default.
|
||||
|
||||
.RE
|
||||
The following five "local" configurations are not stored in the
|
||||
.LP
|
||||
The following "local" configurations are not stored in the
|
||||
.IR $HOME/.config/yadm/config,
|
||||
they are stored in the local repository.
|
||||
|
||||
@ -438,7 +437,7 @@ By default, no class will be matched.
|
||||
The local host can be assigned multiple classes using command:
|
||||
|
||||
.RS
|
||||
yadm config --add local.class <additional-class>
|
||||
yadm config \-\-add local.class <additional-class>
|
||||
.RE
|
||||
.TP
|
||||
.B local.arch
|
||||
@ -452,6 +451,12 @@ Override the OS for the purpose of symlinking alternate files.
|
||||
.TP
|
||||
.B local.user
|
||||
Override the user for the purpose of symlinking alternate files.
|
||||
.TP
|
||||
.B local.distro
|
||||
Override the distro for the purpose of symlinking alternate files.
|
||||
.TP
|
||||
.B local.distro-family
|
||||
Override the distro family for the purpose of symlinking alternate files.
|
||||
|
||||
.SH ALTERNATES
|
||||
|
||||
@ -474,25 +479,28 @@ be omitted. Most attributes can be abbreviated as a single letter.
|
||||
|
||||
<attribute>[.<value>]
|
||||
|
||||
.BR NOTE :
|
||||
Value is compared case-insensitive.
|
||||
|
||||
These are the supported attributes, in the order of the weighted precedence:
|
||||
|
||||
.TP
|
||||
.BR template , " t
|
||||
.BR template ,\ t
|
||||
Valid when the value matches a supported template processor.
|
||||
See the TEMPLATES section for more details.
|
||||
.TP
|
||||
.BR user , " u
|
||||
.BR user ,\ u
|
||||
Valid if the value matches the current user.
|
||||
Current user is calculated by running
|
||||
.BR "id -u -n" .
|
||||
.BR "id \-u \-n" .
|
||||
.TP
|
||||
.BR hostname , " h
|
||||
.BR hostname ,\ h
|
||||
Valid if the value matches the short hostname.
|
||||
Hostname is calculated by running
|
||||
.BR "uname -n" ,
|
||||
.BR "uname \-n" ,
|
||||
and trimming off any domain.
|
||||
.TP
|
||||
.BR class , " c
|
||||
.BR class ,\ c
|
||||
Valid if the value matches the
|
||||
.B local.class
|
||||
configuration.
|
||||
@ -501,37 +509,38 @@ Class must be manually set using
|
||||
See the CONFIGURATION section for more details about setting
|
||||
.BR local.class .
|
||||
.TP
|
||||
.BR distro , " d
|
||||
.BR distro ,\ d
|
||||
Valid if the value matches the distro.
|
||||
Distro is calculated by running
|
||||
.B "lsb_release -si"
|
||||
.B "lsb_release \-si"
|
||||
or by inspecting the ID from
|
||||
.BR "/etc/os-release" .
|
||||
.TP
|
||||
.BR distro_family , " f
|
||||
.BR distro_family ,\ f
|
||||
Valid if the value matches the distro family.
|
||||
Distro family is calculated by inspecting the ID_LIKE line from
|
||||
.BR "/etc/os-release" .
|
||||
.B "/etc/os-release"
|
||||
(or ID if no ID_LIKE line is found).
|
||||
.TP
|
||||
.BR os , " o
|
||||
.BR os ,\ o
|
||||
Valid if the value matches the OS.
|
||||
OS is calculated by running
|
||||
.BR "uname -s" .
|
||||
.BR "uname \-s" .
|
||||
.TP
|
||||
.BR arch , " a
|
||||
.BR arch ,\ a
|
||||
Valid if the value matches the architecture.
|
||||
Architecture is calculated by running
|
||||
.BR "uname -m" .
|
||||
.BR "uname \-m" .
|
||||
.TP
|
||||
.B default
|
||||
Valid when no other alternate is valid.
|
||||
.TP
|
||||
.BR extension , " e
|
||||
.BR extension ,\ e
|
||||
A special "condition" that doesn't affect the selection process. Its purpose is
|
||||
instead to allow the alternate file to end with a certain extension to
|
||||
e.g. make editors highlight the content properly.
|
||||
.LP
|
||||
|
||||
.LP
|
||||
.BR NOTE :
|
||||
The OS for "Windows Subsystem for Linux" is reported as "WSL", even
|
||||
though uname identifies as "Linux".
|
||||
@ -577,7 +586,8 @@ which looks like this:
|
||||
|
||||
.IR $HOME/path/example.txt " -> " $HOME/path/example.txt##os.Darwin
|
||||
|
||||
Since the hostname doesn't match any of the managed files, the more generic version is chosen.
|
||||
Since the hostname doesn't match any of the managed files, the more generic
|
||||
version is chosen.
|
||||
|
||||
If running on a Linux server named "host4", the link will be:
|
||||
|
||||
@ -595,7 +605,7 @@ If no "##default" version exists and no files have valid conditions, then no
|
||||
link will be created.
|
||||
|
||||
Links are also created for directories named this way, as long as they have at
|
||||
least one yadm managed file within them (at the top level).
|
||||
least one yadm managed file within them.
|
||||
|
||||
yadm will automatically create these links by default. This can be disabled
|
||||
using the
|
||||
@ -614,13 +624,15 @@ command. The following sets the class to be "Work".
|
||||
|
||||
yadm config local.class Work
|
||||
|
||||
Similarly, the values of architecture, os, hostname, and user can be manually
|
||||
overridden using the configuration options
|
||||
Similarly, the values of architecture, os, hostname, user, distro, and
|
||||
distro_family can be manually overridden using the configuration options
|
||||
.BR local.arch ,
|
||||
.BR local.os ,
|
||||
.BR local.hostname ,
|
||||
.BR local.user ,
|
||||
.BR local.distro ,
|
||||
and
|
||||
.BR local.user .
|
||||
.BR local.distro-family .
|
||||
|
||||
.SH TEMPLATES
|
||||
|
||||
@ -637,6 +649,9 @@ upon
|
||||
.BR awk ,
|
||||
which is available on most *nix systems. To use this processor,
|
||||
specify the value of "default" or just leave the value off (e.g. "##template").
|
||||
|
||||
.BR NOTE :
|
||||
This template processor performs case-insensitive comparisions in if statements.
|
||||
.TP
|
||||
.B ESH
|
||||
ESH is a template processor written in POSIX compliant shell. It allows
|
||||
@ -652,9 +667,10 @@ To use the j2cli Jinja template processor, specify the value of "j2" or
|
||||
"j2cli".
|
||||
.TP
|
||||
.B envtpl
|
||||
To use the envtpl Jinja template processor, specify the value of "j2" or "envtpl".
|
||||
.LP
|
||||
To use the envtpl Jinja template processor, specify the value of "j2" or
|
||||
"envtpl".
|
||||
|
||||
.LP
|
||||
.BR NOTE :
|
||||
Specifying "j2" as the processor will attempt to use j2cli or envtpl, whichever
|
||||
is available.
|
||||
@ -666,15 +682,15 @@ During processing, the following variables are available in the template:
|
||||
|
||||
Default Jinja or ESH Description
|
||||
------------- ------------- ----------------------------
|
||||
yadm.arch YADM_ARCH uname -m
|
||||
yadm.arch YADM_ARCH uname \-m
|
||||
yadm.class YADM_CLASS Last locally defined class
|
||||
yadm.classes YADM_CLASSES All classes
|
||||
yadm.distro YADM_DISTRO lsb_release -si
|
||||
yadm.distro YADM_DISTRO lsb_release \-si
|
||||
yadm.distro_family YADM_DISTRO_FAMILY ID_LIKE from /etc/os-release
|
||||
yadm.hostname YADM_HOSTNAME uname -n (without domain)
|
||||
yadm.os YADM_OS uname -s
|
||||
yadm.hostname YADM_HOSTNAME uname \-n (without domain)
|
||||
yadm.os YADM_OS uname \-s
|
||||
yadm.source YADM_SOURCE Template filename
|
||||
yadm.user YADM_USER id -u -n
|
||||
yadm.user YADM_USER id \-u \-n
|
||||
env.VAR Environment variable VAR
|
||||
|
||||
.BR NOTE :
|
||||
@ -749,11 +765,11 @@ gpg is used by default, but openssl can be configured with the
|
||||
.I yadm.cipher
|
||||
configuration.
|
||||
|
||||
To use this feature, a list of patterns must be created and saved as
|
||||
To use this feature, a list of patterns (one per line) must be created and
|
||||
saved as
|
||||
.IR $HOME/.config/yadm/encrypt .
|
||||
This list of patterns should be relative to the configured
|
||||
.IR work-tree " (usually
|
||||
.IR $HOME ).
|
||||
.IR work-tree \ (usually\ $HOME ).
|
||||
For example:
|
||||
|
||||
.RS
|
||||
@ -761,20 +777,20 @@ For example:
|
||||
.gnupg/*.gpg
|
||||
.RE
|
||||
|
||||
Standard filename expansions (*, ?, [) are supported.
|
||||
If you have Bash version 4, you may use "**" to match all subdirectories.
|
||||
Other shell expansions like brace and tilde are not supported.
|
||||
Spaces in paths are supported, and should not be quoted.
|
||||
If a directory is specified, its contents will be included, but not recursively.
|
||||
Standard filename expansions (*, ?, [) are supported. Two consecutive asterisks
|
||||
"**" can be used to match all subdirectories. Other shell expansions like
|
||||
brace and tilde are not supported. Spaces in paths are supported, and should
|
||||
not be quoted. If a directory is specified, its contents will be included.
|
||||
Paths beginning with a "!" will be excluded.
|
||||
|
||||
The
|
||||
.B yadm encrypt
|
||||
command will find all files matching the patterns, and prompt for a password. Once a
|
||||
password has confirmed, the matching files will be encrypted and saved as
|
||||
command will find all files matching the patterns, and prompt for a
|
||||
password. Once a password has confirmed, the matching files will be encrypted
|
||||
and saved as
|
||||
.IR $HOME/.local/share/yadm/archive .
|
||||
The "encrypt" and "archive" files should be added to the yadm repository so they are
|
||||
available across multiple systems.
|
||||
The "encrypt" and "archive" files should be added to the yadm repository so
|
||||
they are available across multiple systems.
|
||||
|
||||
To decrypt these files later, or on another system run
|
||||
.B yadm decrypt
|
||||
@ -817,7 +833,6 @@ repository. See the following web sites for more information:
|
||||
- https://github.com/elasticdog/transcrypt
|
||||
|
||||
- https://github.com/AGWA/git-crypt
|
||||
.LP
|
||||
|
||||
.SH PERMISSIONS
|
||||
|
||||
@ -826,7 +841,7 @@ dependent upon the user's umask. Because of this, yadm will automatically
|
||||
update the permissions of some file paths. The "group" and "others" permissions
|
||||
will be removed from the following files:
|
||||
|
||||
.RI - " $HOME/.local/share/yadm/archive
|
||||
.RI -\ $HOME/.local/share/yadm/archive
|
||||
|
||||
- All files matching patterns in
|
||||
.I $HOME/.config/yadm/encrypt
|
||||
@ -898,6 +913,12 @@ Hooks have the following environment variables available to them at runtime:
|
||||
.B YADM_HOOK_COMMAND
|
||||
The command which triggered the hook
|
||||
.TP
|
||||
.B YADM_HOOK_DATA
|
||||
The path to the yadm data directory
|
||||
.TP
|
||||
.B YADM_HOOK_DIR
|
||||
The path to the yadm directory
|
||||
.TP
|
||||
.B YADM_HOOK_EXIT
|
||||
The exit status of the yadm command
|
||||
.TP
|
||||
@ -976,7 +997,7 @@ to the Git index and create a new commit
|
||||
.B yadm remote add origin <url>
|
||||
Add a remote origin to an existing repository
|
||||
.TP
|
||||
.B yadm push -u origin master
|
||||
.B yadm push \-u origin master
|
||||
Initial push of master to origin
|
||||
.TP
|
||||
.B echo ".ssh/*.key" >> $HOME/.config/yadm/encrypt
|
||||
@ -991,10 +1012,12 @@ Report issues or create pull requests at GitHub:
|
||||
|
||||
https://github.com/yadm-dev/yadm/issues
|
||||
|
||||
.SH AUTHOR
|
||||
.SH AUTHORS
|
||||
|
||||
Tim Byrne <sultan@locehilios.com>
|
||||
|
||||
Erik Flodin <erik@flodin.me>
|
||||
|
||||
.SH SEE ALSO
|
||||
|
||||
.BR git (1),
|
||||
|
171
yadm.md
171
yadm.md
@ -28,11 +28,11 @@
|
||||
|
||||
yadm perms
|
||||
|
||||
yadm enter [ command ]
|
||||
yadm enter [command]
|
||||
|
||||
yadm git-crypt [ options ]
|
||||
yadm git-crypt [options]
|
||||
|
||||
yadm transcrypt [ options ]
|
||||
yadm transcrypt [options]
|
||||
|
||||
yadm upgrade [-f]
|
||||
|
||||
@ -95,26 +95,26 @@
|
||||
|
||||
decrypt
|
||||
Decrypt all files stored in $HOME/.local/share/yadm/archive.
|
||||
Files decrypted will be relative to the configured work-tree
|
||||
(usually $HOME). Using the -l option will list the files stored
|
||||
without extracting them.
|
||||
Files decrypted will be relative to the configured work-
|
||||
tree (usually $HOME). Using the -l option will list the files
|
||||
stored without extracting them.
|
||||
|
||||
encrypt
|
||||
Encrypt all files matching the patterns found in $HOME/.con‐
|
||||
Encrypt all files matching the patterns found in $HOME/.con‐
|
||||
fig/yadm/encrypt. See the ENCRYPTION section for more details.
|
||||
|
||||
enter Run a sub-shell with all Git variables set. Exit the sub-shell
|
||||
the same way you leave your normal shell (usually with the
|
||||
"exit" command). This sub-shell can be used to easily interact
|
||||
with your yadm repository using "git" commands. This could be
|
||||
useful if you are using a tool which uses Git directly, such as
|
||||
enter Run a sub-shell with all Git variables set. Exit the sub-shell
|
||||
the same way you leave your normal shell (usually with the
|
||||
"exit" command). This sub-shell can be used to easily interact
|
||||
with your yadm repository using "git" commands. This could be
|
||||
useful if you are using a tool which uses Git directly, such as
|
||||
tig, vim-fugitive, git-cola, etc.
|
||||
|
||||
Optionally, you can provide a command after "enter", and instead
|
||||
of invoking your shell, that command will be run with all of the
|
||||
Git variables exposed to the command's environment.
|
||||
|
||||
Emacs Tramp and Magit can manage files by using this configura‐
|
||||
Emacs Tramp and Magit can manage files by using this configura‐
|
||||
tion:
|
||||
|
||||
(add-to-list 'tramp-methods
|
||||
@ -128,58 +128,58 @@
|
||||
With this config, use (magit-status "/yadm::").
|
||||
|
||||
git-crypt options
|
||||
If git-crypt is installed, this command allows you to pass op‐
|
||||
tions directly to git-crypt, with the environment configured to
|
||||
If git-crypt is installed, this command allows you to pass op‐
|
||||
tions directly to git-crypt, with the environment configured to
|
||||
use the yadm repository.
|
||||
|
||||
git-crypt enables transparent encryption and decryption of files
|
||||
in a git repository. You can read https://github.com/AGWA/git-
|
||||
in a git repository. You can read https://github.com/AGWA/git-
|
||||
crypt for details.
|
||||
|
||||
gitconfig
|
||||
Pass options to the git config command. Since yadm already uses
|
||||
the config command to manage its own configurations, this com‐
|
||||
Pass options to the git config command. Since yadm already uses
|
||||
the config command to manage its own configurations, this com‐
|
||||
mand is provided as a way to change configurations of the repos‐
|
||||
itory managed by yadm. One useful case might be to configure
|
||||
the repository so untracked files are shown in status commands.
|
||||
itory managed by yadm. One useful case might be to configure
|
||||
the repository so untracked files are shown in status commands.
|
||||
yadm initially configures its repository so that untracked files
|
||||
are not shown. If you wish use the default Git behavior (to
|
||||
show untracked files and directories), you can remove this con‐
|
||||
are not shown. If you wish use the default Git behavior (to
|
||||
show untracked files and directories), you can remove this con‐
|
||||
figuration.
|
||||
|
||||
yadm gitconfig --unset status.showUntrackedFiles
|
||||
|
||||
help Print a summary of yadm commands.
|
||||
|
||||
init Initialize a new, empty repository for tracking dotfiles. The
|
||||
repository is stored in $HOME/.local/share/yadm/repo.git. By
|
||||
default, $HOME will be used as the work-tree, but this can be
|
||||
overridden with the -w option. yadm can be forced to overwrite
|
||||
init Initialize a new, empty repository for tracking dotfiles. The
|
||||
repository is stored in $HOME/.local/share/yadm/repo.git. By
|
||||
default, $HOME will be used as the work-tree, but this can be
|
||||
overridden with the -w option. yadm can be forced to overwrite
|
||||
an existing repository by providing the -f option.
|
||||
|
||||
list Print a list of files managed by yadm. The -a option will cause
|
||||
all managed files to be listed. Otherwise, the list will only
|
||||
all managed files to be listed. Otherwise, the list will only
|
||||
include files from the current directory or below.
|
||||
|
||||
introspect category
|
||||
Report internal yadm data. Supported categories are commands,
|
||||
Report internal yadm data. Supported categories are commands,
|
||||
configs, repo, and switches. The purpose of introspection is to
|
||||
support command line completion.
|
||||
|
||||
perms Update permissions as described in the PERMISSIONS section. It
|
||||
is usually unnecessary to run this command, as yadm automati‐
|
||||
cally processes permissions by default. This automatic behavior
|
||||
can be disabled by setting the configuration yadm.auto-perms to
|
||||
perms Update permissions as described in the PERMISSIONS section. It
|
||||
is usually unnecessary to run this command, as yadm automati‐
|
||||
cally processes permissions by default. This automatic behavior
|
||||
can be disabled by setting the configuration yadm.auto-perms to
|
||||
"false".
|
||||
|
||||
transcrypt options
|
||||
If transcrypt is installed, this command allows you to pass op‐
|
||||
If transcrypt is installed, this command allows you to pass op‐
|
||||
tions directly to transcrypt, with the environment configured to
|
||||
use the yadm repository.
|
||||
|
||||
transcrypt enables transparent encryption and decryption of
|
||||
files in a git repository. You can read
|
||||
https://github.com/elasticdog/transcrypt for details.
|
||||
transcrypt enables transparent encryption and decryption of
|
||||
files in a git repository. You can read https://github.com/elas‐
|
||||
ticdog/transcrypt for details.
|
||||
|
||||
upgrade
|
||||
Version 3 of yadm uses a different directory for storing data.
|
||||
@ -223,7 +223,7 @@
|
||||
The following is the full list of universal options. Each option
|
||||
should be followed by a path.
|
||||
|
||||
-Y,--yadm-dir
|
||||
-Y, --yadm-dir
|
||||
Override the yadm directory. yadm stores its configurations
|
||||
relative to this directory.
|
||||
|
||||
@ -329,8 +329,9 @@
|
||||
Disable the permission changes to $HOME/.ssh/*. This feature is
|
||||
enabled by default.
|
||||
|
||||
The following five "local" configurations are not stored in the
|
||||
$HOME/.config/yadm/config, they are stored in the local repository.
|
||||
|
||||
The following "local" configurations are not stored in the $HOME/.con‐
|
||||
fig/yadm/config, they are stored in the local repository.
|
||||
|
||||
|
||||
local.class
|
||||
@ -354,6 +355,14 @@
|
||||
local.user
|
||||
Override the user for the purpose of symlinking alternate files.
|
||||
|
||||
local.distro
|
||||
Override the distro for the purpose of symlinking alternate
|
||||
files.
|
||||
|
||||
local.distro-family
|
||||
Override the distro family for the purpose of symlinking alter‐
|
||||
nate files.
|
||||
|
||||
|
||||
## ALTERNATES
|
||||
When managing a set of files across different systems, it can be useful
|
||||
@ -377,6 +386,8 @@
|
||||
|
||||
<attribute>[.<value>]
|
||||
|
||||
NOTE: Value is compared case-insensitive.
|
||||
|
||||
These are the supported attributes, in the order of the weighted prece‐
|
||||
dence:
|
||||
|
||||
@ -406,13 +417,14 @@
|
||||
|
||||
distro_family, f
|
||||
Valid if the value matches the distro family. Distro family is
|
||||
calculated by inspecting the ID_LIKE line from /etc/os-release.
|
||||
calculated by inspecting the ID_LIKE line from /etc/os-release
|
||||
(or ID if no ID_LIKE line is found).
|
||||
|
||||
os, o Valid if the value matches the OS. OS is calculated by running
|
||||
os, o Valid if the value matches the OS. OS is calculated by running
|
||||
uname -s.
|
||||
|
||||
arch, a
|
||||
Valid if the value matches the architecture. Architecture is
|
||||
Valid if the value matches the architecture. Architecture is
|
||||
calculated by running uname -m.
|
||||
|
||||
default
|
||||
@ -421,30 +433,31 @@
|
||||
extension, e
|
||||
A special "condition" that doesn't affect the selection process.
|
||||
Its purpose is instead to allow the alternate file to end with a
|
||||
certain extension to e.g. make editors highlight the content
|
||||
certain extension to e.g. make editors highlight the content
|
||||
properly.
|
||||
|
||||
NOTE: The OS for "Windows Subsystem for Linux" is reported as "WSL",
|
||||
|
||||
NOTE: The OS for "Windows Subsystem for Linux" is reported as "WSL",
|
||||
even though uname identifies as "Linux".
|
||||
|
||||
You may use any number of conditions, in any order. An alternate will
|
||||
only be used if ALL conditions are valid. For all files managed by
|
||||
yadm's repository or listed in $HOME/.config/yadm/encrypt, if they
|
||||
match this naming convention, symbolic links will be created for the
|
||||
You may use any number of conditions, in any order. An alternate will
|
||||
only be used if ALL conditions are valid. For all files managed by
|
||||
yadm's repository or listed in $HOME/.config/yadm/encrypt, if they
|
||||
match this naming convention, symbolic links will be created for the
|
||||
most appropriate version.
|
||||
|
||||
The "most appropriate" version is determined by calculating a score for
|
||||
each version of a file. A template is always scored higher than any
|
||||
symlink condition. The number of conditions is the next largest factor
|
||||
each version of a file. A template is always scored higher than any
|
||||
symlink condition. The number of conditions is the next largest factor
|
||||
in scoring. Files with more conditions will always be favored. Any in‐
|
||||
valid 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
|
||||
$HOME/.config/yadm/alt directory. The generated symlink or processed
|
||||
$HOME/.config/yadm/alt directory. The generated symlink or processed
|
||||
template will be created using the same relative path.
|
||||
|
||||
Alternate linking may best be demonstrated by example. Assume the fol‐
|
||||
Alternate linking may best be demonstrated by example. Assume the fol‐
|
||||
lowing files are managed by yadm's repository:
|
||||
|
||||
- $HOME/path/example.txt##default
|
||||
@ -467,7 +480,7 @@
|
||||
|
||||
$HOME/path/example.txt -> $HOME/path/example.txt##os.Darwin
|
||||
|
||||
Since the hostname doesn't match any of the managed files, the more
|
||||
Since the hostname doesn't match any of the managed files, the more
|
||||
generic version is chosen.
|
||||
|
||||
If running on a Linux server named "host4", the link will be:
|
||||
@ -482,27 +495,28 @@
|
||||
|
||||
$HOME/path/example.txt -> $HOME/path/example.txt##class.Work
|
||||
|
||||
If no "##default" version exists and no files have valid conditions,
|
||||
If no "##default" version exists and no files have valid conditions,
|
||||
then no link will be created.
|
||||
|
||||
Links are also created for directories named this way, as long as they
|
||||
have at least one yadm managed file within them (at the top level).
|
||||
Links are also created for directories named this way, as long as they
|
||||
have at least one yadm managed file within them.
|
||||
|
||||
yadm will automatically create these links by default. This can be dis‐
|
||||
abled using the yadm.auto-alt configuration. Even if disabled, links
|
||||
abled using the yadm.auto-alt configuration. Even if disabled, links
|
||||
can be manually created by running yadm alt.
|
||||
|
||||
Class is a special value which is stored locally on each host (inside
|
||||
the local repository). To use alternate symlinks using class, you must
|
||||
set the value of class using the configuration local.class. This is
|
||||
Class is a special value which is stored locally on each host (inside
|
||||
the local repository). To use alternate symlinks using class, you must
|
||||
set the value of class using the configuration local.class. This is
|
||||
set like any other yadm configuration with the yadm config command. The
|
||||
following sets the class to be "Work".
|
||||
|
||||
yadm config local.class Work
|
||||
|
||||
Similarly, the values of architecture, os, hostname, and user can be
|
||||
manually overridden using the configuration options local.arch, lo‐
|
||||
cal.os, local.hostname, and local.user.
|
||||
Similarly, the values of architecture, os, hostname, user, distro, and
|
||||
distro_family can be manually overridden using the configuration op‐
|
||||
tions local.arch, local.os, local.hostname, local.user, local.distro,
|
||||
and local.distro-family.
|
||||
|
||||
|
||||
## TEMPLATES
|
||||
@ -519,6 +533,9 @@
|
||||
on most *nix systems. To use this processor, specify the value
|
||||
of "default" or just leave the value off (e.g. "##template").
|
||||
|
||||
NOTE: This template processor performs case-insensitive compari‐
|
||||
sions in if statements.
|
||||
|
||||
ESH ESH is a template processor written in POSIX compliant shell. It
|
||||
allows executing shell commands within templates. This can be
|
||||
used to reference your own configurations within templates, for
|
||||
@ -534,6 +551,7 @@
|
||||
envtpl To use the envtpl Jinja template processor, specify the value of
|
||||
"j2" or "envtpl".
|
||||
|
||||
|
||||
NOTE: Specifying "j2" as the processor will attempt to use j2cli or en‐
|
||||
vtpl, whichever is available.
|
||||
|
||||
@ -614,19 +632,19 @@
|
||||
are supported. gpg is used by default, but openssl can be configured
|
||||
with the yadm.cipher configuration.
|
||||
|
||||
To use this feature, a list of patterns must be created and saved as
|
||||
$HOME/.config/yadm/encrypt. This list of patterns should be relative
|
||||
to the configured work-tree (usually $HOME). For example:
|
||||
To use this feature, a list of patterns (one per line) must be created
|
||||
and saved as $HOME/.config/yadm/encrypt. This list of patterns should
|
||||
be relative to the configured work-tree (usually $HOME). For example:
|
||||
|
||||
.ssh/*.key
|
||||
.gnupg/*.gpg
|
||||
|
||||
Standard filename expansions (*, ?, [) are supported. If you have Bash
|
||||
version 4, you may use "**" to match all subdirectories. Other shell
|
||||
Standard filename expansions (*, ?, [) are supported. Two consecutive
|
||||
asterisks "**" can be used to match all subdirectories. Other shell
|
||||
expansions like brace and tilde are not supported. Spaces in paths are
|
||||
supported, and should not be quoted. If a directory is specified, its
|
||||
contents will be included, but not recursively. Paths beginning with a
|
||||
"!" will be excluded.
|
||||
supported, and should not be quoted. If a directory is specified, its
|
||||
contents will be included. Paths beginning with a "!" will be ex‐
|
||||
cluded.
|
||||
|
||||
The yadm encrypt command will find all files matching the patterns, and
|
||||
prompt for a password. Once a password has confirmed, the matching
|
||||
@ -661,6 +679,7 @@
|
||||
|
||||
- https://github.com/AGWA/git-crypt
|
||||
|
||||
|
||||
## PERMISSIONS
|
||||
When files are checked out of a Git repository, their initial permis‐
|
||||
sions are dependent upon the user's umask. Because of this, yadm will
|
||||
@ -714,6 +733,12 @@
|
||||
YADM_HOOK_COMMAND
|
||||
The command which triggered the hook
|
||||
|
||||
YADM_HOOK_DATA
|
||||
The path to the yadm data directory
|
||||
|
||||
YADM_HOOK_DIR
|
||||
The path to the yadm directory
|
||||
|
||||
YADM_HOOK_EXIT
|
||||
The exit status of the yadm command
|
||||
|
||||
@ -799,9 +824,11 @@
|
||||
https://github.com/yadm-dev/yadm/issues
|
||||
|
||||
|
||||
## AUTHOR
|
||||
## AUTHORS
|
||||
Tim Byrne <sultan@locehilios.com>
|
||||
|
||||
Erik Flodin <erik@flodin.me>
|
||||
|
||||
|
||||
## SEE ALSO
|
||||
git(1), gpg(1) openssl(1) transcrypt(1) git-crypt(1)
|
||||
|
Loading…
Reference in New Issue
Block a user