Compare commits
113 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0a5e7aa353 | ||
![]() |
040dd461bd | ||
![]() |
e4bb8a79a4 | ||
![]() |
2d4dcd05ef | ||
![]() |
5981f6329e | ||
![]() |
0f8538d3e3 | ||
![]() |
67c684473d | ||
![]() |
8f390cf085 | ||
![]() |
36fda72bec | ||
![]() |
a89e5cee89 | ||
![]() |
075cd1b06b | ||
![]() |
19b7a30668 | ||
![]() |
6304553ab3 | ||
![]() |
24ee841372 | ||
![]() |
87ff97bbd6 | ||
![]() |
f163130609 | ||
![]() |
d49005ce6c | ||
![]() |
2cc64a2fa0 | ||
![]() |
2989734359 | ||
![]() |
7573e18a89 | ||
![]() |
beb83077d8 | ||
![]() |
6a49e849c8 | ||
![]() |
222182b296 | ||
![]() |
0d67c44343 | ||
![]() |
a9e7e7679b | ||
![]() |
abf6ea4b61 | ||
![]() |
f59d903769 | ||
![]() |
bd0039a650 | ||
![]() |
82c0b6d02e | ||
![]() |
a4adadcc8c | ||
![]() |
287249df91 | ||
![]() |
c5e4e4eda2 | ||
![]() |
46f72c2768 | ||
![]() |
ebb6715aad | ||
![]() |
39d0c791ce | ||
![]() |
3445763731 | ||
![]() |
718e99c826 | ||
![]() |
50bf8716cd | ||
![]() |
82bfd5e773 | ||
![]() |
b7c5294bd9 | ||
![]() |
0b75e71237 | ||
![]() |
487f030405 | ||
![]() |
b0e0856658 | ||
![]() |
027c7359ac | ||
![]() |
3d3432516f | ||
![]() |
5ae553b078 | ||
![]() |
32bc9abb0c | ||
![]() |
8186705059 | ||
![]() |
f28d4bc1c6 | ||
![]() |
f11974140e | ||
![]() |
71cb08a5f3 | ||
![]() |
1aa9839096 | ||
![]() |
a9fc8b1374 | ||
![]() |
0ae8931e01 | ||
![]() |
2379d63068 | ||
![]() |
42c74efbac | ||
![]() |
2f00dabcdb | ||
![]() |
4caf5f681e | ||
![]() |
4843e1fa14 | ||
![]() |
bacc948bba | ||
![]() |
31e2ce56bc | ||
![]() |
3d82aff3e8 | ||
![]() |
0cac436219 | ||
![]() |
ec307ce4f8 | ||
![]() |
de34cd2e8c | ||
![]() |
a37eabba98 | ||
![]() |
85e05d311a | ||
![]() |
0ecb9c4f2f | ||
![]() |
344b740d9b | ||
![]() |
9aaefa60fe | ||
![]() |
6c57bdd8fb | ||
![]() |
ed4a60257d | ||
![]() |
9beed3307f | ||
![]() |
f8abcd756b | ||
![]() |
db78669479 | ||
![]() |
1544413c91 | ||
![]() |
73af421667 | ||
![]() |
5adb486727 | ||
![]() |
c144d9f3bb | ||
![]() |
a4d39c7504 | ||
![]() |
034045f58c | ||
![]() |
1998a8ed50 | ||
![]() |
6cdbc92c64 | ||
![]() |
216aed2f87 | ||
![]() |
39773765ab | ||
![]() |
8cc30193f3 | ||
![]() |
814e5f8ab3 | ||
![]() |
d11e094f7b | ||
![]() |
e6d7e6f174 | ||
![]() |
999692fe5e | ||
![]() |
1461b1ac33 | ||
![]() |
a8e5b20021 | ||
![]() |
8ece22ab2a | ||
![]() |
79f7aae073 | ||
![]() |
88ee3f09fb | ||
![]() |
7b1bfac12b | ||
![]() |
c4327d0099 | ||
![]() |
31071d9ac9 | ||
![]() |
e6cfd39bbc | ||
![]() |
9c999c7998 | ||
![]() |
96471a6d68 | ||
![]() |
990b4ce119 | ||
![]() |
84136a8633 | ||
![]() |
0c788ae020 | ||
![]() |
fecbb315df | ||
![]() |
e2ed647c2a | ||
![]() |
accec694f5 | ||
![]() |
fb56513d17 | ||
![]() |
f45e66d4da | ||
![]() |
a321c88c7c | ||
![]() |
a5b1067e02 | ||
![]() |
b32025bcc3 | ||
![]() |
77d2da4e9b |
34 changed files with 1203 additions and 729 deletions
5
.github/CONTRIBUTING.md
vendored
5
.github/CONTRIBUTING.md
vendored
|
@ -212,6 +212,11 @@ these principles when making changes.
|
|||
```text
|
||||
$ make test
|
||||
```
|
||||
If you don't use `docker` but an OCI engine akin to `podman`, you can set it through the `OCI` switch for every target
|
||||
|
||||
```text
|
||||
$ make test OCI=podman
|
||||
```
|
||||
|
||||
5. Create a feature branch, based off the `develop` branch.
|
||||
|
||||
|
|
25
.github/workflows/stale.yml
vendored
Normal file
25
.github/workflows/stale.yml
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
name: Close Stale Issues
|
||||
on: # yamllint disable-line rule:truthy
|
||||
schedule:
|
||||
- cron: "30 1 * * *" # Daily
|
||||
jobs:
|
||||
Stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
with:
|
||||
close-issue-message: >-
|
||||
This issue was closed because it has been labeled as stale for 7
|
||||
days with no activity.
|
||||
days-before-close: 7
|
||||
days-before-stale: 60
|
||||
exempt-all-assignees: true
|
||||
exempt-issue-labels: in develop, 1, 2, 3
|
||||
exempt-pr-labels: in develop, 1, 2, 3
|
||||
stale-issue-label: stale
|
||||
stale-issue-message: >-
|
||||
This issue has been labeled as stale because it has been open 60
|
||||
days with no activity. Remove stale label or comment or this will
|
||||
be closed in 7 days.
|
||||
stale-pr-label: stale
|
34
CHANGES
34
CHANGES
|
@ -1,3 +1,37 @@
|
|||
3.2.2
|
||||
* Support spaces in distro/distro-family (#432)
|
||||
* Fix zsh hanging when tab completing add/checkout (#417)
|
||||
* Add yadm-untracked script to contributed files (#418)
|
||||
* Fix documentation typos (#425)
|
||||
* Support docker-like OCI engines for dev testing (#431)
|
||||
|
||||
3.2.1
|
||||
* Fix Bash 3 bad array subscript bug (#411)
|
||||
|
||||
3.2.0
|
||||
* Support architecture for alternates/templates (#202, #203, #393)
|
||||
* Support distro_family for alternates/templates (#213)
|
||||
* Support setting multiple classes (#185, #304)
|
||||
* Support environment variables in default template processor (#347)
|
||||
* Update version command to include Bash & Git versions (#377)
|
||||
|
||||
3.1.1
|
||||
* Fix clone support for older versions of Git (#348)
|
||||
* Fix support for multiple GPG recipients (#342)
|
||||
* Find symlinks in bootstrap-in-dir (#340)
|
||||
|
||||
3.1.0
|
||||
* Use `git clone` directly during clone (#289, #323)
|
||||
* Fix compatibility bug with Git completions (#318, #321)
|
||||
* Support relative paths for --yadm-* and -w (#301)
|
||||
* Improve parsing of if-statement in default template (#303)
|
||||
* Read files without running cat in subshells (#317)
|
||||
* Improve portability of updating read-only files (#320)
|
||||
* Various code improvements (#306, #307, #311)
|
||||
|
||||
3.0.2
|
||||
* Fix parsing by sh (#299)
|
||||
|
||||
3.0.1
|
||||
* Improve handling of submodules at upgrade (#284, #285, #293)
|
||||
* Improve Zsh completions (#292, #298)
|
||||
|
|
15
CONTRIBUTORS
15
CONTRIBUTORS
|
@ -4,18 +4,25 @@ Tim Byrne
|
|||
Erik Flodin
|
||||
Martin Zuther
|
||||
Jan Schulz
|
||||
Ross Smith II
|
||||
Jonathan Daigle
|
||||
Luis López
|
||||
Tin Lai
|
||||
Espen Henriksen
|
||||
Cameron Eagans
|
||||
Klas Mellbourn
|
||||
Ross Smith II
|
||||
James Clark
|
||||
Glenn Waters
|
||||
Nicolas signed-log FORMICHELLA
|
||||
Tomas Cernaj
|
||||
Joshua Cold
|
||||
jonasc
|
||||
Nicolas stig124 FORMICHELLA
|
||||
Chad Wade Day, Jr
|
||||
Sébastien Gross
|
||||
David Mandelberg
|
||||
Paulo Köch
|
||||
Oren Zipori
|
||||
Daniel Gray
|
||||
Paraplegic Racehorse
|
||||
japm48
|
||||
|
@ -23,17 +30,23 @@ Siôn Le Roux
|
|||
Mateusz Piotrowski
|
||||
Uroš Golja
|
||||
Satoshi Ohki
|
||||
Jonas
|
||||
Franciszek Madej
|
||||
Daniel Wagenknecht
|
||||
Stig Palmquist
|
||||
Patrick Hof
|
||||
con-f-use
|
||||
Samisafool
|
||||
Bram Ceulemans
|
||||
Travis A. Everett
|
||||
Sheng Yang
|
||||
Jared Smartt
|
||||
Adam Jimerson
|
||||
dessert1
|
||||
addshore
|
||||
Tim Condit
|
||||
Thomas Luzat
|
||||
Russ Allbery
|
||||
Brayden Banks
|
||||
Alexandre GV
|
||||
Felipe S. S. Schneider
|
||||
|
|
25
Makefile
25
Makefile
|
@ -1,5 +1,6 @@
|
|||
PYTESTS = $(wildcard test/test_*.py)
|
||||
IMAGE = yadm/testbed:2020-12-29
|
||||
IMAGE = docker.io/yadm/testbed:2022-01-07
|
||||
OCI = docker
|
||||
|
||||
.PHONY: all
|
||||
all:
|
||||
|
@ -94,7 +95,7 @@ test:
|
|||
py.test -v $(testargs); \
|
||||
else \
|
||||
$(MAKE) -s require-docker && \
|
||||
docker run \
|
||||
$(OCI) run \
|
||||
--rm -t$(shell test -t 0 && echo i) \
|
||||
-v "$(CURDIR):/yadm:ro" \
|
||||
$(IMAGE) \
|
||||
|
@ -117,7 +118,7 @@ test:
|
|||
.PHONY: testhost
|
||||
testhost: require-docker .testyadm
|
||||
@echo "Starting testhost"
|
||||
@docker run \
|
||||
@$(OCI) run \
|
||||
-w /root \
|
||||
--hostname testhost \
|
||||
--rm -it \
|
||||
|
@ -129,7 +130,7 @@ testhost: require-docker .testyadm
|
|||
scripthost: require-docker .testyadm
|
||||
@echo "Starting scripthost \(recording script\)"
|
||||
@printf '' > script.gz
|
||||
@docker run \
|
||||
@$(OCI) run \
|
||||
-w /root \
|
||||
--hostname scripthost \
|
||||
--rm -it \
|
||||
|
@ -159,7 +160,7 @@ testenv:
|
|||
|
||||
.PHONY: image
|
||||
image:
|
||||
@docker build -f test/Dockerfile . -t "$(IMAGE)"
|
||||
@$(OCI) build -f test/Dockerfile . -t "$(IMAGE)"
|
||||
|
||||
|
||||
.PHONY: man
|
||||
|
@ -175,7 +176,7 @@ man-ps:
|
|||
@groff -man -Tps ./yadm.1 > yadm.ps
|
||||
|
||||
yadm.md: yadm.1
|
||||
@groff -man -Tascii ./yadm.1 | 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
|
||||
|
@ -192,9 +193,9 @@ install:
|
|||
@[ -n "$(PREFIX)" ] || { echo "PREFIX is not set"; exit 1; }
|
||||
@{\
|
||||
set -e ;\
|
||||
bin="$(PREFIX)/bin" ;\
|
||||
doc="$(PREFIX)/share/doc/yadm" ;\
|
||||
man="$(PREFIX)/share/man/man1" ;\
|
||||
bin="$(DESTDIR)$(PREFIX)/bin" ;\
|
||||
doc="$(DESTDIR)$(PREFIX)/share/doc/yadm" ;\
|
||||
man="$(DESTDIR)$(PREFIX)/share/man/man1" ;\
|
||||
install -d "$$bin" "$$doc" "$$man" ;\
|
||||
install -m 0755 yadm "$$bin" ;\
|
||||
install -m 0644 yadm.1 "$$man" ;\
|
||||
|
@ -204,11 +205,11 @@ install:
|
|||
|
||||
.PHONY: sync-clock
|
||||
sync-clock:
|
||||
docker run --rm --privileged alpine hwclock -s
|
||||
$(OCI) run --rm --privileged alpine hwclock -s
|
||||
|
||||
.PHONY: require-docker
|
||||
require-docker:
|
||||
@if ! command -v "docker" > /dev/null 2>&1; then \
|
||||
echo "Sorry, this make target requires docker to be installed."; \
|
||||
@if ! command -v $(OCI) > /dev/null 2>&1; then \
|
||||
echo "Sorry, this make target requires docker to be installed, to use another docker-compatible engine, like podman, re-run the make command adding OCI=podman"; \
|
||||
false; \
|
||||
fi
|
||||
|
|
16
README.md
16
README.md
|
@ -3,7 +3,7 @@
|
|||
[![Latest Version][releases-badge]][releases-link]
|
||||
[![Homebrew Version][homebrew-badge]][homebrew-link]
|
||||
[![OBS Version][obs-badge]][obs-link]
|
||||
[![Arch Version][aur-badge]][aur-link]
|
||||
[![Arch Version][arch-badge]][arch-link]
|
||||
[![License][license-badge]][license-link]<br />
|
||||
[![Master Update][master-date]][master-commits]
|
||||
[![Develop Update][develop-date]][develop-commits]
|
||||
|
@ -56,23 +56,23 @@ The star count helps others discover yadm.
|
|||
[Git]: https://git-scm.com/
|
||||
[GnuPG]: https://gnupg.org/
|
||||
[OpenSSL]: https://www.openssl.org/
|
||||
[aur-badge]: https://img.shields.io/aur/version/yadm.svg
|
||||
[aur-link]: https://aur.archlinux.org/packages/yadm
|
||||
[dev-pages-badge]: https://img.shields.io/github/workflow/status/TheLocehiliosan/yadm/Test%20Site/dev-pages?label=dev-pages
|
||||
[develop-badge]: https://img.shields.io/github/workflow/status/TheLocehiliosan/yadm/Tests/develop?label=develop
|
||||
[arch-badge]: https://img.shields.io/archlinux/v/extra/any/yadm
|
||||
[arch-link]: https://archlinux.org/packages/extra/any/yadm/
|
||||
[dev-pages-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=dev-pages
|
||||
[develop-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=develop
|
||||
[develop-commits]: https://github.com/TheLocehiliosan/yadm/commits/develop
|
||||
[develop-date]: https://img.shields.io/github/last-commit/TheLocehiliosan/yadm/develop.svg?label=develop
|
||||
[dotfiles]: https://en.wikipedia.org/wiki/Hidden_file_and_hidden_directory
|
||||
[gh-pages-badge]: https://img.shields.io/github/workflow/status/TheLocehiliosan/yadm/Test%20Site/gh-pages?label=gh-pages
|
||||
[gh-pages-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=gh-pages
|
||||
[git-crypt]: https://github.com/AGWA/git-crypt
|
||||
[homebrew-badge]: https://img.shields.io/homebrew/v/yadm.svg
|
||||
[homebrew-link]: https://formulae.brew.sh/formula/yadm
|
||||
[license-badge]: https://img.shields.io/github/license/TheLocehiliosan/yadm.svg
|
||||
[license-link]: https://github.com/TheLocehiliosan/yadm/blob/master/LICENSE
|
||||
[master-badge]: https://img.shields.io/github/workflow/status/TheLocehiliosan/yadm/Tests/master?label=master
|
||||
[master-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=master
|
||||
[master-commits]: https://github.com/TheLocehiliosan/yadm/commits/master
|
||||
[master-date]: https://img.shields.io/github/last-commit/TheLocehiliosan/yadm/master.svg?label=master
|
||||
[obs-badge]: https://img.shields.io/badge/OBS-v3.0.1-blue
|
||||
[obs-badge]: https://img.shields.io/badge/OBS-v3.2.2-blue
|
||||
[obs-link]: https://software.opensuse.org//download.html?project=home%3ATheLocehiliosan%3Ayadm&package=yadm
|
||||
[releases-badge]: https://img.shields.io/github/tag/TheLocehiliosan/yadm.svg?label=latest+release
|
||||
[releases-link]: https://github.com/TheLocehiliosan/yadm/releases
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
# test if git completion is missing, but loader exists, attempt to load
|
||||
if ! declare -F _git > /dev/null && declare -F _completion_loader > /dev/null; then
|
||||
_completion_loader git
|
||||
if ! declare -F _git > /dev/null && ! declare -F __git_wrap__git_main > /dev/null; then
|
||||
if declare -F _completion_loader > /dev/null; then
|
||||
_completion_loader git
|
||||
fi
|
||||
fi
|
||||
|
||||
# only operate if git completion is present
|
||||
if declare -F _git > /dev/null; then
|
||||
if declare -F _git > /dev/null || declare -F __git_wrap__git_main > /dev/null; then
|
||||
|
||||
_yadm() {
|
||||
|
||||
|
@ -66,7 +68,11 @@ if declare -F _git > /dev/null; then
|
|||
if [[ " ${yadm_switches[*]} " != *" $penultimate "* ]]; then
|
||||
# TODO: somehow solve the problem with [--yadm-xxx option] being
|
||||
# incompatible with what git expects, namely [--arg=option]
|
||||
_git
|
||||
if declare -F _git > /dev/null; then
|
||||
_git
|
||||
else
|
||||
__git_wrap__git_main
|
||||
fi
|
||||
fi
|
||||
if [[ "$current" =~ ^- ]]; then
|
||||
local matching
|
||||
|
|
|
@ -7,6 +7,20 @@ zstyle -T ':completion:*:yadm:argument-1:descriptions:' format && \
|
|||
zstyle -T ':completion:*:yadm:*:yadm' group-name && \
|
||||
zstyle ':completion:*:yadm:*:yadm' group-name ''
|
||||
|
||||
function _yadm-add(){
|
||||
local -a yadm_options yadm_path
|
||||
yadm_path="$(yadm rev-parse --show-toplevel)"
|
||||
yadm_options=($(yadm status --porcelain=v1 |
|
||||
awk -v yadm_path=${yadm_path} '{printf "%s/%s:%s\n", yadm_path, $2, $1}' ))
|
||||
|
||||
_describe 'command' yadm_options
|
||||
_files
|
||||
}
|
||||
|
||||
function _yadm-checkout(){
|
||||
_yadm-add
|
||||
}
|
||||
|
||||
_yadm-alt() {
|
||||
return 0
|
||||
}
|
||||
|
@ -19,10 +33,12 @@ _yadm-clone() {
|
|||
_arguments \
|
||||
'(--bootstrap --no-bootstrap)--bootstrap[force bootstrap, without prompt]' \
|
||||
'(--bootstrap --no-bootstrap)--no-bootstrap[prevent bootstrap, without prompt]' \
|
||||
'-b[branch name]:' \
|
||||
'-f[force overwrite of existing repository]' \
|
||||
'-w[work tree path]: :_files -/' \
|
||||
'*:'
|
||||
'-w[yadm work tree path]: :_files -/'
|
||||
|
||||
local curcontext="${curcontext%:*:*}:git:"
|
||||
|
||||
words=("git" "${words[@]}") CURRENT=$((CURRENT + 1)) service=git _git
|
||||
}
|
||||
|
||||
_yadm-config() {
|
||||
|
|
|
@ -14,7 +14,7 @@ if [[ ! -d "$BOOTSTRAP_D" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
find "$BOOTSTRAP_D" -type f | sort | while IFS= read -r bootstrap; do
|
||||
find -L "$BOOTSTRAP_D" -type f | sort | while IFS= read -r bootstrap; do
|
||||
if [[ -x "$bootstrap" && ! "$bootstrap" =~ "##" && ! "$bootstrap" =~ "~$" ]]; then
|
||||
if ! "$bootstrap"; then
|
||||
echo "Error: bootstrap '$bootstrap' failed" >&2
|
||||
|
|
9
contrib/commands/README.md
Normal file
9
contrib/commands/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
## Contributed Commands
|
||||
|
||||
Although these commands are available as part of the official
|
||||
**yadm** source tree, they have a somewhat different status. The intention is to
|
||||
keep interesting and potentially useful commands here, building a library of
|
||||
examples that might help others.
|
||||
|
||||
I recommend *careful review* of any code from here before using it. No
|
||||
guarantees of code quality is assumed.
|
71
contrib/commands/yadm-untracked
Executable file
71
contrib/commands/yadm-untracked
Executable file
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# To run: `yadm-untracked <config-file>`
|
||||
#
|
||||
# If you wish to create a YADM alias to run this as, for example `yadm untracked`
|
||||
# then the following command will add the alias:
|
||||
# `yadm gitconfig alias.untracked '!<PATH>/yadm-untracked'`
|
||||
|
||||
# Possible script improvements:
|
||||
# - Reduce the amount of configuration; I have not figured out a way to
|
||||
# get rid of the non-recursive and ignore. The recursive list could be
|
||||
# built from the directories that are present in `yadm list`
|
||||
|
||||
# Configuration... The script looks at the following 3 arrays:
|
||||
#
|
||||
# yadm_tracked_recursively
|
||||
# The directories and files in this list are searched recursively to build
|
||||
# a list of files that you expect are tracked with `yadm`. Items in this
|
||||
# list are relative to the root of your YADM repo (which is $HOME for most).
|
||||
|
||||
# yadm_tracked_nonrecursively
|
||||
# Same as above but don't search recursively
|
||||
#
|
||||
# ignore_files_and_dirs
|
||||
# A list of directories and files that will not be reported as untracked if
|
||||
# found in the above two searches.
|
||||
#
|
||||
# Example configuration file (uncomment it to use):
|
||||
# yadm_tracked_recursively=(
|
||||
# bin .config .vim
|
||||
# )
|
||||
#
|
||||
# yadm_tracked_nonrecursively=(
|
||||
# ~
|
||||
# )
|
||||
#
|
||||
# ignore_files_and_dirs=(
|
||||
# .CFUserTextEncoding .DS_Store .config/gh
|
||||
# .vim/autoload/plug.vim
|
||||
# )
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo 'Usage: yadm-untracked <config-file>'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
yadm_tracked_recursively=()
|
||||
yadm_tracked_nonrecursively=()
|
||||
ignore_files_and_dirs=()
|
||||
|
||||
source $1
|
||||
|
||||
root=`yadm enter echo '$GIT_WORK_TREE'`
|
||||
|
||||
cd $root
|
||||
|
||||
find_list=$(mktemp -t find_list)
|
||||
find ${yadm_tracked_recursively[*]} -type f >$find_list
|
||||
find ${yadm_tracked_nonrecursively[*]} -maxdepth 1 -type f |
|
||||
awk "{sub(\"^\./\", \"\"); sub(\"^$root/\", \"\"); print }" >>$find_list
|
||||
sort -o $find_list $find_list
|
||||
|
||||
yadm_list=$(mktemp -t yadm_list)
|
||||
yadm list >$yadm_list
|
||||
find ${ignore_files_and_dirs[*]} -type f >>$yadm_list
|
||||
sort -o $yadm_list $yadm_list
|
||||
|
||||
# Show the files not in `yadm list`
|
||||
comm -23 $find_list $yadm_list
|
||||
|
||||
rm -f $find_list $yadm_list
|
2
pylintrc
2
pylintrc
|
@ -8,7 +8,7 @@ max-attributes=8
|
|||
max-statements=65
|
||||
|
||||
[SIMILARITIES]
|
||||
min-similarity-lines=6
|
||||
min-similarity-lines=8
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
disable=redefined-outer-name
|
||||
|
|
|
@ -2,8 +2,8 @@ FROM ubuntu:18.04
|
|||
MAINTAINER Tim Byrne <sultan@locehilios.com>
|
||||
|
||||
# Shellcheck and esh versions
|
||||
ARG SC_VER=0.7.1
|
||||
ARG ESH_VER=0.3.0
|
||||
ARG SC_VER=0.8.0
|
||||
ARG ESH_VER=0.3.1
|
||||
|
||||
# Install prerequisites and configure UTF-8 locale
|
||||
RUN \
|
||||
|
|
|
@ -25,7 +25,7 @@ def pytest_addoption(parser):
|
|||
@pytest.fixture(scope='session')
|
||||
def shellcheck_version():
|
||||
"""Version of shellcheck supported"""
|
||||
return '0.7.1'
|
||||
return '0.8.0'
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
|
@ -68,12 +68,29 @@ def tst_distro(runner):
|
|||
return distro
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def tst_distro_family(runner):
|
||||
"""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
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def tst_sys():
|
||||
"""Test session's uname value"""
|
||||
return platform.system()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def tst_arch():
|
||||
"""Test session's uname value"""
|
||||
return platform.machine()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def supported_commands():
|
||||
"""List of supported commands
|
||||
|
@ -109,6 +126,7 @@ def supported_configs():
|
|||
This list should be updated every time yadm learns a new config.
|
||||
"""
|
||||
return [
|
||||
'local.arch',
|
||||
'local.class',
|
||||
'local.hostname',
|
||||
'local.os',
|
||||
|
|
|
@ -82,25 +82,32 @@ def test_relative_link(runner, paths, yadm_alt):
|
|||
@pytest.mark.parametrize('suffix', [
|
||||
'##default',
|
||||
'##default,e.txt', '##default,extension.txt',
|
||||
'##a.$tst_arch', '##arch.$tst_arch',
|
||||
'##o.$tst_sys', '##os.$tst_sys',
|
||||
'##d.$tst_distro', '##distro.$tst_distro',
|
||||
'##f.$tst_distro_family', '##distro_family.$tst_distro_family',
|
||||
'##c.$tst_class', '##class.$tst_class',
|
||||
'##h.$tst_host', '##hostname.$tst_host',
|
||||
'##u.$tst_user', '##user.$tst_user',
|
||||
])
|
||||
def test_alt_conditions(
|
||||
runner, paths,
|
||||
tst_sys, tst_distro, tst_host, tst_user, suffix):
|
||||
tst_arch, tst_sys, tst_distro, tst_distro_family, tst_host, tst_user,
|
||||
suffix):
|
||||
"""Test conditions supported by yadm alt"""
|
||||
yadm_dir, yadm_data = setup_standard_yadm_dir(paths)
|
||||
|
||||
# set the class
|
||||
tst_class = 'testclass'
|
||||
utils.set_local(paths, 'class', tst_class)
|
||||
utils.set_local(paths, 'class', tst_class + ".before")
|
||||
utils.set_local(paths, 'class', tst_class, add=True)
|
||||
utils.set_local(paths, 'class', tst_class + ".after", add=True)
|
||||
|
||||
suffix = string.Template(suffix).substitute(
|
||||
tst_arch=tst_arch,
|
||||
tst_sys=tst_sys,
|
||||
tst_distro=tst_distro,
|
||||
tst_distro_family=tst_distro_family,
|
||||
tst_class=tst_class,
|
||||
tst_host=tst_host,
|
||||
tst_user=tst_user,
|
||||
|
|
|
@ -58,8 +58,8 @@ def test_clone(
|
|||
if not good_remote:
|
||||
# clone should fail
|
||||
assert run.failure
|
||||
assert run.out != ''
|
||||
assert 'Unable to fetch origin' in run.err
|
||||
assert run.out == ''
|
||||
assert 'Unable to clone the repository' in run.err
|
||||
assert not paths.repo.exists()
|
||||
elif repo_exists and not force:
|
||||
# can't overwrite data
|
||||
|
@ -76,8 +76,7 @@ def test_clone(
|
|||
# ensure conflicts are handled properly
|
||||
if conflicts:
|
||||
assert 'NOTE' in run.out
|
||||
assert 'Merging origin/master failed' in run.out
|
||||
assert 'Conflicts preserved' in run.out
|
||||
assert 'Local files with content that differs' in run.out
|
||||
|
||||
# confirm correct Git origin
|
||||
run = runner(
|
||||
|
@ -89,26 +88,19 @@ def test_clone(
|
|||
|
||||
# ensure conflicts are really preserved
|
||||
if conflicts:
|
||||
# test to see if the work tree is actually "clean"
|
||||
# test that the conflicts are preserved in the work tree
|
||||
run = runner(
|
||||
command=yadm_cmd('status', '-uno', '--porcelain'),
|
||||
cwd=paths.work)
|
||||
assert run.success
|
||||
assert run.err == ''
|
||||
assert run.out == '', 'worktree has unexpected changes'
|
||||
assert str(ds1.tracked[0].path) in run.out
|
||||
|
||||
# test to see if the conflicts are stashed
|
||||
run = runner(command=yadm_cmd('stash', 'list'), cwd=paths.work)
|
||||
# verify content of the conflicts
|
||||
run = runner(command=yadm_cmd('diff'), cwd=paths.work)
|
||||
assert run.success
|
||||
assert run.err == ''
|
||||
assert 'Conflicts preserved' in run.out, 'conflicts not stashed'
|
||||
|
||||
# verify content of the stashed conflicts
|
||||
run = runner(
|
||||
command=yadm_cmd('stash', 'show', '-p'), cwd=paths.work)
|
||||
assert run.success
|
||||
assert run.err == ''
|
||||
assert '\n+conflict' in run.out, 'conflicts not stashed'
|
||||
assert '\n+conflict' in run.out, 'conflict overwritten'
|
||||
|
||||
# another force-related assertion
|
||||
if old_repo:
|
||||
|
@ -242,20 +234,20 @@ def test_clone_perms(
|
|||
f'initial private dir perms drwxrwxrwx.+.{private_type}',
|
||||
run.out)
|
||||
assert re.search(
|
||||
f'pre-merge private dir perms drwxrwxrwx.+.{private_type}',
|
||||
f'pre-checkout private dir perms drwxrwxrwx.+.{private_type}',
|
||||
run.out)
|
||||
assert re.search(
|
||||
f'post-merge private dir perms drwxrwxrwx.+.{private_type}',
|
||||
f'post-checkout private dir perms drwxrwxrwx.+.{private_type}',
|
||||
run.out)
|
||||
else:
|
||||
# private directories which are created, should be done prior to
|
||||
# merging, and with secure permissions.
|
||||
# checkout, and with secure permissions.
|
||||
assert 'initial private dir perms' not in run.out
|
||||
assert re.search(
|
||||
f'pre-merge private dir perms drwx------.+.{private_type}',
|
||||
f'pre-checkout private dir perms drwx------.+.{private_type}',
|
||||
run.out)
|
||||
assert re.search(
|
||||
f'post-merge private dir perms drwx------.+.{private_type}',
|
||||
f'post-checkout private dir perms drwx------.+.{private_type}',
|
||||
run.out)
|
||||
|
||||
# standard perms still apply afterwards unless disabled with auto.perms
|
||||
|
@ -297,8 +289,8 @@ def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch):
|
|||
|
||||
if branch == 'invalid':
|
||||
assert run.failure
|
||||
assert 'ERROR: Clone failed' in run.err
|
||||
assert f"'origin/{branch}' does not exist in {remote_url}" in run.err
|
||||
assert 'ERROR: Unable to clone the repository' in run.err
|
||||
assert f"Remote branch {branch} not found in upstream" in run.err
|
||||
else:
|
||||
assert successful_clone(run, paths, repo_config)
|
||||
|
||||
|
@ -321,7 +313,6 @@ def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch):
|
|||
def successful_clone(run, paths, repo_config, expected_code=0):
|
||||
"""Assert clone is successful"""
|
||||
assert run.code == expected_code
|
||||
assert 'Initialized' in run.out
|
||||
assert oct(paths.repo.stat().mode).endswith('00'), 'Repo is not secured'
|
||||
assert repo_config('core.bare') == 'false'
|
||||
assert repo_config('status.showUntrackedFiles') == 'no'
|
||||
|
@ -342,10 +333,11 @@ def remote(paths, ds1_repo_copy):
|
|||
|
||||
def test_no_repo(runner, yadm_cmd, ):
|
||||
"""Test cloning without specifying a repo"""
|
||||
run = runner(command=yadm_cmd('clone'))
|
||||
run = runner(command=yadm_cmd('clone', '-f'))
|
||||
assert run.failure
|
||||
assert run.out == ''
|
||||
assert 'ERROR: No repository provided' in run.err
|
||||
assert 'ERROR: Unable to clone the repository' in run.err
|
||||
assert 'repository \'repo.git\' does not exist' in run.err
|
||||
|
||||
|
||||
def verify_head(paths, branch):
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
"""Unit tests: _default_remote_branch()"""
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize('condition', ['found', 'missing'])
|
||||
def test(runner, paths, condition):
|
||||
"""Test _default_remote_branch()"""
|
||||
test_branch = 'test/branch'
|
||||
output = f'ref: refs/heads/{test_branch}\\tHEAD\\n'
|
||||
if condition == 'missing':
|
||||
output = 'output that is missing ref'
|
||||
script = f"""
|
||||
YADM_TEST=1 source {paths.pgm}
|
||||
function git() {{
|
||||
printf '{output}';
|
||||
printf 'mock stderr\\n' 1>&2
|
||||
}}
|
||||
_default_remote_branch URL
|
||||
"""
|
||||
print(condition)
|
||||
run = runner(command=['bash'], inp=script)
|
||||
assert run.success
|
||||
assert run.err == ''
|
||||
if condition == 'found':
|
||||
assert run.out.strip() == test_branch
|
||||
else:
|
||||
assert run.out.strip() == 'master'
|
|
@ -326,7 +326,7 @@ def test_multi_key(runner, yadm_cmd, gnupg):
|
|||
|
||||
# specify two encryption recipient
|
||||
os.system(' '.join(yadm_cmd(
|
||||
'config', 'yadm.gpg-recipient', f'"{KEY_NAME} second-key"')))
|
||||
'config', 'yadm.gpg-recipient', f'"second-key {KEY_NAME}"')))
|
||||
|
||||
env = os.environ.copy()
|
||||
env['GNUPGHOME'] = gnupg.home
|
||||
|
|
|
@ -45,13 +45,18 @@ def test_init(
|
|||
|
||||
# command args
|
||||
args = ['init']
|
||||
cwd = None
|
||||
if alt_work:
|
||||
args.extend(['-w', paths.work])
|
||||
if force:
|
||||
cwd = paths.work.dirname
|
||||
args.extend(['-w', paths.work.basename])
|
||||
else:
|
||||
args.extend(['-w', paths.work])
|
||||
if force:
|
||||
args.append('-f')
|
||||
|
||||
# run init
|
||||
run = runner(yadm_cmd(*args), env={'HOME': home})
|
||||
run = runner(yadm_cmd(*args), env={'HOME': home}, cwd=cwd)
|
||||
|
||||
if repo_present and not force:
|
||||
assert run.failure
|
||||
|
@ -60,10 +65,11 @@ def test_init(
|
|||
else:
|
||||
assert run.success
|
||||
assert 'Initialized empty shared Git repository' in run.out
|
||||
assert run.err == ''
|
||||
|
||||
if repo_present:
|
||||
assert not old_repo.isfile(), 'Original repo still exists'
|
||||
else:
|
||||
assert run.err == ''
|
||||
|
||||
if alt_work:
|
||||
assert repo_config('core.worktree') == paths.work
|
||||
|
|
|
@ -32,26 +32,30 @@ YDATA = '.local/share/yadm'
|
|||
'override archive',
|
||||
'override bootstrap',
|
||||
])
|
||||
def test_config(runner, paths, override, expect):
|
||||
@pytest.mark.parametrize(
|
||||
'path', ['.', './override', 'override', '.override', '/override'], ids=[
|
||||
'cwd', './relative', 'relative', 'hidden relative', 'absolute'
|
||||
])
|
||||
def test_config(runner, paths, override, expect, path):
|
||||
"""Test configure_paths"""
|
||||
opath = 'override'
|
||||
matches = match_map()
|
||||
args = []
|
||||
if path.startswith('/'):
|
||||
expected_path = path
|
||||
else:
|
||||
expected_path = str(paths.root.join(path))
|
||||
|
||||
args = [override, path] if override else []
|
||||
|
||||
if override == '-Y':
|
||||
matches = match_map('/' + opath)
|
||||
if override == '--yadm-data':
|
||||
matches = match_map(None, '/' + opath)
|
||||
matches = match_map(expected_path)
|
||||
elif override == '--yadm-data':
|
||||
matches = match_map(None, expected_path)
|
||||
else:
|
||||
matches = match_map()
|
||||
|
||||
if override:
|
||||
args = [override, '/' + opath]
|
||||
for ekey in expect.keys():
|
||||
matches[ekey] = f'{expect[ekey]}="/{opath}"'
|
||||
run_test(
|
||||
runner, paths,
|
||||
[override, opath],
|
||||
['must specify a fully qualified'], 1)
|
||||
for ekey in expect.keys():
|
||||
matches[ekey] = f'{expect[ekey]}="{expected_path}"'
|
||||
|
||||
run_test(runner, paths, args, matches.values(), 0)
|
||||
run_test(runner, paths, args, matches.values(), cwd=str(paths.root))
|
||||
|
||||
|
||||
def match_map(yadm_dir=None, yadm_data=None):
|
||||
|
@ -71,7 +75,7 @@ def match_map(yadm_dir=None, yadm_data=None):
|
|||
}
|
||||
|
||||
|
||||
def run_test(runner, paths, args, expected_matches, expected_code=0):
|
||||
def run_test(runner, paths, args, expected_matches, cwd=None):
|
||||
"""Run proces global args, and run configure_paths"""
|
||||
argstring = ' '.join(['"'+a+'"' for a in args])
|
||||
script = f"""
|
||||
|
@ -83,9 +87,8 @@ def run_test(runner, paths, args, expected_matches, expected_code=0):
|
|||
configure_paths
|
||||
declare -p | grep -E '(YADM|GIT)_'
|
||||
"""
|
||||
run = runner(command=['bash'], inp=script)
|
||||
assert run.code == expected_code
|
||||
assert run.success == (run.code == 0)
|
||||
assert (run.err if run.success else run.out) == ''
|
||||
run = runner(command=['bash'], inp=script, cwd=cwd)
|
||||
assert run.success
|
||||
assert run.err == ''
|
||||
for match in expected_matches:
|
||||
assert match in run.out if run.success else run.err
|
||||
assert match in run.out
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
"""Unit tests: is_valid_branch_name"""
|
||||
import pytest
|
||||
|
||||
# Git branches do not allow:
|
||||
# * path component that begins with "."
|
||||
# * double dot
|
||||
# * "~", "^", ":", "\", space
|
||||
# * end with a "/"
|
||||
# * end with ".lock"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'branch, expected', [
|
||||
('master', 'valid'),
|
||||
('path/branch', 'valid'),
|
||||
('path/.branch', 'invalid'),
|
||||
('path..branch', 'invalid'),
|
||||
('path~branch', 'invalid'),
|
||||
('path^branch', 'invalid'),
|
||||
('path:branch', 'invalid'),
|
||||
('path\\branch', 'invalid'),
|
||||
('path branch', 'invalid'),
|
||||
('path/branch/', 'invalid'),
|
||||
('branch.lock', 'invalid'),
|
||||
])
|
||||
def test_is_valid_branch_name(runner, yadm, branch, expected):
|
||||
"""Test function is_valid_branch_name()"""
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
if is_valid_branch_name "{branch}"; then
|
||||
echo valid
|
||||
else
|
||||
echo invalid
|
||||
fi
|
||||
"""
|
||||
run = runner(command=['bash'], inp=script)
|
||||
assert run.success
|
||||
assert run.err == ''
|
||||
assert run.out.strip() == expected
|
26
test/test_unit_query_distro_family.py
Normal file
26
test/test_unit_query_distro_family.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
"""Unit tests: query_distro_family"""
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'condition', ['os-release', 'os-release-quotes', 'missing'])
|
||||
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")
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
OS_RELEASE="{os_release}"
|
||||
query_distro_family
|
||||
"""
|
||||
run = runner(command=['bash'], inp=script)
|
||||
assert run.success
|
||||
assert run.err == ''
|
||||
if 'os-release' in condition:
|
||||
assert run.out.rstrip() == test_family
|
||||
else:
|
||||
assert run.out.rstrip() == ''
|
|
@ -6,25 +6,33 @@ CONDITION = {
|
|||
'labels': ['default'],
|
||||
'modifier': 0,
|
||||
},
|
||||
'arch': {
|
||||
'labels': ['a', 'arch'],
|
||||
'modifier': 1,
|
||||
},
|
||||
'system': {
|
||||
'labels': ['o', 'os'],
|
||||
'modifier': 1,
|
||||
'modifier': 2,
|
||||
},
|
||||
'distro': {
|
||||
'labels': ['d', 'distro'],
|
||||
'modifier': 2,
|
||||
'modifier': 4,
|
||||
},
|
||||
'distro_family': {
|
||||
'labels': ['f', 'distro_family'],
|
||||
'modifier': 8,
|
||||
},
|
||||
'class': {
|
||||
'labels': ['c', 'class'],
|
||||
'modifier': 4,
|
||||
'modifier': 16,
|
||||
},
|
||||
'hostname': {
|
||||
'labels': ['h', 'hostname'],
|
||||
'modifier': 8,
|
||||
'modifier': 32,
|
||||
},
|
||||
'user': {
|
||||
'labels': ['u', 'user'],
|
||||
'modifier': 16,
|
||||
'modifier': 64,
|
||||
},
|
||||
}
|
||||
TEMPLATE_LABELS = ['t', 'template', 'yadm']
|
||||
|
@ -44,6 +52,12 @@ def calculate_score(filename):
|
|||
label, value = condition.split('.', 1)
|
||||
if label in CONDITION['default']['labels']:
|
||||
score += 1000
|
||||
elif label in CONDITION['arch']['labels']:
|
||||
if value == 'testarch':
|
||||
score += 1000 + CONDITION['arch']['modifier']
|
||||
else:
|
||||
score = 0
|
||||
break
|
||||
elif label in CONDITION['system']['labels']:
|
||||
if value == 'testsystem':
|
||||
score += 1000 + CONDITION['system']['modifier']
|
||||
|
@ -82,6 +96,8 @@ def calculate_score(filename):
|
|||
|
||||
@pytest.mark.parametrize(
|
||||
'default', ['default', None], ids=['default', 'no-default'])
|
||||
@pytest.mark.parametrize(
|
||||
'arch', ['arch', None], ids=['arch', 'no-arch'])
|
||||
@pytest.mark.parametrize(
|
||||
'system', ['system', None], ids=['system', 'no-system'])
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -93,10 +109,11 @@ def calculate_score(filename):
|
|||
@pytest.mark.parametrize(
|
||||
'user', ['user', None], ids=['user', 'no-user'])
|
||||
def test_score_values(
|
||||
runner, yadm, default, system, distro, cla, host, user):
|
||||
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'
|
||||
|
@ -111,6 +128,18 @@ def test_score_values(
|
|||
newfile += ','
|
||||
newfile += label
|
||||
filenames[newfile] = calculate_score(newfile)
|
||||
if arch:
|
||||
for filename in list(filenames):
|
||||
for match in [True, False]:
|
||||
for label in CONDITION[arch]['labels']:
|
||||
newfile = filename
|
||||
if not newfile.endswith('##'):
|
||||
newfile += ','
|
||||
newfile += '.'.join([
|
||||
label,
|
||||
local_arch if match else 'badarch'
|
||||
])
|
||||
filenames[newfile] = calculate_score(newfile)
|
||||
if system:
|
||||
for filename in list(filenames):
|
||||
for match in [True, False]:
|
||||
|
@ -176,6 +205,8 @@ def test_score_values(
|
|||
YADM_TEST=1 source {yadm}
|
||||
score=0
|
||||
local_class={local_class}
|
||||
local_classes=({local_class})
|
||||
local_arch={local_arch}
|
||||
local_system={local_system}
|
||||
local_distro={local_distro}
|
||||
local_host={local_host}
|
||||
|
@ -221,6 +252,7 @@ def test_extensions(runner, yadm, ext):
|
|||
def test_score_values_templates(runner, yadm):
|
||||
"""Test score results"""
|
||||
local_class = 'testclass'
|
||||
local_arch = 'arch'
|
||||
local_system = 'testsystem'
|
||||
local_distro = 'testdistro'
|
||||
local_host = 'testhost'
|
||||
|
@ -239,6 +271,7 @@ def test_score_values_templates(runner, yadm):
|
|||
YADM_TEST=1 source {yadm}
|
||||
score=0
|
||||
local_class={local_class}
|
||||
local_arch={local_arch}
|
||||
local_system={local_system}
|
||||
local_distro={local_distro}
|
||||
local_host={local_host}
|
||||
|
@ -282,3 +315,37 @@ def test_template_recording(runner, yadm, cmd_generated):
|
|||
assert run.success
|
||||
assert run.err == ''
|
||||
assert run.out.rstrip() == expected
|
||||
|
||||
|
||||
def test_underscores_in_distro_and_family(runner, yadm):
|
||||
"""Test replacing spaces in distro / distro_family with underscores"""
|
||||
local_distro = 'test distro'
|
||||
local_distro_family = 'test family'
|
||||
filenames = {
|
||||
'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': 0,
|
||||
'filename##distro_family.test_family': 1008,
|
||||
}
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
score=0
|
||||
local_distro="{local_distro}"
|
||||
local_distro_family="{local_distro_family}"
|
||||
"""
|
||||
expected = ''
|
||||
for filename in filenames:
|
||||
script += f"""
|
||||
score_file "{filename}"
|
||||
echo "{filename}"
|
||||
echo "$score"
|
||||
"""
|
||||
expected += filename + '\n'
|
||||
expected += str(filenames[filename]) + '\n'
|
||||
run = runner(command=['bash'], inp=script)
|
||||
assert run.success
|
||||
assert run.err == ''
|
||||
assert run.out == expected
|
||||
|
|
|
@ -7,6 +7,7 @@ import utils
|
|||
'override', [
|
||||
False,
|
||||
'class',
|
||||
'arch',
|
||||
'os',
|
||||
'hostname',
|
||||
'user',
|
||||
|
@ -14,6 +15,7 @@ import utils
|
|||
ids=[
|
||||
'no-override',
|
||||
'override-class',
|
||||
'override-arch',
|
||||
'override-os',
|
||||
'override-hostname',
|
||||
'override-user',
|
||||
|
@ -21,7 +23,7 @@ import utils
|
|||
)
|
||||
@pytest.mark.usefixtures('ds1_copy')
|
||||
def test_set_local_alt_values(
|
||||
runner, yadm, paths, tst_sys, tst_host, tst_user, override):
|
||||
runner, yadm, paths, tst_arch, tst_sys, tst_host, tst_user, override):
|
||||
"""Use issue_legacy_path_warning"""
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm} &&
|
||||
|
@ -29,12 +31,16 @@ def test_set_local_alt_values(
|
|||
YADM_DIR={paths.yadm} YADM_DATA={paths.data} configure_paths &&
|
||||
set_local_alt_values
|
||||
echo "class='$local_class'"
|
||||
echo "arch='$local_arch'"
|
||||
echo "os='$local_system'"
|
||||
echo "host='$local_host'"
|
||||
echo "user='$local_user'"
|
||||
"""
|
||||
|
||||
if override:
|
||||
if override == 'class':
|
||||
utils.set_local(paths, override, 'first')
|
||||
utils.set_local(paths, override, 'override', add=True)
|
||||
elif override:
|
||||
utils.set_local(paths, override, 'override')
|
||||
|
||||
run = runner(command=['bash'], inp=script)
|
||||
|
@ -46,6 +52,11 @@ def test_set_local_alt_values(
|
|||
else:
|
||||
assert "class=''" in run.out
|
||||
|
||||
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:
|
||||
|
@ -62,17 +73,20 @@ def test_set_local_alt_values(
|
|||
assert f"user='{tst_user}'" in run.out
|
||||
|
||||
|
||||
def test_distro(runner, yadm):
|
||||
"""Assert that local_distro is set"""
|
||||
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 run.out.strip() == "distro='testdistro'"
|
||||
assert "distro='testdistro'" in run.out
|
||||
assert "distro_family='testfamily'" in run.out
|
||||
|
|
|
@ -5,17 +5,23 @@ FILE_MODE = 0o754
|
|||
|
||||
# these values are also testing the handling of bizarre characters
|
||||
LOCAL_CLASS = "default_Test+@-!^Class"
|
||||
LOCAL_CLASS2 = "default_Test+@-|^2nd_Class withSpace"
|
||||
LOCAL_ARCH = "default_Test+@-!^Arch"
|
||||
LOCAL_SYSTEM = "default_Test+@-!^System"
|
||||
LOCAL_HOST = "default_Test+@-!^Host"
|
||||
LOCAL_USER = "default_Test+@-!^User"
|
||||
LOCAL_DISTRO = "default_Test+@-!^Distro"
|
||||
LOCAL_DISTRO_FAMILY = "default_Test+@-!^Family"
|
||||
TEMPLATE = f'''
|
||||
start of template
|
||||
default class = >{{{{yadm.class}}}}<
|
||||
default os = >{{{{yadm.os}}}}<
|
||||
default host = >{{{{yadm.hostname}}}}<
|
||||
default user = >{{{{yadm.user}}}}<
|
||||
default distro = >{{{{yadm.distro}}}}<
|
||||
default class = >{{{{yadm.class}}}}<
|
||||
default arch = >{{{{yadm.arch}}}}<
|
||||
default os = >{{{{yadm.os}}}}<
|
||||
default host = >{{{{yadm.hostname}}}}<
|
||||
default user = >{{{{yadm.user}}}}<
|
||||
default distro = >{{{{yadm.distro}}}}<
|
||||
default distro_family = >{{{{yadm.distro_family}}}}<
|
||||
classes = >{{{{yadm.classes}}}}<
|
||||
{{% if yadm.class == "else1" %}}
|
||||
wrong else 1
|
||||
{{% else %}}
|
||||
|
@ -30,9 +36,21 @@ Multiple lines
|
|||
{{% else %}}
|
||||
Should not be included...
|
||||
{{% endif %}}
|
||||
{{% if yadm.class == "{LOCAL_CLASS2}" %}}
|
||||
Included section for second class
|
||||
{{% endif %}}
|
||||
{{% if yadm.class == "wrongclass2" %}}
|
||||
wrong class 2
|
||||
{{% endif %}}
|
||||
{{% if yadm.arch == "wrongarch1" %}}
|
||||
wrong arch 1
|
||||
{{% endif %}}
|
||||
{{% if yadm.arch == "{LOCAL_ARCH}" %}}
|
||||
Included section for arch = {{{{yadm.arch}}}} ({{{{yadm.arch}}}} repeated)
|
||||
{{% endif %}}
|
||||
{{% if yadm.arch == "wrongarch2" %}}
|
||||
wrong arch 2
|
||||
{{% endif %}}
|
||||
{{% if yadm.os == "wrongos1" %}}
|
||||
wrong os 1
|
||||
{{% endif %}}
|
||||
|
@ -69,22 +87,40 @@ Included section for distro = {{{{yadm.distro}}}} ({{{{yadm.distro}}}} again)
|
|||
{{% if yadm.distro == "wrongdistro2" %}}
|
||||
wrong distro 2
|
||||
{{% endif %}}
|
||||
{{% if yadm.distro_family == "wrongfamily1" %}}
|
||||
wrong family 1
|
||||
{{% endif %}}
|
||||
{{% if yadm.distro_family == "{LOCAL_DISTRO_FAMILY}" %}}
|
||||
Included section for distro_family = \
|
||||
{{{{yadm.distro_family}}}} ({{{{yadm.distro_family}}}} again)
|
||||
{{% endif %}}
|
||||
{{% if yadm.distro_family == "wrongfamily2" %}}
|
||||
wrong family 2
|
||||
{{% endif %}}
|
||||
end of template
|
||||
'''
|
||||
EXPECTED = f'''
|
||||
start of template
|
||||
default class = >{LOCAL_CLASS}<
|
||||
default os = >{LOCAL_SYSTEM}<
|
||||
default host = >{LOCAL_HOST}<
|
||||
default user = >{LOCAL_USER}<
|
||||
default distro = >{LOCAL_DISTRO}<
|
||||
default class = >{LOCAL_CLASS}<
|
||||
default arch = >{LOCAL_ARCH}<
|
||||
default os = >{LOCAL_SYSTEM}<
|
||||
default host = >{LOCAL_HOST}<
|
||||
default user = >{LOCAL_USER}<
|
||||
default distro = >{LOCAL_DISTRO}<
|
||||
default distro_family = >{LOCAL_DISTRO_FAMILY}<
|
||||
classes = >{LOCAL_CLASS2}
|
||||
{LOCAL_CLASS}<
|
||||
Included section from else
|
||||
Included section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated)
|
||||
Multiple lines
|
||||
Included section for second class
|
||||
Included section for arch = {LOCAL_ARCH} ({LOCAL_ARCH} repeated)
|
||||
Included section for os = {LOCAL_SYSTEM} ({LOCAL_SYSTEM} repeated)
|
||||
Included section for host = {LOCAL_HOST} ({LOCAL_HOST} again)
|
||||
Included section for user = {LOCAL_USER} ({LOCAL_USER} repeated)
|
||||
Included section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again)
|
||||
Included section for distro_family = \
|
||||
{LOCAL_DISTRO_FAMILY} ({LOCAL_DISTRO_FAMILY} again)
|
||||
end of template
|
||||
'''
|
||||
|
||||
|
@ -127,14 +163,23 @@ def test_template_default(runner, yadm, tmpdir):
|
|||
input_file.chmod(FILE_MODE)
|
||||
output_file = tmpdir.join('output')
|
||||
|
||||
# ensure overwrite works when file exists as read-only (there is some
|
||||
# special processing when this is encountered because some environments do
|
||||
# not properly overwrite read-only files)
|
||||
output_file.write('existing')
|
||||
output_file.chmod(0o400)
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
set_awk
|
||||
local_class="{LOCAL_CLASS}"
|
||||
local_classes=("{LOCAL_CLASS2}" "{LOCAL_CLASS}")
|
||||
local_arch="{LOCAL_ARCH}"
|
||||
local_system="{LOCAL_SYSTEM}"
|
||||
local_host="{LOCAL_HOST}"
|
||||
local_user="{LOCAL_USER}"
|
||||
local_distro="{LOCAL_DISTRO}"
|
||||
local_distro_family="{LOCAL_DISTRO_FAMILY}"
|
||||
template_default "{input_file}" "{output_file}"
|
||||
"""
|
||||
run = runner(command=['bash'], inp=script)
|
||||
|
@ -196,3 +241,22 @@ def test_include(runner, yadm, tmpdir):
|
|||
assert run.err == ''
|
||||
assert output_file.read() == EXPECTED_INCLUDE
|
||||
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
||||
|
||||
|
||||
def test_env(runner, yadm, tmpdir):
|
||||
"""Test env"""
|
||||
|
||||
input_file = tmpdir.join('input')
|
||||
input_file.write('{{env.PWD}}', ensure=True)
|
||||
input_file.chmod(FILE_MODE)
|
||||
output_file = tmpdir.join('output')
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
set_awk
|
||||
template_default "{input_file}" "{output_file}"
|
||||
"""
|
||||
run = runner(command=['bash'], inp=script)
|
||||
assert run.success
|
||||
assert run.err == ''
|
||||
assert output_file.read().strip() == os.environ['PWD']
|
||||
|
|
|
@ -4,31 +4,50 @@ import os
|
|||
FILE_MODE = 0o754
|
||||
|
||||
LOCAL_CLASS = "esh_Test+@-!^Class"
|
||||
LOCAL_CLASS2 = "esh_Test+@-|^2nd_Class withSpace"
|
||||
LOCAL_ARCH = "esh_Test+@-!^Arch"
|
||||
LOCAL_SYSTEM = "esh_Test+@-!^System"
|
||||
LOCAL_HOST = "esh_Test+@-!^Host"
|
||||
LOCAL_USER = "esh_Test+@-!^User"
|
||||
LOCAL_DISTRO = "esh_Test+@-!^Distro"
|
||||
LOCAL_DISTRO_FAMILY = "esh_Test+@-!^Family"
|
||||
TEMPLATE = f'''
|
||||
start of template
|
||||
esh class = ><%=$YADM_CLASS%><
|
||||
esh os = ><%=$YADM_OS%><
|
||||
esh host = ><%=$YADM_HOSTNAME%><
|
||||
esh user = ><%=$YADM_USER%><
|
||||
esh distro = ><%=$YADM_DISTRO%><
|
||||
esh class = ><%=$YADM_CLASS%><
|
||||
esh arch = ><%=$YADM_ARCH%><
|
||||
esh os = ><%=$YADM_OS%><
|
||||
esh host = ><%=$YADM_HOSTNAME%><
|
||||
esh user = ><%=$YADM_USER%><
|
||||
esh distro = ><%=$YADM_DISTRO%><
|
||||
esh distro_family = ><%=$YADM_DISTRO_FAMILY%><
|
||||
esh classes = ><%=$YADM_CLASSES%><
|
||||
<% if [ "$YADM_CLASS" = "wrongclass1" ]; then -%>
|
||||
wrong class 1
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_CLASS" = "{LOCAL_CLASS}" ]; then -%>
|
||||
Included section for class = <%=$YADM_CLASS%> (<%=$YADM_CLASS%> repeated)
|
||||
Included esh section for class = <%=$YADM_CLASS%> (<%=$YADM_CLASS%> repeated)
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_CLASS" = "wrongclass2" ]; then -%>
|
||||
wrong class 2
|
||||
<% fi -%>
|
||||
<% echo "$YADM_CLASSES" | while IFS='' read cls; do
|
||||
if [ "$cls" = "{LOCAL_CLASS2}" ]; then -%>
|
||||
Included esh section for second class
|
||||
<% fi; done -%>
|
||||
<% if [ "$YADM_ARCH" = "wrongarch1" ]; then -%>
|
||||
wrong arch 1
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_ARCH" = "{LOCAL_ARCH}" ]; then -%>
|
||||
Included esh section for arch = <%=$YADM_ARCH%> (<%=$YADM_ARCH%> repeated)
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_ARCH" = "wrongarch2" ]; then -%>
|
||||
wrong arch 2
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_OS" = "wrongos1" ]; then -%>
|
||||
wrong os 1
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_OS" = "{LOCAL_SYSTEM}" ]; then -%>
|
||||
Included section for os = <%=$YADM_OS%> (<%=$YADM_OS%> repeated)
|
||||
Included esh section for os = <%=$YADM_OS%> (<%=$YADM_OS%> repeated)
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_OS" = "wrongos2" ]; then -%>
|
||||
wrong os 2
|
||||
|
@ -37,7 +56,7 @@ wrong os 2
|
|||
wrong host 1
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_HOSTNAME" = "{LOCAL_HOST}" ]; then -%>
|
||||
Included section for host = <%=$YADM_HOSTNAME%> (<%=$YADM_HOSTNAME%> again)
|
||||
Included esh section for host = <%=$YADM_HOSTNAME%> (<%=$YADM_HOSTNAME%> again)
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_HOSTNAME" = "wronghost2" ]; then -%>
|
||||
wrong host 2
|
||||
|
@ -46,7 +65,7 @@ wrong host 2
|
|||
wrong user 1
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_USER" = "{LOCAL_USER}" ]; then -%>
|
||||
Included section for user = <%=$YADM_USER%> (<%=$YADM_USER%> repeated)
|
||||
Included esh section for user = <%=$YADM_USER%> (<%=$YADM_USER%> repeated)
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_USER" = "wronguser2" ]; then -%>
|
||||
wrong user 2
|
||||
|
@ -55,25 +74,42 @@ wrong user 2
|
|||
wrong distro 1
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_DISTRO" = "{LOCAL_DISTRO}" ]; then -%>
|
||||
Included section for distro = <%=$YADM_DISTRO%> (<%=$YADM_DISTRO%> again)
|
||||
Included esh section for distro = <%=$YADM_DISTRO%> (<%=$YADM_DISTRO%> again)
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_DISTRO" = "wrongdistro2" ]; then -%>
|
||||
wrong distro 2
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_DISTRO_FAMILY" = "wrongfamily1" ]; then -%>
|
||||
wrong family 1
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_DISTRO_FAMILY" = "{LOCAL_DISTRO_FAMILY}" ]; then -%>
|
||||
Included esh section for distro_family = \
|
||||
<%=$YADM_DISTRO_FAMILY%> (<%=$YADM_DISTRO_FAMILY%> again)
|
||||
<% fi -%>
|
||||
<% if [ "$YADM_DISTRO" = "wrongfamily2" ]; then -%>
|
||||
wrong family 2
|
||||
<% fi -%>
|
||||
end of template
|
||||
'''
|
||||
EXPECTED = f'''
|
||||
start of template
|
||||
esh class = >{LOCAL_CLASS}<
|
||||
esh os = >{LOCAL_SYSTEM}<
|
||||
esh host = >{LOCAL_HOST}<
|
||||
esh user = >{LOCAL_USER}<
|
||||
esh distro = >{LOCAL_DISTRO}<
|
||||
Included section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated)
|
||||
Included section for os = {LOCAL_SYSTEM} ({LOCAL_SYSTEM} repeated)
|
||||
Included section for host = {LOCAL_HOST} ({LOCAL_HOST} again)
|
||||
Included section for user = {LOCAL_USER} ({LOCAL_USER} repeated)
|
||||
Included section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again)
|
||||
esh class = >{LOCAL_CLASS}<
|
||||
esh arch = >{LOCAL_ARCH}<
|
||||
esh os = >{LOCAL_SYSTEM}<
|
||||
esh host = >{LOCAL_HOST}<
|
||||
esh user = >{LOCAL_USER}<
|
||||
esh distro = >{LOCAL_DISTRO}<
|
||||
esh distro_family = >{LOCAL_DISTRO_FAMILY}<
|
||||
esh classes = >{LOCAL_CLASS2} {LOCAL_CLASS}<
|
||||
Included esh section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated)
|
||||
Included esh section for second class
|
||||
Included esh section for arch = {LOCAL_ARCH} ({LOCAL_ARCH} repeated)
|
||||
Included esh section for os = {LOCAL_SYSTEM} ({LOCAL_SYSTEM} repeated)
|
||||
Included esh section for host = {LOCAL_HOST} ({LOCAL_HOST} again)
|
||||
Included esh section for user = {LOCAL_USER} ({LOCAL_USER} repeated)
|
||||
Included esh section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again)
|
||||
Included esh section for distro_family = \
|
||||
{LOCAL_DISTRO_FAMILY} ({LOCAL_DISTRO_FAMILY} again)
|
||||
end of template
|
||||
'''
|
||||
|
||||
|
@ -86,13 +122,22 @@ def test_template_esh(runner, yadm, tmpdir):
|
|||
input_file.chmod(FILE_MODE)
|
||||
output_file = tmpdir.join('output')
|
||||
|
||||
# ensure overwrite works when file exists as read-only (there is some
|
||||
# special processing when this is encountered because some environments do
|
||||
# not properly overwrite read-only files)
|
||||
output_file.write('existing')
|
||||
output_file.chmod(0o400)
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
local_class="{LOCAL_CLASS}"
|
||||
local_classes=("{LOCAL_CLASS2}" "{LOCAL_CLASS}")
|
||||
local_arch="{LOCAL_ARCH}"
|
||||
local_system="{LOCAL_SYSTEM}"
|
||||
local_host="{LOCAL_HOST}"
|
||||
local_user="{LOCAL_USER}"
|
||||
local_distro="{LOCAL_DISTRO}"
|
||||
local_distro_family="{LOCAL_DISTRO_FAMILY}"
|
||||
template_esh "{input_file}" "{output_file}"
|
||||
"""
|
||||
run = runner(command=['bash'], inp=script)
|
||||
|
|
|
@ -5,31 +5,50 @@ import pytest
|
|||
FILE_MODE = 0o754
|
||||
|
||||
LOCAL_CLASS = "j2_Test+@-!^Class"
|
||||
LOCAL_CLASS2 = "j2_Test+@-|^2nd_Class withSpace"
|
||||
LOCAL_ARCH = "j2_Test+@-!^Arch"
|
||||
LOCAL_SYSTEM = "j2_Test+@-!^System"
|
||||
LOCAL_HOST = "j2_Test+@-!^Host"
|
||||
LOCAL_USER = "j2_Test+@-!^User"
|
||||
LOCAL_DISTRO = "j2_Test+@-!^Distro"
|
||||
LOCAL_DISTRO_FAMILY = "j2_Test+@-!^Family"
|
||||
TEMPLATE = f'''
|
||||
start of template
|
||||
j2 class = >{{{{YADM_CLASS}}}}<
|
||||
j2 os = >{{{{YADM_OS}}}}<
|
||||
j2 host = >{{{{YADM_HOSTNAME}}}}<
|
||||
j2 user = >{{{{YADM_USER}}}}<
|
||||
j2 distro = >{{{{YADM_DISTRO}}}}<
|
||||
j2 class = >{{{{YADM_CLASS}}}}<
|
||||
j2 arch = >{{{{YADM_ARCH}}}}<
|
||||
j2 os = >{{{{YADM_OS}}}}<
|
||||
j2 host = >{{{{YADM_HOSTNAME}}}}<
|
||||
j2 user = >{{{{YADM_USER}}}}<
|
||||
j2 distro = >{{{{YADM_DISTRO}}}}<
|
||||
j2 distro_family = >{{{{YADM_DISTRO_FAMILY}}}}<
|
||||
j2 classes = >{{{{YADM_CLASSES}}}}<
|
||||
{{%- if YADM_CLASS == "wrongclass1" %}}
|
||||
wrong class 1
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_CLASS == "{LOCAL_CLASS}" %}}
|
||||
Included section for class = {{{{YADM_CLASS}}}} ({{{{YADM_CLASS}}}} repeated)
|
||||
Included j2 section for class = \
|
||||
{{{{YADM_CLASS}}}} ({{{{YADM_CLASS}}}} repeated)
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_CLASS == "wrongclass2" %}}
|
||||
wrong class 2
|
||||
{{%- endif %}}
|
||||
{{%- if "{LOCAL_CLASS2}" in YADM_CLASSES.split("\\n") %}}
|
||||
Included j2 section for second class
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_ARCH == "wrongarch1" %}}
|
||||
wrong arch 1
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_ARCH == "{LOCAL_ARCH}" %}}
|
||||
Included j2 section for arch = {{{{YADM_ARCH}}}} ({{{{YADM_ARCH}}}} repeated)
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_ARCH == "wrongarch2" %}}
|
||||
wrong arch 2
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_OS == "wrongos1" %}}
|
||||
wrong os 1
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_OS == "{LOCAL_SYSTEM}" %}}
|
||||
Included section for os = {{{{YADM_OS}}}} ({{{{YADM_OS}}}} repeated)
|
||||
Included j2 section for os = {{{{YADM_OS}}}} ({{{{YADM_OS}}}} repeated)
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_OS == "wrongos2" %}}
|
||||
wrong os 2
|
||||
|
@ -38,7 +57,8 @@ wrong os 2
|
|||
wrong host 1
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_HOSTNAME == "{LOCAL_HOST}" %}}
|
||||
Included section for host = {{{{YADM_HOSTNAME}}}} ({{{{YADM_HOSTNAME}}}} again)
|
||||
Included j2 section for host = \
|
||||
{{{{YADM_HOSTNAME}}}} ({{{{YADM_HOSTNAME}}}} again)
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_HOSTNAME == "wronghost2" %}}
|
||||
wrong host 2
|
||||
|
@ -47,7 +67,7 @@ wrong host 2
|
|||
wrong user 1
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_USER == "{LOCAL_USER}" %}}
|
||||
Included section for user = {{{{YADM_USER}}}} ({{{{YADM_USER}}}} repeated)
|
||||
Included j2 section for user = {{{{YADM_USER}}}} ({{{{YADM_USER}}}} repeated)
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_USER == "wronguser2" %}}
|
||||
wrong user 2
|
||||
|
@ -56,25 +76,44 @@ wrong user 2
|
|||
wrong distro 1
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_DISTRO == "{LOCAL_DISTRO}" %}}
|
||||
Included section for distro = {{{{YADM_DISTRO}}}} ({{{{YADM_DISTRO}}}} again)
|
||||
Included j2 section for distro = \
|
||||
{{{{YADM_DISTRO}}}} ({{{{YADM_DISTRO}}}} again)
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_DISTRO == "wrongdistro2" %}}
|
||||
wrong distro 2
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_DISTRO_FAMILY == "wrongfamily1" %}}
|
||||
wrong family 1
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_DISTRO_FAMILY == "{LOCAL_DISTRO_FAMILY}" %}}
|
||||
Included j2 section for distro_family = \
|
||||
{{{{YADM_DISTRO_FAMILY}}}} ({{{{YADM_DISTRO_FAMILY}}}} again)
|
||||
{{%- endif %}}
|
||||
{{%- if YADM_DISTRO_FAMILY == "wrongfamily2" %}}
|
||||
wrong family 2
|
||||
{{%- endif %}}
|
||||
end of template
|
||||
'''
|
||||
EXPECTED = f'''
|
||||
start of template
|
||||
j2 class = >{LOCAL_CLASS}<
|
||||
j2 os = >{LOCAL_SYSTEM}<
|
||||
j2 host = >{LOCAL_HOST}<
|
||||
j2 user = >{LOCAL_USER}<
|
||||
j2 distro = >{LOCAL_DISTRO}<
|
||||
Included section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated)
|
||||
Included section for os = {LOCAL_SYSTEM} ({LOCAL_SYSTEM} repeated)
|
||||
Included section for host = {LOCAL_HOST} ({LOCAL_HOST} again)
|
||||
Included section for user = {LOCAL_USER} ({LOCAL_USER} repeated)
|
||||
Included section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again)
|
||||
j2 class = >{LOCAL_CLASS}<
|
||||
j2 arch = >{LOCAL_ARCH}<
|
||||
j2 os = >{LOCAL_SYSTEM}<
|
||||
j2 host = >{LOCAL_HOST}<
|
||||
j2 user = >{LOCAL_USER}<
|
||||
j2 distro = >{LOCAL_DISTRO}<
|
||||
j2 distro_family = >{LOCAL_DISTRO_FAMILY}<
|
||||
j2 classes = >{LOCAL_CLASS2}
|
||||
{LOCAL_CLASS}<
|
||||
Included j2 section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated)
|
||||
Included j2 section for second class
|
||||
Included j2 section for arch = {LOCAL_ARCH} ({LOCAL_ARCH} repeated)
|
||||
Included j2 section for os = {LOCAL_SYSTEM} ({LOCAL_SYSTEM} repeated)
|
||||
Included j2 section for host = {LOCAL_HOST} ({LOCAL_HOST} again)
|
||||
Included j2 section for user = {LOCAL_USER} ({LOCAL_USER} repeated)
|
||||
Included j2 section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again)
|
||||
Included j2 section for distro_family = \
|
||||
{LOCAL_DISTRO_FAMILY} ({LOCAL_DISTRO_FAMILY} again)
|
||||
end of template
|
||||
'''
|
||||
|
||||
|
@ -88,13 +127,22 @@ def test_template_j2(runner, yadm, tmpdir, processor):
|
|||
input_file.chmod(FILE_MODE)
|
||||
output_file = tmpdir.join('output')
|
||||
|
||||
# ensure overwrite works when file exists as read-only (there is some
|
||||
# special processing when this is encountered because some environments do
|
||||
# not properly overwrite read-only files)
|
||||
output_file.write('existing')
|
||||
output_file.chmod(0o400)
|
||||
|
||||
script = f"""
|
||||
YADM_TEST=1 source {yadm}
|
||||
local_class="{LOCAL_CLASS}"
|
||||
local_classes=("{LOCAL_CLASS2}" "{LOCAL_CLASS}")
|
||||
local_arch="{LOCAL_ARCH}"
|
||||
local_system="{LOCAL_SYSTEM}"
|
||||
local_host="{LOCAL_HOST}"
|
||||
local_user="{LOCAL_USER}"
|
||||
local_distro="{LOCAL_DISTRO}"
|
||||
local_distro_family="{LOCAL_DISTRO_FAMILY}"
|
||||
template_{processor} "{input_file}" "{output_file}"
|
||||
"""
|
||||
run = runner(command=['bash'], inp=script)
|
||||
|
|
|
@ -53,8 +53,8 @@ def test_upgrade(tmpdir, runner, versions, submodule):
|
|||
if submodule:
|
||||
# When upgrading via 2.5.0 we can't have a submodule that's been added
|
||||
# after being cloned as 2.5.0 fails the upgrade in that case.
|
||||
can_upgraded_cloned_submodule = '2.5.0' not in versions[1:]
|
||||
if can_upgraded_cloned_submodule:
|
||||
can_upgrade_cloned_submodule = '2.5.0' not in versions[1:]
|
||||
if can_upgrade_cloned_submodule:
|
||||
# Check out a repo and then add it as a submodule
|
||||
run = runner(['git', '-C', str(home), 'clone', str(ext_repo), 'b'])
|
||||
assert run.success
|
||||
|
@ -92,7 +92,7 @@ def test_upgrade(tmpdir, runner, versions, submodule):
|
|||
run = run_version(version, 'upgrade', check_stderr=not submodule)
|
||||
if submodule:
|
||||
lines = run.err.splitlines()
|
||||
if can_upgraded_cloned_submodule:
|
||||
if can_upgrade_cloned_submodule:
|
||||
assert 'Migrating git directory of' in lines[0]
|
||||
assert str(home.join('b/.git')) in lines[1]
|
||||
assert str(yadm_dir.join('repo.git/modules/b')) in lines[2]
|
||||
|
@ -108,7 +108,7 @@ def test_upgrade(tmpdir, runner, versions, submodule):
|
|||
assert run.out == 'some data'
|
||||
|
||||
if submodule:
|
||||
if can_upgraded_cloned_submodule:
|
||||
if can_upgrade_cloned_submodule:
|
||||
assert home.join('b/afile').read() == 'some data'
|
||||
assert home.join('a/afile').read() == 'some data'
|
||||
assert home.join('c/afile').read() == 'some data'
|
||||
|
|
|
@ -29,8 +29,10 @@ def test_semantic_version(expected_version):
|
|||
@pytest.mark.parametrize('cmd', ['--version', 'version'])
|
||||
def test_reported_version(
|
||||
runner, yadm_cmd, cmd, expected_version):
|
||||
"""Report correct version"""
|
||||
"""Report correct version and bash/git versions"""
|
||||
run = runner(command=yadm_cmd(cmd))
|
||||
assert run.success
|
||||
assert run.err == ''
|
||||
assert run.out == f'yadm {expected_version}\n'
|
||||
assert 'bash version' in run.out
|
||||
assert 'git version' in run.out
|
||||
assert run.out.endswith(f'\nyadm version {expected_version}\n')
|
||||
|
|
|
@ -21,11 +21,12 @@ INCLUDE_DIRS = ['', 'test alt']
|
|||
INCLUDE_CONTENT = '8780846c02e34c930d0afd127906668f'
|
||||
|
||||
|
||||
def set_local(paths, variable, value):
|
||||
def set_local(paths, variable, value, add=False):
|
||||
"""Set local override"""
|
||||
add = "--add" if add else ""
|
||||
os.system(
|
||||
f'GIT_DIR={str(paths.repo)} '
|
||||
f'git config --local "local.{variable}" "{value}"'
|
||||
f'git config --local {add} "local.{variable}" "{value}"'
|
||||
)
|
||||
|
||||
|
||||
|
|
567
yadm
567
yadm
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
# yadm - Yet Another Dotfiles Manager
|
||||
# Copyright (C) 2015-2021 Tim Byrne
|
||||
# Copyright (C) 2015-2023 Tim Byrne
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -15,12 +15,13 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# shellcheck shell=bash
|
||||
# execute script with bash (shebang line is /bin/sh for portability)
|
||||
if [ -z "$BASH_VERSION" ]; then
|
||||
[ "$YADM_TEST" != 1 ] && exec bash "$0" "$@"
|
||||
fi
|
||||
|
||||
VERSION=3.0.1
|
||||
VERSION=3.2.2
|
||||
|
||||
YADM_WORK="$HOME"
|
||||
YADM_DIR=
|
||||
|
@ -127,13 +128,10 @@ function main() {
|
|||
;;
|
||||
-l) # used by decrypt()
|
||||
DO_LIST="YES"
|
||||
[ "$YADM_COMMAND" = "config" ] && YADM_ARGS+=("$1")
|
||||
[[ "$YADM_COMMAND" =~ ^(clone|config)$ ]] && YADM_ARGS+=("$1")
|
||||
;;
|
||||
-w) # used by init() and clone()
|
||||
if [[ ! "$2" =~ ^/ ]] ; then
|
||||
error_out "You must specify a fully qualified work tree"
|
||||
fi
|
||||
YADM_WORK="$2"
|
||||
YADM_WORK="$(qualify_path "$2" "work tree")"
|
||||
shift
|
||||
;;
|
||||
*) # any unhandled arguments
|
||||
|
@ -172,8 +170,8 @@ function score_file() {
|
|||
tgt="${src%%##*}"
|
||||
conditions="${src#*##}"
|
||||
|
||||
if [ "${tgt#$YADM_ALT/}" != "${tgt}" ]; then
|
||||
tgt="${YADM_BASE}/${tgt#$YADM_ALT/}"
|
||||
if [ "${tgt#"$YADM_ALT/"}" != "${tgt}" ]; then
|
||||
tgt="${YADM_BASE}/${tgt#"$YADM_ALT/"}"
|
||||
fi
|
||||
|
||||
score=0
|
||||
|
@ -191,37 +189,51 @@ function score_file() {
|
|||
if [[ "$label" =~ ^(default)$ ]]; then
|
||||
score=$((score + 0))
|
||||
# variable conditions
|
||||
elif [[ "$label" =~ ^(o|os)$ ]]; then
|
||||
if [ "$value" = "$local_system" ]; then
|
||||
elif [[ "$label" =~ ^(a|arch)$ ]]; then
|
||||
if [ "$value" = "$local_arch" ]; then
|
||||
score=$((score + 1))
|
||||
else
|
||||
score=0
|
||||
return
|
||||
fi
|
||||
elif [[ "$label" =~ ^(d|distro)$ ]]; then
|
||||
if [ "$value" = "$local_distro" ]; then
|
||||
elif [[ "$label" =~ ^(o|os)$ ]]; then
|
||||
if [ "$value" = "$local_system" ]; then
|
||||
score=$((score + 2))
|
||||
else
|
||||
score=0
|
||||
return
|
||||
fi
|
||||
elif [[ "$label" =~ ^(c|class)$ ]]; then
|
||||
if [ "$value" = "$local_class" ]; then
|
||||
elif [[ "$label" =~ ^(d|distro)$ ]]; then
|
||||
if [ "${value/\ /_}" = "${local_distro/\ /_}" ]; then
|
||||
score=$((score + 4))
|
||||
else
|
||||
score=0
|
||||
return
|
||||
fi
|
||||
elif [[ "$label" =~ ^(f|distro_family)$ ]]; then
|
||||
if [ "${value/\ /_}" = "${local_distro_family/\ /_}" ]; then
|
||||
score=$((score + 8))
|
||||
else
|
||||
score=0
|
||||
return
|
||||
fi
|
||||
elif [[ "$label" =~ ^(c|class)$ ]]; then
|
||||
if in_list "$value" "${local_classes[@]}"; then
|
||||
score=$((score + 16))
|
||||
else
|
||||
score=0
|
||||
return
|
||||
fi
|
||||
elif [[ "$label" =~ ^(h|hostname)$ ]]; then
|
||||
if [ "$value" = "$local_host" ]; then
|
||||
score=$((score + 8))
|
||||
score=$((score + 32))
|
||||
else
|
||||
score=0
|
||||
return
|
||||
fi
|
||||
elif [[ "$label" =~ ^(u|user)$ ]]; then
|
||||
if [ "$value" = "$local_user" ]; then
|
||||
score=$((score + 16))
|
||||
score=$((score + 64))
|
||||
else
|
||||
score=0
|
||||
return
|
||||
|
@ -355,26 +367,29 @@ function template_default() {
|
|||
|
||||
# the explicit "space + tab" character class used below is used because not
|
||||
# all versions of awk seem to support the POSIX character classes [[:blank:]]
|
||||
awk_pgm=$(cat << "EOF"
|
||||
read -r -d '' awk_pgm << "EOF"
|
||||
# built-in default template processor
|
||||
BEGIN {
|
||||
blank = "[ ]"
|
||||
c["class"] = class
|
||||
c["os"] = os
|
||||
c["hostname"] = host
|
||||
c["user"] = user
|
||||
c["distro"] = distro
|
||||
c["source"] = source
|
||||
ifs = "^{%" blank "*if"
|
||||
els = "^{%" blank "*else" blank "*%}$"
|
||||
end = "^{%" blank "*endif" blank "*%}$"
|
||||
skp = "^{%" blank "*(if|else|endif)"
|
||||
vld = conditions()
|
||||
inc_start = "^{%" blank "*include" blank "+\"?"
|
||||
inc_end = "\"?" blank "*%}$"
|
||||
inc = inc_start ".+" inc_end
|
||||
prt = 1
|
||||
err = 0
|
||||
blank = "[ ]"
|
||||
c["class"] = class
|
||||
c["classes"] = classes
|
||||
c["arch"] = arch
|
||||
c["os"] = os
|
||||
c["hostname"] = host
|
||||
c["user"] = user
|
||||
c["distro"] = distro
|
||||
c["distro_family"] = distro_family
|
||||
c["source"] = source
|
||||
ifs = "^{%" blank "*if"
|
||||
els = "^{%" blank "*else" blank "*%}$"
|
||||
end = "^{%" blank "*endif" blank "*%}$"
|
||||
skp = "^{%" blank "*(if|else|endif)"
|
||||
vld = conditions()
|
||||
inc_start = "^{%" blank "*include" blank "+\"?"
|
||||
inc_end = "\"?" blank "*%}$"
|
||||
inc = inc_start ".+" inc_end
|
||||
prt = 1
|
||||
err = 0
|
||||
}
|
||||
END { exit err }
|
||||
{ replace_vars() } # variable replacements
|
||||
|
@ -411,35 +426,47 @@ function replace_vars() {
|
|||
for (label in c) {
|
||||
gsub(("{{" blank "*yadm\\." label blank "*}}"), c[label])
|
||||
}
|
||||
for (label in ENVIRON) {
|
||||
gsub(("{{" blank "*env\\." label blank "*}}"), ENVIRON[label])
|
||||
}
|
||||
}
|
||||
function condition_helper(label, value) {
|
||||
gsub(/[\\.^$(){}\[\]|*+?]/, "\\\\&", value)
|
||||
return sprintf("yadm\\.%s" blank "*==" blank "*\"%s\"", label, value)
|
||||
}
|
||||
function conditions() {
|
||||
pattern = ifs blank "*("
|
||||
pattern = ifs blank "+("
|
||||
for (label in c) {
|
||||
value = c[label]
|
||||
gsub(/[\\.^$(){}\[\]|*+?]/, "\\\\&", value)
|
||||
pattern = sprintf("%syadm\\.%s" blank "*==" blank "*\"%s\"|", pattern, label, value)
|
||||
if (label != "class") {
|
||||
value = c[label]
|
||||
pattern = sprintf("%s%s|", pattern, condition_helper(label, value));
|
||||
}
|
||||
}
|
||||
sub(/\|$/,")",pattern)
|
||||
split(classes, cls_array, "\n")
|
||||
for (idx in cls_array) {
|
||||
value = cls_array[idx]
|
||||
pattern = sprintf("%s%s|", pattern, condition_helper("class", value));
|
||||
}
|
||||
sub(/\|$/, ")" blank "*%}$", pattern)
|
||||
return pattern
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
"${AWK_PROGRAM[0]}" \
|
||||
-v class="$local_class" \
|
||||
-v arch="$local_arch" \
|
||||
-v os="$local_system" \
|
||||
-v host="$local_host" \
|
||||
-v user="$local_user" \
|
||||
-v distro="$local_distro" \
|
||||
-v distro_family="$local_distro_family" \
|
||||
-v source="$input" \
|
||||
-v source_dir="$(dirname "$input")" \
|
||||
-v classes="$(join_string $'\n' "${local_classes[@]}")" \
|
||||
"$awk_pgm" \
|
||||
"$input" > "$temp_file" || rm -f "$temp_file"
|
||||
|
||||
if [ -f "$temp_file" ] ; then
|
||||
copy_perms "$input" "$temp_file"
|
||||
mv -f "$temp_file" "$output"
|
||||
fi
|
||||
move_file "$input" "$output" "$temp_file"
|
||||
}
|
||||
|
||||
function template_j2cli() {
|
||||
|
@ -448,17 +475,17 @@ function template_j2cli() {
|
|||
temp_file="${output}.$$.$RANDOM"
|
||||
|
||||
YADM_CLASS="$local_class" \
|
||||
YADM_ARCH="$local_arch" \
|
||||
YADM_OS="$local_system" \
|
||||
YADM_HOSTNAME="$local_host" \
|
||||
YADM_USER="$local_user" \
|
||||
YADM_DISTRO="$local_distro" \
|
||||
YADM_DISTRO_FAMILY="$local_distro_family" \
|
||||
YADM_SOURCE="$input" \
|
||||
YADM_CLASSES="$(join_string $'\n' "${local_classes[@]}")" \
|
||||
"$J2CLI_PROGRAM" "$input" -o "$temp_file"
|
||||
|
||||
if [ -f "$temp_file" ] ; then
|
||||
copy_perms "$input" "$temp_file"
|
||||
mv -f "$temp_file" "$output"
|
||||
fi
|
||||
move_file "$input" "$output" "$temp_file"
|
||||
}
|
||||
|
||||
function template_envtpl() {
|
||||
|
@ -467,17 +494,17 @@ function template_envtpl() {
|
|||
temp_file="${output}.$$.$RANDOM"
|
||||
|
||||
YADM_CLASS="$local_class" \
|
||||
YADM_ARCH="$local_arch" \
|
||||
YADM_OS="$local_system" \
|
||||
YADM_HOSTNAME="$local_host" \
|
||||
YADM_USER="$local_user" \
|
||||
YADM_DISTRO="$local_distro" \
|
||||
YADM_DISTRO_FAMILY="$local_distro_family" \
|
||||
YADM_SOURCE="$input" \
|
||||
YADM_CLASSES="$(join_string $'\n' "${local_classes[@]}")" \
|
||||
"$ENVTPL_PROGRAM" --keep-template "$input" -o "$temp_file"
|
||||
|
||||
if [ -f "$temp_file" ] ; then
|
||||
copy_perms "$input" "$temp_file"
|
||||
mv -f "$temp_file" "$output"
|
||||
fi
|
||||
move_file "$input" "$output" "$temp_file"
|
||||
}
|
||||
|
||||
function template_esh() {
|
||||
|
@ -485,18 +512,34 @@ function template_esh() {
|
|||
output="$2"
|
||||
temp_file="${output}.$$.$RANDOM"
|
||||
|
||||
YADM_CLASSES="$(join_string $'\n' "${local_classes[@]}")" \
|
||||
"$ESH_PROGRAM" -o "$temp_file" "$input" \
|
||||
YADM_CLASS="$local_class" \
|
||||
YADM_ARCH="$local_arch" \
|
||||
YADM_OS="$local_system" \
|
||||
YADM_HOSTNAME="$local_host" \
|
||||
YADM_USER="$local_user" \
|
||||
YADM_DISTRO="$local_distro" \
|
||||
YADM_DISTRO_FAMILY="$local_distro_family" \
|
||||
YADM_SOURCE="$input"
|
||||
|
||||
if [ -f "$temp_file" ] ; then
|
||||
copy_perms "$input" "$temp_file"
|
||||
mv -f "$temp_file" "$output"
|
||||
fi
|
||||
move_file "$input" "$output" "$temp_file"
|
||||
}
|
||||
|
||||
function move_file() {
|
||||
local input=$1
|
||||
local output=$2
|
||||
local temp_file=$3
|
||||
|
||||
[ ! -f "$temp_file" ] && return
|
||||
|
||||
# if the output files already exists as read-only, change it to be writable.
|
||||
# there are some environments in which a read-only file will prevent the move
|
||||
# from being successful.
|
||||
[[ -e "$output" && ! -w "$output" ]] && chmod u+w "$output"
|
||||
|
||||
mv -f "$temp_file" "$output"
|
||||
copy_perms "$input" "$output"
|
||||
}
|
||||
|
||||
# ****** yadm Commands ******
|
||||
|
@ -508,10 +551,13 @@ function alt() {
|
|||
|
||||
# gather values for processing alternates
|
||||
local local_class
|
||||
local -a local_classes
|
||||
local local_arch
|
||||
local local_system
|
||||
local local_host
|
||||
local local_user
|
||||
local local_distro
|
||||
local local_distro_family
|
||||
set_local_alt_values
|
||||
|
||||
# only be noisy if the "alt" command was run directly
|
||||
|
@ -538,8 +584,8 @@ function alt() {
|
|||
if [[ $possible_alt =~ .\#\#. ]]; then
|
||||
base_alt="${possible_alt%%##*}"
|
||||
yadm_alt="${YADM_BASE}/${base_alt}"
|
||||
if [ "${yadm_alt#$YADM_ALT/}" != "${yadm_alt}" ]; then
|
||||
base_alt="${yadm_alt#$YADM_ALT/}"
|
||||
if [ "${yadm_alt#"$YADM_ALT/"}" != "${yadm_alt}" ]; then
|
||||
base_alt="${yadm_alt#"$YADM_ALT/"}"
|
||||
fi
|
||||
possible_alts+=("$YADM_BASE/${base_alt}")
|
||||
fi
|
||||
|
@ -559,7 +605,8 @@ function report_invalid_alts() {
|
|||
for invalid in "${INVALID_ALT[@]}"; do
|
||||
path_list="$path_list * $invalid"$'\n'
|
||||
done
|
||||
cat <<EOF >&2
|
||||
local msg
|
||||
IFS='' read -r -d '' msg <<EOF
|
||||
|
||||
**WARNING**
|
||||
Invalid alternates have been detected.
|
||||
|
@ -581,6 +628,7 @@ function report_invalid_alts() {
|
|||
${path_list}
|
||||
***********
|
||||
EOF
|
||||
printf '%s\n' "$msg" >&2
|
||||
}
|
||||
|
||||
function remove_stale_links() {
|
||||
|
@ -604,7 +652,17 @@ function remove_stale_links() {
|
|||
|
||||
function set_local_alt_values() {
|
||||
|
||||
local_class="$(config local.class)"
|
||||
local -a all_classes
|
||||
all_classes=$(config --get-all local.class)
|
||||
while IFS='' read -r class; do
|
||||
local_classes+=("$class")
|
||||
local_class="$class"
|
||||
done <<< "$all_classes"
|
||||
|
||||
local_arch="$(config local.arch)"
|
||||
if [ -z "$local_arch" ] ; then
|
||||
local_arch=$(uname -m)
|
||||
fi
|
||||
|
||||
local_system="$(config local.os)"
|
||||
if [ -z "$local_system" ] ; then
|
||||
|
@ -623,6 +681,7 @@ function set_local_alt_values() {
|
|||
fi
|
||||
|
||||
local_distro="$(query_distro)"
|
||||
local_distro_family="$(query_distro_family)"
|
||||
|
||||
}
|
||||
|
||||
|
@ -705,84 +764,75 @@ function clean() {
|
|||
|
||||
}
|
||||
|
||||
function _default_remote_branch() {
|
||||
local ls_remote
|
||||
ls_remote=$("$GIT_PROGRAM" ls-remote -q --symref "$1" 2>/dev/null)
|
||||
match="^ref:[[:blank:]]+refs/heads/([^[:blank:]]+)"
|
||||
if [[ "$ls_remote" =~ $match ]] ; then
|
||||
echo "${BASH_REMATCH[1]}"
|
||||
else
|
||||
echo master
|
||||
fi
|
||||
}
|
||||
|
||||
function clone() {
|
||||
|
||||
DO_BOOTSTRAP=1
|
||||
local branch=
|
||||
|
||||
local repo_url=
|
||||
local -a args
|
||||
local -i do_checkout=1
|
||||
while [[ $# -gt 0 ]] ; do
|
||||
key="$1"
|
||||
case $key in
|
||||
-b)
|
||||
if ! is_valid_branch_name "$2"; then
|
||||
error_out "You must provide a branch name when using '-b'"
|
||||
fi
|
||||
branch="$2"
|
||||
shift
|
||||
;;
|
||||
case "$1" in
|
||||
--bootstrap) # force bootstrap, without prompt
|
||||
DO_BOOTSTRAP=2
|
||||
;;
|
||||
--no-bootstrap) # prevent bootstrap, without prompt
|
||||
DO_BOOTSTRAP=3
|
||||
;;
|
||||
*) # use first found argument as the URL
|
||||
[ -z "$repo_url" ] && repo_url="$1"
|
||||
--checkout)
|
||||
do_checkout=1
|
||||
;;
|
||||
-n|--no-checkout)
|
||||
do_checkout=0
|
||||
;;
|
||||
--bare|--mirror|--recurse-submodules*|--recursive|--separate-git-dir=*)
|
||||
# ignore arguments without separate parameter
|
||||
;;
|
||||
--separate-git-dir)
|
||||
# ignore arguments with separate parameter
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
args+=("$1")
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ -z "$repo_url" ] && error_out "No repository provided"
|
||||
|
||||
[ -z "$branch" ] && branch=$(_default_remote_branch "$repo_url")
|
||||
|
||||
[ -n "$DEBUG" ] && display_private_perms "initial"
|
||||
|
||||
# shellcheck disable=SC2119
|
||||
# clone will begin with a bare repo
|
||||
init
|
||||
# safety check, don't attempt to clone when the repo is already present
|
||||
[ -d "$YADM_REPO" ] && [ -z "$FORCE" ] &&
|
||||
error_out "Git repo already exists. [$YADM_REPO]\nUse '-f' if you want to force it to be overwritten."
|
||||
|
||||
# configure local HEAD with the correct branch
|
||||
printf 'ref: refs/heads/%s\n' "$branch" > "${YADM_REPO}/HEAD"
|
||||
|
||||
# add the specified remote, and configure the repo to track origin/$branch
|
||||
debug "Adding remote to new repo"
|
||||
"$GIT_PROGRAM" remote add origin "$repo_url"
|
||||
debug "Configuring new repo to track origin/${branch}"
|
||||
"$GIT_PROGRAM" config "branch.${branch}.remote" origin
|
||||
"$GIT_PROGRAM" config "branch.${branch}.merge" "refs/heads/${branch}"
|
||||
|
||||
# fetch / merge (and possibly fallback to reset)
|
||||
debug "Doing an initial fetch of the origin"
|
||||
"$GIT_PROGRAM" fetch origin || {
|
||||
debug "Removing repo after failed clone"
|
||||
# remove existing if forcing the clone to happen anyway
|
||||
[ -d "$YADM_REPO" ] && {
|
||||
debug "Removing existing repo prior to clone"
|
||||
"$GIT_PROGRAM" -C "$YADM_WORK" submodule deinit -f --all
|
||||
rm -rf "$YADM_REPO"
|
||||
error_out "Unable to fetch origin $repo_url"
|
||||
}
|
||||
debug "Verifying '${branch}' is a valid branch to merge"
|
||||
[ -f "${YADM_REPO}/refs/remotes/origin/${branch}" ] || {
|
||||
debug "Removing repo after failed clone"
|
||||
rm -rf "$YADM_REPO"
|
||||
error_out "Clone failed, 'origin/${branch}' does not exist in $repo_url"
|
||||
|
||||
local wc
|
||||
wc="$(mk_tmp_dir)"
|
||||
[ -d "$wc" ] || error_out "Unable to create temporary directory"
|
||||
|
||||
# first clone without checkout
|
||||
debug "Doing an initial clone of the repository"
|
||||
(cd "$wc" &&
|
||||
"$GIT_PROGRAM" -c core.sharedrepository=0600 clone --no-checkout \
|
||||
--separate-git-dir="$YADM_REPO" "${args[@]}" repo.git) || {
|
||||
debug "Removing repo after failed clone"
|
||||
rm -rf "$YADM_REPO" "$wc"
|
||||
error_out "Unable to clone the repository"
|
||||
}
|
||||
configure_repo
|
||||
rm -rf "$wc"
|
||||
|
||||
# then reset the index as the --no-checkout flag makes the index empty
|
||||
"$GIT_PROGRAM" reset --quiet -- .
|
||||
|
||||
if [ "$YADM_WORK" = "$HOME" ]; then
|
||||
debug "Determining if repo tracks private directories"
|
||||
for private_dir in $(private_dirs all); do
|
||||
found_log=$("$GIT_PROGRAM" log -n 1 "origin/${branch}" -- "$private_dir" 2>/dev/null)
|
||||
found_log=$("$GIT_PROGRAM" log -n 1 -- "$private_dir" 2>/dev/null)
|
||||
if [ -n "$found_log" ]; then
|
||||
debug "Private directory $private_dir is tracked by repo"
|
||||
assert_private_dirs "$private_dir"
|
||||
|
@ -790,58 +840,42 @@ function clone() {
|
|||
done
|
||||
fi
|
||||
|
||||
[ -n "$DEBUG" ] && display_private_perms "pre-merge"
|
||||
debug "Doing an initial merge of origin/${branch}"
|
||||
"$GIT_PROGRAM" merge "origin/${branch}" || {
|
||||
debug "Merge failed, doing a reset and stashing conflicts."
|
||||
"$GIT_PROGRAM" reset "origin/${branch}"
|
||||
if cd "$YADM_WORK"; then # necessary because of a bug in Git
|
||||
"$GIT_PROGRAM" -c user.name='yadm clone' -c user.email='yadm' stash save Conflicts preserved from yadm clone command 2>&1
|
||||
cat <<EOF
|
||||
# finally check out (unless instructed not to) all files that don't exist in $YADM_WORK
|
||||
if [[ $do_checkout -ne 0 ]]; then
|
||||
[ -n "$DEBUG" ] && display_private_perms "pre-checkout"
|
||||
|
||||
cd_work "Clone" || return
|
||||
|
||||
"$GIT_PROGRAM" ls-files --deleted | while IFS= read -r file; do
|
||||
"$GIT_PROGRAM" checkout -- ":/$file"
|
||||
done
|
||||
|
||||
if [ -n "$("$GIT_PROGRAM" ls-files --modified)" ]; then
|
||||
local msg
|
||||
IFS='' read -r -d '' msg <<EOF
|
||||
**NOTE**
|
||||
Merging origin/${branch} failed.
|
||||
Local files with content that differs from the ones just
|
||||
cloned were found in $YADM_WORK. They have been left
|
||||
unmodified.
|
||||
|
||||
As a result, yadm did 'reset origin/${branch}', and then
|
||||
stashed the conflicting data.
|
||||
|
||||
This likely happened because you had files in \$HOME
|
||||
which conflicted with files tracked by origin/${branch}.
|
||||
|
||||
You can review the stashed conflicts with the
|
||||
command 'yadm stash show -p' from within your
|
||||
\$HOME directory. If you want to restore the
|
||||
stashed data, you can run 'yadm stash apply' or
|
||||
'yadm stash pop' and then handle the conflicts
|
||||
in another way.
|
||||
EOF
|
||||
else
|
||||
# skip auto_bootstrap if conflicts could not be stashed
|
||||
DO_BOOTSTRAP=0
|
||||
cat <<EOF
|
||||
**NOTE**
|
||||
Merging origin/${branch} failed.
|
||||
yadm did 'reset origin/${branch}' instead.
|
||||
|
||||
yadm did not stash these conflicts beacuse it was unable
|
||||
to change to the $YADM_WORK directory.
|
||||
|
||||
Please review and resolve any differences appropriately
|
||||
Please review and resolve any differences appropriately.
|
||||
If you know what you're doing, and want to overwrite the
|
||||
tracked files, consider 'yadm reset --hard origin/${branch}'
|
||||
tracked files, consider 'yadm checkout "$YADM_WORK"'.
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
printf '%s\n' "$msg"
|
||||
fi
|
||||
|
||||
[ -n "$DEBUG" ] && display_private_perms "post-merge"
|
||||
[ -n "$DEBUG" ] && display_private_perms "post-checkout"
|
||||
|
||||
CHANGES_POSSIBLE=1
|
||||
CHANGES_POSSIBLE=1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
function config() {
|
||||
|
||||
use_repo_config=0
|
||||
local_options="^local\.(class|os|hostname|user)$"
|
||||
local_options="^local\.(class|arch|os|hostname|user)$"
|
||||
for option in "$@"; do
|
||||
[[ "$option" =~ $local_options ]] && use_repo_config=1
|
||||
done
|
||||
|
@ -855,11 +889,13 @@ function config() {
|
|||
echo " ${supported_config}"
|
||||
done
|
||||
echo
|
||||
cat << EOF
|
||||
local msg
|
||||
read -r -d '' msg << EOF
|
||||
Please read the CONFIGURATION section in the man
|
||||
page for more details about configurations, and
|
||||
how to adjust them.
|
||||
EOF
|
||||
printf '%s\n' "$msg"
|
||||
elif [ "$use_repo_config" -eq 1 ]; then
|
||||
|
||||
require_repo
|
||||
|
@ -885,7 +921,10 @@ function _set_gpg_options() {
|
|||
if [ "$gpg_key" = "ASK" ]; then
|
||||
GPG_OPTS=("--no-default-recipient" "-e")
|
||||
elif [ "$gpg_key" != "" ]; then
|
||||
GPG_OPTS=("-e" "-r $gpg_key")
|
||||
GPG_OPTS=("-e")
|
||||
for key in $gpg_key; do
|
||||
GPG_OPTS+=("-r $key")
|
||||
done
|
||||
else
|
||||
GPG_OPTS=("-c")
|
||||
fi
|
||||
|
@ -1110,7 +1149,8 @@ function git_command() {
|
|||
|
||||
function help() {
|
||||
|
||||
cat << EOF
|
||||
local msg
|
||||
IFS='' read -r -d '' msg << EOF
|
||||
Usage: yadm <command> [options...]
|
||||
|
||||
Manage dotfiles maintained in a Git repository. Manage alternate files
|
||||
|
@ -1137,13 +1177,13 @@ Commands:
|
|||
Files:
|
||||
\$HOME/.config/yadm/config - yadm's configuration file
|
||||
\$HOME/.config/yadm/encrypt - List of globs to encrypt/decrypt
|
||||
\$HOME/.config/yadm/bootstrap - Script run via: yadm bootstrap
|
||||
\$HOME/.config/yadm/bootstrap - Script run via: yadm bootstrap
|
||||
\$HOME/.local/share/yadm/repo.git - yadm's Git repository
|
||||
\$HOME/.local/share/yadm/archive - Encrypted data stored here
|
||||
|
||||
Use "man yadm" for complete documentation.
|
||||
EOF
|
||||
|
||||
printf '%s\n' "$msg"
|
||||
exit_with_hook 1
|
||||
|
||||
}
|
||||
|
@ -1158,6 +1198,7 @@ function init() {
|
|||
# remove existing if forcing the init to happen anyway
|
||||
[ -d "$YADM_REPO" ] && {
|
||||
debug "Removing existing repo prior to init"
|
||||
"$GIT_PROGRAM" -C "$YADM_WORK" submodule deinit -f --all
|
||||
rm -rf "$YADM_REPO"
|
||||
}
|
||||
|
||||
|
@ -1179,7 +1220,8 @@ function introspect() {
|
|||
}
|
||||
|
||||
function introspect_commands() {
|
||||
cat <<-EOF
|
||||
local msg
|
||||
read -r -d '' msg <<-EOF
|
||||
alt
|
||||
bootstrap
|
||||
clean
|
||||
|
@ -1199,10 +1241,13 @@ transcrypt
|
|||
upgrade
|
||||
version
|
||||
EOF
|
||||
printf '%s' "$msg"
|
||||
}
|
||||
|
||||
function introspect_configs() {
|
||||
cat <<-EOF
|
||||
local msg
|
||||
read -r -d '' msg <<-EOF
|
||||
local.arch
|
||||
local.class
|
||||
local.hostname
|
||||
local.os
|
||||
|
@ -1222,6 +1267,7 @@ yadm.openssl-old
|
|||
yadm.openssl-program
|
||||
yadm.ssh-perms
|
||||
EOF
|
||||
printf '%s' "$msg"
|
||||
}
|
||||
|
||||
function introspect_repo() {
|
||||
|
@ -1229,7 +1275,8 @@ function introspect_repo() {
|
|||
}
|
||||
|
||||
function introspect_switches() {
|
||||
cat <<-EOF
|
||||
local msg
|
||||
read -r -d '' msg <<-EOF
|
||||
--yadm-archive
|
||||
--yadm-bootstrap
|
||||
--yadm-config
|
||||
|
@ -1239,6 +1286,7 @@ function introspect_switches() {
|
|||
--yadm-repo
|
||||
-Y
|
||||
EOF
|
||||
printf '%s' "$msg"
|
||||
}
|
||||
|
||||
function list() {
|
||||
|
@ -1323,7 +1371,10 @@ function upgrade() {
|
|||
# been cloned first and then added as a submodule.
|
||||
"$GIT_PROGRAM" submodule absorbgitdirs
|
||||
|
||||
local submodule_status
|
||||
submodule_status=$("$GIT_PROGRAM" -C "$YADM_WORK" submodule status)
|
||||
while read -r sha submodule rest; do
|
||||
[ "$submodule" == "" ] && continue
|
||||
if [[ "$sha" = -* ]]; then
|
||||
continue
|
||||
fi
|
||||
|
@ -1334,7 +1385,7 @@ function upgrade() {
|
|||
error_out "Unable to upgrade. Could not deinit submodule $submodule"
|
||||
}
|
||||
submodules+=("$submodule")
|
||||
done < <("$GIT_PROGRAM" -C "$YADM_WORK" submodule status)
|
||||
done <<< "$submodule_status"
|
||||
|
||||
assert_parent "$YADM_REPO"
|
||||
mv "$LEGACY_REPO" "$YADM_REPO"
|
||||
|
@ -1370,7 +1421,7 @@ function upgrade() {
|
|||
;
|
||||
do
|
||||
if [ -e "$legacy_path" ]; then
|
||||
new_filename=${legacy_path#$YADM_LEGACY_DIR/}
|
||||
new_filename="${legacy_path#"$YADM_LEGACY_DIR/"}"
|
||||
new_filename="$YADM_DIR/$new_filename"
|
||||
actions_performed=1
|
||||
echo "Moving $legacy_path to $new_filename"
|
||||
|
@ -1401,7 +1452,9 @@ function upgrade() {
|
|||
|
||||
function version() {
|
||||
|
||||
echo "yadm $VERSION"
|
||||
echo "bash version $BASH_VERSION"
|
||||
printf " "; "$GIT_PROGRAM" --version
|
||||
echo "yadm version $VERSION"
|
||||
exit_with_hook 0
|
||||
|
||||
}
|
||||
|
@ -1456,18 +1509,6 @@ function exclude_encrypted() {
|
|||
|
||||
}
|
||||
|
||||
function is_valid_branch_name() {
|
||||
# Git branches do not allow:
|
||||
# * path component that begins with "."
|
||||
# * double dot
|
||||
# * "~", "^", ":", "\", space
|
||||
# * end with a "/"
|
||||
# * end with ".lock"
|
||||
pattern='(\/\.|\.\.|[~^:\\ ]|\/$|\.lock$)'
|
||||
[[ "$1" =~ $pattern ]] && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
function query_distro() {
|
||||
distro=""
|
||||
if command -v "$LSB_RELEASE_PROGRAM" &> /dev/null; then
|
||||
|
@ -1484,6 +1525,20 @@ function query_distro() {
|
|||
echo "$distro"
|
||||
}
|
||||
|
||||
function query_distro_family() {
|
||||
family=""
|
||||
if [ -f "$OS_RELEASE" ]; then
|
||||
while IFS='' read -r line || [ -n "$line" ]; do
|
||||
if [[ "$line" = ID_LIKE=* ]]; then
|
||||
family="${line#ID_LIKE=}"
|
||||
family="${family//\"}"
|
||||
break
|
||||
fi
|
||||
done < "$OS_RELEASE"
|
||||
fi
|
||||
echo "$family"
|
||||
}
|
||||
|
||||
function process_global_args() {
|
||||
|
||||
# global arguments are removed before the main processing is done
|
||||
|
@ -1492,52 +1547,31 @@ function process_global_args() {
|
|||
key="$1"
|
||||
case $key in
|
||||
-Y|--yadm-dir) # override the standard YADM_DIR
|
||||
if [[ ! "$2" =~ ^/ ]] ; then
|
||||
error_out "You must specify a fully qualified yadm directory"
|
||||
fi
|
||||
YADM_DIR="$2"
|
||||
YADM_DIR="$(qualify_path "$2" "yadm")"
|
||||
shift
|
||||
;;
|
||||
--yadm-data) # override the standard YADM_DATA
|
||||
if [[ ! "$2" =~ ^/ ]] ; then
|
||||
error_out "You must specify a fully qualified yadm data directory"
|
||||
fi
|
||||
YADM_DATA="$2"
|
||||
YADM_DATA="$(qualify_path "$2" "data")"
|
||||
shift
|
||||
;;
|
||||
--yadm-repo) # override the standard YADM_REPO
|
||||
if [[ ! "$2" =~ ^/ ]] ; then
|
||||
error_out "You must specify a fully qualified repo path"
|
||||
fi
|
||||
YADM_OVERRIDE_REPO="$2"
|
||||
YADM_OVERRIDE_REPO="$(qualify_path "$2" "repo")"
|
||||
shift
|
||||
;;
|
||||
--yadm-config) # override the standard YADM_CONFIG
|
||||
if [[ ! "$2" =~ ^/ ]] ; then
|
||||
error_out "You must specify a fully qualified config path"
|
||||
fi
|
||||
YADM_OVERRIDE_CONFIG="$2"
|
||||
YADM_OVERRIDE_CONFIG="$(qualify_path "$2" "config")"
|
||||
shift
|
||||
;;
|
||||
--yadm-encrypt) # override the standard YADM_ENCRYPT
|
||||
if [[ ! "$2" =~ ^/ ]] ; then
|
||||
error_out "You must specify a fully qualified encrypt path"
|
||||
fi
|
||||
YADM_OVERRIDE_ENCRYPT="$2"
|
||||
YADM_OVERRIDE_ENCRYPT="$(qualify_path "$2" "encrypt")"
|
||||
shift
|
||||
;;
|
||||
--yadm-archive) # override the standard YADM_ARCHIVE
|
||||
if [[ ! "$2" =~ ^/ ]] ; then
|
||||
error_out "You must specify a fully qualified archive path"
|
||||
fi
|
||||
YADM_OVERRIDE_ARCHIVE="$2"
|
||||
YADM_OVERRIDE_ARCHIVE="$(qualify_path "$2" "archive")"
|
||||
shift
|
||||
;;
|
||||
--yadm-bootstrap) # override the standard YADM_BOOTSTRAP
|
||||
if [[ ! "$2" =~ ^/ ]] ; then
|
||||
error_out "You must specify a fully qualified bootstrap path"
|
||||
fi
|
||||
YADM_OVERRIDE_BOOTSTRAP="$2"
|
||||
YADM_OVERRIDE_BOOTSTRAP="$(qualify_path "$2" "bootstrap")"
|
||||
shift
|
||||
;;
|
||||
*) # main arguments are kept intact
|
||||
|
@ -1549,6 +1583,20 @@ function process_global_args() {
|
|||
|
||||
}
|
||||
|
||||
function qualify_path() {
|
||||
local path="$1"
|
||||
if [ -z "$path" ]; then
|
||||
error_out "You can't specify an empty $2 path"
|
||||
fi
|
||||
|
||||
if [ "$path" = "." ]; then
|
||||
path="$PWD"
|
||||
elif [[ "$path" != /* ]]; then
|
||||
path="$PWD/${path#./}"
|
||||
fi
|
||||
echo "$path"
|
||||
}
|
||||
|
||||
function set_yadm_dirs() {
|
||||
|
||||
# only resolve YADM_DATA if it hasn't been provided already
|
||||
|
@ -1608,7 +1656,8 @@ function issue_legacy_path_warning() {
|
|||
path_list="$path_list * $legacy_path"$'\n'
|
||||
done
|
||||
|
||||
cat <<EOF >&2
|
||||
local msg
|
||||
IFS='' read -r -d '' msg <<EOF
|
||||
|
||||
**WARNING**
|
||||
Legacy paths have been detected.
|
||||
|
@ -1632,7 +1681,7 @@ function issue_legacy_path_warning() {
|
|||
${path_list}
|
||||
***********
|
||||
EOF
|
||||
|
||||
printf '%s\n' "$msg" >&2
|
||||
LEGACY_WARNING_ISSUED=1
|
||||
|
||||
}
|
||||
|
@ -1708,13 +1757,11 @@ function configure_repo() {
|
|||
|
||||
function set_operating_system() {
|
||||
|
||||
local proc_version
|
||||
proc_version=$(cat "$PROC_VERSION" 2>/dev/null)
|
||||
if [[ "$proc_version" =~ [Mm]icrosoft ]]; then
|
||||
if [[ "$(<$PROC_VERSION)" =~ [Mm]icrosoft ]]; then
|
||||
OPERATING_SYSTEM="WSL"
|
||||
else
|
||||
OPERATING_SYSTEM=$(uname -s)
|
||||
fi
|
||||
fi 2>/dev/null
|
||||
|
||||
case "$OPERATING_SYSTEM" in
|
||||
CYGWIN*|MINGW*|MSYS*)
|
||||
|
@ -1868,6 +1915,9 @@ function parse_encrypt() {
|
|||
|
||||
ENCRYPT_INCLUDE_FILES=()
|
||||
ENCRYPT_EXCLUDE_FILES=()
|
||||
FINAL_INCLUDE=()
|
||||
|
||||
[ -f "$YADM_ENCRYPT" ] || return
|
||||
|
||||
cd_work "Parsing encrypt" || return
|
||||
|
||||
|
@ -1880,47 +1930,44 @@ function parse_encrypt() {
|
|||
shopt -s globstar &> /dev/null
|
||||
|
||||
exclude_pattern="^!(.+)"
|
||||
if [ -f "$YADM_ENCRYPT" ] ; then
|
||||
# parse both included/excluded
|
||||
while IFS='' read -r line || [ -n "$line" ]; do
|
||||
if [[ ! $line =~ ^# && ! $line =~ ^[[:blank:]]*$ ]] ; then
|
||||
local IFS=$'\n'
|
||||
for pattern in $line; do
|
||||
if [[ "$pattern" =~ $exclude_pattern ]]; then
|
||||
for ex_file in ${BASH_REMATCH[1]}; do
|
||||
if [ -e "$ex_file" ]; then
|
||||
ENCRYPT_EXCLUDE_FILES+=("$ex_file")
|
||||
fi
|
||||
done
|
||||
else
|
||||
for in_file in $pattern; do
|
||||
if [ -e "$in_file" ]; then
|
||||
ENCRYPT_INCLUDE_FILES+=("$in_file")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done < "$YADM_ENCRYPT"
|
||||
|
||||
# remove excludes from the includes
|
||||
#(SC2068 is disabled because in this case, we desire globbing)
|
||||
FINAL_INCLUDE=()
|
||||
#shellcheck disable=SC2068
|
||||
for included in "${ENCRYPT_INCLUDE_FILES[@]}"; do
|
||||
skip=
|
||||
#shellcheck disable=SC2068
|
||||
for ex_file in ${ENCRYPT_EXCLUDE_FILES[@]}; do
|
||||
[ "$included" == "$ex_file" ] && { skip=1; break; }
|
||||
# parse both included/excluded
|
||||
while IFS='' read -r line || [ -n "$line" ]; do
|
||||
if [[ ! $line =~ ^# && ! $line =~ ^[[:blank:]]*$ ]] ; then
|
||||
local IFS=$'\n'
|
||||
for pattern in $line; do
|
||||
if [[ "$pattern" =~ $exclude_pattern ]]; then
|
||||
for ex_file in ${BASH_REMATCH[1]}; do
|
||||
if [ -e "$ex_file" ]; then
|
||||
ENCRYPT_EXCLUDE_FILES+=("$ex_file")
|
||||
fi
|
||||
done
|
||||
else
|
||||
for in_file in $pattern; do
|
||||
if [ -e "$in_file" ]; then
|
||||
ENCRYPT_INCLUDE_FILES+=("$in_file")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
[ -n "$skip" ] || FINAL_INCLUDE+=("$included")
|
||||
done
|
||||
fi
|
||||
done < "$YADM_ENCRYPT"
|
||||
|
||||
# sort the encrypted files
|
||||
#shellcheck disable=SC2207
|
||||
IFS=$'\n' ENCRYPT_INCLUDE_FILES=($(LC_ALL=C sort <<<"${FINAL_INCLUDE[*]}"))
|
||||
unset IFS
|
||||
fi
|
||||
# remove excludes from the includes
|
||||
#(SC2068 is disabled because in this case, we desire globbing)
|
||||
#shellcheck disable=SC2068
|
||||
for included in "${ENCRYPT_INCLUDE_FILES[@]}"; do
|
||||
skip=
|
||||
#shellcheck disable=SC2068
|
||||
for ex_file in ${ENCRYPT_EXCLUDE_FILES[@]}; do
|
||||
[ "$included" == "$ex_file" ] && { skip=1; break; }
|
||||
done
|
||||
[ -n "$skip" ] || FINAL_INCLUDE+=("$included")
|
||||
done
|
||||
|
||||
# sort the encrypted files
|
||||
#shellcheck disable=SC2207
|
||||
IFS=$'\n' ENCRYPT_INCLUDE_FILES=($(LC_ALL=C sort <<<"${FINAL_INCLUDE[*]}"))
|
||||
unset IFS
|
||||
|
||||
if [ "$unset_globstar" = "1" ]; then
|
||||
shopt -u globstar &> /dev/null
|
||||
|
@ -1954,7 +2001,7 @@ function relative_path() {
|
|||
result=""
|
||||
|
||||
count=0
|
||||
while [ "${full#$common_part}" == "${full}" ]; do
|
||||
while [ "${full#"$common_part"}" == "${full}" ]; do
|
||||
[ "$count" = "500" ] && return # this is a failsafe
|
||||
# no match, means that candidate common part is not correct
|
||||
# go up one level (reduce common part)
|
||||
|
@ -1975,7 +2022,7 @@ function relative_path() {
|
|||
|
||||
# since we now have identified the common part,
|
||||
# compute the non-common part
|
||||
forward_part="${full#$common_part}"
|
||||
forward_part="${full#"$common_part"}"
|
||||
|
||||
# and now stick all parts together
|
||||
if [[ -n $result ]] && [[ -n $forward_part ]]; then
|
||||
|
@ -2040,6 +2087,16 @@ function join_string {
|
|||
printf "%s" "${*:2}"
|
||||
}
|
||||
|
||||
function in_list {
|
||||
local element="$1"
|
||||
shift
|
||||
|
||||
for e in "$@"; do
|
||||
[[ "$e" = "$element" ]] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
function get_mode {
|
||||
local filename="$1"
|
||||
local mode
|
||||
|
@ -2068,6 +2125,12 @@ function copy_perms {
|
|||
return 0
|
||||
}
|
||||
|
||||
function mk_tmp_dir {
|
||||
local tempdir="$YADM_DATA/tmp.$$.$RANDOM"
|
||||
assert_parent "$tempdir/"
|
||||
echo "$tempdir"
|
||||
}
|
||||
|
||||
# ****** Prerequisites Functions ******
|
||||
|
||||
function require_archive() {
|
||||
|
|
112
yadm.1
112
yadm.1
|
@ -1,5 +1,5 @@
|
|||
.\" vim: set spell so=8:
|
||||
.TH yadm 1 "7 January 2021" "3.0.1"
|
||||
.TH yadm 1 "23 January 2023" "3.2.2"
|
||||
|
||||
.SH NAME
|
||||
|
||||
|
@ -116,32 +116,12 @@ if it exists.
|
|||
.TP
|
||||
.BI clone " url
|
||||
Clone a remote repository for tracking dotfiles.
|
||||
After the contents of the remote repository have been fetched, a "merge" of
|
||||
After the contents of the remote repository have been fetched, a "check out" of
|
||||
the remote HEAD branch is attempted.
|
||||
If there are conflicting files already present in the
|
||||
.IR work-tree ,
|
||||
this merge will fail and instead a "reset" of the remote HEAD branch
|
||||
will be done, followed by a "stash". This "stash" operation will preserve the
|
||||
original data.
|
||||
|
||||
You can review the stashed conflicts by running the command
|
||||
|
||||
.RS
|
||||
.RS
|
||||
yadm stash show -p
|
||||
.RE
|
||||
|
||||
from within your
|
||||
.I $HOME
|
||||
directory. If you want to restore the stashed data, you can run
|
||||
|
||||
.RS
|
||||
yadm stash apply
|
||||
.RE
|
||||
or
|
||||
.RS
|
||||
yadm stash pop
|
||||
.RE
|
||||
the local version will be left unmodified and you'll have to review and resolve
|
||||
the difference.
|
||||
|
||||
The repository is stored in
|
||||
.IR $HOME/.local/share/yadm/repo.git .
|
||||
|
@ -161,7 +141,6 @@ exists). The options
|
|||
.BR --bootstrap " or " --no-bootstrap
|
||||
will either force the bootstrap to be run, or prevent it from being run,
|
||||
without prompting the user.
|
||||
.RE
|
||||
.TP
|
||||
.B config
|
||||
This command manages configurations for yadm.
|
||||
|
@ -331,7 +310,7 @@ 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 fully qualified path.
|
||||
Each option should be followed by a path.
|
||||
.TP
|
||||
.B -Y,--yadm-dir
|
||||
Override the yadm directory.
|
||||
|
@ -448,7 +427,7 @@ Disable the permission changes to
|
|||
This feature is enabled by default.
|
||||
|
||||
.RE
|
||||
The following four "local" configurations are not stored in the
|
||||
The following five "local" configurations are not stored in the
|
||||
.IR $HOME/.config/yadm/config,
|
||||
they are stored in the local repository.
|
||||
|
||||
|
@ -456,6 +435,14 @@ they are stored in the local repository.
|
|||
.B local.class
|
||||
Specify a class for the purpose of symlinking alternate files.
|
||||
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>
|
||||
.RE
|
||||
.TP
|
||||
.B local.arch
|
||||
Override the architecture for the purpose of symlinking alternate files.
|
||||
.TP
|
||||
.B local.hostname
|
||||
Override the hostname for the purpose of symlinking alternate files.
|
||||
|
@ -499,17 +486,11 @@ Valid if the value matches the current user.
|
|||
Current user is calculated by running
|
||||
.BR "id -u -n" .
|
||||
.TP
|
||||
.BR distro , " d
|
||||
Valid if the value matches the distro.
|
||||
Distro is calculated by running
|
||||
.B "lsb_release -si"
|
||||
or by inspecting the ID from
|
||||
.BR "/etc/os-release" .
|
||||
.TP
|
||||
.BR os , " o
|
||||
Valid if the value matches the OS.
|
||||
OS is calculated by running
|
||||
.BR "uname -s" .
|
||||
.BR hostname , " h
|
||||
Valid if the value matches the short hostname.
|
||||
Hostname is calculated by running
|
||||
.BR "uname -n" ,
|
||||
and trimming off any domain.
|
||||
.TP
|
||||
.BR class , " c
|
||||
Valid if the value matches the
|
||||
|
@ -520,11 +501,27 @@ Class must be manually set using
|
|||
See the CONFIGURATION section for more details about setting
|
||||
.BR local.class .
|
||||
.TP
|
||||
.BR hostname , " h
|
||||
Valid if the value matches the short hostname.
|
||||
Hostname is calculated by running
|
||||
.BR "uname -n" ,
|
||||
and trimming off any domain.
|
||||
.BR distro , " d
|
||||
Valid if the value matches the distro.
|
||||
Distro is calculated by running
|
||||
.B "lsb_release -si"
|
||||
or by inspecting the ID from
|
||||
.BR "/etc/os-release" .
|
||||
.TP
|
||||
.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" .
|
||||
.TP
|
||||
.BR os , " o
|
||||
Valid if the value matches the OS.
|
||||
OS is calculated by running
|
||||
.BR "uname -s" .
|
||||
.TP
|
||||
.BR arch , " a
|
||||
Valid if the value matches the architecture.
|
||||
Architecture is calculated by running
|
||||
.BR "uname -m" .
|
||||
.TP
|
||||
.B default
|
||||
Valid when no other alternate is valid.
|
||||
|
@ -598,7 +595,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.
|
||||
least one yadm managed file within them (at the top level).
|
||||
|
||||
yadm will automatically create these links by default. This can be disabled
|
||||
using the
|
||||
|
@ -617,8 +614,9 @@ command. The following sets the class to be "Work".
|
|||
|
||||
yadm config local.class Work
|
||||
|
||||
Similarly, the values of os, hostname, and user can be manually overridden
|
||||
using the configuration options
|
||||
Similarly, the values of architecture, os, hostname, and user can be manually
|
||||
overridden using the configuration options
|
||||
.BR local.arch ,
|
||||
.BR local.os ,
|
||||
.BR local.hostname ,
|
||||
and
|
||||
|
@ -666,14 +664,18 @@ to create or overwrite files.
|
|||
|
||||
During processing, the following variables are available in the template:
|
||||
|
||||
Default Jinja or ESH Description
|
||||
------------- ------------- --------------------------
|
||||
yadm.class YADM_CLASS Locally defined yadm class
|
||||
yadm.distro YADM_DISTRO lsb_release -si
|
||||
yadm.hostname YADM_HOSTNAME uname -n (without domain)
|
||||
yadm.os YADM_OS uname -s
|
||||
yadm.user YADM_USER id -u -n
|
||||
yadm.source YADM_SOURCE Template filename
|
||||
Default Jinja or ESH Description
|
||||
------------- ------------- ----------------------------
|
||||
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_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.source YADM_SOURCE Template filename
|
||||
yadm.user YADM_USER id -u -n
|
||||
env.VAR Environment variable VAR
|
||||
|
||||
.BR NOTE :
|
||||
The OS for "Windows Subsystem for Linux" is reported as "WSL", even
|
||||
|
@ -744,7 +746,7 @@ and
|
|||
.BR openssl (1)
|
||||
are supported.
|
||||
gpg is used by default, but openssl can be configured with the
|
||||
.I yadm.cypher
|
||||
.I yadm.cipher
|
||||
configuration.
|
||||
|
||||
To use this feature, a list of patterns must be created and saved as
|
||||
|
|
399
yadm.md
399
yadm.md
|
@ -42,80 +42,68 @@
|
|||
|
||||
|
||||
## DESCRIPTION
|
||||
yadm is a tool for managing a collection of files across multiple com-
|
||||
puters, using a shared Git repository. In addition, yadm provides a
|
||||
feature to select alternate versions of files for particular systems.
|
||||
Lastly, yadm supplies the ability to manage a subset of secure files,
|
||||
yadm is a tool for managing a collection of files across multiple com‐
|
||||
puters, using a shared Git repository. In addition, yadm provides a
|
||||
feature to select alternate versions of files for particular systems.
|
||||
Lastly, yadm supplies the ability to manage a subset of secure files,
|
||||
which are encrypted before they are included in the repository.
|
||||
|
||||
|
||||
## COMMANDS
|
||||
git-command or git-alias
|
||||
Any command not internally handled by yadm is passed through to
|
||||
git(1). Git commands or aliases are invoked with the yadm man-
|
||||
Any command not internally handled by yadm is passed through to
|
||||
git(1). Git commands or aliases are invoked with the yadm man‐
|
||||
aged repository. The working directory for Git commands will be
|
||||
the configured work-tree (usually $HOME).
|
||||
|
||||
Dotfiles are managed by using standard git commands; add, com-
|
||||
Dotfiles are managed by using standard git commands; add, com‐
|
||||
mit, push, pull, etc.
|
||||
|
||||
The config command is not passed directly through. Instead use
|
||||
The config command is not passed directly through. Instead use
|
||||
the gitconfig command (see below).
|
||||
|
||||
alt Create symbolic links and process templates for any managed
|
||||
files matching the naming rules described in the ALTERNATES and
|
||||
TEMPLATES sections. It is usually unnecessary to run this com-
|
||||
mand, as yadm automatically processes alternates by default.
|
||||
This automatic behavior can be disabled by setting the configu-
|
||||
alt Create symbolic links and process templates for any managed
|
||||
files matching the naming rules described in the ALTERNATES and
|
||||
TEMPLATES sections. It is usually unnecessary to run this com‐
|
||||
mand, as yadm automatically processes alternates by default.
|
||||
This automatic behavior can be disabled by setting the configu‐
|
||||
ration yadm.auto-alt to "false".
|
||||
|
||||
bootstrap
|
||||
Execute $HOME/.config/yadm/bootstrap if it exists.
|
||||
|
||||
clone url
|
||||
Clone a remote repository for tracking dotfiles. After the con-
|
||||
tents of the remote repository have been fetched, a "merge" of
|
||||
the remote HEAD branch is attempted. If there are conflicting
|
||||
files already present in the work-tree, this merge will fail and
|
||||
instead a "reset" of the remote HEAD branch will be done, fol-
|
||||
lowed by a "stash". This "stash" operation will preserve the
|
||||
original data.
|
||||
Clone a remote repository for tracking dotfiles. After the con‐
|
||||
tents of the remote repository have been fetched, a "check out"
|
||||
of the remote HEAD branch is attempted. If there are conflict‐
|
||||
ing files already present in the work-tree, the local version
|
||||
will be left unmodified and you'll have to review and resolve
|
||||
the difference.
|
||||
|
||||
You can review the stashed conflicts by running the command
|
||||
|
||||
yadm stash show -p
|
||||
|
||||
from within your $HOME directory. If you want to restore the
|
||||
stashed data, you can run
|
||||
|
||||
yadm stash apply
|
||||
or
|
||||
yadm stash pop
|
||||
|
||||
The repository is stored in $HOME/.local/share/yadm/repo.git.
|
||||
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. If you want
|
||||
to use a branch other than the remote HEAD branch you can spec-
|
||||
ify it using the -b option. By default yadm will ask the user
|
||||
if the bootstrap program should be run (if it exists). The
|
||||
options --bootstrap or --no-bootstrap will either force the
|
||||
bootstrap to be run, or prevent it from being run, without
|
||||
prompting the user.
|
||||
overridden with the -w option. yadm can be forced to overwrite
|
||||
an existing repository by providing the -f option. If you want
|
||||
to use a branch other than the remote HEAD branch you can spec‐
|
||||
ify it using the -b option. By default yadm will ask the user
|
||||
if the bootstrap program should be run (if it exists). The op‐
|
||||
tions --bootstrap or --no-bootstrap will either force the boot‐
|
||||
strap to be run, or prevent it from being run, without prompting
|
||||
the user.
|
||||
|
||||
config This command manages configurations for yadm. This command
|
||||
config This command manages configurations for yadm. This command
|
||||
works exactly the way git-config(1) does. See the CONFIGURATION
|
||||
section for more details.
|
||||
|
||||
decrypt
|
||||
Decrypt all files stored in $HOME/.local/share/yadm/archive.
|
||||
Files decrypted will be relative to the configured work-tree
|
||||
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.
|
||||
|
||||
encrypt
|
||||
Encrypt all files matching the patterns found in $HOME/.con-
|
||||
fig/yadm/encrypt. See the ENCRYPTION section for more details.
|
||||
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
|
||||
|
@ -128,7 +116,7 @@
|
|||
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
|
||||
|
@ -142,9 +130,9 @@
|
|||
With this config, use (magit-status "/yadm::").
|
||||
|
||||
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.
|
||||
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-
|
||||
|
@ -152,13 +140,13 @@
|
|||
|
||||
gitconfig
|
||||
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-
|
||||
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.
|
||||
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-
|
||||
show untracked files and directories), you can remove this con‐
|
||||
figuration.
|
||||
|
||||
yadm gitconfig --unset status.showUntrackedFiles
|
||||
|
@ -181,15 +169,15 @@
|
|||
support command line completion.
|
||||
|
||||
perms Update permissions as described in the PERMISSIONS section. It
|
||||
is usually unnecessary to run this command, as yadm automati-
|
||||
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
|
||||
options directly to transcrypt, with the environment configured
|
||||
to use the yadm repository.
|
||||
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
|
||||
|
@ -198,7 +186,7 @@
|
|||
upgrade
|
||||
Version 3 of yadm uses a different directory for storing data.
|
||||
When you start to use version 3 for the first time, you may see
|
||||
warnings about moving your data to this new directory. The eas-
|
||||
warnings about moving your data to this new directory. The eas‐
|
||||
iest way to accomplish this is by running "yadm upgrade". This
|
||||
command will start by moving your yadm repo to the new path.
|
||||
Next it will move any archive data. If the archive is tracked
|
||||
|
@ -206,18 +194,18 @@
|
|||
that file in the repo's index.
|
||||
|
||||
Upgrading will attempt to de-initialize and re-initialize your
|
||||
submodules. If 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 submod-
|
||||
ules, you can use the -f option with the "upgrade" command to
|
||||
force the de-initialization.
|
||||
submodules. If your submodules cannot be de-initialized, the up‐
|
||||
grade 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 -f option with the "upgrade" command to force
|
||||
the de-initialization.
|
||||
|
||||
After running "yadm upgrade", you should run "yadm status" to
|
||||
review changes which have been staged, and commit them to your
|
||||
repository.
|
||||
|
||||
You can read https://yadm.io/docs/upgrade_from_2 for more infor-
|
||||
You can read https://yadm.io/docs/upgrade_from_2 for more infor‐
|
||||
mation.
|
||||
|
||||
version
|
||||
|
@ -226,7 +214,7 @@
|
|||
|
||||
## OPTIONS
|
||||
yadm supports a set of universal options that alter the paths it uses.
|
||||
The default paths are documented in the FILES section. Any path speci-
|
||||
The default paths are documented in the FILES section. Any path speci‐
|
||||
fied by these options must be fully qualified. If you always want to
|
||||
override one or more of these paths, it may be useful to create an
|
||||
alias for the yadm command. For example, the following alias could be
|
||||
|
@ -235,7 +223,7 @@
|
|||
alias yadm='yadm --yadm-repo /alternate/path/to/repo'
|
||||
|
||||
The following is the full list of universal options. Each option
|
||||
should be followed by a fully qualified path.
|
||||
should be followed by a path.
|
||||
|
||||
-Y,--yadm-dir
|
||||
Override the yadm directory. yadm stores its configurations
|
||||
|
@ -273,24 +261,24 @@
|
|||
The following is the full list of supported configurations:
|
||||
|
||||
yadm.alt-copy
|
||||
If set to "true", alternate files will be copies instead of sym-
|
||||
If set to "true", alternate files will be copies instead of sym‐
|
||||
bolic links. This might be desirable, because some systems may
|
||||
not properly support symlinks.
|
||||
|
||||
yadm.auto-alt
|
||||
Disable the automatic linking described in the section ALTER-
|
||||
Disable the automatic linking described in the section ALTER‐
|
||||
NATES. If disabled, you may still run "yadm alt" manually to
|
||||
create the alternate links. This feature is enabled by default.
|
||||
create the alternate links. This feature is enabled by default.
|
||||
|
||||
yadm.auto-exclude
|
||||
Disable the automatic exclusion of patterns defined in
|
||||
Disable the automatic exclusion of patterns defined in
|
||||
$HOME/.config/yadm/encrypt. This feature is enabled by default.
|
||||
|
||||
yadm.auto-perms
|
||||
Disable the automatic permission changes described in the sec-
|
||||
Disable the automatic permission changes described in the sec‐
|
||||
tion PERMISSIONS. If disabled, you may still run yadm perms
|
||||
manually to update permissions. This feature is enabled by
|
||||
default.
|
||||
manually to update permissions. This feature is enabled by de‐
|
||||
fault.
|
||||
|
||||
yadm.auto-private-dirs
|
||||
Disable the automatic creating of private directories described
|
||||
|
@ -299,20 +287,20 @@
|
|||
yadm.cipher
|
||||
Configure which encryption system is used by the encrypt/decrypt
|
||||
commands. Valid options are "gpg" and "openssl". The default is
|
||||
"gpg". Detailed information can be found in the section ENCRYP-
|
||||
"gpg". Detailed information can be found in the section ENCRYP‐
|
||||
TION.
|
||||
|
||||
yadm.git-program
|
||||
Specify an alternate program to use instead of "git". By
|
||||
default, the first "git" found in $PATH is used.
|
||||
Specify an alternate program to use instead of "git". By de‐
|
||||
fault, the first "git" found in $PATH is used.
|
||||
|
||||
yadm.gpg-perms
|
||||
Disable the permission changes to $HOME/.gnupg/*. This feature
|
||||
is enabled by default.
|
||||
|
||||
yadm.gpg-program
|
||||
Specify an alternate program to use instead of "gpg". By
|
||||
default, the first "gpg" found in $PATH is used.
|
||||
Specify an alternate program to use instead of "gpg". By de‐
|
||||
fault, the first "gpg" found in $PATH is used.
|
||||
|
||||
yadm.gpg-recipient
|
||||
Asymmetrically encrypt files with a gpg public/private key pair.
|
||||
|
@ -320,9 +308,9 @@
|
|||
The key must exist in your public keyrings. Multiple recipients
|
||||
can be specified (separated by space). If left blank or not
|
||||
provided, symmetric encryption is used instead. If set to
|
||||
"ASK", gpg will interactively ask for recipients. See the
|
||||
ENCRYPTION section for more details. This feature is disabled
|
||||
by default.
|
||||
"ASK", gpg will interactively ask for recipients. See the EN‐
|
||||
CRYPTION section for more details. This feature is disabled by
|
||||
default.
|
||||
|
||||
yadm.openssl-ciphername
|
||||
Specify which cipher should be used by openssl. "aes-256-cbc"
|
||||
|
@ -343,13 +331,20 @@
|
|||
Disable the permission changes to $HOME/.ssh/*. This feature is
|
||||
enabled by default.
|
||||
|
||||
The following four "local" configurations are not stored in the
|
||||
The following five "local" configurations are not stored in the
|
||||
$HOME/.config/yadm/config, they are stored in the local repository.
|
||||
|
||||
|
||||
local.class
|
||||
Specify a class for the purpose of symlinking alternate files.
|
||||
By default, no class will be matched.
|
||||
By default, no class will be matched. The local host can be as‐
|
||||
signed multiple classes using command:
|
||||
|
||||
yadm config --add local.class <additional-class>
|
||||
|
||||
local.arch
|
||||
Override the architecture for the purpose of symlinking alter‐
|
||||
nate files.
|
||||
|
||||
local.hostname
|
||||
Override the hostname for the purpose of symlinking alternate
|
||||
|
@ -367,52 +362,60 @@
|
|||
to have an automated way of choosing an alternate version of a file for
|
||||
a different operating system, host, user, etc.
|
||||
|
||||
yadm will automatically create a symbolic link to the appropriate ver-
|
||||
sion of a file, when a valid suffix is appended to the filename. The
|
||||
suffix contains the conditions that must be met for that file to be
|
||||
yadm will automatically create a symbolic link to the appropriate ver‐
|
||||
sion of a file, when a valid suffix is appended to the filename. The
|
||||
suffix contains the conditions that must be met for that file to be
|
||||
used.
|
||||
|
||||
The suffix begins with "##", followed by any number of conditions sepa-
|
||||
The suffix begins with "##", followed by any number of conditions sepa‐
|
||||
rated by commas.
|
||||
|
||||
##<condition>[,<condition>,...]
|
||||
|
||||
Each condition is an attribute/value pair, separated by a period. Some
|
||||
conditions do not require a "value", and in that case, the period and
|
||||
value can be omitted. Most attributes can be abbreviated as a single
|
||||
Each condition is an attribute/value pair, separated by a period. Some
|
||||
conditions do not require a "value", and in that case, the period and
|
||||
value can be omitted. Most attributes can be abbreviated as a single
|
||||
letter.
|
||||
|
||||
<attribute>[.<value>]
|
||||
|
||||
These are the supported attributes, in the order of the weighted prece-
|
||||
These are the supported attributes, in the order of the weighted prece‐
|
||||
dence:
|
||||
|
||||
|
||||
template, t
|
||||
Valid when the value matches a supported template processor.
|
||||
Valid when the value matches a supported template processor.
|
||||
See the TEMPLATES section for more details.
|
||||
|
||||
user, u
|
||||
Valid if the value matches the current user. Current user is
|
||||
Valid if the value matches the current user. Current user is
|
||||
calculated by running id -u -n.
|
||||
|
||||
distro, d
|
||||
Valid if the value matches the distro. Distro is calculated by
|
||||
running lsb_release -si or by inspecting the ID from /etc/os-
|
||||
release.
|
||||
|
||||
os, o Valid if the value matches the OS. OS is calculated by running
|
||||
uname -s.
|
||||
hostname, h
|
||||
Valid if the value matches the short hostname. Hostname is cal‐
|
||||
culated by running uname -n, and trimming off any domain.
|
||||
|
||||
class, c
|
||||
Valid if the value matches the local.class configuration. Class
|
||||
must be manually set using yadm config local.class <class>. See
|
||||
the CONFIGURATION section for more details about setting
|
||||
local.class.
|
||||
the CONFIGURATION section for more details about setting lo‐
|
||||
cal.class.
|
||||
|
||||
hostname, h
|
||||
Valid if the value matches the short hostname. Hostname is cal-
|
||||
culated by running uname -n, and trimming off any domain.
|
||||
distro, d
|
||||
Valid if the value matches the distro. Distro is calculated by
|
||||
running lsb_release -si or by inspecting the ID from /etc/os-re‐
|
||||
lease.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
calculated by running uname -m.
|
||||
|
||||
default
|
||||
Valid when no other alternate is valid.
|
||||
|
@ -420,31 +423,30 @@
|
|||
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
|
||||
in scoring. Files with more conditions will always be favored. Any
|
||||
invalid condition will disqualify that file completely.
|
||||
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
|
||||
|
@ -459,7 +461,7 @@
|
|||
If running on a Macbook named "host2", yadm will create a symbolic link
|
||||
which looks like this:
|
||||
|
||||
$HOME/path/example.txt -> $HOME/path/example.txt##os.Darwin,host-
|
||||
$HOME/path/example.txt -> $HOME/path/example.txt##os.Darwin,host‐
|
||||
name.host2
|
||||
|
||||
However, on another Mackbook named "host3", yadm will create a symbolic
|
||||
|
@ -467,7 +469,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,81 +484,84 @@
|
|||
|
||||
$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.
|
||||
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).
|
||||
|
||||
yadm will automatically create these links by default. This can be dis-
|
||||
abled using the yadm.auto-alt configuration. Even if disabled, links
|
||||
yadm will automatically create these links by default. This can be dis‐
|
||||
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 os, hostname, and user can be manually over-
|
||||
ridden using the configuration options local.os, local.hostname, and
|
||||
local.user.
|
||||
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.
|
||||
|
||||
|
||||
## TEMPLATES
|
||||
If a template condition is defined in an alternate file's "##" suffix,
|
||||
If a template condition is defined in an alternate file's "##" suffix,
|
||||
and the necessary dependencies for the template are available, then the
|
||||
file will be processed to create or overwrite files.
|
||||
|
||||
Supported template processors:
|
||||
|
||||
default
|
||||
This is yadm's built-in template processor. This processor is
|
||||
very basic, with a Jinja-like syntax. The advantage of this pro-
|
||||
cessor is that it only depends upon awk, which is available on
|
||||
most *nix systems. To use this processor, specify the value of
|
||||
This is yadm's built-in template processor. This processor is
|
||||
very basic, with a Jinja-like syntax. The advantage of this pro‐
|
||||
cessor is that it only depends upon 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").
|
||||
|
||||
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
|
||||
allows executing shell commands within templates. This can be
|
||||
used to reference your own configurations within templates, for
|
||||
example:
|
||||
|
||||
<% yadm config mysection.myconfig %>
|
||||
|
||||
To use the ESH template processor, specify the value of "esh"
|
||||
|
||||
j2cli To use the j2cli Jinja template processor, specify the value of
|
||||
j2cli To use the j2cli Jinja template processor, specify the value of
|
||||
"j2" or "j2cli".
|
||||
|
||||
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.
|
||||
|
||||
NOTE: Specifying "j2" as the processor will attempt to use j2cli or
|
||||
envtpl, whichever is available.
|
||||
|
||||
If the template processor specified is available, templates will be
|
||||
If the template processor specified is available, templates will be
|
||||
processed to create or overwrite files.
|
||||
|
||||
During processing, the following variables are available in the tem-
|
||||
During processing, the following variables are available in the tem‐
|
||||
plate:
|
||||
|
||||
Default Jinja or ESH Description
|
||||
------------- ------------- --------------------------
|
||||
yadm.class YADM_CLASS Locally defined yadm class
|
||||
yadm.distro YADM_DISTRO lsb_release -si
|
||||
yadm.hostname YADM_HOSTNAME uname -n (without domain)
|
||||
yadm.os YADM_OS uname -s
|
||||
yadm.user YADM_USER id -u -n
|
||||
yadm.source YADM_SOURCE Template filename
|
||||
Default Jinja or ESH Description
|
||||
------------- ------------- ----------------------------
|
||||
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_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.source YADM_SOURCE Template filename
|
||||
yadm.user YADM_USER id -u -n
|
||||
env.VAR Environment variable VAR
|
||||
|
||||
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".
|
||||
|
||||
NOTE: If lsb_release is not available, DISTRO will be the ID specified
|
||||
NOTE: If lsb_release is not available, DISTRO will be the ID specified
|
||||
in /etc/os-release.
|
||||
|
||||
Examples:
|
||||
|
@ -570,7 +575,7 @@
|
|||
{% include "whatever.extra" %}
|
||||
{% endif %}
|
||||
|
||||
would output a file named whatever with the following content if the
|
||||
would output a file named whatever with the following content if the
|
||||
user is "harvey":
|
||||
|
||||
config=work-Linux
|
||||
|
@ -580,7 +585,7 @@
|
|||
config=dev-whatever
|
||||
admin=false
|
||||
|
||||
An equivalent Jinja template named whatever##template.j2 would look
|
||||
An equivalent Jinja template named whatever##template.j2 would look
|
||||
like:
|
||||
|
||||
{% if YADM_USER == 'harvey' -%}
|
||||
|
@ -590,7 +595,7 @@
|
|||
{% include 'whatever.extra' %}
|
||||
{% endif -%}
|
||||
|
||||
An equivalent ESH templated named whatever##template.esh would look
|
||||
An equivalent ESH templated named whatever##template.esh would look
|
||||
like:
|
||||
|
||||
<% if [ "$YADM_USER" = "harvey" ]; then -%>
|
||||
|
@ -602,55 +607,55 @@
|
|||
|
||||
|
||||
## ENCRYPTION
|
||||
It can be useful to manage confidential files, like SSH or GPG keys,
|
||||
across multiple systems. However, doing so would put plain text data
|
||||
It can be useful to manage confidential files, like SSH or GPG keys,
|
||||
across multiple systems. However, doing so would put plain text data
|
||||
into a Git repository, which often resides on a public system. yadm can
|
||||
make it easy to encrypt and decrypt a set of files so the encrypted
|
||||
version can be maintained in the Git repository. This feature will
|
||||
make it easy to encrypt and decrypt a set of files so the encrypted
|
||||
version can be maintained in the Git repository. This feature will
|
||||
only work if a supported tool is available. Both gpg(1) and openssl(1)
|
||||
are supported. gpg is used by default, but openssl can be configured
|
||||
with the yadm.cypher configuration.
|
||||
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 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:
|
||||
|
||||
.ssh/*.key
|
||||
.gnupg/*.gpg
|
||||
|
||||
Standard filename expansions (*, ?, [) are supported. If you have Bash
|
||||
version 4, you may use "**" to match all subdirectories. Other shell
|
||||
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
|
||||
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.
|
||||
|
||||
The 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 $HOME/.local/share/yadm/archive.
|
||||
The "encrypt" and "archive" files should be added to the yadm reposi-
|
||||
prompt for a password. Once a password has confirmed, the matching
|
||||
files will be encrypted and saved as $HOME/.local/share/yadm/archive.
|
||||
The "encrypt" and "archive" files should be added to the yadm reposi‐
|
||||
tory so they are available across multiple systems.
|
||||
|
||||
To decrypt these files later, or on another system run yadm decrypt and
|
||||
provide the correct password. After files are decrypted, permissions
|
||||
provide the correct password. After files are decrypted, permissions
|
||||
are automatically updated as described in the PERMISSIONS section.
|
||||
|
||||
Symmetric encryption is used by default, but asymmetric encryption may
|
||||
Symmetric encryption is used by default, but asymmetric encryption may
|
||||
be enabled using the yadm.gpg-recipient configuration.
|
||||
|
||||
NOTE: It is recommended that you use a private repository when keeping
|
||||
NOTE: It is recommended that you use a private repository when keeping
|
||||
confidential files, even though they are encrypted.
|
||||
|
||||
Patterns found in $HOME/.config/yadm/encrypt are automatically added to
|
||||
the repository's info/exclude file every time yadm encrypt is run.
|
||||
This is to prevent accidentally committing sensitive data to the repos-
|
||||
the repository's info/exclude file every time yadm encrypt is run.
|
||||
This is to prevent accidentally committing sensitive data to the repos‐
|
||||
itory. This can be disabled using the yadm.auto-exclude configuration.
|
||||
|
||||
Using transcrypt or git-crypt
|
||||
|
||||
A completely separate option for encrypting data is to install and use
|
||||
transcrypt or git-crypt. Once installed, you can use these tools by
|
||||
running yadm transcrypt or yadm git-crypt. These tools enables trans-
|
||||
running yadm transcrypt or yadm git-crypt. These tools enables trans‐
|
||||
parent encryption and decryption of files in a git repository. See the
|
||||
following web sites for more information:
|
||||
|
||||
|
@ -658,10 +663,8 @@
|
|||
|
||||
- https://github.com/AGWA/git-crypt
|
||||
|
||||
|
||||
|
||||
## PERMISSIONS
|
||||
When files are checked out of a Git repository, their initial permis-
|
||||
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
|
||||
automatically update the permissions of some file paths. The "group"
|
||||
and "others" permissions will be removed from the following files:
|
||||
|
@ -674,11 +677,11 @@
|
|||
|
||||
- The GPG directory and files, .gnupg/*
|
||||
|
||||
yadm will automatically update permissions by default. This can be dis-
|
||||
abled using the yadm.auto-perms configuration. Even if disabled, per-
|
||||
missions can be manually updated by running yadm perms. The .ssh
|
||||
directory processing can be disabled using the yadm.ssh-perms configu-
|
||||
ration. The .gnupg directory processing can be disabled using the
|
||||
yadm will automatically update permissions by default. This can be dis‐
|
||||
abled using the yadm.auto-perms configuration. Even if disabled, per‐
|
||||
missions can be manually updated by running yadm perms. The .ssh di‐
|
||||
rectory processing can be disabled using the yadm.ssh-perms configura‐
|
||||
tion. The .gnupg directory processing can be disabled using the
|
||||
yadm.gpg-perms configuration.
|
||||
|
||||
When cloning a repo which includes data in a .ssh or .gnupg directory,
|
||||
|
@ -688,18 +691,18 @@
|
|||
|
||||
When running a Git command and .ssh or .gnupg directories do not exist,
|
||||
yadm will create those directories with mask 0700 prior to running the
|
||||
Git command. This can be disabled using the yadm.auto-private-dirs con-
|
||||
Git command. This can be disabled using the yadm.auto-private-dirs con‐
|
||||
figuration.
|
||||
|
||||
|
||||
## HOOKS
|
||||
For every command yadm supports, a program can be provided to run
|
||||
before or after that command. These are referred to as "hooks". yadm
|
||||
For every command yadm supports, a program can be provided to run be‐
|
||||
fore or after that command. These are referred to as "hooks". yadm
|
||||
looks for hooks in the directory $HOME/.config/yadm/hooks. Each hook
|
||||
is named using a prefix of pre_ or post_, followed by the command which
|
||||
should trigger the hook. For example, to create a hook which is run
|
||||
after every yadm pull command, create a hook named post_pull. Hooks
|
||||
must have the executable file permission set.
|
||||
should trigger the hook. For example, to create a hook which is run af‐
|
||||
ter every yadm pull command, create a hook named post_pull. Hooks must
|
||||
have the executable file permission set.
|
||||
|
||||
If a pre_ hook is defined, and the hook terminates with a non-zero exit
|
||||
status, yadm will refuse to run the yadm command. For example, if a
|
||||
|
@ -730,15 +733,15 @@
|
|||
|
||||
## FILES
|
||||
All of yadm's configurations are relative to the "yadm directory".
|
||||
yadm uses the "XDG Base Directory Specification" to determine this
|
||||
directory. If the environment variable $XDG_CONFIG_HOME is defined as
|
||||
a fully qualified path, this directory will be $XDG_CONFIG_HOME/yadm.
|
||||
yadm uses the "XDG Base Directory Specification" to determine this di‐
|
||||
rectory. If the environment variable $XDG_CONFIG_HOME is defined as a
|
||||
fully qualified path, this directory will be $XDG_CONFIG_HOME/yadm.
|
||||
Otherwise it will be $HOME/.config/yadm.
|
||||
|
||||
Similarly, yadm's data files are relative to the "yadm data directory".
|
||||
yadm uses the "XDG Base Directory Specification" to determine this
|
||||
directory. If the environment variable $XDG_DATA_HOME is defined as a
|
||||
fully qualified path, this directory will be $XDG_DATA_HOME/yadm. Oth-
|
||||
yadm uses the "XDG Base Directory Specification" to determine this di‐
|
||||
rectory. If the environment variable $XDG_DATA_HOME is defined as a
|
||||
fully qualified path, this directory will be $XDG_DATA_HOME/yadm. Oth‐
|
||||
erwise it will be $HOME/.local/share/yadm.
|
||||
|
||||
The following are the default paths yadm uses for its own data. Most
|
||||
|
@ -746,7 +749,7 @@
|
|||
section for details.
|
||||
|
||||
$HOME/.config/yadm
|
||||
The yadm directory. By default, all configs yadm stores is rela-
|
||||
The yadm directory. By default, all configs yadm stores is rela‐
|
||||
tive to this directory.
|
||||
|
||||
$HOME/.local/share/yadm
|
||||
|
@ -758,7 +761,7 @@
|
|||
|
||||
$YADM_DIR/alt
|
||||
This is a directory to keep "alternate files" without having
|
||||
them side-by-side with the resulting symlink or processed tem-
|
||||
them side-by-side with the resulting symlink or processed tem‐
|
||||
plate. Alternate files placed in this directory will be created
|
||||
relative to $HOME instead.
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
%{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}-%{version}}
|
||||
Name: yadm
|
||||
Summary: Yet Another Dotfiles Manager
|
||||
Version: 3.0.1
|
||||
Version: 3.2.2
|
||||
Group: Development/Tools
|
||||
Release: 1%{?dist}
|
||||
URL: https://yadm.io
|
||||
|
|
Loading…
Add table
Reference in a new issue