Compare commits
No commits in common. "master" and "3.0.2" have entirely different histories.
82 changed files with 2809 additions and 3263 deletions
2
.flake8
2
.flake8
|
@ -1,2 +0,0 @@
|
||||||
[flake8]
|
|
||||||
max-line-length = 120
|
|
35
.github/CONTRIBUTING.md
vendored
35
.github/CONTRIBUTING.md
vendored
|
@ -1,7 +1,7 @@
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
Thank you for considering contributing to **yadm**. We develop this project in
|
Thank you for considering contributing to **yadm**. I develop this project in my
|
||||||
our limited spare time, so help is very appreciated.
|
limited spare time, so help is very appreciated.
|
||||||
|
|
||||||
All contributors must follow our [Code of Conduct][conduct]. Please make sure
|
All contributors must follow our [Code of Conduct][conduct]. Please make sure
|
||||||
you are welcoming and friendly during your interactions, and report any
|
you are welcoming and friendly during your interactions, and report any
|
||||||
|
@ -17,8 +17,7 @@ To contribute, you can:
|
||||||
* Star the yadm repo, the star count helps others discover yadm.
|
* Star the yadm repo, the star count helps others discover yadm.
|
||||||
* Report [bugs](#reporting-a-bug)
|
* Report [bugs](#reporting-a-bug)
|
||||||
* Request [features/enhancements](#suggesting-a-feature-or-enhancement)
|
* Request [features/enhancements](#suggesting-a-feature-or-enhancement)
|
||||||
* Contribute changes to [code, tests](#contributing-code), and
|
* Contribute changes to [code, tests](#contributing-code), and [documentation](#improving-documentation)
|
||||||
[documentation](#improving-documentation)
|
|
||||||
* Maintain installation [packages](#maintaining-packages)
|
* Maintain installation [packages](#maintaining-packages)
|
||||||
* Help other users by [answering support questions](#answering-support-questions)
|
* Help other users by [answering support questions](#answering-support-questions)
|
||||||
|
|
||||||
|
@ -71,14 +70,14 @@ Consider trying to reproduce the bug inside a docker container using the
|
||||||
[yadm/testbed][] docker image. Doing so will greatly increase the likelihood of
|
[yadm/testbed][] docker image. Doing so will greatly increase the likelihood of
|
||||||
the problem being fixed.
|
the problem being fixed.
|
||||||
|
|
||||||
The easiest way to start this container, is to clone the [yadm
|
The easiest way to start this container, is to clone the [TheLocehiliosan/yadm
|
||||||
repo][yadm-repo], and use the `scripthost` make target. _(You will need `make`
|
repo][yadm-repo], and use the `scripthost` make target. _(You will need `make`
|
||||||
and `docker` installed.)_
|
and `docker` installed.)_
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
$ git clone https://github.com/yadm-dev/yadm.git
|
$ git clone https://github.com/TheLocehiliosan/yadm.git
|
||||||
$ cd yadm
|
$ cd yadm
|
||||||
$ make scripthost version=1.12.0
|
$ make scripthost version=1.12.0
|
||||||
Starting scripthost version="1.12.0" (recording script)
|
Starting scripthost version="1.12.0" (recording script)
|
||||||
|
@ -204,7 +203,7 @@ these principles when making changes.
|
||||||
3. Add the official repository (`upstream`) as a remote repository.
|
3. Add the official repository (`upstream`) as a remote repository.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
$ git remote add upstream https://github.com/yadm-dev/yadm.git
|
$ git remote add upstream https://github.com/TheLocehiliosan/yadm.git
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Verify you can run the test harness. _(This will require dependencies:
|
4. Verify you can run the test harness. _(This will require dependencies:
|
||||||
|
@ -213,11 +212,6 @@ these principles when making changes.
|
||||||
```text
|
```text
|
||||||
$ make test
|
$ 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.
|
5. Create a feature branch, based off the `develop` branch.
|
||||||
|
|
||||||
|
@ -362,24 +356,25 @@ see if you can help.
|
||||||
[attach-help]: https://help.github.com/en/articles/file-attachments-on-issues-and-pull-requests
|
[attach-help]: https://help.github.com/en/articles/file-attachments-on-issues-and-pull-requests
|
||||||
[commit-style]: https://chris.beams.io/posts/git-commit/#seven-rules
|
[commit-style]: https://chris.beams.io/posts/git-commit/#seven-rules
|
||||||
[conduct]: CODE_OF_CONDUCT.md
|
[conduct]: CODE_OF_CONDUCT.md
|
||||||
[contrib-hooks]: https://github.com/yadm-dev/yadm/tree/master/contrib/hooks
|
[contrib-hooks]: https://github.com/TheLocehiliosan/yadm/tree/master/contrib/hooks
|
||||||
[flake8]: https://pypi.org/project/flake8/
|
[flake8]: https://pypi.org/project/flake8/
|
||||||
[groff-man]: https://www.gnu.org/software/groff/manual/html_node/man.html
|
[groff-man]: https://www.gnu.org/software/groff/manual/html_node/man.html
|
||||||
[hooks-help]: https://github.com/yadm-dev/yadm/blob/master/yadm.md#hooks
|
[hooks-help]: https://github.com/TheLocehiliosan/yadm/blob/master/yadm.md#hooks
|
||||||
[html-proofer]: https://github.com/gjtorikian/html-proofer
|
[html-proofer]: https://github.com/gjtorikian/html-proofer
|
||||||
[jekyll]: https://jekyllrb.com
|
[jekyll]: https://jekyllrb.com
|
||||||
[new-bug]: https://github.com/yadm-dev/yadm/issues/new?template=BUG_REPORT.md
|
[new-bug]: https://github.com/TheLocehiliosan/yadm/issues/new?template=BUG_REPORT.md
|
||||||
[new-feature]: https://github.com/yadm-dev/yadm/issues/new?template=FEATURE_REQUEST.md
|
[new-feature]: https://github.com/TheLocehiliosan/yadm/issues/new?template=FEATURE_REQUEST.md
|
||||||
[open-issues]: https://github.com/yadm-dev/yadm/issues
|
[open-issues]: https://github.com/TheLocehiliosan/yadm/issues
|
||||||
[pr-help]: https://help.github.com/en/articles/creating-a-pull-request-from-a-fork
|
[pr-help]: https://help.github.com/en/articles/creating-a-pull-request-from-a-fork
|
||||||
[pylint]: https://pylint.org/
|
[pylint]: https://pylint.org/
|
||||||
[pytest]: https://pytest.org/
|
[pytest]: https://pytest.org/
|
||||||
[questions]: https://github.com/yadm-dev/yadm/labels/question
|
[questions]: https://github.com/TheLocehiliosan/yadm/labels/question
|
||||||
|
[refactor]: https://github.com/TheLocehiliosan/yadm/issues/146
|
||||||
[shellcheck]: https://www.shellcheck.net
|
[shellcheck]: https://www.shellcheck.net
|
||||||
[signing-commits]: https://help.github.com/en/articles/signing-commits
|
[signing-commits]: https://help.github.com/en/articles/signing-commits
|
||||||
[tpope-style]: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
[tpope-style]: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
||||||
[yadm-man]: https://github.com/yadm-dev/yadm/blob/master/yadm.md
|
[yadm-man]: https://github.com/TheLocehiliosan/yadm/blob/master/yadm.md
|
||||||
[yadm-repo]: https://github.com/yadm-dev/yadm
|
[yadm-repo]: https://github.com/TheLocehiliosan/yadm
|
||||||
[yadm/jekyll]: https://hub.docker.com/r/yadm/jekyll
|
[yadm/jekyll]: https://hub.docker.com/r/yadm/jekyll
|
||||||
[yadm/testbed]: https://hub.docker.com/r/yadm/testbed
|
[yadm/testbed]: https://hub.docker.com/r/yadm/testbed
|
||||||
[yamllint]: https://github.com/adrienverge/yamllint
|
[yamllint]: https://github.com/adrienverge/yamllint
|
||||||
|
|
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
|
@ -1,6 +1,6 @@
|
||||||
<!--
|
<!--
|
||||||
Before submitting, please search open and closed issues at
|
Before submitting, please search open and closed issues at
|
||||||
https://github.com/yadm-dev/yadm/issues to avoid duplication.
|
https://github.com/TheLocehiliosan/yadm/issues to avoid duplication.
|
||||||
|
|
||||||
If you have found a security vulnerability, do NOT open an issue.
|
If you have found a security vulnerability, do NOT open an issue.
|
||||||
Email yadm@yadm.io instead.
|
Email yadm@yadm.io instead.
|
||||||
|
|
6
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
6
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
|
@ -8,7 +8,7 @@ assignees: ''
|
||||||
---
|
---
|
||||||
<!--
|
<!--
|
||||||
Before submitting, please search open and closed issues at
|
Before submitting, please search open and closed issues at
|
||||||
https://github.com/yadm-dev/yadm/issues to avoid duplication.
|
https://github.com/TheLocehiliosan/yadm/issues to avoid duplication.
|
||||||
|
|
||||||
If you have found a security vulnerability, do NOT open an issue.
|
If you have found a security vulnerability, do NOT open an issue.
|
||||||
Email yadm@yadm.io instead.
|
Email yadm@yadm.io instead.
|
||||||
|
@ -25,10 +25,10 @@ Can this be reproduced with the yadm/testbed docker image: [Yes/No]
|
||||||
Consider trying to reproduce the bug inside a docker container using the
|
Consider trying to reproduce the bug inside a docker container using the
|
||||||
yadm/testbed docker image. https://hub.docker.com/r/yadm/testbed
|
yadm/testbed docker image. https://hub.docker.com/r/yadm/testbed
|
||||||
|
|
||||||
The easiest way to start this container, is to clone the yadm-dev/yadm
|
The easiest way to start this container, is to clone the TheLocehiliosan/yadm
|
||||||
repo, and use the "scripthost" make target. For example:
|
repo, and use the "scripthost" make target. For example:
|
||||||
|
|
||||||
$ git clone https://github.com/yadm-dev/yadm.git
|
$ git clone https://github.com/TheLocehiliosan/yadm.git
|
||||||
$ cd yadm
|
$ cd yadm
|
||||||
$ make scripthost version=1.11.0
|
$ make scripthost version=1.11.0
|
||||||
Starting scripthost version="1.11.0" (recording script)
|
Starting scripthost version="1.11.0" (recording script)
|
||||||
|
|
2
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
vendored
2
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
vendored
|
@ -8,7 +8,7 @@ assignees: ''
|
||||||
---
|
---
|
||||||
<!--
|
<!--
|
||||||
Before submitting, please search open and closed issues at
|
Before submitting, please search open and closed issues at
|
||||||
https://github.com/yadm-dev/yadm/issues to avoid duplication.
|
https://github.com/TheLocehiliosan/yadm/issues to avoid duplication.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
### Is your feature request related to a problem? Please describe.
|
### Is your feature request related to a problem? Please describe.
|
||||||
|
|
2
.github/ISSUE_TEMPLATE/OTHER.md
vendored
2
.github/ISSUE_TEMPLATE/OTHER.md
vendored
|
@ -8,7 +8,7 @@ assignees: ''
|
||||||
---
|
---
|
||||||
<!--
|
<!--
|
||||||
Before submitting, please search open and closed issues at
|
Before submitting, please search open and closed issues at
|
||||||
https://github.com/yadm-dev/yadm/issues to avoid duplication.
|
https://github.com/TheLocehiliosan/yadm/issues to avoid duplication.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
### This issue is about
|
### This issue is about
|
||||||
|
|
4
.github/ISSUE_TEMPLATE/SUPPORT.md
vendored
4
.github/ISSUE_TEMPLATE/SUPPORT.md
vendored
|
@ -8,11 +8,11 @@ assignees: ''
|
||||||
---
|
---
|
||||||
<!--
|
<!--
|
||||||
Before submitting, please search open and closed issues at
|
Before submitting, please search open and closed issues at
|
||||||
https://github.com/yadm-dev/yadm/issues to avoid duplication.
|
https://github.com/TheLocehiliosan/yadm/issues to avoid duplication.
|
||||||
|
|
||||||
Please also consult:
|
Please also consult:
|
||||||
* The FAQ: https://yadm.io/docs/faq
|
* The FAQ: https://yadm.io/docs/faq
|
||||||
* The manual: https://github.com/yadm-dev/yadm/blob/master/yadm.md
|
* The manual: https://github.com/TheLocehiliosan/yadm/blob/master/yadm.md
|
||||||
-->
|
-->
|
||||||
|
|
||||||
### This question is about
|
### This question is about
|
||||||
|
|
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -29,6 +29,6 @@ Be sure to preface the issue/PR numbers with a "#".
|
||||||
|
|
||||||
Please review [yadm's Contributing Guide][3] for best practices.
|
Please review [yadm's Contributing Guide][3] for best practices.
|
||||||
|
|
||||||
[1]: https://github.com/yadm-dev/yadm/blob/master/.github/CONTRIBUTING.md#test-conventions
|
[1]: https://github.com/TheLocehiliosan/yadm/blob/master/.github/CONTRIBUTING.md#test-conventions
|
||||||
[2]: https://help.github.com/en/articles/signing-commits
|
[2]: https://help.github.com/en/articles/signing-commits
|
||||||
[3]: https://github.com/yadm-dev/yadm/blob/master/.github/CONTRIBUTING.md
|
[3]: https://github.com/TheLocehiliosan/yadm/blob/master/.github/CONTRIBUTING.md
|
||||||
|
|
2
.github/workflows/schedule.yml
vendored
2
.github/workflows/schedule.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
||||||
docker create -t
|
docker create -t
|
||||||
--name yadm-website
|
--name yadm-website
|
||||||
--entrypoint test/validate
|
--entrypoint test/validate
|
||||||
yadm/jekyll:2024-10-31;
|
yadm/jekyll:2019-10-17;
|
||||||
docker cp ./ yadm-website:/srv/jekyll
|
docker cp ./ yadm-website:/srv/jekyll
|
||||||
- name: Test Site
|
- name: Test Site
|
||||||
run: docker start yadm-website -a
|
run: docker start yadm-website -a
|
||||||
|
|
25
.github/workflows/stale.yml
vendored
25
.github/workflows/stale.yml
vendored
|
@ -1,25 +0,0 @@
|
||||||
---
|
|
||||||
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
|
|
38
CHANGES
38
CHANGES
|
@ -1,41 +1,3 @@
|
||||||
3.3.0
|
|
||||||
* Support nested ifs in default template (#436)
|
|
||||||
* Support include and ifs in default template includes (#406)
|
|
||||||
* Support environment variables in ifs in default template (#488)
|
|
||||||
* Support != in default template (#358, #477)
|
|
||||||
* Fix multiple classes in default template on macOS (#437)
|
|
||||||
|
|
||||||
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
|
3.0.2
|
||||||
* Fix parsing by sh (#299)
|
* Fix parsing by sh (#299)
|
||||||
|
|
||||||
|
|
21
CONTRIBUTORS
21
CONTRIBUTORS
|
@ -4,51 +4,36 @@ Tim Byrne
|
||||||
Erik Flodin
|
Erik Flodin
|
||||||
Martin Zuther
|
Martin Zuther
|
||||||
Jan Schulz
|
Jan Schulz
|
||||||
Ross Smith II
|
|
||||||
Jonathan Daigle
|
Jonathan Daigle
|
||||||
Luis López
|
Luis López
|
||||||
Tin Lai
|
Tin Lai
|
||||||
Espen Henriksen
|
Espen Henriksen
|
||||||
Cameron Eagans
|
Cameron Eagans
|
||||||
Klas Mellbourn
|
Klas Mellbourn
|
||||||
James Clark
|
Ross Smith II
|
||||||
Glenn Waters
|
|
||||||
Nicolas signed-log FORMICHELLA
|
|
||||||
Tomas Cernaj
|
Tomas Cernaj
|
||||||
Joshua Cold
|
|
||||||
jonasc
|
jonasc
|
||||||
Nicolas stig124 FORMICHELLA
|
|
||||||
Chad Wade Day, Jr
|
Chad Wade Day, Jr
|
||||||
Sébastien Gross
|
Sébastien Gross
|
||||||
David Mandelberg
|
David Mandelberg
|
||||||
Paulo Köch
|
|
||||||
Oren Zipori
|
|
||||||
Daniel Gray
|
Daniel Gray
|
||||||
Paraplegic Racehorse
|
Paraplegic Racehorse
|
||||||
|
japm48
|
||||||
Siôn Le Roux
|
Siôn Le Roux
|
||||||
Mateusz Piotrowski
|
Mateusz Piotrowski
|
||||||
japm48
|
|
||||||
Uroš Golja
|
Uroš Golja
|
||||||
Satoshi Ohki
|
Satoshi Ohki
|
||||||
Jonas
|
|
||||||
Franciszek Madej
|
Franciszek Madej
|
||||||
Daniel Wagenknecht
|
Daniel Wagenknecht
|
||||||
Stig Palmquist
|
Stig Palmquist
|
||||||
Patrick Hof
|
Patrick Hof
|
||||||
Samisafool
|
|
||||||
LFdev
|
|
||||||
con-f-use
|
con-f-use
|
||||||
Bram Ceulemans
|
|
||||||
Travis A. Everett
|
Travis A. Everett
|
||||||
Sheng Yang
|
Sheng Yang
|
||||||
Jared Smartt
|
|
||||||
Adam Jimerson
|
Adam Jimerson
|
||||||
|
addshore
|
||||||
Tim Condit
|
Tim Condit
|
||||||
Thomas Luzat
|
Thomas Luzat
|
||||||
Russ Allbery
|
Russ Allbery
|
||||||
Patrick Roddy
|
|
||||||
dessert1
|
|
||||||
Brayden Banks
|
Brayden Banks
|
||||||
Alexandre GV
|
Alexandre GV
|
||||||
addshore
|
|
||||||
Felipe S. S. Schneider
|
|
||||||
|
|
25
Makefile
25
Makefile
|
@ -1,6 +1,5 @@
|
||||||
PYTESTS = $(wildcard test/test_*.py)
|
PYTESTS = $(wildcard test/test_*.py)
|
||||||
IMAGE = docker.io/yadm/testbed:2023-07-12
|
IMAGE = yadm/testbed:2020-12-29
|
||||||
OCI = docker
|
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all:
|
all:
|
||||||
|
@ -95,7 +94,7 @@ test:
|
||||||
py.test -v $(testargs); \
|
py.test -v $(testargs); \
|
||||||
else \
|
else \
|
||||||
$(MAKE) -s require-docker && \
|
$(MAKE) -s require-docker && \
|
||||||
$(OCI) run \
|
docker run \
|
||||||
--rm -t$(shell test -t 0 && echo i) \
|
--rm -t$(shell test -t 0 && echo i) \
|
||||||
-v "$(CURDIR):/yadm:ro" \
|
-v "$(CURDIR):/yadm:ro" \
|
||||||
$(IMAGE) \
|
$(IMAGE) \
|
||||||
|
@ -118,7 +117,7 @@ test:
|
||||||
.PHONY: testhost
|
.PHONY: testhost
|
||||||
testhost: require-docker .testyadm
|
testhost: require-docker .testyadm
|
||||||
@echo "Starting testhost"
|
@echo "Starting testhost"
|
||||||
@$(OCI) run \
|
@docker run \
|
||||||
-w /root \
|
-w /root \
|
||||||
--hostname testhost \
|
--hostname testhost \
|
||||||
--rm -it \
|
--rm -it \
|
||||||
|
@ -130,7 +129,7 @@ testhost: require-docker .testyadm
|
||||||
scripthost: require-docker .testyadm
|
scripthost: require-docker .testyadm
|
||||||
@echo "Starting scripthost \(recording script\)"
|
@echo "Starting scripthost \(recording script\)"
|
||||||
@printf '' > script.gz
|
@printf '' > script.gz
|
||||||
@$(OCI) run \
|
@docker run \
|
||||||
-w /root \
|
-w /root \
|
||||||
--hostname scripthost \
|
--hostname scripthost \
|
||||||
--rm -it \
|
--rm -it \
|
||||||
|
@ -160,7 +159,7 @@ testenv:
|
||||||
|
|
||||||
.PHONY: image
|
.PHONY: image
|
||||||
image:
|
image:
|
||||||
@$(OCI) build -f test/Dockerfile . -t "$(IMAGE)"
|
@docker build -f test/Dockerfile . -t "$(IMAGE)"
|
||||||
|
|
||||||
|
|
||||||
.PHONY: man
|
.PHONY: man
|
||||||
|
@ -176,7 +175,7 @@ man-ps:
|
||||||
@groff -man -Tps ./yadm.1 > yadm.ps
|
@groff -man -Tps ./yadm.1 > yadm.ps
|
||||||
|
|
||||||
yadm.md: yadm.1
|
yadm.md: yadm.1
|
||||||
@groff -man -Tutf8 -Z ./yadm.1 | grotty -c | col -bx | sed 's/^[A-Z]/## &/g' | sed '/yadm(1)/d' > yadm.md
|
@groff -man -Tascii ./yadm.1 | col -bx | sed 's/^[A-Z]/## &/g' | sed '/yadm(1)/d' > yadm.md
|
||||||
|
|
||||||
.PHONY: contrib
|
.PHONY: contrib
|
||||||
contrib: SHELL = /bin/bash
|
contrib: SHELL = /bin/bash
|
||||||
|
@ -193,9 +192,9 @@ install:
|
||||||
@[ -n "$(PREFIX)" ] || { echo "PREFIX is not set"; exit 1; }
|
@[ -n "$(PREFIX)" ] || { echo "PREFIX is not set"; exit 1; }
|
||||||
@{\
|
@{\
|
||||||
set -e ;\
|
set -e ;\
|
||||||
bin="$(DESTDIR)$(PREFIX)/bin" ;\
|
bin="$(PREFIX)/bin" ;\
|
||||||
doc="$(DESTDIR)$(PREFIX)/share/doc/yadm" ;\
|
doc="$(PREFIX)/share/doc/yadm" ;\
|
||||||
man="$(DESTDIR)$(PREFIX)/share/man/man1" ;\
|
man="$(PREFIX)/share/man/man1" ;\
|
||||||
install -d "$$bin" "$$doc" "$$man" ;\
|
install -d "$$bin" "$$doc" "$$man" ;\
|
||||||
install -m 0755 yadm "$$bin" ;\
|
install -m 0755 yadm "$$bin" ;\
|
||||||
install -m 0644 yadm.1 "$$man" ;\
|
install -m 0644 yadm.1 "$$man" ;\
|
||||||
|
@ -205,11 +204,11 @@ install:
|
||||||
|
|
||||||
.PHONY: sync-clock
|
.PHONY: sync-clock
|
||||||
sync-clock:
|
sync-clock:
|
||||||
$(OCI) run --rm --privileged alpine hwclock -s
|
docker run --rm --privileged alpine hwclock -s
|
||||||
|
|
||||||
.PHONY: require-docker
|
.PHONY: require-docker
|
||||||
require-docker:
|
require-docker:
|
||||||
@if ! command -v $(OCI) > /dev/null 2>&1; then \
|
@if ! command -v "docker" > /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"; \
|
echo "Sorry, this make target requires docker to be installed."; \
|
||||||
false; \
|
false; \
|
||||||
fi
|
fi
|
||||||
|
|
45
README.md
45
README.md
|
@ -3,7 +3,7 @@
|
||||||
[![Latest Version][releases-badge]][releases-link]
|
[![Latest Version][releases-badge]][releases-link]
|
||||||
[![Homebrew Version][homebrew-badge]][homebrew-link]
|
[![Homebrew Version][homebrew-badge]][homebrew-link]
|
||||||
[![OBS Version][obs-badge]][obs-link]
|
[![OBS Version][obs-badge]][obs-link]
|
||||||
[![Arch Version][arch-badge]][arch-link]
|
[![Arch Version][aur-badge]][aur-link]
|
||||||
[![License][license-badge]][license-link]<br />
|
[![License][license-badge]][license-link]<br />
|
||||||
[![Master Update][master-date]][master-commits]
|
[![Master Update][master-date]][master-commits]
|
||||||
[![Develop Update][develop-date]][develop-commits]
|
[![Develop Update][develop-date]][develop-commits]
|
||||||
|
@ -56,31 +56,32 @@ The star count helps others discover yadm.
|
||||||
[Git]: https://git-scm.com/
|
[Git]: https://git-scm.com/
|
||||||
[GnuPG]: https://gnupg.org/
|
[GnuPG]: https://gnupg.org/
|
||||||
[OpenSSL]: https://www.openssl.org/
|
[OpenSSL]: https://www.openssl.org/
|
||||||
[arch-badge]: https://img.shields.io/archlinux/v/extra/any/yadm
|
[aur-badge]: https://img.shields.io/aur/version/yadm.svg
|
||||||
[arch-link]: https://archlinux.org/packages/extra/any/yadm/
|
[aur-link]: https://aur.archlinux.org/packages/yadm
|
||||||
[dev-pages-badge]: https://img.shields.io/github/actions/workflow/status/yadm-dev/yadm/test.yml?branch=dev-pages
|
[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/actions/workflow/status/yadm-dev/yadm/test.yml?branch=develop
|
[develop-badge]: https://img.shields.io/github/workflow/status/TheLocehiliosan/yadm/Tests/develop?label=develop
|
||||||
[develop-commits]: https://github.com/yadm-dev/yadm/commits/develop
|
[develop-commits]: https://github.com/TheLocehiliosan/yadm/commits/develop
|
||||||
[develop-date]: https://img.shields.io/github/last-commit/yadm-dev/yadm/develop.svg?label=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
|
[dotfiles]: https://en.wikipedia.org/wiki/Hidden_file_and_hidden_directory
|
||||||
[gh-pages-badge]: https://img.shields.io/github/actions/workflow/status/yadm-dev/yadm/test.yml?branch=gh-pages
|
[gh-pages-badge]: https://img.shields.io/github/workflow/status/TheLocehiliosan/yadm/Test%20Site/gh-pages?label=gh-pages
|
||||||
[git-crypt]: https://github.com/AGWA/git-crypt
|
[git-crypt]: https://github.com/AGWA/git-crypt
|
||||||
[homebrew-badge]: https://img.shields.io/homebrew/v/yadm.svg
|
[homebrew-badge]: https://img.shields.io/homebrew/v/yadm.svg
|
||||||
[homebrew-link]: https://formulae.brew.sh/formula/yadm
|
[homebrew-link]: https://formulae.brew.sh/formula/yadm
|
||||||
[license-badge]: https://img.shields.io/github/license/yadm-dev/yadm.svg
|
[license-badge]: https://img.shields.io/github/license/TheLocehiliosan/yadm.svg
|
||||||
[license-link]: https://github.com/yadm-dev/yadm/blob/master/LICENSE
|
[license-link]: https://github.com/TheLocehiliosan/yadm/blob/master/LICENSE
|
||||||
[master-badge]: https://img.shields.io/github/actions/workflow/status/yadm-dev/yadm/test.yml?branch=master
|
[master-badge]: https://img.shields.io/github/workflow/status/TheLocehiliosan/yadm/Tests/master?label=master
|
||||||
[master-commits]: https://github.com/yadm-dev/yadm/commits/master
|
[master-commits]: https://github.com/TheLocehiliosan/yadm/commits/master
|
||||||
[master-date]: https://img.shields.io/github/last-commit/yadm-dev/yadm/master.svg?label=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.3.0-blue
|
[obs-badge]: https://img.shields.io/badge/OBS-v3.0.2-blue
|
||||||
[obs-link]: https://software.opensuse.org//download.html?project=home%3ATheLocehiliosan%3Ayadm&package=yadm
|
[obs-link]: https://software.opensuse.org//download.html?project=home%3ATheLocehiliosan%3Ayadm&package=yadm
|
||||||
[releases-badge]: https://img.shields.io/github/tag/yadm-dev/yadm.svg?label=latest+release
|
[releases-badge]: https://img.shields.io/github/tag/TheLocehiliosan/yadm.svg?label=latest+release
|
||||||
[releases-link]: https://github.com/yadm-dev/yadm/releases
|
[releases-link]: https://github.com/TheLocehiliosan/yadm/releases
|
||||||
[transcrypt]: https://github.com/elasticdog/transcrypt
|
[transcrypt]: https://github.com/elasticdog/transcrypt
|
||||||
[website-commits]: https://github.com/yadm-dev/yadm/commits/gh-pages
|
[travis-ci]: https://travis-ci.com/TheLocehiliosan/yadm/branches
|
||||||
[website-date]: https://img.shields.io/github/last-commit/yadm-dev/yadm/gh-pages.svg?label=website
|
[website-commits]: https://github.com/TheLocehiliosan/yadm/commits/gh-pages
|
||||||
|
[website-date]: https://img.shields.io/github/last-commit/TheLocehiliosan/yadm/gh-pages.svg?label=website
|
||||||
[website-link]: https://yadm.io/
|
[website-link]: https://yadm.io/
|
||||||
[workflow-dev-pages]: https://github.com/yadm-dev/yadm/actions?query=workflow%3a%22test+site%22+branch%3adev-pages
|
[workflow-dev-pages]: https://github.com/thelocehiliosan/yadm/actions?query=workflow%3a%22test+site%22+branch%3adev-pages
|
||||||
[workflow-develop]: https://github.com/yadm-dev/yadm/actions?query=workflow%3ATests+branch%3Adevelop
|
[workflow-develop]: https://github.com/TheLocehiliosan/yadm/actions?query=workflow%3ATests+branch%3Adevelop
|
||||||
[workflow-gh-pages]: https://github.com/yadm-dev/yadm/actions?query=workflow%3a%22test+site%22+branch%3agh-pages
|
[workflow-gh-pages]: https://github.com/thelocehiliosan/yadm/actions?query=workflow%3a%22test+site%22+branch%3agh-pages
|
||||||
[workflow-master]: https://github.com/yadm-dev/yadm/actions?query=workflow%3ATests+branch%3Amaster
|
[workflow-master]: https://github.com/TheLocehiliosan/yadm/actions?query=workflow%3ATests+branch%3Amaster
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
# source <(curl -L bootstrap.yadm.io)
|
# source <(curl -L bootstrap.yadm.io)
|
||||||
#
|
#
|
||||||
|
|
||||||
YADM_REPO="https://github.com/yadm-dev/yadm"
|
YADM_REPO="https://github.com/TheLocehiliosan/yadm"
|
||||||
YADM_RELEASE=${release:-master}
|
YADM_RELEASE=${release:-master}
|
||||||
REPO_URL=""
|
REPO_URL=""
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ Load `_yadm` as a plugin in your `.zshrc`:
|
||||||
|
|
||||||
```zsh
|
```zsh
|
||||||
fpath=("$ZPLUG_HOME/bin" $fpath)
|
fpath=("$ZPLUG_HOME/bin" $fpath)
|
||||||
zplug "yadm-dev/yadm", use:"completion/zsh/_yadm", as:command, defer:2
|
zplug "TheLocehiliosan/yadm", use:"completion/zsh/_yadm", as:command, defer:2
|
||||||
```
|
```
|
||||||
|
|
||||||
## Fish (manual installation)
|
## Fish (manual installation)
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
# test if git completion is missing, but loader exists, attempt to load
|
# test if git completion is missing, but loader exists, attempt to load
|
||||||
if ! declare -F _git > /dev/null && ! declare -F __git_wrap__git_main > /dev/null; then
|
if ! declare -F _git > /dev/null && declare -F _completion_loader > /dev/null; then
|
||||||
if declare -F _completion_loader > /dev/null; then
|
_completion_loader git
|
||||||
_completion_loader git
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# only operate if git completion is present
|
# only operate if git completion is present
|
||||||
if declare -F _git > /dev/null || declare -F __git_wrap__git_main > /dev/null; then
|
if declare -F _git > /dev/null; then
|
||||||
|
|
||||||
_yadm() {
|
_yadm() {
|
||||||
|
|
||||||
|
@ -68,11 +66,7 @@ if declare -F _git > /dev/null || declare -F __git_wrap__git_main > /dev/null; t
|
||||||
if [[ " ${yadm_switches[*]} " != *" $penultimate "* ]]; then
|
if [[ " ${yadm_switches[*]} " != *" $penultimate "* ]]; then
|
||||||
# TODO: somehow solve the problem with [--yadm-xxx option] being
|
# TODO: somehow solve the problem with [--yadm-xxx option] being
|
||||||
# incompatible with what git expects, namely [--arg=option]
|
# incompatible with what git expects, namely [--arg=option]
|
||||||
if declare -F _git > /dev/null; then
|
_git
|
||||||
_git
|
|
||||||
else
|
|
||||||
__git_wrap__git_main
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
if [[ "$current" =~ ^- ]]; then
|
if [[ "$current" =~ ^- ]]; then
|
||||||
local matching
|
local matching
|
||||||
|
|
|
@ -7,20 +7,6 @@ zstyle -T ':completion:*:yadm:argument-1:descriptions:' format && \
|
||||||
zstyle -T ':completion:*:yadm:*:yadm' group-name && \
|
zstyle -T ':completion:*:yadm:*:yadm' group-name && \
|
||||||
zstyle ':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() {
|
_yadm-alt() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -33,12 +19,10 @@ _yadm-clone() {
|
||||||
_arguments \
|
_arguments \
|
||||||
'(--bootstrap --no-bootstrap)--bootstrap[force bootstrap, without prompt]' \
|
'(--bootstrap --no-bootstrap)--bootstrap[force bootstrap, without prompt]' \
|
||||||
'(--bootstrap --no-bootstrap)--no-bootstrap[prevent bootstrap, without prompt]' \
|
'(--bootstrap --no-bootstrap)--no-bootstrap[prevent bootstrap, without prompt]' \
|
||||||
|
'-b[branch name]:' \
|
||||||
'-f[force overwrite of existing repository]' \
|
'-f[force overwrite of existing repository]' \
|
||||||
'-w[yadm work tree path]: :_files -/'
|
'-w[work tree path]: :_files -/' \
|
||||||
|
'*:'
|
||||||
local curcontext="${curcontext%:*:*}:git:"
|
|
||||||
|
|
||||||
words=("git" "${words[@]}") CURRENT=$((CURRENT + 1)) service=git _git
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_yadm-config() {
|
_yadm-config() {
|
||||||
|
|
|
@ -14,7 +14,7 @@ if [[ ! -d "$BOOTSTRAP_D" ]]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
find -L "$BOOTSTRAP_D" -type f | sort | while IFS= read -r bootstrap; do
|
find "$BOOTSTRAP_D" -type f | sort | while IFS= read -r bootstrap; do
|
||||||
if [[ -x "$bootstrap" && ! "$bootstrap" =~ "##" && ! "$bootstrap" =~ "~$" ]]; then
|
if [[ -x "$bootstrap" && ! "$bootstrap" =~ "##" && ! "$bootstrap" =~ "~$" ]]; then
|
||||||
if ! "$bootstrap"; then
|
if ! "$bootstrap"; then
|
||||||
echo "Error: bootstrap '$bootstrap' failed" >&2
|
echo "Error: bootstrap '$bootstrap' failed" >&2
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
## 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.
|
|
|
@ -1,71 +0,0 @@
|
||||||
#!/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
|
|
|
@ -11,4 +11,4 @@ this is a place to share it.
|
||||||
I recommend *careful review* of any code from here before using it. No
|
I recommend *careful review* of any code from here before using it. No
|
||||||
guarantees of code quality is assumed.
|
guarantees of code quality is assumed.
|
||||||
|
|
||||||
[hooks-help]: https://github.com/yadm-dev/yadm/blob/master/yadm.md#hooks
|
[hooks-help]: https://github.com/TheLocehiliosan/yadm/blob/master/yadm.md#hooks
|
||||||
|
|
17
pylintrc
Normal file
17
pylintrc
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[BASIC]
|
||||||
|
good-names=pytestmark
|
||||||
|
|
||||||
|
[DESIGN]
|
||||||
|
max-args=14
|
||||||
|
max-locals=28
|
||||||
|
max-attributes=8
|
||||||
|
max-statements=65
|
||||||
|
|
||||||
|
[SIMILARITIES]
|
||||||
|
min-similarity-lines=6
|
||||||
|
|
||||||
|
[MESSAGES CONTROL]
|
||||||
|
disable=redefined-outer-name
|
||||||
|
|
||||||
|
[TYPECHECK]
|
||||||
|
ignored-modules=py
|
|
@ -1,31 +0,0 @@
|
||||||
[tool.pytest.ini_options]
|
|
||||||
cache_dir = "/tmp"
|
|
||||||
addopts = "-ra"
|
|
||||||
markers = [
|
|
||||||
"deprecated", # marks tests for deprecated features (deselect with '-m "not deprecated"')
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.pylint.design]
|
|
||||||
max-args = 14
|
|
||||||
max-locals = 28
|
|
||||||
max-attributes = 8
|
|
||||||
max-statements = 65
|
|
||||||
|
|
||||||
[tool.pylint.format]
|
|
||||||
max-line-length = 120
|
|
||||||
|
|
||||||
[tool.pylint."messages control"]
|
|
||||||
disable = [
|
|
||||||
"redefined-outer-name",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.pylint.similarities]
|
|
||||||
ignore-imports = "yes"
|
|
||||||
min-similarity-lines = 8
|
|
||||||
|
|
||||||
[tool.black]
|
|
||||||
line-length = 120
|
|
||||||
|
|
||||||
[tool.isort]
|
|
||||||
line_length = 120
|
|
||||||
profile = "black"
|
|
5
pytest.ini
Normal file
5
pytest.ini
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[pytest]
|
||||||
|
cache_dir = /tmp
|
||||||
|
addopts = -ra
|
||||||
|
markers =
|
||||||
|
deprecated: marks tests for deprecated features (deselect with '-m "not deprecated"')
|
|
@ -1,9 +1,9 @@
|
||||||
FROM ubuntu:23.04
|
FROM ubuntu:18.04
|
||||||
MAINTAINER Tim Byrne <sultan@locehilios.com>
|
MAINTAINER Tim Byrne <sultan@locehilios.com>
|
||||||
|
|
||||||
# Shellcheck and esh versions
|
# Shellcheck and esh versions
|
||||||
ARG SC_VER=0.9.0
|
ARG SC_VER=0.7.1
|
||||||
ARG ESH_VER=0.3.2
|
ARG ESH_VER=0.3.0
|
||||||
|
|
||||||
# Install prerequisites and configure UTF-8 locale
|
# Install prerequisites and configure UTF-8 locale
|
||||||
RUN \
|
RUN \
|
||||||
|
@ -41,8 +41,8 @@ RUN cd /opt \
|
||||||
|
|
||||||
# Upgrade pip3 and install requirements
|
# Upgrade pip3 and install requirements
|
||||||
COPY test/requirements.txt /tmp/requirements.txt
|
COPY test/requirements.txt /tmp/requirements.txt
|
||||||
RUN python3 -m pip install --break-system-packages --upgrade pip setuptools \
|
RUN python3 -m pip install --upgrade pip setuptools \
|
||||||
&& python3 -m pip install --break-system-packages --upgrade -r /tmp/requirements.txt \
|
&& python3 -m pip install --upgrade -r /tmp/requirements.txt \
|
||||||
&& rm -f /tmp/requirements
|
&& rm -f /tmp/requirements
|
||||||
|
|
||||||
# Install esh
|
# Install esh
|
||||||
|
@ -57,8 +57,8 @@ RUN mkdir /yadm \
|
||||||
&& echo "\t@false" >> /yadm/Makefile
|
&& echo "\t@false" >> /yadm/Makefile
|
||||||
|
|
||||||
# Include released versions of yadm to test upgrades
|
# Include released versions of yadm to test upgrades
|
||||||
ADD https://raw.githubusercontent.com/yadm-dev/yadm/1.12.0/yadm /usr/local/bin/yadm-1.12.0
|
ADD https://raw.githubusercontent.com/TheLocehiliosan/yadm/1.12.0/yadm /usr/local/bin/yadm-1.12.0
|
||||||
ADD https://raw.githubusercontent.com/yadm-dev/yadm/2.5.0/yadm /usr/local/bin/yadm-2.5.0
|
ADD https://raw.githubusercontent.com/TheLocehiliosan/yadm/2.5.0/yadm /usr/local/bin/yadm-2.5.0
|
||||||
RUN chmod +x /usr/local/bin/yadm-*
|
RUN chmod +x /usr/local/bin/yadm-*
|
||||||
|
|
||||||
# Configure git to make it easier to test yadm manually
|
# Configure git to make it easier to test yadm manually
|
||||||
|
|
454
test/conftest.py
454
test/conftest.py
|
@ -3,12 +3,11 @@
|
||||||
import collections
|
import collections
|
||||||
import contextlib
|
import contextlib
|
||||||
import copy
|
import copy
|
||||||
|
import distutils.dir_util # pylint: disable=no-name-in-module,import-error
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import pwd
|
import pwd
|
||||||
import shutil
|
from subprocess import Popen, PIPE
|
||||||
from subprocess import PIPE, Popen
|
|
||||||
|
|
||||||
import py
|
import py
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -23,168 +22,139 @@ def pytest_addoption(parser):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def shellcheck_version():
|
def shellcheck_version():
|
||||||
"""Version of shellcheck supported"""
|
"""Version of shellcheck supported"""
|
||||||
return "0.9.0"
|
return '0.7.1'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def pylint_version():
|
def pylint_version():
|
||||||
"""Version of pylint supported"""
|
"""Version of pylint supported"""
|
||||||
return "2.17.0"
|
return '2.6.0'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def isort_version():
|
|
||||||
"""Version of isort supported"""
|
|
||||||
return "5.12.0"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def flake8_version():
|
def flake8_version():
|
||||||
"""Version of flake8 supported"""
|
"""Version of flake8 supported"""
|
||||||
return "6.0.0"
|
return '3.8.4'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def black_version():
|
|
||||||
"""Version of black supported"""
|
|
||||||
return "23.1.0"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def yamllint_version():
|
def yamllint_version():
|
||||||
"""Version of yamllint supported"""
|
"""Version of yamllint supported"""
|
||||||
return "1.30.0"
|
return '1.25.0'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def tst_user():
|
def tst_user():
|
||||||
"""Test session's user id"""
|
"""Test session's user id"""
|
||||||
return pwd.getpwuid(os.getuid()).pw_name
|
return pwd.getpwuid(os.getuid()).pw_name
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def tst_host():
|
def tst_host():
|
||||||
"""Test session's short hostname value"""
|
"""Test session's short hostname value"""
|
||||||
return platform.node().split(".")[0]
|
return platform.node().split('.')[0]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def tst_distro(runner):
|
def tst_distro(runner):
|
||||||
"""Test session's distro"""
|
"""Test session's distro"""
|
||||||
distro = ""
|
distro = ''
|
||||||
with contextlib.suppress(Exception):
|
with contextlib.suppress(Exception):
|
||||||
run = runner(command=["lsb_release", "-si"], report=False)
|
run = runner(command=['lsb_release', '-si'], report=False)
|
||||||
distro = run.out.strip()
|
distro = run.out.strip()
|
||||||
return distro
|
return distro
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@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():
|
def tst_sys():
|
||||||
"""Test session's uname value"""
|
"""Test session's uname value"""
|
||||||
return platform.system()
|
return platform.system()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def tst_arch():
|
|
||||||
"""Test session's uname value"""
|
|
||||||
return platform.machine()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def supported_commands():
|
def supported_commands():
|
||||||
"""List of supported commands
|
"""List of supported commands
|
||||||
|
|
||||||
This list should be updated every time yadm learns a new command.
|
This list should be updated every time yadm learns a new command.
|
||||||
"""
|
"""
|
||||||
return [
|
return [
|
||||||
"alt",
|
'alt',
|
||||||
"bootstrap",
|
'bootstrap',
|
||||||
"clean",
|
'clean',
|
||||||
"clone",
|
'clone',
|
||||||
"config",
|
'config',
|
||||||
"decrypt",
|
'decrypt',
|
||||||
"encrypt",
|
'encrypt',
|
||||||
"enter",
|
'enter',
|
||||||
"git-crypt",
|
'git-crypt',
|
||||||
"gitconfig",
|
'gitconfig',
|
||||||
"help",
|
'help',
|
||||||
"init",
|
'init',
|
||||||
"introspect",
|
'introspect',
|
||||||
"list",
|
'list',
|
||||||
"perms",
|
'perms',
|
||||||
"transcrypt",
|
'transcrypt',
|
||||||
"upgrade",
|
'upgrade',
|
||||||
"version",
|
'version',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def supported_configs():
|
def supported_configs():
|
||||||
"""List of supported config options
|
"""List of supported config options
|
||||||
|
|
||||||
This list should be updated every time yadm learns a new config.
|
This list should be updated every time yadm learns a new config.
|
||||||
"""
|
"""
|
||||||
return [
|
return [
|
||||||
"local.arch",
|
'local.class',
|
||||||
"local.class",
|
'local.hostname',
|
||||||
"local.hostname",
|
'local.os',
|
||||||
"local.os",
|
'local.user',
|
||||||
"local.user",
|
'yadm.alt-copy',
|
||||||
"yadm.alt-copy",
|
'yadm.auto-alt',
|
||||||
"yadm.auto-alt",
|
'yadm.auto-exclude',
|
||||||
"yadm.auto-exclude",
|
'yadm.auto-perms',
|
||||||
"yadm.auto-perms",
|
'yadm.auto-private-dirs',
|
||||||
"yadm.auto-private-dirs",
|
'yadm.cipher',
|
||||||
"yadm.cipher",
|
'yadm.git-program',
|
||||||
"yadm.git-program",
|
'yadm.gpg-perms',
|
||||||
"yadm.gpg-perms",
|
'yadm.gpg-program',
|
||||||
"yadm.gpg-program",
|
'yadm.gpg-recipient',
|
||||||
"yadm.gpg-recipient",
|
'yadm.openssl-ciphername',
|
||||||
"yadm.openssl-ciphername",
|
'yadm.openssl-old',
|
||||||
"yadm.openssl-old",
|
'yadm.openssl-program',
|
||||||
"yadm.openssl-program",
|
'yadm.ssh-perms',
|
||||||
"yadm.ssh-perms",
|
]
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def supported_switches():
|
def supported_switches():
|
||||||
"""List of supported switches
|
"""List of supported switches
|
||||||
|
|
||||||
This list should be updated every time yadm learns a new switch.
|
This list should be updated every time yadm learns a new switch.
|
||||||
"""
|
"""
|
||||||
return [
|
return [
|
||||||
"--yadm-archive",
|
'--yadm-archive',
|
||||||
"--yadm-bootstrap",
|
'--yadm-bootstrap',
|
||||||
"--yadm-config",
|
'--yadm-config',
|
||||||
"--yadm-data",
|
'--yadm-data',
|
||||||
"--yadm-dir",
|
'--yadm-dir',
|
||||||
"--yadm-encrypt",
|
'--yadm-encrypt',
|
||||||
"--yadm-repo",
|
'--yadm-repo',
|
||||||
"-Y",
|
'-Y',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def supported_local_configs(supported_configs):
|
def supported_local_configs(supported_configs):
|
||||||
"""List of supported local config options"""
|
"""List of supported local config options"""
|
||||||
return [c for c in supported_configs if c.startswith("local.")]
|
return [c for c in supported_configs if c.startswith('local.')]
|
||||||
|
|
||||||
|
|
||||||
class Runner:
|
class Runner():
|
||||||
"""Class for running commands
|
"""Class for running commands
|
||||||
|
|
||||||
Within yadm tests, this object should be used when running commands that
|
Within yadm tests, this object should be used when running commands that
|
||||||
|
@ -197,9 +167,17 @@ class Runner:
|
||||||
Other instances of simply running commands should use os.system().
|
Other instances of simply running commands should use os.system().
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, command, inp=None, shell=False, cwd=None, env=None, expect=None, report=True):
|
def __init__(
|
||||||
|
self,
|
||||||
|
command,
|
||||||
|
inp=None,
|
||||||
|
shell=False,
|
||||||
|
cwd=None,
|
||||||
|
env=None,
|
||||||
|
expect=None,
|
||||||
|
report=True):
|
||||||
if shell:
|
if shell:
|
||||||
self.command = " ".join([str(cmd) for cmd in command])
|
self.command = ' '.join([str(cmd) for cmd in command])
|
||||||
else:
|
else:
|
||||||
self.command = command
|
self.command = command
|
||||||
if env is None:
|
if env is None:
|
||||||
|
@ -208,7 +186,7 @@ class Runner:
|
||||||
merged_env.update(env)
|
merged_env.update(env)
|
||||||
self.inp = inp
|
self.inp = inp
|
||||||
self.wrap(expect)
|
self.wrap(expect)
|
||||||
with Popen(
|
process = Popen(
|
||||||
self.command,
|
self.command,
|
||||||
stdin=PIPE,
|
stdin=PIPE,
|
||||||
stdout=PIPE,
|
stdout=PIPE,
|
||||||
|
@ -216,57 +194,67 @@ class Runner:
|
||||||
shell=shell,
|
shell=shell,
|
||||||
cwd=cwd,
|
cwd=cwd,
|
||||||
env=merged_env,
|
env=merged_env,
|
||||||
) as process:
|
)
|
||||||
input_bytes = self.inp
|
input_bytes = self.inp
|
||||||
if self.inp:
|
if self.inp:
|
||||||
input_bytes = self.inp.encode()
|
input_bytes = self.inp.encode()
|
||||||
(out_bstream, err_bstream) = process.communicate(input=input_bytes)
|
(out_bstream, err_bstream) = process.communicate(input=input_bytes)
|
||||||
self.out = out_bstream.decode()
|
self.out = out_bstream.decode()
|
||||||
self.err = err_bstream.decode()
|
self.err = err_bstream.decode()
|
||||||
self.code = process.wait()
|
self.code = process.wait()
|
||||||
self.success = self.code == 0
|
self.success = self.code == 0
|
||||||
self.failure = self.code != 0
|
self.failure = self.code != 0
|
||||||
if report:
|
if report:
|
||||||
self.report()
|
self.report()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"Runner({self.command})"
|
return f'Runner({self.command})'
|
||||||
|
|
||||||
def report(self):
|
def report(self):
|
||||||
"""Print code/stdout/stderr"""
|
"""Print code/stdout/stderr"""
|
||||||
print(f"{self}")
|
print(f'{self}')
|
||||||
print(f" RUN: code:{self.code}")
|
print(f' RUN: code:{self.code}')
|
||||||
if self.inp:
|
if self.inp:
|
||||||
print(f" RUN: input:\n{self.inp}")
|
print(f' RUN: input:\n{self.inp}')
|
||||||
print(f" RUN: stdout:\n{self.out}")
|
print(f' RUN: stdout:\n{self.out}')
|
||||||
print(f" RUN: stderr:\n{self.err}")
|
print(f' RUN: stderr:\n{self.err}')
|
||||||
|
|
||||||
def wrap(self, expect):
|
def wrap(self, expect):
|
||||||
"""Wrap command with expect"""
|
"""Wrap command with expect"""
|
||||||
if not expect:
|
if not expect:
|
||||||
return
|
return
|
||||||
cmdline = " ".join([f'"{w}"' for w in self.command])
|
cmdline = ' '.join([f'"{w}"' for w in self.command])
|
||||||
expect_script = f"set timeout 2\nspawn {cmdline}\n"
|
expect_script = f'set timeout 2\nspawn {cmdline}\n'
|
||||||
for question, answer in expect:
|
for question, answer in expect:
|
||||||
expect_script += "expect {\n" f'"{question}" {{send "{answer}\\r"}}\n' "timeout {close;exit 128}\n" "}\n"
|
expect_script += (
|
||||||
expect_script += "expect eof\n" "foreach {pid spawnid os_error_flag value} [wait] break\n" "exit $value"
|
'expect {\n'
|
||||||
|
f'"{question}" {{send "{answer}\\r"}}\n'
|
||||||
|
'timeout {close;exit 128}\n'
|
||||||
|
'}\n')
|
||||||
|
expect_script += (
|
||||||
|
'expect eof\n'
|
||||||
|
'foreach {pid spawnid os_error_flag value} [wait] break\n'
|
||||||
|
'exit $value')
|
||||||
self.inp = expect_script
|
self.inp = expect_script
|
||||||
print(f"EXPECT:{expect_script}")
|
print(f'EXPECT:{expect_script}')
|
||||||
self.command = ["expect"]
|
self.command = ['expect']
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def runner():
|
def runner():
|
||||||
"""Class for running commands"""
|
"""Class for running commands"""
|
||||||
return Runner
|
return Runner
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def config_git():
|
def config_git():
|
||||||
"""Configure global git configuration, if missing"""
|
"""Configure global git configuration, if missing"""
|
||||||
os.system("git config init.defaultBranch || git config --global init.defaultBranch master")
|
os.system(
|
||||||
os.system('git config user.name || git config --global user.name "test"')
|
'git config user.name || '
|
||||||
os.system('git config user.email || git config --global user.email "test@test.test"')
|
'git config --global user.name "test"')
|
||||||
|
os.system(
|
||||||
|
'git config user.email || '
|
||||||
|
'git config --global user.email "test@test.test"')
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
|
@ -276,19 +264,19 @@ def repo_config(runner, paths):
|
||||||
def query_func(key):
|
def query_func(key):
|
||||||
"""Query a yadm repo configuration value"""
|
"""Query a yadm repo configuration value"""
|
||||||
run = runner(
|
run = runner(
|
||||||
command=("git", "config", "--local", key),
|
command=('git', 'config', '--local', key),
|
||||||
env={"GIT_DIR": paths.repo},
|
env={'GIT_DIR': paths.repo},
|
||||||
report=False,
|
report=False,
|
||||||
)
|
)
|
||||||
return run.out.rstrip()
|
return run.out.rstrip()
|
||||||
|
|
||||||
return query_func
|
return query_func
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def yadm():
|
def yadm():
|
||||||
"""Path to yadm program to be tested"""
|
"""Path to yadm program to be tested"""
|
||||||
full_path = os.path.realpath("yadm")
|
full_path = os.path.realpath('yadm')
|
||||||
assert os.path.isfile(full_path), "yadm program file isn't present"
|
assert os.path.isfile(full_path), "yadm program file isn't present"
|
||||||
return full_path
|
return full_path
|
||||||
|
|
||||||
|
@ -297,40 +285,38 @@ def yadm():
|
||||||
def paths(tmpdir, yadm):
|
def paths(tmpdir, yadm):
|
||||||
"""Function scoped test paths"""
|
"""Function scoped test paths"""
|
||||||
|
|
||||||
dir_root = tmpdir.mkdir("root")
|
dir_root = tmpdir.mkdir('root')
|
||||||
dir_remote = dir_root.mkdir("remote")
|
dir_remote = dir_root.mkdir('remote')
|
||||||
dir_work = dir_root.mkdir("work")
|
dir_work = dir_root.mkdir('work')
|
||||||
dir_xdg_data = dir_root.mkdir("xdg_data")
|
dir_xdg_data = dir_root.mkdir('xdg_data')
|
||||||
dir_xdg_home = dir_root.mkdir("xdg_home")
|
dir_xdg_home = dir_root.mkdir('xdg_home')
|
||||||
dir_data = dir_xdg_data.mkdir("yadm")
|
dir_data = dir_xdg_data.mkdir('yadm')
|
||||||
dir_yadm = dir_xdg_home.mkdir("yadm")
|
dir_yadm = dir_xdg_home.mkdir('yadm')
|
||||||
dir_hooks = dir_yadm.mkdir("hooks")
|
dir_hooks = dir_yadm.mkdir('hooks')
|
||||||
dir_repo = dir_data.mkdir("repo.git")
|
dir_repo = dir_data.mkdir('repo.git')
|
||||||
file_archive = dir_data.join("archive")
|
file_archive = dir_data.join('archive')
|
||||||
file_bootstrap = dir_yadm.join("bootstrap")
|
file_bootstrap = dir_yadm.join('bootstrap')
|
||||||
file_config = dir_yadm.join("config")
|
file_config = dir_yadm.join('config')
|
||||||
file_encrypt = dir_yadm.join("encrypt")
|
file_encrypt = dir_yadm.join('encrypt')
|
||||||
paths = collections.namedtuple(
|
paths = collections.namedtuple(
|
||||||
"Paths",
|
'Paths', [
|
||||||
[
|
'pgm',
|
||||||
"pgm",
|
'root',
|
||||||
"root",
|
'remote',
|
||||||
"remote",
|
'work',
|
||||||
"work",
|
'xdg_data',
|
||||||
"xdg_data",
|
'xdg_home',
|
||||||
"xdg_home",
|
'data',
|
||||||
"data",
|
'yadm',
|
||||||
"yadm",
|
'hooks',
|
||||||
"hooks",
|
'repo',
|
||||||
"repo",
|
'archive',
|
||||||
"archive",
|
'bootstrap',
|
||||||
"bootstrap",
|
'config',
|
||||||
"config",
|
'encrypt',
|
||||||
"encrypt",
|
])
|
||||||
],
|
os.environ['XDG_CONFIG_HOME'] = str(dir_xdg_home)
|
||||||
)
|
os.environ['XDG_DATA_HOME'] = str(dir_xdg_data)
|
||||||
os.environ["XDG_CONFIG_HOME"] = str(dir_xdg_home)
|
|
||||||
os.environ["XDG_DATA_HOME"] = str(dir_xdg_data)
|
|
||||||
return paths(
|
return paths(
|
||||||
yadm,
|
yadm,
|
||||||
dir_root,
|
dir_root,
|
||||||
|
@ -346,25 +332,19 @@ def paths(tmpdir, yadm):
|
||||||
file_bootstrap,
|
file_bootstrap,
|
||||||
file_config,
|
file_config,
|
||||||
file_encrypt,
|
file_encrypt,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def yadm_cmd(paths):
|
def yadm_cmd(paths):
|
||||||
"""Generate custom command_list function"""
|
"""Generate custom command_list function"""
|
||||||
|
|
||||||
def command_list(*args):
|
def command_list(*args):
|
||||||
"""Produce params for running yadm with -Y"""
|
"""Produce params for running yadm with -Y"""
|
||||||
return [paths.pgm] + list(args)
|
return [paths.pgm] + list(args)
|
||||||
|
|
||||||
return command_list
|
return command_list
|
||||||
|
|
||||||
|
|
||||||
class NoRelativePath(Exception):
|
class DataFile():
|
||||||
"""Exception when finding relative paths"""
|
|
||||||
|
|
||||||
|
|
||||||
class DataFile:
|
|
||||||
"""Datafile object"""
|
"""Datafile object"""
|
||||||
|
|
||||||
def __init__(self, path, tracked=True, private=False):
|
def __init__(self, path, tracked=True, private=False):
|
||||||
|
@ -383,7 +363,7 @@ class DataFile:
|
||||||
"""Relative path property"""
|
"""Relative path property"""
|
||||||
if self.__parent:
|
if self.__parent:
|
||||||
return self.__parent.join(self.path)
|
return self.__parent.join(self.path)
|
||||||
raise NoRelativePath("Unable to provide relative path, no parent")
|
raise BaseException('Unable to provide relative path, no parent')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tracked(self):
|
def tracked(self):
|
||||||
|
@ -400,18 +380,22 @@ class DataFile:
|
||||||
self.__parent = parent
|
self.__parent = parent
|
||||||
|
|
||||||
|
|
||||||
class DataSet:
|
class DataSet():
|
||||||
"""Dataset object"""
|
"""Dataset object"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__files = []
|
self.__files = list()
|
||||||
self.__dirs = []
|
self.__dirs = list()
|
||||||
self.__tracked_dirs = []
|
self.__tracked_dirs = list()
|
||||||
self.__private_dirs = []
|
self.__private_dirs = list()
|
||||||
self.__relpath = None
|
self.__relpath = None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"[DS with {len(self)} files; " f"{len(self.tracked)} tracked, " f"{len(self.private)} private]"
|
return (
|
||||||
|
f'[DS with {len(self)} files; '
|
||||||
|
f'{len(self.tracked)} tracked, '
|
||||||
|
f'{len(self.private)} private]'
|
||||||
|
)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.__files)
|
return iter(self.__files)
|
||||||
|
@ -449,17 +433,17 @@ class DataSet:
|
||||||
@property
|
@property
|
||||||
def plain_dirs(self):
|
def plain_dirs(self):
|
||||||
"""List of directories in DataSet not starting with '.'"""
|
"""List of directories in DataSet not starting with '.'"""
|
||||||
return [d for d in self.dirs if not d.startswith(".")]
|
return [d for d in self.dirs if not d.startswith('.')]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hidden_dirs(self):
|
def hidden_dirs(self):
|
||||||
"""List of directories in DataSet starting with '.'"""
|
"""List of directories in DataSet starting with '.'"""
|
||||||
return [d for d in self.dirs if d.startswith(".")]
|
return [d for d in self.dirs if d.startswith('.')]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tracked_dirs(self):
|
def tracked_dirs(self):
|
||||||
"""List of directories in DataSet not starting with '.'"""
|
"""List of directories in DataSet not starting with '.'"""
|
||||||
return [d for d in self.__tracked_dirs if not d.startswith(".")]
|
return [d for d in self.__tracked_dirs if not d.startswith('.')]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def private_dirs(self):
|
def private_dirs(self):
|
||||||
|
@ -489,23 +473,23 @@ class DataSet:
|
||||||
datafile.relative_to(self.__relpath)
|
datafile.relative_to(self.__relpath)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def ds1_dset(tst_sys):
|
def ds1_dset(tst_sys):
|
||||||
"""Meta-data for dataset one files"""
|
"""Meta-data for dataset one files"""
|
||||||
dset = DataSet()
|
dset = DataSet()
|
||||||
dset.add_file("t1")
|
dset.add_file('t1')
|
||||||
dset.add_file("d1/t2")
|
dset.add_file('d1/t2')
|
||||||
dset.add_file(f"test_alt_copy##os.{tst_sys}")
|
dset.add_file(f'test_alt_copy##os.{tst_sys}')
|
||||||
dset.add_file("u1", tracked=False)
|
dset.add_file('u1', tracked=False)
|
||||||
dset.add_file("d2/u2", tracked=False)
|
dset.add_file('d2/u2', tracked=False)
|
||||||
dset.add_file(".ssh/p1", tracked=False, private=True)
|
dset.add_file('.ssh/p1', tracked=False, private=True)
|
||||||
dset.add_file(".ssh/.p2", tracked=False, private=True)
|
dset.add_file('.ssh/.p2', tracked=False, private=True)
|
||||||
dset.add_file(".gnupg/p3", tracked=False, private=True)
|
dset.add_file('.gnupg/p3', tracked=False, private=True)
|
||||||
dset.add_file(".gnupg/.p4", tracked=False, private=True)
|
dset.add_file('.gnupg/.p4', tracked=False, private=True)
|
||||||
return dset
|
return dset
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def ds1_data(tmpdir_factory, config_git, ds1_dset, runner):
|
def ds1_data(tmpdir_factory, config_git, ds1_dset, runner):
|
||||||
"""A set of test data, worktree & repo"""
|
"""A set of test data, worktree & repo"""
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
|
@ -513,40 +497,65 @@ def ds1_data(tmpdir_factory, config_git, ds1_dset, runner):
|
||||||
# @pytest.mark.usefixtures('config_git')
|
# @pytest.mark.usefixtures('config_git')
|
||||||
# cannot be applied to another fixture.
|
# cannot be applied to another fixture.
|
||||||
|
|
||||||
data = tmpdir_factory.mktemp("ds1")
|
data = tmpdir_factory.mktemp('ds1')
|
||||||
|
|
||||||
work = data.mkdir("work")
|
work = data.mkdir('work')
|
||||||
for datafile in ds1_dset:
|
for datafile in ds1_dset:
|
||||||
work.join(datafile.path).write(datafile.path, ensure=True)
|
work.join(datafile.path).write(datafile.path, ensure=True)
|
||||||
|
|
||||||
repo = data.mkdir("repo.git")
|
repo = data.mkdir('repo.git')
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GIT_DIR"] = str(repo)
|
env['GIT_DIR'] = str(repo)
|
||||||
runner(command=["git", "init", "--shared=0600", "--bare", str(repo)], report=False)
|
runner(
|
||||||
runner(command=["git", "config", "core.bare", "false"], env=env, report=False)
|
command=['git', 'init', '--shared=0600', '--bare', str(repo)],
|
||||||
runner(command=["git", "config", "status.showUntrackedFiles", "no"], env=env, report=False)
|
report=False)
|
||||||
runner(command=["git", "config", "yadm.managed", "true"], env=env, report=False)
|
runner(
|
||||||
runner(command=["git", "config", "core.worktree", str(work)], env=env, report=False)
|
command=['git', 'config', 'core.bare', 'false'],
|
||||||
runner(command=["git", "add"] + [str(work.join(f.path)) for f in ds1_dset if f.tracked], env=env)
|
env=env,
|
||||||
runner(command=["git", "commit", "--allow-empty", "-m", "Initial commit"], env=env, report=False)
|
report=False)
|
||||||
|
runner(
|
||||||
|
command=['git', 'config', 'status.showUntrackedFiles', 'no'],
|
||||||
|
env=env,
|
||||||
|
report=False)
|
||||||
|
runner(
|
||||||
|
command=['git', 'config', 'yadm.managed', 'true'],
|
||||||
|
env=env,
|
||||||
|
report=False)
|
||||||
|
runner(
|
||||||
|
command=['git', 'config', 'core.worktree', str(work)],
|
||||||
|
env=env,
|
||||||
|
report=False)
|
||||||
|
runner(
|
||||||
|
command=['git', 'add'] +
|
||||||
|
[str(work.join(f.path)) for f in ds1_dset if f.tracked],
|
||||||
|
env=env)
|
||||||
|
runner(
|
||||||
|
command=['git', 'commit', '--allow-empty', '-m', 'Initial commit'],
|
||||||
|
env=env,
|
||||||
|
report=False)
|
||||||
|
|
||||||
data = collections.namedtuple("Data", ["work", "repo"])
|
data = collections.namedtuple('Data', ['work', 'repo'])
|
||||||
return data(work, repo)
|
return data(work, repo)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def ds1_work_copy(ds1_data, paths):
|
def ds1_work_copy(ds1_data, paths):
|
||||||
"""Function scoped copy of ds1_data.work"""
|
"""Function scoped copy of ds1_data.work"""
|
||||||
shutil.copytree(str(ds1_data.work), str(paths.work), dirs_exist_ok=True)
|
distutils.dir_util.copy_tree( # pylint: disable=no-member
|
||||||
|
str(ds1_data.work), str(paths.work))
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def ds1_repo_copy(runner, ds1_data, paths):
|
def ds1_repo_copy(runner, ds1_data, paths):
|
||||||
"""Function scoped copy of ds1_data.repo"""
|
"""Function scoped copy of ds1_data.repo"""
|
||||||
shutil.copytree(str(ds1_data.repo), str(paths.repo), dirs_exist_ok=True)
|
distutils.dir_util.copy_tree( # pylint: disable=no-member
|
||||||
|
str(ds1_data.repo), str(paths.repo))
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GIT_DIR"] = str(paths.repo)
|
env['GIT_DIR'] = str(paths.repo)
|
||||||
runner(command=["git", "config", "core.worktree", str(paths.work)], env=env, report=False)
|
runner(
|
||||||
|
command=['git', 'config', 'core.worktree', str(paths.work)],
|
||||||
|
env=env,
|
||||||
|
report=False)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
|
@ -571,27 +580,30 @@ def ds1(ds1_work_copy, paths, ds1_dset):
|
||||||
return dscopy
|
return dscopy
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def gnupg(tmpdir_factory, runner):
|
def gnupg(tmpdir_factory, runner):
|
||||||
"""Location of GNUPGHOME"""
|
"""Location of GNUPGHOME"""
|
||||||
|
|
||||||
def register_gpg_password(password):
|
def register_gpg_password(password):
|
||||||
"""Publish a new GPG mock password"""
|
"""Publish a new GPG mock password"""
|
||||||
py.path.local("/tmp/mock-password").write(password)
|
py.path.local('/tmp/mock-password').write(password)
|
||||||
|
|
||||||
home = tmpdir_factory.mktemp("gnupghome")
|
home = tmpdir_factory.mktemp('gnupghome')
|
||||||
home.chmod(0o700)
|
home.chmod(0o700)
|
||||||
conf = home.join("gpg.conf")
|
conf = home.join('gpg.conf')
|
||||||
conf.write("no-secmem-warning\n")
|
conf.write('no-secmem-warning\n')
|
||||||
conf.chmod(0o600)
|
conf.chmod(0o600)
|
||||||
agentconf = home.join("gpg-agent.conf")
|
agentconf = home.join('gpg-agent.conf')
|
||||||
agentconf.write(f'pinentry-program {os.path.abspath("test/pinentry-mock")}\n' "max-cache-ttl 0\n")
|
agentconf.write(
|
||||||
|
f'pinentry-program {os.path.abspath("test/pinentry-mock")}\n'
|
||||||
|
'max-cache-ttl 0\n'
|
||||||
|
)
|
||||||
agentconf.chmod(0o600)
|
agentconf.chmod(0o600)
|
||||||
data = collections.namedtuple("GNUPG", ["home", "pw"])
|
data = collections.namedtuple('GNUPG', ['home', 'pw'])
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GNUPGHOME"] = home
|
env['GNUPGHOME'] = home
|
||||||
|
|
||||||
# this pre-populates std files in the GNUPGHOME
|
# this pre-populates std files in the GNUPGHOME
|
||||||
runner(["gpg", "-k"], env=env)
|
runner(['gpg', '-k'], env=env)
|
||||||
|
|
||||||
return data(home, register_gpg_password)
|
return data(home, register_gpg_password)
|
||||||
|
|
|
@ -1,20 +1,12 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# This program is a custom mock pinentry program
|
# This program is a custom mock pinentry program
|
||||||
# It uses whatever password is found in the /tmp directory
|
# It always uses whatever password is found in the /tmp directory
|
||||||
# If the password is empty, replies CANCEL causing an error to similate invalid
|
password="$(cat /tmp/mock-password 2>/dev/null)"
|
||||||
# credentials
|
|
||||||
echo "OK Pleased to meet you"
|
echo "OK Pleased to meet you"
|
||||||
while read -r line; do
|
while read -r line; do
|
||||||
if [[ $line =~ GETPIN ]]; then
|
if [[ $line =~ GETPIN ]]; then
|
||||||
password="$(cat /tmp/mock-password 2>/dev/null)"
|
echo -n "D "
|
||||||
if [ -n "$password" ]; then
|
echo "$password"
|
||||||
echo -n "D "
|
|
||||||
echo "$password"
|
|
||||||
echo "OK";
|
|
||||||
else
|
|
||||||
echo "CANCEL";
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "OK";
|
|
||||||
fi
|
fi
|
||||||
|
echo "OK";
|
||||||
done
|
done
|
||||||
|
|
1
test/pylintrc
Symbolic link
1
test/pylintrc
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../pylintrc
|
|
@ -1,8 +1,6 @@
|
||||||
black==23.1.0
|
|
||||||
envtpl
|
envtpl
|
||||||
flake8==6.0.0
|
flake8==3.8.4
|
||||||
isort==5.12.0
|
|
||||||
j2cli
|
j2cli
|
||||||
pylint==2.17.0
|
pylint==2.6.0
|
||||||
pytest==7.2.2
|
pytest==6.2.1
|
||||||
yamllint==1.30.0
|
yamllint==1.25.0
|
||||||
|
|
224
test/test_alt.py
224
test/test_alt.py
|
@ -1,7 +1,6 @@
|
||||||
"""Test alt"""
|
"""Test alt"""
|
||||||
import os
|
import os
|
||||||
import string
|
import string
|
||||||
|
|
||||||
import py
|
import py
|
||||||
import pytest
|
import pytest
|
||||||
import utils
|
import utils
|
||||||
|
@ -9,34 +8,34 @@ import utils
|
||||||
TEST_PATHS = [utils.ALT_FILE1, utils.ALT_FILE2, utils.ALT_DIR]
|
TEST_PATHS = [utils.ALT_FILE1, utils.ALT_FILE2, utils.ALT_DIR]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
@pytest.mark.parametrize("yadm_alt", [True, False], ids=["alt", "worktree"])
|
@pytest.mark.parametrize('yadm_alt', [True, False], ids=['alt', 'worktree'])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"tracked,encrypt,exclude",
|
'tracked,encrypt,exclude', [
|
||||||
[
|
|
||||||
(False, False, False),
|
(False, False, False),
|
||||||
(True, False, False),
|
(True, False, False),
|
||||||
(False, True, False),
|
(False, True, False),
|
||||||
(False, True, True),
|
(False, True, True),
|
||||||
],
|
], ids=['untracked', 'tracked', 'encrypted', 'excluded'])
|
||||||
ids=["untracked", "tracked", "encrypted", "excluded"],
|
def test_alt_source(
|
||||||
)
|
runner, paths,
|
||||||
def test_alt_source(runner, paths, tracked, encrypt, exclude, yadm_alt):
|
tracked, encrypt, exclude,
|
||||||
|
yadm_alt):
|
||||||
"""Test yadm alt operates on all expected sources of alternates"""
|
"""Test yadm alt operates on all expected sources of alternates"""
|
||||||
yadm_dir, yadm_data = setup_standard_yadm_dir(paths)
|
yadm_dir, yadm_data = setup_standard_yadm_dir(paths)
|
||||||
|
|
||||||
utils.create_alt_files(
|
utils.create_alt_files(
|
||||||
paths, "##default", tracked=tracked, encrypt=encrypt, exclude=exclude, yadm_alt=yadm_alt, yadm_dir=yadm_dir
|
paths, '##default', tracked=tracked, encrypt=encrypt, exclude=exclude,
|
||||||
)
|
yadm_alt=yadm_alt, yadm_dir=yadm_dir)
|
||||||
run = runner([paths.pgm, "-Y", yadm_dir, "--yadm-data", yadm_data, "alt"])
|
run = runner([paths.pgm, '-Y', yadm_dir, '--yadm-data', yadm_data, 'alt'])
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
linked = utils.parse_alt_output(run.out)
|
linked = utils.parse_alt_output(run.out)
|
||||||
|
|
||||||
basepath = yadm_dir.join("alt") if yadm_alt else paths.work
|
basepath = yadm_dir.join('alt') if yadm_alt else paths.work
|
||||||
|
|
||||||
for link_path in TEST_PATHS:
|
for link_path in TEST_PATHS:
|
||||||
source_file_content = link_path + "##default"
|
source_file_content = link_path + '##default'
|
||||||
source_file = basepath.join(source_file_content)
|
source_file = basepath.join(source_file_content)
|
||||||
link_file = paths.work.join(link_path)
|
link_file = paths.work.join(link_path)
|
||||||
if tracked or (encrypt and not exclude):
|
if tracked or (encrypt and not exclude):
|
||||||
|
@ -46,84 +45,71 @@ def test_alt_source(runner, paths, tracked, encrypt, exclude, yadm_alt):
|
||||||
assert link_file.read() == source_file_content
|
assert link_file.read() == source_file_content
|
||||||
assert str(source_file) in linked
|
assert str(source_file) in linked
|
||||||
else:
|
else:
|
||||||
assert link_file.join(utils.CONTAINED).read() == source_file_content
|
assert link_file.join(
|
||||||
|
utils.CONTAINED).read() == source_file_content
|
||||||
assert str(source_file) in linked
|
assert str(source_file) in linked
|
||||||
else:
|
else:
|
||||||
assert not link_file.exists()
|
assert not link_file.exists()
|
||||||
assert str(source_file) not in linked
|
assert str(source_file) not in linked
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
@pytest.mark.parametrize("yadm_alt", [True, False], ids=["alt", "worktree"])
|
@pytest.mark.parametrize('yadm_alt', [True, False], ids=['alt', 'worktree'])
|
||||||
def test_relative_link(runner, paths, yadm_alt):
|
def test_relative_link(runner, paths, yadm_alt):
|
||||||
"""Confirm links created are relative"""
|
"""Confirm links created are relative"""
|
||||||
yadm_dir, yadm_data = setup_standard_yadm_dir(paths)
|
yadm_dir, yadm_data = setup_standard_yadm_dir(paths)
|
||||||
|
|
||||||
utils.create_alt_files(
|
utils.create_alt_files(
|
||||||
paths, "##default", tracked=True, encrypt=False, exclude=False, yadm_alt=yadm_alt, yadm_dir=yadm_dir
|
paths, '##default', tracked=True, encrypt=False, exclude=False,
|
||||||
)
|
yadm_alt=yadm_alt, yadm_dir=yadm_dir)
|
||||||
run = runner([paths.pgm, "-Y", yadm_dir, "--yadm-data", yadm_data, "alt"])
|
run = runner([paths.pgm, '-Y', yadm_dir, '--yadm-data', yadm_data, 'alt'])
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
|
|
||||||
basepath = yadm_dir.join("alt") if yadm_alt else paths.work
|
basepath = yadm_dir.join('alt') if yadm_alt else paths.work
|
||||||
|
|
||||||
for link_path in TEST_PATHS:
|
for link_path in TEST_PATHS:
|
||||||
source_file_content = link_path + "##default"
|
source_file_content = link_path + '##default'
|
||||||
source_file = basepath.join(source_file_content)
|
source_file = basepath.join(source_file_content)
|
||||||
link_file = paths.work.join(link_path)
|
link_file = paths.work.join(link_path)
|
||||||
link = link_file.readlink()
|
link = link_file.readlink()
|
||||||
relpath = os.path.relpath(source_file, start=os.path.dirname(link_file))
|
relpath = os.path.relpath(
|
||||||
|
source_file, start=os.path.dirname(link_file))
|
||||||
assert link == relpath
|
assert link == relpath
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize('suffix', [
|
||||||
"suffix",
|
'##default',
|
||||||
[
|
'##default,e.txt', '##default,extension.txt',
|
||||||
"##default",
|
'##o.$tst_sys', '##os.$tst_sys',
|
||||||
"##default,e.txt",
|
'##d.$tst_distro', '##distro.$tst_distro',
|
||||||
"##default,extension.txt",
|
'##c.$tst_class', '##class.$tst_class',
|
||||||
"##a.$tst_arch",
|
'##h.$tst_host', '##hostname.$tst_host',
|
||||||
"##arch.$tst_arch",
|
'##u.$tst_user', '##user.$tst_user',
|
||||||
"##o.$tst_sys",
|
])
|
||||||
"##os.$tst_sys",
|
def test_alt_conditions(
|
||||||
"##d.$tst_distro",
|
runner, paths,
|
||||||
"##distro.$tst_distro",
|
tst_sys, tst_distro, tst_host, tst_user, suffix):
|
||||||
"##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_arch, tst_sys, tst_distro, tst_distro_family, tst_host, tst_user, suffix):
|
|
||||||
"""Test conditions supported by yadm alt"""
|
"""Test conditions supported by yadm alt"""
|
||||||
yadm_dir, yadm_data = setup_standard_yadm_dir(paths)
|
yadm_dir, yadm_data = setup_standard_yadm_dir(paths)
|
||||||
|
|
||||||
# set the class
|
# set the class
|
||||||
tst_class = "testclass"
|
tst_class = 'testclass'
|
||||||
utils.set_local(paths, "class", tst_class + ".before")
|
utils.set_local(paths, 'class', tst_class)
|
||||||
utils.set_local(paths, "class", tst_class, add=True)
|
|
||||||
utils.set_local(paths, "class", tst_class + ".after", add=True)
|
|
||||||
|
|
||||||
suffix = string.Template(suffix).substitute(
|
suffix = string.Template(suffix).substitute(
|
||||||
tst_arch=tst_arch,
|
|
||||||
tst_sys=tst_sys,
|
tst_sys=tst_sys,
|
||||||
tst_distro=tst_distro,
|
tst_distro=tst_distro,
|
||||||
tst_distro_family=tst_distro_family,
|
|
||||||
tst_class=tst_class,
|
tst_class=tst_class,
|
||||||
tst_host=tst_host,
|
tst_host=tst_host,
|
||||||
tst_user=tst_user,
|
tst_user=tst_user,
|
||||||
)
|
)
|
||||||
|
|
||||||
utils.create_alt_files(paths, suffix)
|
utils.create_alt_files(paths, suffix)
|
||||||
run = runner([paths.pgm, "-Y", yadm_dir, "--yadm-data", yadm_data, "alt"])
|
run = runner([paths.pgm, '-Y', yadm_dir, '--yadm-data', yadm_data, 'alt'])
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
linked = utils.parse_alt_output(run.out)
|
linked = utils.parse_alt_output(run.out)
|
||||||
|
|
||||||
for link_path in TEST_PATHS:
|
for link_path in TEST_PATHS:
|
||||||
|
@ -134,31 +120,27 @@ def test_alt_conditions(runner, paths, tst_arch, tst_sys, tst_distro, tst_distro
|
||||||
assert paths.work.join(link_path).read() == source_file
|
assert paths.work.join(link_path).read() == source_file
|
||||||
assert str(paths.work.join(source_file)) in linked
|
assert str(paths.work.join(source_file)) in linked
|
||||||
else:
|
else:
|
||||||
assert paths.work.join(link_path).join(utils.CONTAINED).read() == source_file
|
assert paths.work.join(link_path).join(
|
||||||
|
utils.CONTAINED).read() == source_file
|
||||||
assert str(paths.work.join(source_file)) in linked
|
assert str(paths.work.join(source_file)) in linked
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
@pytest.mark.parametrize("kind", ["default", "", None, "envtpl", "j2cli", "j2", "esh"])
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"label",
|
'kind', ['default', '', None, 'envtpl', 'j2cli', 'j2', 'esh'])
|
||||||
[
|
@pytest.mark.parametrize('label', ['t', 'template', 'yadm', ])
|
||||||
"t",
|
def test_alt_templates(
|
||||||
"template",
|
runner, paths, kind, label):
|
||||||
"yadm",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_alt_templates(runner, paths, kind, label):
|
|
||||||
"""Test templates supported by yadm alt"""
|
"""Test templates supported by yadm alt"""
|
||||||
yadm_dir, yadm_data = setup_standard_yadm_dir(paths)
|
yadm_dir, yadm_data = setup_standard_yadm_dir(paths)
|
||||||
|
|
||||||
suffix = f"##{label}.{kind}"
|
suffix = f'##{label}.{kind}'
|
||||||
if kind is None:
|
if kind is None:
|
||||||
suffix = f"##{label}"
|
suffix = f'##{label}'
|
||||||
utils.create_alt_files(paths, suffix)
|
utils.create_alt_files(paths, suffix)
|
||||||
run = runner([paths.pgm, "-Y", yadm_dir, "--yadm-data", yadm_data, "alt"])
|
run = runner([paths.pgm, '-Y', yadm_dir, '--yadm-data', yadm_data, 'alt'])
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
created = utils.parse_alt_output(run.out, linked=False)
|
created = utils.parse_alt_output(run.out, linked=False)
|
||||||
|
|
||||||
for created_path in TEST_PATHS:
|
for created_path in TEST_PATHS:
|
||||||
|
@ -169,39 +151,41 @@ def test_alt_templates(runner, paths, kind, label):
|
||||||
assert str(paths.work.join(source_file)) in created
|
assert str(paths.work.join(source_file)) in created
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
@pytest.mark.parametrize("autoalt", [None, "true", "false"])
|
@pytest.mark.parametrize('autoalt', [None, 'true', 'false'])
|
||||||
def test_auto_alt(runner, yadm_cmd, paths, autoalt):
|
def test_auto_alt(runner, yadm_cmd, paths, autoalt):
|
||||||
"""Test auto alt"""
|
"""Test auto alt"""
|
||||||
|
|
||||||
# set the value of auto-alt
|
# set the value of auto-alt
|
||||||
if autoalt:
|
if autoalt:
|
||||||
os.system(" ".join(yadm_cmd("config", "yadm.auto-alt", autoalt)))
|
os.system(' '.join(yadm_cmd('config', 'yadm.auto-alt', autoalt)))
|
||||||
|
|
||||||
utils.create_alt_files(paths, "##default")
|
utils.create_alt_files(paths, '##default')
|
||||||
run = runner(yadm_cmd("status"))
|
run = runner(yadm_cmd('status'))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
linked = utils.parse_alt_output(run.out)
|
linked = utils.parse_alt_output(run.out)
|
||||||
|
|
||||||
for link_path in TEST_PATHS:
|
for link_path in TEST_PATHS:
|
||||||
source_file = link_path + "##default"
|
source_file = link_path + '##default'
|
||||||
if autoalt == "false":
|
if autoalt == 'false':
|
||||||
assert not paths.work.join(link_path).exists()
|
assert not paths.work.join(link_path).exists()
|
||||||
else:
|
else:
|
||||||
assert paths.work.join(link_path).islink()
|
assert paths.work.join(link_path).islink()
|
||||||
target = py.path.local(os.path.realpath(paths.work.join(link_path)))
|
target = py.path.local(
|
||||||
|
os.path.realpath(paths.work.join(link_path)))
|
||||||
if target.isfile():
|
if target.isfile():
|
||||||
assert paths.work.join(link_path).read() == source_file
|
assert paths.work.join(link_path).read() == source_file
|
||||||
# no linking output when run via auto-alt
|
# no linking output when run via auto-alt
|
||||||
assert str(paths.work.join(source_file)) not in linked
|
assert str(paths.work.join(source_file)) not in linked
|
||||||
else:
|
else:
|
||||||
assert paths.work.join(link_path).join(utils.CONTAINED).read() == source_file
|
assert paths.work.join(link_path).join(
|
||||||
|
utils.CONTAINED).read() == source_file
|
||||||
# no linking output when run via auto-alt
|
# no linking output when run via auto-alt
|
||||||
assert str(paths.work.join(source_file)) not in linked
|
assert str(paths.work.join(source_file)) not in linked
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
def test_stale_link_removal(runner, yadm_cmd, paths):
|
def test_stale_link_removal(runner, yadm_cmd, paths):
|
||||||
"""Stale links to alternative files are removed
|
"""Stale links to alternative files are removed
|
||||||
|
|
||||||
|
@ -210,47 +194,48 @@ def test_stale_link_removal(runner, yadm_cmd, paths):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# set the class
|
# set the class
|
||||||
tst_class = "testclass"
|
tst_class = 'testclass'
|
||||||
utils.set_local(paths, "class", tst_class)
|
utils.set_local(paths, 'class', tst_class)
|
||||||
|
|
||||||
# create files which match the test class
|
# create files which match the test class
|
||||||
utils.create_alt_files(paths, f"##class.{tst_class}")
|
utils.create_alt_files(paths, f'##class.{tst_class}')
|
||||||
|
|
||||||
# run alt to trigger linking
|
# run alt to trigger linking
|
||||||
run = runner(yadm_cmd("alt"))
|
run = runner(yadm_cmd('alt'))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
linked = utils.parse_alt_output(run.out)
|
linked = utils.parse_alt_output(run.out)
|
||||||
|
|
||||||
# assert the proper linking has occurred
|
# assert the proper linking has occurred
|
||||||
for stale_path in TEST_PATHS:
|
for stale_path in TEST_PATHS:
|
||||||
source_file = stale_path + "##class." + tst_class
|
source_file = stale_path + '##class.' + tst_class
|
||||||
assert paths.work.join(stale_path).islink()
|
assert paths.work.join(stale_path).islink()
|
||||||
target = py.path.local(os.path.realpath(paths.work.join(stale_path)))
|
target = py.path.local(os.path.realpath(paths.work.join(stale_path)))
|
||||||
if target.isfile():
|
if target.isfile():
|
||||||
assert paths.work.join(stale_path).read() == source_file
|
assert paths.work.join(stale_path).read() == source_file
|
||||||
assert str(paths.work.join(source_file)) in linked
|
assert str(paths.work.join(source_file)) in linked
|
||||||
else:
|
else:
|
||||||
assert paths.work.join(stale_path).join(utils.CONTAINED).read() == source_file
|
assert paths.work.join(stale_path).join(
|
||||||
|
utils.CONTAINED).read() == source_file
|
||||||
assert str(paths.work.join(source_file)) in linked
|
assert str(paths.work.join(source_file)) in linked
|
||||||
|
|
||||||
# change the class so there are no valid alternates
|
# change the class so there are no valid alternates
|
||||||
utils.set_local(paths, "class", "changedclass")
|
utils.set_local(paths, 'class', 'changedclass')
|
||||||
|
|
||||||
# run alt to trigger linking
|
# run alt to trigger linking
|
||||||
run = runner(yadm_cmd("alt"))
|
run = runner(yadm_cmd('alt'))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
linked = utils.parse_alt_output(run.out)
|
linked = utils.parse_alt_output(run.out)
|
||||||
|
|
||||||
# assert the linking is removed
|
# assert the linking is removed
|
||||||
for stale_path in TEST_PATHS:
|
for stale_path in TEST_PATHS:
|
||||||
source_file = stale_path + "##class." + tst_class
|
source_file = stale_path + '##class.' + tst_class
|
||||||
assert not paths.work.join(stale_path).exists()
|
assert not paths.work.join(stale_path).exists()
|
||||||
assert str(paths.work.join(source_file)) not in linked
|
assert str(paths.work.join(source_file)) not in linked
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("ds1_repo_copy")
|
@pytest.mark.usefixtures('ds1_repo_copy')
|
||||||
def test_template_overwrite_symlink(runner, yadm_cmd, paths, tst_sys):
|
def test_template_overwrite_symlink(runner, yadm_cmd, paths, tst_sys):
|
||||||
"""Remove symlinks before processing a template
|
"""Remove symlinks before processing a template
|
||||||
|
|
||||||
|
@ -259,44 +244,45 @@ def test_template_overwrite_symlink(runner, yadm_cmd, paths, tst_sys):
|
||||||
be removed just before processing a template.
|
be removed just before processing a template.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
target = paths.work.join(f"test_link##os.{tst_sys}")
|
target = paths.work.join(f'test_link##os.{tst_sys}')
|
||||||
target.write("target")
|
target.write('target')
|
||||||
|
|
||||||
link = paths.work.join("test_link")
|
link = paths.work.join('test_link')
|
||||||
link.mksymlinkto(target, absolute=1)
|
link.mksymlinkto(target, absolute=1)
|
||||||
|
|
||||||
template = paths.work.join("test_link##template.default")
|
template = paths.work.join('test_link##template.default')
|
||||||
template.write("test-data")
|
template.write('test-data')
|
||||||
|
|
||||||
run = runner(yadm_cmd("add", target, template))
|
run = runner(yadm_cmd('add', target, template))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
assert not link.islink()
|
assert not link.islink()
|
||||||
assert target.read().strip() == "target"
|
assert target.read().strip() == 'target'
|
||||||
assert link.read().strip() == "test-data"
|
assert link.read().strip() == 'test-data'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
@pytest.mark.parametrize("style", ["symlink", "template"])
|
@pytest.mark.parametrize('style', ['symlink', 'template'])
|
||||||
def test_ensure_alt_path(runner, paths, style):
|
def test_ensure_alt_path(runner, paths, style):
|
||||||
"""Test that directories are created before making alternates"""
|
"""Test that directories are created before making alternates"""
|
||||||
yadm_dir, yadm_data = setup_standard_yadm_dir(paths)
|
yadm_dir, yadm_data = setup_standard_yadm_dir(paths)
|
||||||
suffix = "default" if style == "symlink" else "template"
|
suffix = 'default' if style == 'symlink' else 'template'
|
||||||
filename = "a/b/c/file"
|
filename = 'a/b/c/file'
|
||||||
source = yadm_dir.join(f"alt/{filename}##{suffix}")
|
source = yadm_dir.join(f'alt/{filename}##{suffix}')
|
||||||
source.write("test-data", ensure=True)
|
source.write('test-data', ensure=True)
|
||||||
run = runner([paths.pgm, "-Y", yadm_dir, "--yadm-data", yadm_data, "add", source])
|
run = runner([
|
||||||
|
paths.pgm, '-Y', yadm_dir, '--yadm-data', yadm_data, 'add', source])
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
assert paths.work.join(filename).read().strip() == "test-data"
|
assert paths.work.join(filename).read().strip() == 'test-data'
|
||||||
|
|
||||||
|
|
||||||
def setup_standard_yadm_dir(paths):
|
def setup_standard_yadm_dir(paths):
|
||||||
"""Configure a yadm home within the work tree"""
|
"""Configure a yadm home within the work tree"""
|
||||||
std_yadm_dir = paths.work.mkdir(".config").mkdir("yadm")
|
std_yadm_dir = paths.work.mkdir('.config').mkdir('yadm')
|
||||||
std_yadm_data = paths.work.mkdir(".local").mkdir("share").mkdir("yadm")
|
std_yadm_data = paths.work.mkdir('.local').mkdir('share').mkdir('yadm')
|
||||||
std_yadm_data.join("repo.git").mksymlinkto(paths.repo, absolute=1)
|
std_yadm_data.join('repo.git').mksymlinkto(paths.repo, absolute=1)
|
||||||
std_yadm_dir.join("encrypt").mksymlinkto(paths.encrypt, absolute=1)
|
std_yadm_dir.join('encrypt').mksymlinkto(paths.encrypt, absolute=1)
|
||||||
return std_yadm_dir, std_yadm_data
|
return std_yadm_dir, std_yadm_data
|
||||||
|
|
|
@ -1,46 +1,45 @@
|
||||||
"""Test yadm.alt-copy"""
|
"""Test yadm.alt-copy"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"setting, expect_link, pre_existing",
|
'setting, expect_link, pre_existing', [
|
||||||
[
|
|
||||||
(None, True, None),
|
(None, True, None),
|
||||||
(True, False, None),
|
(True, False, None),
|
||||||
(False, True, None),
|
(False, True, None),
|
||||||
(True, False, "link"),
|
(True, False, 'link'),
|
||||||
(True, False, "file"),
|
(True, False, 'file'),
|
||||||
],
|
],
|
||||||
ids=[
|
ids=[
|
||||||
"unset",
|
'unset',
|
||||||
"true",
|
'true',
|
||||||
"false",
|
'false',
|
||||||
"pre-existing symlink",
|
'pre-existing symlink',
|
||||||
"pre-existing file",
|
'pre-existing file',
|
||||||
],
|
])
|
||||||
)
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
def test_alt_copy(
|
||||||
def test_alt_copy(runner, yadm_cmd, paths, tst_sys, setting, expect_link, pre_existing):
|
runner, yadm_cmd, paths, tst_sys,
|
||||||
|
setting, expect_link, pre_existing):
|
||||||
"""Test yadm.alt-copy"""
|
"""Test yadm.alt-copy"""
|
||||||
|
|
||||||
if setting is not None:
|
if setting is not None:
|
||||||
os.system(" ".join(yadm_cmd("config", "yadm.alt-copy", str(setting))))
|
os.system(' '.join(yadm_cmd('config', 'yadm.alt-copy', str(setting))))
|
||||||
|
|
||||||
expected_content = f"test_alt_copy##os.{tst_sys}"
|
expected_content = f'test_alt_copy##os.{tst_sys}'
|
||||||
|
|
||||||
alt_path = paths.work.join("test_alt_copy")
|
alt_path = paths.work.join('test_alt_copy')
|
||||||
if pre_existing == "symlink":
|
if pre_existing == 'symlink':
|
||||||
alt_path.mklinkto(expected_content)
|
alt_path.mklinkto(expected_content)
|
||||||
elif pre_existing == "file":
|
elif pre_existing == 'file':
|
||||||
alt_path.write("wrong content")
|
alt_path.write('wrong content')
|
||||||
|
|
||||||
run = runner(yadm_cmd("alt"))
|
run = runner(yadm_cmd('alt'))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "Linking" in run.out
|
assert 'Linking' in run.out
|
||||||
|
|
||||||
assert alt_path.read() == expected_content
|
assert alt_path.read() == expected_content
|
||||||
assert alt_path.islink() == expect_link
|
assert alt_path.islink() == expect_link
|
||||||
|
|
|
@ -2,14 +2,13 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
pytestmark = pytest.mark.usefixtures("ds1_copy")
|
pytestmark = pytest.mark.usefixtures('ds1_copy')
|
||||||
PRIVATE_DIRS = [".gnupg", ".ssh"]
|
PRIVATE_DIRS = ['.gnupg', '.ssh']
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("home", [True, False], ids=["home", "not-home"])
|
@pytest.mark.parametrize('home', [True, False], ids=['home', 'not-home'])
|
||||||
def test_pdirs_missing(runner, yadm_cmd, paths, home):
|
def test_pdirs_missing(runner, yadm_cmd, paths, home):
|
||||||
"""Private dirs (private dirs missing)
|
"""Private dirs (private dirs missing)
|
||||||
|
|
||||||
|
@ -25,15 +24,15 @@ def test_pdirs_missing(runner, yadm_cmd, paths, home):
|
||||||
path.remove()
|
path.remove()
|
||||||
assert not path.exists()
|
assert not path.exists()
|
||||||
|
|
||||||
env = {"DEBUG": "yes"}
|
env = {'DEBUG': 'yes'}
|
||||||
if home:
|
if home:
|
||||||
env["HOME"] = paths.work
|
env['HOME'] = paths.work
|
||||||
|
|
||||||
# run status
|
# run status
|
||||||
run = runner(command=yadm_cmd("status"), env=env)
|
run = runner(command=yadm_cmd('status'), env=env)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "On branch master" in run.out
|
assert 'On branch master' in run.out
|
||||||
|
|
||||||
# confirm directories are created
|
# confirm directories are created
|
||||||
# and are protected
|
# and are protected
|
||||||
|
@ -41,15 +40,17 @@ def test_pdirs_missing(runner, yadm_cmd, paths, home):
|
||||||
path = paths.work.join(pdir)
|
path = paths.work.join(pdir)
|
||||||
if home:
|
if home:
|
||||||
assert path.exists()
|
assert path.exists()
|
||||||
assert oct(path.stat().mode).endswith("00"), "Directory is " "not secured"
|
assert oct(path.stat().mode).endswith('00'), ('Directory is '
|
||||||
|
'not secured')
|
||||||
else:
|
else:
|
||||||
assert not path.exists()
|
assert not path.exists()
|
||||||
|
|
||||||
# confirm directories are created before command is run:
|
# confirm directories are created before command is run:
|
||||||
if home:
|
if home:
|
||||||
assert re.search(
|
assert re.search(
|
||||||
r"Creating.+\.(gnupg|ssh).+Creating.+\.(gnupg|ssh).+Running git command git status", run.out, re.DOTALL
|
(r'Creating.+\.(gnupg|ssh).+Creating.+\.(gnupg|ssh).+'
|
||||||
), "directories created before command is run"
|
r'Running git command git status'),
|
||||||
|
run.out, re.DOTALL), 'directories created before command is run'
|
||||||
|
|
||||||
|
|
||||||
def test_pdirs_missing_apd_false(runner, yadm_cmd, paths):
|
def test_pdirs_missing_apd_false(runner, yadm_cmd, paths):
|
||||||
|
@ -69,13 +70,14 @@ def test_pdirs_missing_apd_false(runner, yadm_cmd, paths):
|
||||||
assert not path.exists()
|
assert not path.exists()
|
||||||
|
|
||||||
# set configuration
|
# set configuration
|
||||||
os.system(" ".join(yadm_cmd("config", "--bool", "yadm.auto-private-dirs", "false")))
|
os.system(' '.join(yadm_cmd(
|
||||||
|
'config', '--bool', 'yadm.auto-private-dirs', 'false')))
|
||||||
|
|
||||||
# run status
|
# run status
|
||||||
run = runner(command=yadm_cmd("status"))
|
run = runner(command=yadm_cmd('status'))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "On branch master" in run.out
|
assert 'On branch master' in run.out
|
||||||
|
|
||||||
# confirm directories are STILL missing
|
# confirm directories are STILL missing
|
||||||
for pdir in PRIVATE_DIRS:
|
for pdir in PRIVATE_DIRS:
|
||||||
|
@ -97,18 +99,19 @@ def test_pdirs_exist_apd_false(runner, yadm_cmd, paths):
|
||||||
if not path.isdir():
|
if not path.isdir():
|
||||||
path.mkdir()
|
path.mkdir()
|
||||||
path.chmod(0o777)
|
path.chmod(0o777)
|
||||||
assert oct(path.stat().mode).endswith("77"), "Directory is secure."
|
assert oct(path.stat().mode).endswith('77'), 'Directory is secure.'
|
||||||
|
|
||||||
# set configuration
|
# set configuration
|
||||||
os.system(" ".join(yadm_cmd("config", "--bool", "yadm.auto-perms", "false")))
|
os.system(' '.join(yadm_cmd(
|
||||||
|
'config', '--bool', 'yadm.auto-perms', 'false')))
|
||||||
|
|
||||||
# run status
|
# run status
|
||||||
run = runner(command=yadm_cmd("status"))
|
run = runner(command=yadm_cmd('status'))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "On branch master" in run.out
|
assert 'On branch master' in run.out
|
||||||
|
|
||||||
# created directories are STILL permissive
|
# created directories are STILL permissive
|
||||||
for pdir in PRIVATE_DIRS:
|
for pdir in PRIVATE_DIRS:
|
||||||
path = paths.work.join(pdir)
|
path = paths.work.join(pdir)
|
||||||
assert oct(path.stat().mode).endswith("77"), "Directory is secure"
|
assert oct(path.stat().mode).endswith('77'), 'Directory is secure'
|
||||||
|
|
|
@ -4,30 +4,32 @@ import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"exists, executable, code, expect",
|
'exists, executable, code, expect', [
|
||||||
[
|
(False, False, 1, 'Cannot execute bootstrap'),
|
||||||
(False, False, 1, "Cannot execute bootstrap"),
|
(True, False, 1, 'is not an executable program'),
|
||||||
(True, False, 1, "is not an executable program"),
|
(True, True, 123, 'Bootstrap successful'),
|
||||||
(True, True, 123, "Bootstrap successful"),
|
], ids=[
|
||||||
],
|
'missing',
|
||||||
ids=[
|
'not executable',
|
||||||
"missing",
|
'executable',
|
||||||
"not executable",
|
])
|
||||||
"executable",
|
def test_bootstrap(
|
||||||
],
|
runner, yadm_cmd, paths, exists, executable, code, expect):
|
||||||
)
|
|
||||||
def test_bootstrap(runner, yadm_cmd, paths, exists, executable, code, expect):
|
|
||||||
"""Test bootstrap command"""
|
"""Test bootstrap command"""
|
||||||
if exists:
|
if exists:
|
||||||
paths.bootstrap.write("")
|
paths.bootstrap.write('')
|
||||||
if executable:
|
if executable:
|
||||||
paths.bootstrap.write("#!/bin/bash\n" f"echo {expect}\n" f"exit {code}\n")
|
paths.bootstrap.write(
|
||||||
|
'#!/bin/bash\n'
|
||||||
|
f'echo {expect}\n'
|
||||||
|
f'exit {code}\n'
|
||||||
|
)
|
||||||
paths.bootstrap.chmod(0o775)
|
paths.bootstrap.chmod(0o775)
|
||||||
run = runner(command=yadm_cmd("bootstrap"))
|
run = runner(command=yadm_cmd('bootstrap'))
|
||||||
assert run.code == code
|
assert run.code == code
|
||||||
if exists and executable:
|
if exists and executable:
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert expect in run.out
|
assert expect in run.out
|
||||||
else:
|
else:
|
||||||
assert expect in run.err
|
assert expect in run.err
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
|
|
||||||
def test_clean_command(runner, yadm_cmd):
|
def test_clean_command(runner, yadm_cmd):
|
||||||
"""Run with clean command"""
|
"""Run with clean command"""
|
||||||
run = runner(command=yadm_cmd("clean"))
|
run = runner(command=yadm_cmd('clean'))
|
||||||
# do nothing, this is a dangerous Git command when managing dot files
|
# do nothing, this is a dangerous Git command when managing dot files
|
||||||
# report the command as disabled and exit as a failure
|
# report the command as disabled and exit as a failure
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
assert "disabled" in run.err
|
assert 'disabled' in run.err
|
||||||
|
|
|
@ -2,32 +2,30 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
BOOTSTRAP_CODE = 123
|
BOOTSTRAP_CODE = 123
|
||||||
BOOTSTRAP_MSG = "Bootstrap successful"
|
BOOTSTRAP_MSG = 'Bootstrap successful'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("remote")
|
@pytest.mark.usefixtures('remote')
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"good_remote, repo_exists, force, conflicts",
|
'good_remote, repo_exists, force, conflicts', [
|
||||||
[
|
|
||||||
(False, False, False, False),
|
(False, False, False, False),
|
||||||
(True, False, False, False),
|
(True, False, False, False),
|
||||||
(True, True, False, False),
|
(True, True, False, False),
|
||||||
(True, True, True, False),
|
(True, True, True, False),
|
||||||
(True, False, False, True),
|
(True, False, False, True),
|
||||||
],
|
], ids=[
|
||||||
ids=[
|
'bad remote',
|
||||||
"bad remote",
|
'simple',
|
||||||
"simple",
|
'existing repo',
|
||||||
"existing repo",
|
'-f',
|
||||||
"-f",
|
'conflicts',
|
||||||
"conflicts",
|
])
|
||||||
],
|
def test_clone(
|
||||||
)
|
runner, paths, yadm_cmd, repo_config, ds1,
|
||||||
def test_clone(runner, paths, yadm_cmd, repo_config, ds1, good_remote, repo_exists, force, conflicts):
|
good_remote, repo_exists, force, conflicts):
|
||||||
"""Test basic clone operation"""
|
"""Test basic clone operation"""
|
||||||
|
|
||||||
# clear out the work path
|
# clear out the work path
|
||||||
|
@ -35,70 +33,82 @@ def test_clone(runner, paths, yadm_cmd, repo_config, ds1, good_remote, repo_exis
|
||||||
paths.work.mkdir()
|
paths.work.mkdir()
|
||||||
|
|
||||||
# determine remote url
|
# determine remote url
|
||||||
remote_url = f"file://{paths.remote}"
|
remote_url = f'file://{paths.remote}'
|
||||||
if not good_remote:
|
if not good_remote:
|
||||||
remote_url = "file://bad_remote"
|
remote_url = 'file://bad_remote'
|
||||||
|
|
||||||
old_repo = None
|
old_repo = None
|
||||||
if repo_exists:
|
if repo_exists:
|
||||||
# put a repo in the way
|
# put a repo in the way
|
||||||
paths.repo.mkdir()
|
paths.repo.mkdir()
|
||||||
old_repo = paths.repo.join("old_repo")
|
old_repo = paths.repo.join('old_repo')
|
||||||
old_repo.write("old_repo")
|
old_repo.write('old_repo')
|
||||||
|
|
||||||
if conflicts:
|
if conflicts:
|
||||||
ds1.tracked[0].relative.write("conflict")
|
ds1.tracked[0].relative.write('conflict')
|
||||||
assert ds1.tracked[0].relative.exists()
|
assert ds1.tracked[0].relative.exists()
|
||||||
|
|
||||||
# run the clone command
|
# run the clone command
|
||||||
args = ["clone", "-w", paths.work]
|
args = ['clone', '-w', paths.work]
|
||||||
if force:
|
if force:
|
||||||
args += ["-f"]
|
args += ['-f']
|
||||||
args += [remote_url]
|
args += [remote_url]
|
||||||
run = runner(command=yadm_cmd(*args))
|
run = runner(command=yadm_cmd(*args))
|
||||||
|
|
||||||
if not good_remote:
|
if not good_remote:
|
||||||
# clone should fail
|
# clone should fail
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert run.out == ""
|
assert run.out != ''
|
||||||
assert "Unable to clone the repository" in run.err
|
assert 'Unable to fetch origin' in run.err
|
||||||
assert not paths.repo.exists()
|
assert not paths.repo.exists()
|
||||||
elif repo_exists and not force:
|
elif repo_exists and not force:
|
||||||
# can't overwrite data
|
# can't overwrite data
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
assert "Git repo already exists" in run.err
|
assert 'Git repo already exists' in run.err
|
||||||
else:
|
else:
|
||||||
# clone should succeed, and repo should be configured properly
|
# clone should succeed, and repo should be configured properly
|
||||||
assert successful_clone(run, paths, repo_config)
|
assert successful_clone(run, paths, repo_config)
|
||||||
|
|
||||||
# these clones should have master as HEAD
|
# these clones should have master as HEAD
|
||||||
verify_head(paths, "master")
|
verify_head(paths, 'master')
|
||||||
|
|
||||||
# ensure conflicts are handled properly
|
# ensure conflicts are handled properly
|
||||||
if conflicts:
|
if conflicts:
|
||||||
assert "NOTE" in run.out
|
assert 'NOTE' in run.out
|
||||||
assert "Local files with content that differs" in run.out
|
assert 'Merging origin/master failed' in run.out
|
||||||
|
assert 'Conflicts preserved' in run.out
|
||||||
|
|
||||||
# confirm correct Git origin
|
# confirm correct Git origin
|
||||||
run = runner(command=("git", "remote", "-v", "show"), env={"GIT_DIR": paths.repo})
|
run = runner(
|
||||||
|
command=('git', 'remote', '-v', 'show'),
|
||||||
|
env={'GIT_DIR': paths.repo})
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert f"origin\t{remote_url}" in run.out
|
assert f'origin\t{remote_url}' in run.out
|
||||||
|
|
||||||
# ensure conflicts are really preserved
|
# ensure conflicts are really preserved
|
||||||
if conflicts:
|
if conflicts:
|
||||||
# test that the conflicts are preserved in the work tree
|
# test to see if the work tree is actually "clean"
|
||||||
run = runner(command=yadm_cmd("status", "-uno", "--porcelain"), cwd=paths.work)
|
run = runner(
|
||||||
|
command=yadm_cmd('status', '-uno', '--porcelain'),
|
||||||
|
cwd=paths.work)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert str(ds1.tracked[0].path) in run.out
|
assert run.out == '', 'worktree has unexpected changes'
|
||||||
|
|
||||||
# verify content of the conflicts
|
# test to see if the conflicts are stashed
|
||||||
run = runner(command=yadm_cmd("diff"), cwd=paths.work)
|
run = runner(command=yadm_cmd('stash', 'list'), cwd=paths.work)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "\n+conflict" in run.out, "conflict overwritten"
|
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'
|
||||||
|
|
||||||
# another force-related assertion
|
# another force-related assertion
|
||||||
if old_repo:
|
if old_repo:
|
||||||
|
@ -108,56 +118,54 @@ def test_clone(runner, paths, yadm_cmd, repo_config, ds1, good_remote, repo_exis
|
||||||
assert old_repo.exists()
|
assert old_repo.exists()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("remote")
|
@pytest.mark.usefixtures('remote')
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"bs_exists, bs_param, answer",
|
'bs_exists, bs_param, answer', [
|
||||||
[
|
(False, '--bootstrap', None),
|
||||||
(False, "--bootstrap", None),
|
(True, '--bootstrap', None),
|
||||||
(True, "--bootstrap", None),
|
(True, '--no-bootstrap', None),
|
||||||
(True, "--no-bootstrap", None),
|
(True, None, 'n'),
|
||||||
(True, None, "n"),
|
(True, None, 'y'),
|
||||||
(True, None, "y"),
|
], ids=[
|
||||||
],
|
'force, missing',
|
||||||
ids=[
|
'force, existing',
|
||||||
"force, missing",
|
'prevent',
|
||||||
"force, existing",
|
'existing, answer n',
|
||||||
"prevent",
|
'existing, answer y',
|
||||||
"existing, answer n",
|
])
|
||||||
"existing, answer y",
|
def test_clone_bootstrap(
|
||||||
],
|
runner, paths, yadm_cmd, repo_config, bs_exists, bs_param, answer):
|
||||||
)
|
|
||||||
def test_clone_bootstrap(runner, paths, yadm_cmd, repo_config, bs_exists, bs_param, answer):
|
|
||||||
"""Test bootstrap clone features"""
|
"""Test bootstrap clone features"""
|
||||||
|
|
||||||
# establish a bootstrap
|
# establish a bootstrap
|
||||||
create_bootstrap(paths, bs_exists)
|
create_bootstrap(paths, bs_exists)
|
||||||
|
|
||||||
# run the clone command
|
# run the clone command
|
||||||
args = ["clone", "-w", paths.work]
|
args = ['clone', '-w', paths.work]
|
||||||
if bs_param:
|
if bs_param:
|
||||||
args += [bs_param]
|
args += [bs_param]
|
||||||
args += [f"file://{paths.remote}"]
|
args += [f'file://{paths.remote}']
|
||||||
expect = []
|
expect = []
|
||||||
if answer:
|
if answer:
|
||||||
expect.append(("Would you like to execute it now", answer))
|
expect.append(('Would you like to execute it now', answer))
|
||||||
run = runner(command=yadm_cmd(*args), expect=expect)
|
run = runner(command=yadm_cmd(*args), expect=expect)
|
||||||
|
|
||||||
if answer:
|
if answer:
|
||||||
assert "Would you like to execute it now" in run.out
|
assert 'Would you like to execute it now' in run.out
|
||||||
|
|
||||||
expected_code = 0
|
expected_code = 0
|
||||||
if bs_exists and bs_param != "--no-bootstrap":
|
if bs_exists and bs_param != '--no-bootstrap':
|
||||||
expected_code = BOOTSTRAP_CODE
|
expected_code = BOOTSTRAP_CODE
|
||||||
|
|
||||||
if answer == "y":
|
if answer == 'y':
|
||||||
expected_code = BOOTSTRAP_CODE
|
expected_code = BOOTSTRAP_CODE
|
||||||
assert BOOTSTRAP_MSG in run.out
|
assert BOOTSTRAP_MSG in run.out
|
||||||
elif answer == "n":
|
elif answer == 'n':
|
||||||
expected_code = 0
|
expected_code = 0
|
||||||
assert BOOTSTRAP_MSG not in run.out
|
assert BOOTSTRAP_MSG not in run.out
|
||||||
|
|
||||||
assert successful_clone(run, paths, repo_config, expected_code)
|
assert successful_clone(run, paths, repo_config, expected_code)
|
||||||
verify_head(paths, "master")
|
verify_head(paths, 'master')
|
||||||
|
|
||||||
if not bs_exists:
|
if not bs_exists:
|
||||||
assert BOOTSTRAP_MSG not in run.out
|
assert BOOTSTRAP_MSG not in run.out
|
||||||
|
@ -166,90 +174,108 @@ def test_clone_bootstrap(runner, paths, yadm_cmd, repo_config, bs_exists, bs_par
|
||||||
def create_bootstrap(paths, exists):
|
def create_bootstrap(paths, exists):
|
||||||
"""Create bootstrap file for test"""
|
"""Create bootstrap file for test"""
|
||||||
if exists:
|
if exists:
|
||||||
paths.bootstrap.write("#!/bin/sh\n" f"echo {BOOTSTRAP_MSG}\n" f"exit {BOOTSTRAP_CODE}\n")
|
paths.bootstrap.write(
|
||||||
|
'#!/bin/sh\n'
|
||||||
|
f'echo {BOOTSTRAP_MSG}\n'
|
||||||
|
f'exit {BOOTSTRAP_CODE}\n')
|
||||||
paths.bootstrap.chmod(0o775)
|
paths.bootstrap.chmod(0o775)
|
||||||
assert paths.bootstrap.exists()
|
assert paths.bootstrap.exists()
|
||||||
else:
|
else:
|
||||||
assert not paths.bootstrap.exists()
|
assert not paths.bootstrap.exists()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("remote")
|
@pytest.mark.usefixtures('remote')
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"private_type, in_repo, in_work",
|
'private_type, in_repo, in_work', [
|
||||||
[
|
('ssh', False, True),
|
||||||
("ssh", False, True),
|
('gnupg', False, True),
|
||||||
("gnupg", False, True),
|
('ssh', True, True),
|
||||||
("ssh", True, True),
|
('gnupg', True, True),
|
||||||
("gnupg", True, True),
|
('ssh', True, False),
|
||||||
("ssh", True, False),
|
('gnupg', True, False),
|
||||||
("gnupg", True, False),
|
], ids=[
|
||||||
],
|
'open ssh, not tracked',
|
||||||
ids=[
|
'open gnupg, not tracked',
|
||||||
"open ssh, not tracked",
|
'open ssh, tracked',
|
||||||
"open gnupg, not tracked",
|
'open gnupg, tracked',
|
||||||
"open ssh, tracked",
|
'missing ssh, tracked',
|
||||||
"open gnupg, tracked",
|
'missing gnupg, tracked',
|
||||||
"missing ssh, tracked",
|
])
|
||||||
"missing gnupg, tracked",
|
def test_clone_perms(
|
||||||
],
|
runner, yadm_cmd, paths, repo_config,
|
||||||
)
|
private_type, in_repo, in_work):
|
||||||
def test_clone_perms(runner, yadm_cmd, paths, repo_config, private_type, in_repo, in_work):
|
|
||||||
"""Test clone permission-related functions"""
|
"""Test clone permission-related functions"""
|
||||||
|
|
||||||
# update remote repo to include private data
|
# update remote repo to include private data
|
||||||
if in_repo:
|
if in_repo:
|
||||||
rpath = paths.work.mkdir(f".{private_type}").join("related")
|
rpath = paths.work.mkdir(f'.{private_type}').join('related')
|
||||||
rpath.write("related")
|
rpath.write('related')
|
||||||
os.system(f'GIT_DIR="{paths.remote}" git add {rpath}')
|
os.system(f'GIT_DIR="{paths.remote}" git add {rpath}')
|
||||||
os.system(f'GIT_DIR="{paths.remote}" git commit -m "{rpath}"')
|
os.system(f'GIT_DIR="{paths.remote}" git commit -m "{rpath}"')
|
||||||
rpath.remove()
|
rpath.remove()
|
||||||
|
|
||||||
# ensure local private data is insecure at the start
|
# ensure local private data is insecure at the start
|
||||||
if in_work:
|
if in_work:
|
||||||
pdir = paths.work.join(f".{private_type}")
|
pdir = paths.work.join(f'.{private_type}')
|
||||||
if not pdir.exists():
|
if not pdir.exists():
|
||||||
pdir.mkdir()
|
pdir.mkdir()
|
||||||
pfile = pdir.join("existing")
|
pfile = pdir.join('existing')
|
||||||
pfile.write("existing")
|
pfile.write('existing')
|
||||||
pdir.chmod(0o777)
|
pdir.chmod(0o777)
|
||||||
pfile.chmod(0o777)
|
pfile.chmod(0o777)
|
||||||
else:
|
else:
|
||||||
paths.work.remove()
|
paths.work.remove()
|
||||||
paths.work.mkdir()
|
paths.work.mkdir()
|
||||||
|
|
||||||
env = {"HOME": paths.work}
|
env = {'HOME': paths.work}
|
||||||
run = runner(yadm_cmd("clone", "-d", "-w", paths.work, f"file://{paths.remote}"), env=env)
|
run = runner(
|
||||||
|
yadm_cmd('clone', '-d', '-w', paths.work, f'file://{paths.remote}'),
|
||||||
|
env=env
|
||||||
|
)
|
||||||
|
|
||||||
assert successful_clone(run, paths, repo_config)
|
assert successful_clone(run, paths, repo_config)
|
||||||
verify_head(paths, "master")
|
verify_head(paths, 'master')
|
||||||
if in_work:
|
if in_work:
|
||||||
# private directories which already exist, should be left as they are,
|
# private directories which already exist, should be left as they are,
|
||||||
# which in this test is "insecure".
|
# which in this test is "insecure".
|
||||||
assert re.search(f"initial private dir perms drwxrwxrwx.+.{private_type}", run.out)
|
assert re.search(
|
||||||
assert re.search(f"pre-checkout private dir perms drwxrwxrwx.+.{private_type}", run.out)
|
f'initial private dir perms drwxrwxrwx.+.{private_type}',
|
||||||
assert re.search(f"post-checkout private dir perms drwxrwxrwx.+.{private_type}", run.out)
|
run.out)
|
||||||
|
assert re.search(
|
||||||
|
f'pre-merge private dir perms drwxrwxrwx.+.{private_type}',
|
||||||
|
run.out)
|
||||||
|
assert re.search(
|
||||||
|
f'post-merge private dir perms drwxrwxrwx.+.{private_type}',
|
||||||
|
run.out)
|
||||||
else:
|
else:
|
||||||
# private directories which are created, should be done prior to
|
# private directories which are created, should be done prior to
|
||||||
# checkout, and with secure permissions.
|
# merging, and with secure permissions.
|
||||||
assert "initial private dir perms" not in run.out
|
assert 'initial private dir perms' not in run.out
|
||||||
assert re.search(f"pre-checkout private dir perms drwx------.+.{private_type}", run.out)
|
assert re.search(
|
||||||
assert re.search(f"post-checkout private dir perms drwx------.+.{private_type}", run.out)
|
f'pre-merge private dir perms drwx------.+.{private_type}',
|
||||||
|
run.out)
|
||||||
|
assert re.search(
|
||||||
|
f'post-merge private dir perms drwx------.+.{private_type}',
|
||||||
|
run.out)
|
||||||
|
|
||||||
# standard perms still apply afterwards unless disabled with auto.perms
|
# standard perms still apply afterwards unless disabled with auto.perms
|
||||||
assert oct(paths.work.join(f".{private_type}").stat().mode).endswith(
|
assert oct(
|
||||||
"00"
|
paths.work.join(f'.{private_type}').stat().mode).endswith('00'), (
|
||||||
), f".{private_type} has not been secured by auto.perms"
|
f'.{private_type} has not been secured by auto.perms')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("remote")
|
@pytest.mark.usefixtures('remote')
|
||||||
@pytest.mark.parametrize("branch", ["master", "default", "valid", "invalid"])
|
@pytest.mark.parametrize(
|
||||||
|
'branch', ['master', 'default', 'valid', 'invalid'])
|
||||||
def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch):
|
def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch):
|
||||||
"""Test cloning a branch other than master"""
|
"""Test cloning a branch other than master"""
|
||||||
|
|
||||||
# add a "valid" branch to the remote
|
# add a "valid" branch to the remote
|
||||||
os.system(f'GIT_DIR="{paths.remote}" git checkout -b valid')
|
os.system(f'GIT_DIR="{paths.remote}" git checkout -b valid')
|
||||||
os.system(f'GIT_DIR="{paths.remote}" git commit ' f'--allow-empty -m "This branch is valid"')
|
os.system(
|
||||||
if branch != "default":
|
f'GIT_DIR="{paths.remote}" git commit '
|
||||||
|
f'--allow-empty -m "This branch is valid"')
|
||||||
|
if branch != 'default':
|
||||||
# When branch == 'default', the "default" branch of the remote repo
|
# When branch == 'default', the "default" branch of the remote repo
|
||||||
# will remain "valid" to validate identification the correct default
|
# will remain "valid" to validate identification the correct default
|
||||||
# branch by inspecting the repo. Otherwise it will be set back to
|
# branch by inspecting the repo. Otherwise it will be set back to
|
||||||
|
@ -260,43 +286,46 @@ def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch):
|
||||||
paths.work.remove()
|
paths.work.remove()
|
||||||
paths.work.mkdir()
|
paths.work.mkdir()
|
||||||
|
|
||||||
remote_url = f"file://{paths.remote}"
|
remote_url = f'file://{paths.remote}'
|
||||||
|
|
||||||
# run the clone command
|
# run the clone command
|
||||||
args = ["clone", "-w", paths.work]
|
args = ['clone', '-w', paths.work]
|
||||||
if branch not in ["master", "default"]:
|
if branch not in ['master', 'default']:
|
||||||
args += ["-b", branch]
|
args += ['-b', branch]
|
||||||
args += [remote_url]
|
args += [remote_url]
|
||||||
run = runner(command=yadm_cmd(*args))
|
run = runner(command=yadm_cmd(*args))
|
||||||
|
|
||||||
if branch == "invalid":
|
if branch == 'invalid':
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert "ERROR: Unable to clone the repository" in run.err
|
assert 'ERROR: Clone failed' in run.err
|
||||||
assert f"Remote branch {branch} not found in upstream" in run.err
|
assert f"'origin/{branch}' does not exist in {remote_url}" in run.err
|
||||||
else:
|
else:
|
||||||
assert successful_clone(run, paths, repo_config)
|
assert successful_clone(run, paths, repo_config)
|
||||||
|
|
||||||
# confirm correct Git origin
|
# confirm correct Git origin
|
||||||
run = runner(command=("git", "remote", "-v", "show"), env={"GIT_DIR": paths.repo})
|
run = runner(
|
||||||
|
command=('git', 'remote', '-v', 'show'),
|
||||||
|
env={'GIT_DIR': paths.repo})
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert f"origin\t{remote_url}" in run.out
|
assert f'origin\t{remote_url}' in run.out
|
||||||
run = runner(command=yadm_cmd("show"))
|
run = runner(command=yadm_cmd('show'))
|
||||||
if branch == "master":
|
if branch == 'master':
|
||||||
assert "Initial commit" in run.out
|
assert 'Initial commit' in run.out
|
||||||
verify_head(paths, "master")
|
verify_head(paths, 'master')
|
||||||
else:
|
else:
|
||||||
assert "This branch is valid" in run.out
|
assert 'This branch is valid' in run.out
|
||||||
verify_head(paths, "valid")
|
verify_head(paths, 'valid')
|
||||||
|
|
||||||
|
|
||||||
def successful_clone(run, paths, repo_config, expected_code=0):
|
def successful_clone(run, paths, repo_config, expected_code=0):
|
||||||
"""Assert clone is successful"""
|
"""Assert clone is successful"""
|
||||||
assert run.code == expected_code
|
assert run.code == expected_code
|
||||||
assert oct(paths.repo.stat().mode).endswith("00"), "Repo is not secured"
|
assert 'Initialized' in run.out
|
||||||
assert repo_config("core.bare") == "false"
|
assert oct(paths.repo.stat().mode).endswith('00'), 'Repo is not secured'
|
||||||
assert repo_config("status.showUntrackedFiles") == "no"
|
assert repo_config('core.bare') == 'false'
|
||||||
assert repo_config("yadm.managed") == "true"
|
assert repo_config('status.showUntrackedFiles') == 'no'
|
||||||
|
assert repo_config('yadm.managed') == 'true'
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -311,18 +340,14 @@ def remote(paths, ds1_repo_copy):
|
||||||
paths.repo.move(paths.remote)
|
paths.repo.move(paths.remote)
|
||||||
|
|
||||||
|
|
||||||
def test_no_repo(
|
def test_no_repo(runner, yadm_cmd, ):
|
||||||
runner,
|
|
||||||
yadm_cmd,
|
|
||||||
):
|
|
||||||
"""Test cloning without specifying a repo"""
|
"""Test cloning without specifying a repo"""
|
||||||
run = runner(command=yadm_cmd("clone", "-f"))
|
run = runner(command=yadm_cmd('clone'))
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
assert "ERROR: Unable to clone the repository" in run.err
|
assert 'ERROR: No repository provided' in run.err
|
||||||
assert "repository 'repo.git' does not exist" in run.err
|
|
||||||
|
|
||||||
|
|
||||||
def verify_head(paths, branch):
|
def verify_head(paths, branch):
|
||||||
"""Assert the local repo has the correct head branch"""
|
"""Assert the local repo has the correct head branch"""
|
||||||
assert paths.repo.join("HEAD").read() == f"ref: refs/heads/{branch}\n"
|
assert paths.repo.join('HEAD').read() == f'ref: refs/heads/{branch}\n'
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
"""Test config"""
|
"""Test config"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
TEST_SECTION = "test"
|
TEST_SECTION = 'test'
|
||||||
TEST_ATTRIBUTE = "attribute"
|
TEST_ATTRIBUTE = 'attribute'
|
||||||
TEST_KEY = f"{TEST_SECTION}.{TEST_ATTRIBUTE}"
|
TEST_KEY = f'{TEST_SECTION}.{TEST_ATTRIBUTE}'
|
||||||
TEST_VALUE = "testvalue"
|
TEST_VALUE = 'testvalue'
|
||||||
TEST_FILE = f"[{TEST_SECTION}]\n\t{TEST_ATTRIBUTE} = {TEST_VALUE}"
|
TEST_FILE = f'[{TEST_SECTION}]\n\t{TEST_ATTRIBUTE} = {TEST_VALUE}'
|
||||||
|
|
||||||
|
|
||||||
def test_config_no_params(runner, yadm_cmd, supported_configs):
|
def test_config_no_params(runner, yadm_cmd, supported_configs):
|
||||||
|
@ -19,11 +18,11 @@ def test_config_no_params(runner, yadm_cmd, supported_configs):
|
||||||
Exit with 0
|
Exit with 0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
run = runner(yadm_cmd("config"))
|
run = runner(yadm_cmd('config'))
|
||||||
|
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "Please read the CONFIGURATION section" in run.out
|
assert 'Please read the CONFIGURATION section' in run.out
|
||||||
for config in supported_configs:
|
for config in supported_configs:
|
||||||
assert config in run.out
|
assert config in run.out
|
||||||
|
|
||||||
|
@ -35,11 +34,11 @@ def test_config_read_missing(runner, yadm_cmd):
|
||||||
Exit with 0
|
Exit with 0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
run = runner(yadm_cmd("config", TEST_KEY))
|
run = runner(yadm_cmd('config', TEST_KEY))
|
||||||
|
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
|
|
||||||
|
|
||||||
def test_config_write(runner, yadm_cmd, paths):
|
def test_config_write(runner, yadm_cmd, paths):
|
||||||
|
@ -50,11 +49,11 @@ def test_config_write(runner, yadm_cmd, paths):
|
||||||
Exit with 0
|
Exit with 0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
run = runner(yadm_cmd("config", TEST_KEY, TEST_VALUE))
|
run = runner(yadm_cmd('config', TEST_KEY, TEST_VALUE))
|
||||||
|
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
assert paths.config.read().strip() == TEST_FILE
|
assert paths.config.read().strip() == TEST_FILE
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,10 +65,10 @@ def test_config_read(runner, yadm_cmd, paths):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
paths.config.write(TEST_FILE)
|
paths.config.write(TEST_FILE)
|
||||||
run = runner(yadm_cmd("config", TEST_KEY))
|
run = runner(yadm_cmd('config', TEST_KEY))
|
||||||
|
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out.strip() == TEST_VALUE
|
assert run.out.strip() == TEST_VALUE
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,16 +82,16 @@ def test_config_update(runner, yadm_cmd, paths):
|
||||||
|
|
||||||
paths.config.write(TEST_FILE)
|
paths.config.write(TEST_FILE)
|
||||||
|
|
||||||
run = runner(yadm_cmd("config", TEST_KEY, TEST_VALUE + "extra"))
|
run = runner(yadm_cmd('config', TEST_KEY, TEST_VALUE + 'extra'))
|
||||||
|
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
|
|
||||||
assert paths.config.read().strip() == TEST_FILE + "extra"
|
assert paths.config.read().strip() == TEST_FILE + 'extra'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("ds1_repo_copy")
|
@pytest.mark.usefixtures('ds1_repo_copy')
|
||||||
def test_config_local_read(runner, yadm_cmd, paths, supported_local_configs):
|
def test_config_local_read(runner, yadm_cmd, paths, supported_local_configs):
|
||||||
"""Read local attribute
|
"""Read local attribute
|
||||||
|
|
||||||
|
@ -102,17 +101,19 @@ def test_config_local_read(runner, yadm_cmd, paths, supported_local_configs):
|
||||||
|
|
||||||
# populate test values
|
# populate test values
|
||||||
for config in supported_local_configs:
|
for config in supported_local_configs:
|
||||||
os.system(f'GIT_DIR="{paths.repo}" ' f'git config --local "{config}" "value_of_{config}"')
|
os.system(
|
||||||
|
f'GIT_DIR="{paths.repo}" '
|
||||||
|
f'git config --local "{config}" "value_of_{config}"')
|
||||||
|
|
||||||
# run yadm config
|
# run yadm config
|
||||||
for config in supported_local_configs:
|
for config in supported_local_configs:
|
||||||
run = runner(yadm_cmd("config", config))
|
run = runner(yadm_cmd('config', config))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out.strip() == f"value_of_{config}"
|
assert run.out.strip() == f'value_of_{config}'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("ds1_repo_copy")
|
@pytest.mark.usefixtures('ds1_repo_copy')
|
||||||
def test_config_local_write(runner, yadm_cmd, paths, supported_local_configs):
|
def test_config_local_write(runner, yadm_cmd, paths, supported_local_configs):
|
||||||
"""Write local attribute
|
"""Write local attribute
|
||||||
|
|
||||||
|
@ -123,37 +124,40 @@ def test_config_local_write(runner, yadm_cmd, paths, supported_local_configs):
|
||||||
|
|
||||||
# run yadm config
|
# run yadm config
|
||||||
for config in supported_local_configs:
|
for config in supported_local_configs:
|
||||||
run = runner(yadm_cmd("config", config, f"value_of_{config}"))
|
run = runner(yadm_cmd('config', config, f'value_of_{config}'))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
|
|
||||||
# verify test values
|
# verify test values
|
||||||
for config in supported_local_configs:
|
for config in supported_local_configs:
|
||||||
run = runner(command=("git", "config", config), env={"GIT_DIR": paths.repo})
|
run = runner(
|
||||||
|
command=('git', 'config', config),
|
||||||
|
env={'GIT_DIR': paths.repo})
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out.strip() == f"value_of_{config}"
|
assert run.out.strip() == f'value_of_{config}'
|
||||||
|
|
||||||
|
|
||||||
def test_config_without_parent_directory(runner, yadm_cmd, paths):
|
def test_config_without_parent_directory(runner, yadm_cmd, paths):
|
||||||
"""Write/read attribute to/from config file with non-existent parent dir
|
"""Write and read attribute to/from config file with non-existent parent dir
|
||||||
|
|
||||||
Update configuration file
|
Update configuration file
|
||||||
Display value
|
Display value
|
||||||
Exit with 0
|
Exit with 0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
config_file = paths.root + "/folder/does/not/exist/config"
|
config_file = paths.root + '/folder/does/not/exist/config'
|
||||||
|
|
||||||
run = runner(yadm_cmd("--yadm-config", config_file, "config", TEST_KEY, TEST_VALUE))
|
run = runner(
|
||||||
|
yadm_cmd('--yadm-config', config_file, 'config', TEST_KEY, TEST_VALUE))
|
||||||
|
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
|
|
||||||
run = runner(yadm_cmd("--yadm-config", config_file, "config", TEST_KEY))
|
run = runner(yadm_cmd('--yadm-config', config_file, 'config', TEST_KEY))
|
||||||
|
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out.strip() == TEST_VALUE
|
assert run.out.strip() == TEST_VALUE
|
||||||
|
|
27
test/test_default_remote_branch.py
Normal file
27
test/test_default_remote_branch.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
"""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'
|
|
@ -1,31 +1,30 @@
|
||||||
"""Test encryption"""
|
"""Test encryption"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import shlex
|
import pipes
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
KEY_FILE = "test/test_key"
|
KEY_FILE = 'test/test_key'
|
||||||
KEY_FINGERPRINT = "F8BBFC746C58945442349BCEBA54FFD04C599B1A"
|
KEY_FINGERPRINT = 'F8BBFC746C58945442349BCEBA54FFD04C599B1A'
|
||||||
KEY_NAME = "yadm-test1"
|
KEY_NAME = 'yadm-test1'
|
||||||
KEY_TRUST = "test/ownertrust.txt"
|
KEY_TRUST = 'test/ownertrust.txt'
|
||||||
PASSPHRASE = "ExamplePassword"
|
PASSPHRASE = 'ExamplePassword'
|
||||||
|
|
||||||
pytestmark = pytest.mark.usefixtures("config_git")
|
pytestmark = pytest.mark.usefixtures('config_git')
|
||||||
|
|
||||||
|
|
||||||
def add_asymmetric_key(runner, gnupg):
|
def add_asymmetric_key(runner, gnupg):
|
||||||
"""Add asymmetric key"""
|
"""Add asymmetric key"""
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GNUPGHOME"] = gnupg.home
|
env['GNUPGHOME'] = gnupg.home
|
||||||
runner(
|
runner(
|
||||||
["gpg", "--import", shlex.quote(KEY_FILE)],
|
['gpg', '--import', pipes.quote(KEY_FILE)],
|
||||||
env=env,
|
env=env,
|
||||||
shell=True,
|
shell=True,
|
||||||
)
|
)
|
||||||
runner(
|
runner(
|
||||||
["gpg", "--import-ownertrust", "<", shlex.quote(KEY_TRUST)],
|
['gpg', '--import-ownertrust', '<', pipes.quote(KEY_TRUST)],
|
||||||
env=env,
|
env=env,
|
||||||
shell=True,
|
shell=True,
|
||||||
)
|
)
|
||||||
|
@ -34,14 +33,20 @@ def add_asymmetric_key(runner, gnupg):
|
||||||
def remove_asymmetric_key(runner, gnupg):
|
def remove_asymmetric_key(runner, gnupg):
|
||||||
"""Remove asymmetric key"""
|
"""Remove asymmetric key"""
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GNUPGHOME"] = gnupg.home
|
env['GNUPGHOME'] = gnupg.home
|
||||||
runner(
|
runner(
|
||||||
["gpg", "--batch", "--yes", "--delete-secret-keys", shlex.quote(KEY_FINGERPRINT)],
|
[
|
||||||
|
'gpg', '--batch', '--yes',
|
||||||
|
'--delete-secret-keys', pipes.quote(KEY_FINGERPRINT)
|
||||||
|
],
|
||||||
env=env,
|
env=env,
|
||||||
shell=True,
|
shell=True,
|
||||||
)
|
)
|
||||||
runner(
|
runner(
|
||||||
["gpg", "--batch", "--yes", "--delete-key", shlex.quote(KEY_FINGERPRINT)],
|
[
|
||||||
|
'gpg', '--batch', '--yes',
|
||||||
|
'--delete-key', pipes.quote(KEY_FINGERPRINT)
|
||||||
|
],
|
||||||
env=env,
|
env=env,
|
||||||
shell=True,
|
shell=True,
|
||||||
)
|
)
|
||||||
|
@ -73,48 +78,48 @@ def encrypt_targets(yadm_cmd, paths):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# init empty yadm repo
|
# init empty yadm repo
|
||||||
os.system(" ".join(yadm_cmd("init", "-w", str(paths.work), "-f")))
|
os.system(' '.join(yadm_cmd('init', '-w', str(paths.work), '-f')))
|
||||||
|
|
||||||
expected = []
|
expected = []
|
||||||
|
|
||||||
# standard files w/ dirs & spaces
|
# standard files w/ dirs & spaces
|
||||||
paths.work.join("inc file1").write("inc file1")
|
paths.work.join('inc file1').write('inc file1')
|
||||||
expected.append("inc file1")
|
expected.append('inc file1')
|
||||||
paths.encrypt.write("inc file1\n")
|
paths.encrypt.write('inc file1\n')
|
||||||
paths.work.join("inc dir").mkdir()
|
paths.work.join('inc dir').mkdir()
|
||||||
paths.work.join("inc dir/inc file2").write("inc file2")
|
paths.work.join('inc dir/inc file2').write('inc file2')
|
||||||
expected.append("inc dir/inc file2")
|
expected.append('inc dir/inc file2')
|
||||||
paths.encrypt.write("inc dir/inc file2\n", mode="a")
|
paths.encrypt.write('inc dir/inc file2\n', mode='a')
|
||||||
|
|
||||||
# standard globs w/ dirs & spaces
|
# standard globs w/ dirs & spaces
|
||||||
paths.work.join("globs file1").write("globs file1")
|
paths.work.join('globs file1').write('globs file1')
|
||||||
expected.append("globs file1")
|
expected.append('globs file1')
|
||||||
paths.work.join("globs dir").mkdir()
|
paths.work.join('globs dir').mkdir()
|
||||||
paths.work.join("globs dir/globs file2").write("globs file2")
|
paths.work.join('globs dir/globs file2').write('globs file2')
|
||||||
expected.append("globs dir/globs file2")
|
expected.append('globs dir/globs file2')
|
||||||
paths.encrypt.write("globs*\n", mode="a")
|
paths.encrypt.write('globs*\n', mode='a')
|
||||||
|
|
||||||
# blank lines
|
# blank lines
|
||||||
paths.encrypt.write("\n \n\t\n", mode="a")
|
paths.encrypt.write('\n \n\t\n', mode='a')
|
||||||
|
|
||||||
# comments
|
# comments
|
||||||
paths.work.join("commentfile1").write("commentfile1")
|
paths.work.join('commentfile1').write('commentfile1')
|
||||||
paths.encrypt.write("#commentfile1\n", mode="a")
|
paths.encrypt.write('#commentfile1\n', mode='a')
|
||||||
paths.encrypt.write(" #commentfile1\n", mode="a")
|
paths.encrypt.write(' #commentfile1\n', mode='a')
|
||||||
|
|
||||||
# exclusions
|
# exclusions
|
||||||
paths.work.join("extest").mkdir()
|
paths.work.join('extest').mkdir()
|
||||||
paths.encrypt.write("extest/*\n", mode="a") # include within extest
|
paths.encrypt.write('extest/*\n', mode='a') # include within extest
|
||||||
paths.work.join("extest/inglob1").write("inglob1")
|
paths.work.join('extest/inglob1').write('inglob1')
|
||||||
paths.work.join("extest/exglob1").write("exglob1")
|
paths.work.join('extest/exglob1').write('exglob1')
|
||||||
paths.work.join("extest/exglob2").write("exglob2")
|
paths.work.join('extest/exglob2').write('exglob2')
|
||||||
paths.encrypt.write("!extest/ex*\n", mode="a") # exclude the ex*
|
paths.encrypt.write('!extest/ex*\n', mode='a') # exclude the ex*
|
||||||
expected.append("extest/inglob1") # should be left with only in*
|
expected.append('extest/inglob1') # should be left with only in*
|
||||||
|
|
||||||
return expected
|
return expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope='session')
|
||||||
def decrypt_targets(tmpdir_factory, runner, gnupg):
|
def decrypt_targets(tmpdir_factory, runner, gnupg):
|
||||||
"""Fixture for setting data to decrypt
|
"""Fixture for setting data to decrypt
|
||||||
|
|
||||||
|
@ -123,203 +128,234 @@ def decrypt_targets(tmpdir_factory, runner, gnupg):
|
||||||
* creates a list of expected decrypted files
|
* creates a list of expected decrypted files
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tmpdir = tmpdir_factory.mktemp("decrypt_targets")
|
tmpdir = tmpdir_factory.mktemp('decrypt_targets')
|
||||||
symmetric = tmpdir.join("symmetric.tar.gz.gpg")
|
symmetric = tmpdir.join('symmetric.tar.gz.gpg')
|
||||||
asymmetric = tmpdir.join("asymmetric.tar.gz.gpg")
|
asymmetric = tmpdir.join('asymmetric.tar.gz.gpg')
|
||||||
|
|
||||||
expected = []
|
expected = []
|
||||||
|
|
||||||
tmpdir.join("decrypt1").write("decrypt1")
|
tmpdir.join('decrypt1').write('decrypt1')
|
||||||
expected.append("decrypt1")
|
expected.append('decrypt1')
|
||||||
tmpdir.join("decrypt2").write("decrypt2")
|
tmpdir.join('decrypt2').write('decrypt2')
|
||||||
expected.append("decrypt2")
|
expected.append('decrypt2')
|
||||||
tmpdir.join("subdir").mkdir()
|
tmpdir.join('subdir').mkdir()
|
||||||
tmpdir.join("subdir/decrypt3").write("subdir/decrypt3")
|
tmpdir.join('subdir/decrypt3').write('subdir/decrypt3')
|
||||||
expected.append("subdir/decrypt3")
|
expected.append('subdir/decrypt3')
|
||||||
|
|
||||||
gnupg.pw(PASSPHRASE)
|
gnupg.pw(PASSPHRASE)
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GNUPGHOME"] = gnupg.home
|
env['GNUPGHOME'] = gnupg.home
|
||||||
run = runner(
|
run = runner(
|
||||||
["tar", "cvf", "-"]
|
['tar', 'cvf', '-'] +
|
||||||
+ expected
|
expected +
|
||||||
+ ["|", "gpg", "--batch", "--yes", "-c"]
|
['|', 'gpg', '--batch', '--yes', '-c'] +
|
||||||
+ ["--output", shlex.quote(str(symmetric))],
|
['--output', pipes.quote(str(symmetric))],
|
||||||
cwd=tmpdir,
|
cwd=tmpdir,
|
||||||
env=env,
|
env=env,
|
||||||
shell=True,
|
shell=True)
|
||||||
)
|
|
||||||
assert run.success
|
assert run.success
|
||||||
|
|
||||||
gnupg.pw("")
|
gnupg.pw('')
|
||||||
add_asymmetric_key(runner, gnupg)
|
add_asymmetric_key(runner, gnupg)
|
||||||
run = runner(
|
run = runner(
|
||||||
["tar", "cvf", "-"]
|
['tar', 'cvf', '-'] +
|
||||||
+ expected
|
expected +
|
||||||
+ ["|", "gpg", "--batch", "--yes", "-e"]
|
['|', 'gpg', '--batch', '--yes', '-e'] +
|
||||||
+ ["-r", shlex.quote(KEY_NAME)]
|
['-r', pipes.quote(KEY_NAME)] +
|
||||||
+ ["--output", shlex.quote(str(asymmetric))],
|
['--output', pipes.quote(str(asymmetric))],
|
||||||
cwd=tmpdir,
|
cwd=tmpdir,
|
||||||
env=env,
|
env=env,
|
||||||
shell=True,
|
shell=True)
|
||||||
)
|
|
||||||
assert run.success
|
assert run.success
|
||||||
remove_asymmetric_key(runner, gnupg)
|
remove_asymmetric_key(runner, gnupg)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"asymmetric": asymmetric,
|
'asymmetric': asymmetric,
|
||||||
"expected": expected,
|
'expected': expected,
|
||||||
"symmetric": symmetric,
|
'symmetric': symmetric,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("bad_phrase", [False, True], ids=["good_phrase", "bad_phrase"])
|
@pytest.mark.parametrize(
|
||||||
@pytest.mark.parametrize("missing_encrypt", [False, True], ids=["encrypt_exists", "encrypt_missing"])
|
'bad_phrase', [False, True],
|
||||||
@pytest.mark.parametrize("overwrite", [False, True], ids=["clean", "overwrite"])
|
ids=['good_phrase', 'bad_phrase'])
|
||||||
def test_symmetric_encrypt(runner, yadm_cmd, paths, encrypt_targets, gnupg, bad_phrase, overwrite, missing_encrypt):
|
@pytest.mark.parametrize(
|
||||||
|
'missing_encrypt', [False, True],
|
||||||
|
ids=['encrypt_exists', 'encrypt_missing'])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'overwrite', [False, True],
|
||||||
|
ids=['clean', 'overwrite'])
|
||||||
|
def test_symmetric_encrypt(
|
||||||
|
runner, yadm_cmd, paths, encrypt_targets,
|
||||||
|
gnupg, bad_phrase, overwrite, missing_encrypt):
|
||||||
"""Test symmetric encryption"""
|
"""Test symmetric encryption"""
|
||||||
|
|
||||||
if missing_encrypt:
|
if missing_encrypt:
|
||||||
paths.encrypt.remove()
|
paths.encrypt.remove()
|
||||||
|
|
||||||
if bad_phrase:
|
if bad_phrase:
|
||||||
gnupg.pw("")
|
gnupg.pw('')
|
||||||
else:
|
else:
|
||||||
gnupg.pw(PASSPHRASE)
|
gnupg.pw(PASSPHRASE)
|
||||||
|
|
||||||
if overwrite:
|
if overwrite:
|
||||||
paths.archive.write("existing archive")
|
paths.archive.write('existing archive')
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GNUPGHOME"] = gnupg.home
|
env['GNUPGHOME'] = gnupg.home
|
||||||
run = runner(yadm_cmd("encrypt"), env=env)
|
run = runner(yadm_cmd('encrypt'), env=env)
|
||||||
|
|
||||||
if missing_encrypt or bad_phrase:
|
if missing_encrypt or bad_phrase:
|
||||||
assert run.failure
|
assert run.failure
|
||||||
else:
|
else:
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
|
|
||||||
if missing_encrypt:
|
if missing_encrypt:
|
||||||
assert "does not exist" in run.err
|
assert 'does not exist' in run.err
|
||||||
elif bad_phrase:
|
elif bad_phrase:
|
||||||
assert "Invalid IPC" in run.err
|
assert 'Invalid passphrase' in run.err
|
||||||
else:
|
else:
|
||||||
assert encrypted_data_valid(runner, gnupg, paths.archive, encrypt_targets)
|
assert encrypted_data_valid(
|
||||||
|
runner, gnupg, paths.archive, encrypt_targets)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("bad_phrase", [False, True], ids=["good_phrase", "bad_phrase"])
|
@pytest.mark.parametrize(
|
||||||
@pytest.mark.parametrize("archive_exists", [True, False], ids=["archive_exists", "archive_missing"])
|
'bad_phrase', [False, True],
|
||||||
@pytest.mark.parametrize("dolist", [False, True], ids=["decrypt", "list"])
|
ids=['good_phrase', 'bad_phrase'])
|
||||||
def test_symmetric_decrypt(runner, yadm_cmd, paths, decrypt_targets, gnupg, dolist, archive_exists, bad_phrase):
|
@pytest.mark.parametrize(
|
||||||
|
'archive_exists', [True, False],
|
||||||
|
ids=['archive_exists', 'archive_missing'])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'dolist', [False, True],
|
||||||
|
ids=['decrypt', 'list'])
|
||||||
|
def test_symmetric_decrypt(
|
||||||
|
runner, yadm_cmd, paths, decrypt_targets, gnupg,
|
||||||
|
dolist, archive_exists, bad_phrase):
|
||||||
"""Test decryption"""
|
"""Test decryption"""
|
||||||
|
|
||||||
# init empty yadm repo
|
# init empty yadm repo
|
||||||
os.system(" ".join(yadm_cmd("init", "-w", str(paths.work), "-f")))
|
os.system(' '.join(yadm_cmd('init', '-w', str(paths.work), '-f')))
|
||||||
|
|
||||||
if bad_phrase:
|
if bad_phrase:
|
||||||
gnupg.pw("")
|
gnupg.pw('')
|
||||||
time.sleep(1) # allow gpg-agent cache to expire
|
time.sleep(1) # allow gpg-agent cache to expire
|
||||||
else:
|
else:
|
||||||
gnupg.pw(PASSPHRASE)
|
gnupg.pw(PASSPHRASE)
|
||||||
|
|
||||||
if archive_exists:
|
if archive_exists:
|
||||||
decrypt_targets["symmetric"].copy(paths.archive)
|
decrypt_targets['symmetric'].copy(paths.archive)
|
||||||
|
|
||||||
# to test overwriting
|
# to test overwriting
|
||||||
paths.work.join("decrypt1").write("pre-existing file")
|
paths.work.join('decrypt1').write('pre-existing file')
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GNUPGHOME"] = gnupg.home
|
env['GNUPGHOME'] = gnupg.home
|
||||||
|
|
||||||
args = []
|
args = []
|
||||||
|
|
||||||
if dolist:
|
if dolist:
|
||||||
args.append("-l")
|
args.append('-l')
|
||||||
run = runner(yadm_cmd("decrypt") + args, env=env)
|
run = runner(yadm_cmd('decrypt') + args, env=env)
|
||||||
|
|
||||||
if archive_exists and not bad_phrase:
|
if archive_exists and not bad_phrase:
|
||||||
assert run.success
|
assert run.success
|
||||||
assert "encrypted with 1 passphrase" in run.err
|
assert 'encrypted with 1 passphrase' in run.err
|
||||||
if dolist:
|
if dolist:
|
||||||
for filename in decrypt_targets["expected"]:
|
for filename in decrypt_targets['expected']:
|
||||||
if filename != "decrypt1": # this one should exist
|
if filename != 'decrypt1': # this one should exist
|
||||||
assert not paths.work.join(filename).exists()
|
assert not paths.work.join(filename).exists()
|
||||||
assert filename in run.out
|
assert filename in run.out
|
||||||
else:
|
else:
|
||||||
for filename in decrypt_targets["expected"]:
|
for filename in decrypt_targets['expected']:
|
||||||
assert paths.work.join(filename).read() == filename
|
assert paths.work.join(filename).read() == filename
|
||||||
else:
|
else:
|
||||||
assert run.failure
|
assert run.failure
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("asymmetric_key")
|
@pytest.mark.usefixtures('asymmetric_key')
|
||||||
@pytest.mark.parametrize("ask", [False, True], ids=["no_ask", "ask"])
|
@pytest.mark.parametrize(
|
||||||
@pytest.mark.parametrize("key_exists", [True, False], ids=["key_exists", "key_missing"])
|
'ask', [False, True],
|
||||||
@pytest.mark.parametrize("overwrite", [False, True], ids=["clean", "overwrite"])
|
ids=['no_ask', 'ask'])
|
||||||
def test_asymmetric_encrypt(runner, yadm_cmd, paths, encrypt_targets, gnupg, overwrite, key_exists, ask):
|
@pytest.mark.parametrize(
|
||||||
|
'key_exists', [True, False],
|
||||||
|
ids=['key_exists', 'key_missing'])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'overwrite', [False, True],
|
||||||
|
ids=['clean', 'overwrite'])
|
||||||
|
def test_asymmetric_encrypt(
|
||||||
|
runner, yadm_cmd, paths, encrypt_targets, gnupg,
|
||||||
|
overwrite, key_exists, ask):
|
||||||
"""Test asymmetric encryption"""
|
"""Test asymmetric encryption"""
|
||||||
|
|
||||||
# specify encryption recipient
|
# specify encryption recipient
|
||||||
if ask:
|
if ask:
|
||||||
os.system(" ".join(yadm_cmd("config", "yadm.gpg-recipient", "ASK")))
|
os.system(' '.join(yadm_cmd('config', 'yadm.gpg-recipient', 'ASK')))
|
||||||
expect = [("Enter the user ID", KEY_NAME), ("Enter the user ID", "")]
|
expect = [('Enter the user ID', KEY_NAME), ('Enter the user ID', '')]
|
||||||
else:
|
else:
|
||||||
os.system(" ".join(yadm_cmd("config", "yadm.gpg-recipient", KEY_NAME)))
|
os.system(' '.join(yadm_cmd('config', 'yadm.gpg-recipient', KEY_NAME)))
|
||||||
expect = []
|
expect = []
|
||||||
|
|
||||||
if overwrite:
|
if overwrite:
|
||||||
paths.archive.write("existing archive")
|
paths.archive.write('existing archive')
|
||||||
|
|
||||||
if not key_exists:
|
if not key_exists:
|
||||||
remove_asymmetric_key(runner, gnupg)
|
remove_asymmetric_key(runner, gnupg)
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GNUPGHOME"] = gnupg.home
|
env['GNUPGHOME'] = gnupg.home
|
||||||
|
|
||||||
run = runner(yadm_cmd("encrypt"), env=env, expect=expect)
|
run = runner(yadm_cmd('encrypt'), env=env, expect=expect)
|
||||||
|
|
||||||
if key_exists:
|
if key_exists:
|
||||||
assert run.success
|
assert run.success
|
||||||
assert encrypted_data_valid(runner, gnupg, paths.archive, encrypt_targets)
|
assert encrypted_data_valid(
|
||||||
|
runner, gnupg, paths.archive, encrypt_targets)
|
||||||
else:
|
else:
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert "Unable to write" in run.out if expect else run.err
|
assert 'Unable to write' in run.out if expect else run.err
|
||||||
|
|
||||||
if ask:
|
if ask:
|
||||||
assert "Enter the user ID" in run.out
|
assert 'Enter the user ID' in run.out
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("asymmetric_key")
|
@pytest.mark.usefixtures('asymmetric_key')
|
||||||
@pytest.mark.usefixtures("encrypt_targets")
|
@pytest.mark.usefixtures('encrypt_targets')
|
||||||
def test_multi_key(runner, yadm_cmd, gnupg):
|
def test_multi_key(runner, yadm_cmd, gnupg):
|
||||||
"""Test multiple recipients"""
|
"""Test multiple recipients"""
|
||||||
|
|
||||||
# specify two encryption recipient
|
# specify two encryption recipient
|
||||||
os.system(" ".join(yadm_cmd("config", "yadm.gpg-recipient", f'"second-key {KEY_NAME}"')))
|
os.system(' '.join(yadm_cmd(
|
||||||
|
'config', 'yadm.gpg-recipient', f'"{KEY_NAME} second-key"')))
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GNUPGHOME"] = gnupg.home
|
env['GNUPGHOME'] = gnupg.home
|
||||||
|
|
||||||
run = runner(yadm_cmd("encrypt"), env=env)
|
run = runner(yadm_cmd('encrypt'), env=env)
|
||||||
|
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert "second-key: skipped: No public key" in run.err
|
assert 'second-key: skipped: No public key' in run.err
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("asymmetric_key")
|
@pytest.mark.usefixtures('asymmetric_key')
|
||||||
@pytest.mark.parametrize("key_exists", [True, False], ids=["key_exists", "key_missing"])
|
@pytest.mark.parametrize(
|
||||||
@pytest.mark.parametrize("dolist", [False, True], ids=["decrypt", "list"])
|
'key_exists', [True, False],
|
||||||
def test_asymmetric_decrypt(runner, yadm_cmd, paths, decrypt_targets, gnupg, dolist, key_exists):
|
ids=['key_exists', 'key_missing'])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'dolist', [False, True],
|
||||||
|
ids=['decrypt', 'list'])
|
||||||
|
def test_asymmetric_decrypt(
|
||||||
|
runner, yadm_cmd, paths, decrypt_targets, gnupg,
|
||||||
|
dolist, key_exists):
|
||||||
"""Test decryption"""
|
"""Test decryption"""
|
||||||
|
|
||||||
# init empty yadm repo
|
# init empty yadm repo
|
||||||
os.system(" ".join(yadm_cmd("init", "-w", str(paths.work), "-f")))
|
os.system(' '.join(yadm_cmd('init', '-w', str(paths.work), '-f')))
|
||||||
|
|
||||||
decrypt_targets["asymmetric"].copy(paths.archive)
|
decrypt_targets['asymmetric'].copy(paths.archive)
|
||||||
|
|
||||||
# to test overwriting
|
# to test overwriting
|
||||||
paths.work.join("decrypt1").write("pre-existing file")
|
paths.work.join('decrypt1').write('pre-existing file')
|
||||||
|
|
||||||
if not key_exists:
|
if not key_exists:
|
||||||
remove_asymmetric_key(runner, gnupg)
|
remove_asymmetric_key(runner, gnupg)
|
||||||
|
@ -327,28 +363,32 @@ def test_asymmetric_decrypt(runner, yadm_cmd, paths, decrypt_targets, gnupg, dol
|
||||||
args = []
|
args = []
|
||||||
|
|
||||||
if dolist:
|
if dolist:
|
||||||
args.append("-l")
|
args.append('-l')
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GNUPGHOME"] = gnupg.home
|
env['GNUPGHOME'] = gnupg.home
|
||||||
run = runner(yadm_cmd("decrypt") + args, env=env)
|
run = runner(yadm_cmd('decrypt') + args, env=env)
|
||||||
|
|
||||||
if key_exists:
|
if key_exists:
|
||||||
assert run.success
|
assert run.success
|
||||||
if dolist:
|
if dolist:
|
||||||
for filename in decrypt_targets["expected"]:
|
for filename in decrypt_targets['expected']:
|
||||||
if filename != "decrypt1": # this one should exist
|
if filename != 'decrypt1': # this one should exist
|
||||||
assert not paths.work.join(filename).exists()
|
assert not paths.work.join(filename).exists()
|
||||||
assert filename in run.out
|
assert filename in run.out
|
||||||
else:
|
else:
|
||||||
for filename in decrypt_targets["expected"]:
|
for filename in decrypt_targets['expected']:
|
||||||
assert paths.work.join(filename).read() == filename
|
assert paths.work.join(filename).read() == filename
|
||||||
else:
|
else:
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert "Unable to extract encrypted files" in run.err
|
assert 'Unable to extract encrypted files' in run.err
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("untracked", [False, "y", "n"], ids=["tracked", "untracked_answer_y", "untracked_answer_n"])
|
@pytest.mark.parametrize(
|
||||||
def test_offer_to_add(runner, yadm_cmd, paths, encrypt_targets, gnupg, untracked):
|
'untracked',
|
||||||
|
[False, 'y', 'n'],
|
||||||
|
ids=['tracked', 'untracked_answer_y', 'untracked_answer_n'])
|
||||||
|
def test_offer_to_add(
|
||||||
|
runner, yadm_cmd, paths, encrypt_targets, gnupg, untracked):
|
||||||
"""Test offer to add encrypted archive
|
"""Test offer to add encrypted archive
|
||||||
|
|
||||||
All the other encryption tests use an archive outside of the work tree.
|
All the other encryption tests use an archive outside of the work tree.
|
||||||
|
@ -356,75 +396,85 @@ def test_offer_to_add(runner, yadm_cmd, paths, encrypt_targets, gnupg, untracked
|
||||||
should be an offer to add it to the repo if it is not tracked.
|
should be an offer to add it to the repo if it is not tracked.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
worktree_archive = paths.work.join("worktree-archive.tar.gpg")
|
worktree_archive = paths.work.join('worktree-archive.tar.gpg')
|
||||||
|
|
||||||
expect = []
|
expect = []
|
||||||
|
|
||||||
gnupg.pw(PASSPHRASE)
|
gnupg.pw(PASSPHRASE)
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GNUPGHOME"] = gnupg.home
|
env['GNUPGHOME'] = gnupg.home
|
||||||
|
|
||||||
if untracked:
|
if untracked:
|
||||||
expect.append(("add it now", untracked))
|
expect.append(('add it now', untracked))
|
||||||
else:
|
else:
|
||||||
worktree_archive.write("exists")
|
worktree_archive.write('exists')
|
||||||
os.system(" ".join(yadm_cmd("add", str(worktree_archive))))
|
os.system(' '.join(yadm_cmd('add', str(worktree_archive))))
|
||||||
|
|
||||||
run = runner(yadm_cmd("encrypt", "--yadm-archive", str(worktree_archive)), env=env, expect=expect)
|
run = runner(
|
||||||
|
yadm_cmd('encrypt', '--yadm-archive', str(worktree_archive)),
|
||||||
|
env=env,
|
||||||
|
expect=expect
|
||||||
|
)
|
||||||
|
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert encrypted_data_valid(runner, gnupg, worktree_archive, encrypt_targets)
|
assert encrypted_data_valid(
|
||||||
|
runner, gnupg, worktree_archive, encrypt_targets)
|
||||||
|
|
||||||
run = runner(yadm_cmd("status", "--porcelain", "-uall", str(worktree_archive)))
|
run = runner(
|
||||||
|
yadm_cmd('status', '--porcelain', '-uall', str(worktree_archive)))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
|
|
||||||
if untracked == "y":
|
if untracked == 'y':
|
||||||
# should be added to the index
|
# should be added to the index
|
||||||
assert f"A {worktree_archive.basename}" in run.out
|
assert f'A {worktree_archive.basename}' in run.out
|
||||||
elif untracked == "n":
|
elif untracked == 'n':
|
||||||
# should NOT be added to the index
|
# should NOT be added to the index
|
||||||
assert f"?? {worktree_archive.basename}" in run.out
|
assert f'?? {worktree_archive.basename}' in run.out
|
||||||
else:
|
else:
|
||||||
# should appear modified in the index
|
# should appear modified in the index
|
||||||
assert f"AM {worktree_archive.basename}" in run.out
|
assert f'AM {worktree_archive.basename}' in run.out
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
def test_encrypt_added_to_exclude(runner, yadm_cmd, paths, gnupg):
|
def test_encrypt_added_to_exclude(runner, yadm_cmd, paths, gnupg):
|
||||||
"""Confirm that .config/yadm/encrypt is added to exclude"""
|
"""Confirm that .config/yadm/encrypt is added to exclude"""
|
||||||
|
|
||||||
gnupg.pw(PASSPHRASE)
|
gnupg.pw(PASSPHRASE)
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GNUPGHOME"] = gnupg.home
|
env['GNUPGHOME'] = gnupg.home
|
||||||
|
|
||||||
exclude_file = paths.repo.join("info/exclude")
|
exclude_file = paths.repo.join('info/exclude')
|
||||||
paths.encrypt.write("test-encrypt-data\n")
|
paths.encrypt.write('test-encrypt-data\n')
|
||||||
paths.work.join("test-encrypt-data").write("")
|
paths.work.join('test-encrypt-data').write('')
|
||||||
exclude_file.write("original-data", ensure=True)
|
exclude_file.write('original-data', ensure=True)
|
||||||
|
|
||||||
run = runner(yadm_cmd("encrypt"), env=env)
|
run = runner(yadm_cmd('encrypt'), env=env)
|
||||||
|
|
||||||
assert "test-encrypt-data" in paths.repo.join("info/exclude").read()
|
assert 'test-encrypt-data' in paths.repo.join('info/exclude').read()
|
||||||
assert "original-data" in paths.repo.join("info/exclude").read()
|
assert 'original-data' in paths.repo.join('info/exclude').read()
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
|
|
||||||
|
|
||||||
def encrypted_data_valid(runner, gnupg, encrypted, expected):
|
def encrypted_data_valid(runner, gnupg, encrypted, expected):
|
||||||
"""Verify encrypted data matches expectations"""
|
"""Verify encrypted data matches expectations"""
|
||||||
gnupg.pw(PASSPHRASE)
|
gnupg.pw(PASSPHRASE)
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["GNUPGHOME"] = gnupg.home
|
env['GNUPGHOME'] = gnupg.home
|
||||||
run = runner(
|
run = runner([
|
||||||
["gpg", "-d", shlex.quote(str(encrypted)), "2>/dev/null", "|", "tar", "t"], env=env, shell=True, report=False
|
'gpg',
|
||||||
)
|
'-d', pipes.quote(str(encrypted)),
|
||||||
|
'2>/dev/null',
|
||||||
|
'|', 'tar', 't'], env=env, shell=True, report=False)
|
||||||
file_count = 0
|
file_count = 0
|
||||||
for filename in run.out.splitlines():
|
for filename in run.out.splitlines():
|
||||||
if filename.endswith("/"):
|
if filename.endswith('/'):
|
||||||
continue
|
continue
|
||||||
file_count += 1
|
file_count += 1
|
||||||
assert filename in expected, f"Unexpected file in archive: {filename}"
|
assert filename in expected, (
|
||||||
assert file_count == len(expected), "Number of files in archive does not match expected"
|
f'Unexpected file in archive: {filename}')
|
||||||
|
assert file_count == len(expected), (
|
||||||
|
'Number of files in archive does not match expected')
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -1,117 +1,110 @@
|
||||||
"""Test enter"""
|
"""Test enter"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"shell, success",
|
'shell, success', [
|
||||||
[
|
('delete', True), # if there is no shell variable, bash creates it
|
||||||
("delete", True), # if there is no shell variable, bash creates it
|
('', False),
|
||||||
("", False),
|
('/usr/bin/env', True),
|
||||||
("/usr/bin/env", True),
|
('noexec', False),
|
||||||
("noexec", False),
|
], ids=[
|
||||||
],
|
'shell-missing',
|
||||||
ids=[
|
'shell-empty',
|
||||||
"shell-missing",
|
'shell-env',
|
||||||
"shell-empty",
|
'shell-noexec',
|
||||||
"shell-env",
|
])
|
||||||
"shell-noexec",
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
],
|
|
||||||
)
|
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
|
||||||
def test_enter(runner, yadm_cmd, paths, shell, success):
|
def test_enter(runner, yadm_cmd, paths, shell, success):
|
||||||
"""Enter tests"""
|
"""Enter tests"""
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
if shell == "delete":
|
if shell == 'delete':
|
||||||
# remove shell
|
# remove shell
|
||||||
if "SHELL" in env:
|
if 'SHELL' in env:
|
||||||
del env["SHELL"]
|
del env['SHELL']
|
||||||
elif shell == "noexec":
|
elif shell == 'noexec':
|
||||||
# specify a non-executable path
|
# specify a non-executable path
|
||||||
noexec = paths.root.join("noexec")
|
noexec = paths.root.join('noexec')
|
||||||
noexec.write("")
|
noexec.write('')
|
||||||
noexec.chmod(0o664)
|
noexec.chmod(0o664)
|
||||||
env["SHELL"] = str(noexec)
|
env['SHELL'] = str(noexec)
|
||||||
else:
|
else:
|
||||||
env["SHELL"] = shell
|
env['SHELL'] = shell
|
||||||
|
|
||||||
run = runner(command=yadm_cmd("enter"), env=env)
|
run = runner(command=yadm_cmd('enter'), env=env)
|
||||||
assert run.success == success
|
assert run.success == success
|
||||||
prompt = f"yadm shell ({paths.repo})"
|
prompt = f'yadm shell ({paths.repo})'
|
||||||
if success:
|
if success:
|
||||||
assert run.out.startswith("Entering yadm repo")
|
assert run.out.startswith('Entering yadm repo')
|
||||||
assert run.out.rstrip().endswith("Leaving yadm repo")
|
assert run.out.rstrip().endswith('Leaving yadm repo')
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
else:
|
else:
|
||||||
assert "does not refer to an executable" in run.err
|
assert 'does not refer to an executable' in run.err
|
||||||
if "env" in shell:
|
if 'env' in shell:
|
||||||
assert f"GIT_DIR={paths.repo}" in run.out
|
assert f'GIT_DIR={paths.repo}' in run.out
|
||||||
assert f"GIT_WORK_TREE={paths.work}" in run.out
|
assert f'GIT_WORK_TREE={paths.work}' in run.out
|
||||||
assert f"PROMPT={prompt}" in run.out
|
assert f'PROMPT={prompt}' in run.out
|
||||||
assert f"PS1={prompt}" in run.out
|
assert f'PS1={prompt}' in run.out
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"shell, opts, path",
|
'shell, opts, path', [
|
||||||
[
|
('bash', '--norc', '\\w'),
|
||||||
("bash", "--norc", "\\w"),
|
('csh', '-f', '%~'),
|
||||||
("csh", "-f", "%~"),
|
('zsh', '-f', '%~'),
|
||||||
("zsh", "-f", "%~"),
|
], ids=[
|
||||||
],
|
'bash',
|
||||||
ids=[
|
'csh',
|
||||||
"bash",
|
'zsh',
|
||||||
"csh",
|
])
|
||||||
"zsh",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"cmd",
|
'cmd',
|
||||||
[False, "cmd", "cmd-bad-exit"],
|
[False, 'cmd', 'cmd-bad-exit'],
|
||||||
ids=["no-cmd", "cmd", "cmd-bad-exit"],
|
ids=['no-cmd', 'cmd', 'cmd-bad-exit'])
|
||||||
)
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"term",
|
'term', ['', 'dumb'],
|
||||||
["", "dumb"],
|
ids=['term-empty', 'term-dumb'])
|
||||||
ids=["term-empty", "term-dumb"],
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
)
|
def test_enter_shell_ops(runner, yadm_cmd, paths, shell,
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
opts, path, cmd, term):
|
||||||
def test_enter_shell_ops(runner, yadm_cmd, paths, shell, opts, path, cmd, term):
|
|
||||||
"""Enter tests for specific shell options"""
|
"""Enter tests for specific shell options"""
|
||||||
|
|
||||||
change_exit = "\nfalse" if cmd == "cmd-bad-exit" else ""
|
change_exit = '\nfalse' if cmd == 'cmd-bad-exit' else ''
|
||||||
|
|
||||||
# Create custom shell to detect options passed
|
# Create custom shell to detect options passed
|
||||||
custom_shell = paths.root.join(shell)
|
custom_shell = paths.root.join(shell)
|
||||||
custom_shell.write(f"#!/bin/sh\necho OPTS=$*\necho PROMPT=$PROMPT{change_exit}")
|
custom_shell.write(
|
||||||
|
f'#!/bin/sh\necho OPTS=$*\necho PROMPT=$PROMPT{change_exit}'
|
||||||
|
)
|
||||||
custom_shell.chmod(0o775)
|
custom_shell.chmod(0o775)
|
||||||
|
|
||||||
test_cmd = ["test1", "test2", "test3"]
|
test_cmd = ['test1', 'test2', 'test3']
|
||||||
|
|
||||||
enter_cmd = ["enter"]
|
enter_cmd = ['enter']
|
||||||
if cmd:
|
if cmd:
|
||||||
enter_cmd += test_cmd
|
enter_cmd += test_cmd
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["TERM"] = term
|
env['TERM'] = term
|
||||||
env["SHELL"] = custom_shell
|
env['SHELL'] = custom_shell
|
||||||
|
|
||||||
if shell == "zsh" and term == "dumb":
|
if shell == 'zsh' and term == 'dumb':
|
||||||
opts += " --no-zle"
|
opts += ' --no-zle'
|
||||||
|
|
||||||
run = runner(command=yadm_cmd(*enter_cmd), env=env)
|
run = runner(command=yadm_cmd(*enter_cmd), env=env)
|
||||||
if cmd == "cmd-bad-exit":
|
if cmd == 'cmd-bad-exit':
|
||||||
assert run.failure
|
assert run.failure
|
||||||
else:
|
else:
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert f"OPTS={opts}" in run.out
|
assert f'OPTS={opts}' in run.out
|
||||||
assert f"PROMPT=yadm shell ({paths.repo}) {path} >" in run.out
|
assert f'PROMPT=yadm shell ({paths.repo}) {path} >' in run.out
|
||||||
if cmd:
|
if cmd:
|
||||||
assert "-c " + " ".join(test_cmd) in run.out
|
assert '-c ' + ' '.join(test_cmd) in run.out
|
||||||
assert "Entering yadm repo" not in run.out
|
assert 'Entering yadm repo' not in run.out
|
||||||
assert "Leaving yadm repo" not in run.out
|
assert 'Leaving yadm repo' not in run.out
|
||||||
else:
|
else:
|
||||||
assert "Entering yadm repo" in run.out
|
assert 'Entering yadm repo' in run.out
|
||||||
assert "Leaving yadm repo" in run.out
|
assert 'Leaving yadm repo' in run.out
|
||||||
|
|
|
@ -4,30 +4,28 @@ import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"crypt",
|
'crypt',
|
||||||
[False, "installed", "installed-but-failed"],
|
[False, 'installed', 'installed-but-failed'],
|
||||||
ids=["not-installed", "installed", "installed-but-failed"],
|
ids=['not-installed', 'installed', 'installed-but-failed']
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"cmd,var",
|
'cmd,var', [
|
||||||
[
|
['git_crypt', 'GIT_CRYPT_PROGRAM'],
|
||||||
["git_crypt", "GIT_CRYPT_PROGRAM"],
|
['transcrypt', 'TRANSCRYPT_PROGRAM'],
|
||||||
["transcrypt", "TRANSCRYPT_PROGRAM"],
|
],
|
||||||
],
|
ids=['git-crypt', 'transcrypt'])
|
||||||
ids=["git-crypt", "transcrypt"],
|
|
||||||
)
|
|
||||||
def test_ext_encryption(runner, yadm, paths, tmpdir, crypt, cmd, var):
|
def test_ext_encryption(runner, yadm, paths, tmpdir, crypt, cmd, var):
|
||||||
"""External encryption tests"""
|
"""External encryption tests"""
|
||||||
|
|
||||||
paths.repo.ensure(dir=True)
|
paths.repo.ensure(dir=True)
|
||||||
bindir = tmpdir.mkdir("bin")
|
bindir = tmpdir.mkdir('bin')
|
||||||
pgm = bindir.join("test-ext-crypt")
|
pgm = bindir.join('test-ext-crypt')
|
||||||
|
|
||||||
if crypt:
|
if crypt:
|
||||||
pgm.write("#!/bin/sh\necho ext-crypt ran\n")
|
pgm.write('#!/bin/sh\necho ext-crypt ran\n')
|
||||||
pgm.chmod(0o775)
|
pgm.chmod(0o775)
|
||||||
if crypt == "installed-but-failed":
|
if crypt == 'installed-but-failed':
|
||||||
pgm.write("false\n", mode="a")
|
pgm.write('false\n', mode='a')
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
|
@ -36,15 +34,15 @@ def test_ext_encryption(runner, yadm, paths, tmpdir, crypt, cmd, var):
|
||||||
{cmd} "param1"
|
{cmd} "param1"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
|
|
||||||
if crypt:
|
if crypt:
|
||||||
if crypt == "installed-but-failed":
|
if crypt == 'installed-but-failed':
|
||||||
assert run.failure
|
assert run.failure
|
||||||
else:
|
else:
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.out.strip() == "ext-crypt ran"
|
assert run.out.strip() == 'ext-crypt ran'
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
else:
|
else:
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert f"command '{pgm}' cannot be located" in run.err
|
assert f"command '{pgm}' cannot be located" in run.err
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
"""Test git"""
|
"""Test git"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
def test_git(runner, yadm_cmd, paths):
|
def test_git(runner, yadm_cmd, paths):
|
||||||
"""Test series of passthrough git commands
|
"""Test series of passthrough git commands
|
||||||
|
|
||||||
|
@ -18,42 +17,42 @@ def test_git(runner, yadm_cmd, paths):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# passthru unknown commands to Git
|
# passthru unknown commands to Git
|
||||||
run = runner(command=yadm_cmd("bogus"))
|
run = runner(command=yadm_cmd('bogus'))
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert "git: 'bogus' is not a git command." in run.err
|
assert "git: 'bogus' is not a git command." in run.err
|
||||||
assert "See 'git --help'" in run.err
|
assert "See 'git --help'" in run.err
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
|
|
||||||
# git command 'add' - badfile
|
# git command 'add' - badfile
|
||||||
run = runner(command=yadm_cmd("add", "-v", "does_not_exist"))
|
run = runner(command=yadm_cmd('add', '-v', 'does_not_exist'))
|
||||||
assert run.code == 128
|
assert run.code == 128
|
||||||
assert "pathspec 'does_not_exist' did not match any files" in run.err
|
assert "pathspec 'does_not_exist' did not match any files" in run.err
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
|
|
||||||
# git command 'add'
|
# git command 'add'
|
||||||
newfile = paths.work.join("test_git")
|
newfile = paths.work.join('test_git')
|
||||||
newfile.write("test_git")
|
newfile.write('test_git')
|
||||||
run = runner(command=yadm_cmd("add", "-v", str(newfile)))
|
run = runner(command=yadm_cmd('add', '-v', str(newfile)))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "add 'test_git'" in run.out
|
assert "add 'test_git'" in run.out
|
||||||
|
|
||||||
# git command 'status'
|
# git command 'status'
|
||||||
run = runner(command=yadm_cmd("status"))
|
run = runner(command=yadm_cmd('status'))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert re.search(r"new file:\s+test_git", run.out)
|
assert re.search(r'new file:\s+test_git', run.out)
|
||||||
|
|
||||||
# git command 'commit'
|
# git command 'commit'
|
||||||
run = runner(command=yadm_cmd("commit", "-m", "Add test_git"))
|
run = runner(command=yadm_cmd('commit', '-m', 'Add test_git'))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "1 file changed" in run.out
|
assert '1 file changed' in run.out
|
||||||
assert "1 insertion" in run.out
|
assert '1 insertion' in run.out
|
||||||
assert re.search(r"create mode .+ test_git", run.out)
|
assert re.search(r'create mode .+ test_git', run.out)
|
||||||
|
|
||||||
# git command 'log'
|
# git command 'log'
|
||||||
run = runner(command=yadm_cmd("log", "--oneline"))
|
run = runner(command=yadm_cmd('log', '--oneline'))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "Add test_git" in run.out
|
assert 'Add test_git' in run.out
|
||||||
|
|
|
@ -6,14 +6,14 @@ def test_missing_command(runner, yadm_cmd):
|
||||||
"""Run without any command"""
|
"""Run without any command"""
|
||||||
run = runner(command=yadm_cmd())
|
run = runner(command=yadm_cmd())
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out.startswith("Usage: yadm")
|
assert run.out.startswith('Usage: yadm')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("cmd", ["--help", "help"])
|
@pytest.mark.parametrize('cmd', ['--help', 'help'])
|
||||||
def test_help_command(runner, yadm_cmd, cmd):
|
def test_help_command(runner, yadm_cmd, cmd):
|
||||||
"""Run with help command"""
|
"""Run with help command"""
|
||||||
run = runner(command=yadm_cmd(cmd))
|
run = runner(command=yadm_cmd(cmd))
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out.startswith("Usage: yadm")
|
assert run.out.startswith('Usage: yadm')
|
||||||
|
|
|
@ -4,8 +4,7 @@ import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"pre, pre_code, post, post_code",
|
'pre, pre_code, post, post_code', [
|
||||||
[
|
|
||||||
(False, 0, False, 0),
|
(False, 0, False, 0),
|
||||||
(True, 0, False, 0),
|
(True, 0, False, 0),
|
||||||
(True, 5, False, 0),
|
(True, 5, False, 0),
|
||||||
|
@ -13,83 +12,83 @@ import pytest
|
||||||
(False, 0, True, 5),
|
(False, 0, True, 5),
|
||||||
(True, 0, True, 0),
|
(True, 0, True, 0),
|
||||||
(True, 5, True, 5),
|
(True, 5, True, 5),
|
||||||
],
|
], ids=[
|
||||||
ids=[
|
'no-hooks',
|
||||||
"no-hooks",
|
'pre-success',
|
||||||
"pre-success",
|
'pre-fail',
|
||||||
"pre-fail",
|
'post-success',
|
||||||
"post-success",
|
'post-fail',
|
||||||
"post-fail",
|
'pre-post-success',
|
||||||
"pre-post-success",
|
'pre-post-fail',
|
||||||
"pre-post-fail",
|
])
|
||||||
],
|
@pytest.mark.parametrize('cmd', ['--version', 'version'])
|
||||||
)
|
def test_hooks(
|
||||||
@pytest.mark.parametrize("cmd", ["--version", "version"])
|
runner, yadm_cmd, paths, cmd,
|
||||||
def test_hooks(runner, yadm_cmd, paths, cmd, pre, pre_code, post, post_code):
|
pre, pre_code, post, post_code):
|
||||||
"""Test pre/post hook"""
|
"""Test pre/post hook"""
|
||||||
|
|
||||||
# generate hooks
|
# generate hooks
|
||||||
if pre:
|
if pre:
|
||||||
create_hook(paths, "pre_version", pre_code)
|
create_hook(paths, 'pre_version', pre_code)
|
||||||
if post:
|
if post:
|
||||||
create_hook(paths, "post_version", post_code)
|
create_hook(paths, 'post_version', post_code)
|
||||||
|
|
||||||
# run yadm
|
# run yadm
|
||||||
run = runner(yadm_cmd(cmd))
|
run = runner(yadm_cmd(cmd))
|
||||||
# when a pre hook fails, yadm should exit with the hook's code
|
# when a pre hook fails, yadm should exit with the hook's code
|
||||||
assert run.code == pre_code
|
assert run.code == pre_code
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
|
|
||||||
if pre:
|
if pre:
|
||||||
assert "HOOK:pre_version" in run.out
|
assert 'HOOK:pre_version' in run.out
|
||||||
# if pre hook is missing or successful, yadm itself should exit 0
|
# if pre hook is missing or successful, yadm itself should exit 0
|
||||||
if run.success:
|
if run.success:
|
||||||
if post:
|
if post:
|
||||||
assert "HOOK:post_version" in run.out
|
assert 'HOOK:post_version' in run.out
|
||||||
else:
|
else:
|
||||||
# when a pre hook fails, yadm should not run the command
|
# when a pre hook fails, yadm should not run the command
|
||||||
assert "version will not be run" in run.out
|
assert 'version will not be run' in run.out
|
||||||
# when a pre hook fails, yadm should not run the post hook
|
# when a pre hook fails, yadm should not run the post hook
|
||||||
assert "HOOK:post_version" not in run.out
|
assert 'HOOK:post_version' not in run.out
|
||||||
|
|
||||||
|
|
||||||
# repo fixture is needed to test the population of YADM_HOOK_WORK
|
# repo fixture is needed to test the population of YADM_HOOK_WORK
|
||||||
@pytest.mark.usefixtures("ds1_repo_copy")
|
@pytest.mark.usefixtures('ds1_repo_copy')
|
||||||
def test_hook_env(runner, yadm_cmd, paths):
|
def test_hook_env(runner, yadm_cmd, paths):
|
||||||
"""Test hook environment"""
|
"""Test hook environment"""
|
||||||
|
|
||||||
# test will be done with a non existent "git" passthru command
|
# test will be done with a non existent "git" passthru command
|
||||||
# which should exit with a failing code
|
# which should exit with a failing code
|
||||||
cmd = "passthrucmd"
|
cmd = 'passthrucmd'
|
||||||
|
|
||||||
# write the hook
|
# write the hook
|
||||||
hook = paths.hooks.join(f"post_{cmd}")
|
hook = paths.hooks.join(f'post_{cmd}')
|
||||||
hook.write("#!/bin/bash\nenv\ndeclare\n")
|
hook.write('#!/bin/bash\nenv\ndeclare\n')
|
||||||
hook.chmod(0o755)
|
hook.chmod(0o755)
|
||||||
|
|
||||||
run = runner(yadm_cmd(cmd, "extra_args"))
|
run = runner(yadm_cmd(cmd, 'extra_args'))
|
||||||
|
|
||||||
# expect passthru to fail
|
# expect passthru to fail
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert f"'{cmd}' is not a git command" in run.err
|
assert f"'{cmd}' is not a git command" in run.err
|
||||||
|
|
||||||
# verify hook environment
|
# verify hook environment
|
||||||
assert "YADM_HOOK_EXIT=1\n" in run.out
|
assert 'YADM_HOOK_EXIT=1\n' in run.out
|
||||||
assert f"YADM_HOOK_COMMAND={cmd}\n" in run.out
|
assert f'YADM_HOOK_COMMAND={cmd}\n' in run.out
|
||||||
assert f"YADM_HOOK_DIR={paths.yadm}\n" in run.out
|
assert f'YADM_HOOK_DIR={paths.yadm}\n' in run.out
|
||||||
assert f"YADM_HOOK_FULL_COMMAND={cmd} extra_args\n" in run.out
|
assert f'YADM_HOOK_FULL_COMMAND={cmd} extra_args\n' in run.out
|
||||||
assert f"YADM_HOOK_REPO={paths.repo}\n" in run.out
|
assert f'YADM_HOOK_REPO={paths.repo}\n' in run.out
|
||||||
assert f"YADM_HOOK_WORK={paths.work}\n" in run.out
|
assert f'YADM_HOOK_WORK={paths.work}\n' in run.out
|
||||||
assert "YADM_ENCRYPT_INCLUDE_FILES=\n" in run.out
|
assert 'YADM_ENCRYPT_INCLUDE_FILES=\n' in run.out
|
||||||
|
|
||||||
# verify the hook environment contains certain exported functions
|
# verify the hook environment contains certain exported functions
|
||||||
for func in [
|
for func in [
|
||||||
"builtin_dirname",
|
'builtin_dirname',
|
||||||
"relative_path",
|
'relative_path',
|
||||||
"unix_path",
|
'unix_path',
|
||||||
"mixed_path",
|
'mixed_path',
|
||||||
]:
|
]:
|
||||||
assert f"BASH_FUNC_{func}" in run.out
|
assert f'BASH_FUNC_{func}' in run.out
|
||||||
|
|
||||||
# verify the hook environment contains the list of encrypted files
|
# verify the hook environment contains the list of encrypted files
|
||||||
script = f"""
|
script = f"""
|
||||||
|
@ -99,10 +98,10 @@ def test_hook_env(runner, yadm_cmd, paths):
|
||||||
ENCRYPT_INCLUDE_FILES=(a b c)
|
ENCRYPT_INCLUDE_FILES=(a b c)
|
||||||
invoke_hook "post"
|
invoke_hook "post"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "YADM_ENCRYPT_INCLUDE_FILES=a\nb\nc\n" in run.out
|
assert 'YADM_ENCRYPT_INCLUDE_FILES=a\nb\nc\n' in run.out
|
||||||
|
|
||||||
|
|
||||||
def test_escaped(runner, yadm_cmd, paths):
|
def test_escaped(runner, yadm_cmd, paths):
|
||||||
|
@ -110,33 +109,35 @@ def test_escaped(runner, yadm_cmd, paths):
|
||||||
|
|
||||||
# test will be done with a non existent "git" passthru command
|
# test will be done with a non existent "git" passthru command
|
||||||
# which should exit with a failing code
|
# which should exit with a failing code
|
||||||
cmd = "passthrucmd"
|
cmd = 'passthrucmd'
|
||||||
|
|
||||||
# write the hook
|
# write the hook
|
||||||
hook = paths.hooks.join(f"post_{cmd}")
|
hook = paths.hooks.join(f'post_{cmd}')
|
||||||
hook.write("#!/bin/bash\nenv\n")
|
hook.write('#!/bin/bash\nenv\n')
|
||||||
hook.chmod(0o755)
|
hook.chmod(0o755)
|
||||||
|
|
||||||
run = runner(yadm_cmd(cmd, "a b", "c\td", "e\\f"))
|
run = runner(yadm_cmd(cmd, 'a b', 'c\td', 'e\\f'))
|
||||||
|
|
||||||
# expect passthru to fail
|
# expect passthru to fail
|
||||||
assert run.failure
|
assert run.failure
|
||||||
|
|
||||||
# verify escaped values
|
# verify escaped values
|
||||||
assert f"YADM_HOOK_FULL_COMMAND={cmd} a\\ b c\\\td e\\\\f\n" in run.out
|
assert (
|
||||||
|
f'YADM_HOOK_FULL_COMMAND={cmd} '
|
||||||
|
'a\\ b c\\\td e\\\\f\n') in run.out
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("condition", ["exec", "no-exec", "mingw"])
|
@pytest.mark.parametrize('condition', ['exec', 'no-exec', 'mingw'])
|
||||||
def test_executable(runner, paths, condition):
|
def test_executable(runner, paths, condition):
|
||||||
"""Verify hook must be exectuable"""
|
"""Verify hook must be exectuable"""
|
||||||
cmd = "version"
|
cmd = 'version'
|
||||||
hook = paths.hooks.join(f"pre_{cmd}")
|
hook = paths.hooks.join(f'pre_{cmd}')
|
||||||
hook.write("#!/bin/sh\necho HOOK\n")
|
hook.write('#!/bin/sh\necho HOOK\n')
|
||||||
hook.chmod(0o644)
|
hook.chmod(0o644)
|
||||||
if condition == "exec":
|
if condition == 'exec':
|
||||||
hook.chmod(0o755)
|
hook.chmod(0o755)
|
||||||
|
|
||||||
mingw = 'OPERATING_SYSTEM="MINGWx"' if condition == "mingw" else ""
|
mingw = 'OPERATING_SYSTEM="MINGWx"' if condition == 'mingw' else ''
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {paths.pgm}
|
YADM_TEST=1 source {paths.pgm}
|
||||||
YADM_HOOKS="{paths.hooks}"
|
YADM_HOOKS="{paths.hooks}"
|
||||||
|
@ -144,23 +145,27 @@ def test_executable(runner, paths, condition):
|
||||||
{mingw}
|
{mingw}
|
||||||
invoke_hook "pre"
|
invoke_hook "pre"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
|
|
||||||
if condition != "mingw":
|
if condition != 'mingw':
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
else:
|
else:
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert "Permission denied" in run.err
|
assert 'Permission denied' in run.err
|
||||||
|
|
||||||
if condition == "exec":
|
if condition == 'exec':
|
||||||
assert "HOOK" in run.out
|
assert 'HOOK' in run.out
|
||||||
elif condition == "no-exec":
|
elif condition == 'no-exec':
|
||||||
assert "HOOK" not in run.out
|
assert 'HOOK' not in run.out
|
||||||
|
|
||||||
|
|
||||||
def create_hook(paths, name, code):
|
def create_hook(paths, name, code):
|
||||||
"""Create hook"""
|
"""Create hook"""
|
||||||
hook = paths.hooks.join(name)
|
hook = paths.hooks.join(name)
|
||||||
hook.write("#!/bin/sh\n" f"echo HOOK:{name}\n" f"exit {code}\n")
|
hook.write(
|
||||||
|
'#!/bin/sh\n'
|
||||||
|
f'echo HOOK:{name}\n'
|
||||||
|
f'exit {code}\n'
|
||||||
|
)
|
||||||
hook.chmod(0o755)
|
hook.chmod(0o755)
|
||||||
|
|
|
@ -4,24 +4,22 @@ import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"alt_work, repo_present, force",
|
'alt_work, repo_present, force', [
|
||||||
[
|
|
||||||
(False, False, False),
|
(False, False, False),
|
||||||
(True, False, False),
|
(True, False, False),
|
||||||
(False, True, False),
|
(False, True, False),
|
||||||
(False, True, True),
|
(False, True, True),
|
||||||
(True, True, True),
|
(True, True, True),
|
||||||
],
|
], ids=[
|
||||||
ids=[
|
'simple',
|
||||||
"simple",
|
'-w',
|
||||||
"-w",
|
'existing repo',
|
||||||
"existing repo",
|
'-f',
|
||||||
"-f",
|
'-w & -f',
|
||||||
"-w & -f",
|
])
|
||||||
],
|
@pytest.mark.usefixtures('ds1_work_copy')
|
||||||
)
|
def test_init(
|
||||||
@pytest.mark.usefixtures("ds1_work_copy")
|
runner, yadm_cmd, paths, repo_config, alt_work, repo_present, force):
|
||||||
def test_init(runner, yadm_cmd, paths, repo_config, alt_work, repo_present, force):
|
|
||||||
"""Test init
|
"""Test init
|
||||||
|
|
||||||
Repos should have attribs:
|
Repos should have attribs:
|
||||||
|
@ -33,54 +31,48 @@ def test_init(runner, yadm_cmd, paths, repo_config, alt_work, repo_present, forc
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# these tests will assume this for $HOME
|
# these tests will assume this for $HOME
|
||||||
home = str(paths.root.mkdir("HOME"))
|
home = str(paths.root.mkdir('HOME'))
|
||||||
|
|
||||||
# ds1_work_copy comes WITH an empty repo dir present.
|
# ds1_work_copy comes WITH an empty repo dir present.
|
||||||
old_repo = paths.repo.join("old_repo")
|
old_repo = paths.repo.join('old_repo')
|
||||||
if repo_present:
|
if repo_present:
|
||||||
# Let's put some data in it, so we can confirm that data is gone when
|
# Let's put some data in it, so we can confirm that data is gone when
|
||||||
# forced to be overwritten.
|
# forced to be overwritten.
|
||||||
old_repo.write("old repo data")
|
old_repo.write('old repo data')
|
||||||
assert old_repo.isfile()
|
assert old_repo.isfile()
|
||||||
else:
|
else:
|
||||||
paths.repo.remove()
|
paths.repo.remove()
|
||||||
|
|
||||||
# command args
|
# command args
|
||||||
args = ["init"]
|
args = ['init']
|
||||||
cwd = None
|
|
||||||
if alt_work:
|
if alt_work:
|
||||||
if force:
|
args.extend(['-w', paths.work])
|
||||||
cwd = paths.work.dirname
|
|
||||||
args.extend(["-w", paths.work.basename])
|
|
||||||
else:
|
|
||||||
args.extend(["-w", paths.work])
|
|
||||||
if force:
|
if force:
|
||||||
args.append("-f")
|
args.append('-f')
|
||||||
|
|
||||||
# run init
|
# run init
|
||||||
runner(["git", "config", "--global", "init.defaultBranch", "master"], env={"HOME": home}, cwd=cwd)
|
run = runner(yadm_cmd(*args), env={'HOME': home})
|
||||||
run = runner(yadm_cmd(*args), env={"HOME": home}, cwd=cwd)
|
|
||||||
|
|
||||||
if repo_present and not force:
|
if repo_present and not force:
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert "repo already exists" in run.err
|
assert 'repo already exists' in run.err
|
||||||
assert old_repo.isfile(), "Missing original repo"
|
assert old_repo.isfile(), 'Missing original repo'
|
||||||
else:
|
else:
|
||||||
assert run.success
|
assert run.success
|
||||||
assert "Initialized empty shared Git repository" in run.out
|
assert 'Initialized empty shared Git repository' in run.out
|
||||||
|
assert run.err == ''
|
||||||
|
|
||||||
if repo_present:
|
if repo_present:
|
||||||
assert not old_repo.isfile(), "Original repo still exists"
|
assert not old_repo.isfile(), 'Original repo still exists'
|
||||||
else:
|
|
||||||
assert run.err == ""
|
|
||||||
|
|
||||||
if alt_work:
|
if alt_work:
|
||||||
assert repo_config("core.worktree") == paths.work
|
assert repo_config('core.worktree') == paths.work
|
||||||
else:
|
else:
|
||||||
assert repo_config("core.worktree") == home
|
assert repo_config('core.worktree') == home
|
||||||
|
|
||||||
# uniform repo assertions
|
# uniform repo assertions
|
||||||
assert oct(paths.repo.stat().mode).endswith("00"), "Repo is not secure"
|
assert oct(paths.repo.stat().mode).endswith('00'), (
|
||||||
assert repo_config("core.bare") == "false"
|
'Repo is not secure')
|
||||||
assert repo_config("status.showUntrackedFiles") == "no"
|
assert repo_config('core.bare') == 'false'
|
||||||
assert repo_config("yadm.managed") == "true"
|
assert repo_config('status.showUntrackedFiles') == 'no'
|
||||||
|
assert repo_config('yadm.managed') == 'true'
|
||||||
|
|
|
@ -4,38 +4,38 @@ import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"name",
|
'name', [
|
||||||
[
|
'',
|
||||||
"",
|
'invalid',
|
||||||
"invalid",
|
'commands',
|
||||||
"commands",
|
'configs',
|
||||||
"configs",
|
'repo',
|
||||||
"repo",
|
'switches',
|
||||||
"switches",
|
])
|
||||||
],
|
def test_introspect_category(
|
||||||
)
|
runner, yadm_cmd, paths, name,
|
||||||
def test_introspect_category(runner, yadm_cmd, paths, name, supported_commands, supported_configs, supported_switches):
|
supported_commands, supported_configs, supported_switches):
|
||||||
"""Validate introspection category"""
|
"""Validate introspection category"""
|
||||||
if name:
|
if name:
|
||||||
run = runner(command=yadm_cmd("introspect", name))
|
run = runner(command=yadm_cmd('introspect', name))
|
||||||
else:
|
else:
|
||||||
run = runner(command=yadm_cmd("introspect"))
|
run = runner(command=yadm_cmd('introspect'))
|
||||||
|
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
|
|
||||||
expected = []
|
expected = []
|
||||||
if name == "commands":
|
if name == 'commands':
|
||||||
expected = supported_commands
|
expected = supported_commands
|
||||||
elif name == "configs":
|
elif name == 'configs':
|
||||||
expected = supported_configs
|
expected = supported_configs
|
||||||
elif name == "switches":
|
elif name == 'switches':
|
||||||
expected = supported_switches
|
expected = supported_switches
|
||||||
|
|
||||||
# assert values
|
# assert values
|
||||||
if name in ("", "invalid"):
|
if name in ('', 'invalid'):
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
if name == "repo":
|
if name == 'repo':
|
||||||
assert run.out.rstrip() == paths.repo
|
assert run.out.rstrip() == paths.repo
|
||||||
|
|
||||||
# make sure every expected value is present
|
# make sure every expected value is present
|
||||||
|
|
|
@ -1,34 +1,31 @@
|
||||||
"""Test list"""
|
"""Test list"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"location",
|
'location', [
|
||||||
[
|
'work',
|
||||||
"work",
|
'outside',
|
||||||
"outside",
|
'subdir',
|
||||||
"subdir",
|
])
|
||||||
],
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
)
|
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
|
||||||
def test_list(runner, yadm_cmd, paths, ds1, location):
|
def test_list(runner, yadm_cmd, paths, ds1, location):
|
||||||
"""List tests"""
|
"""List tests"""
|
||||||
if location == "work":
|
if location == 'work':
|
||||||
run_dir = paths.work
|
run_dir = paths.work
|
||||||
elif location == "outside":
|
elif location == 'outside':
|
||||||
run_dir = paths.work.join("..")
|
run_dir = paths.work.join('..')
|
||||||
elif location == "subdir":
|
elif location == 'subdir':
|
||||||
# first directory with tracked data
|
# first directory with tracked data
|
||||||
run_dir = paths.work.join(ds1.tracked_dirs[0])
|
run_dir = paths.work.join(ds1.tracked_dirs[0])
|
||||||
with run_dir.as_cwd():
|
with run_dir.as_cwd():
|
||||||
# test with '-a'
|
# test with '-a'
|
||||||
# should get all tracked files, relative to the work path
|
# should get all tracked files, relative to the work path
|
||||||
run = runner(command=yadm_cmd("list", "-a"))
|
run = runner(command=yadm_cmd('list', '-a'))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
returned_files = set(run.out.splitlines())
|
returned_files = set(run.out.splitlines())
|
||||||
expected_files = {e.path for e in ds1 if e.tracked}
|
expected_files = {e.path for e in ds1 if e.tracked}
|
||||||
assert returned_files == expected_files
|
assert returned_files == expected_files
|
||||||
|
@ -36,14 +33,16 @@ def test_list(runner, yadm_cmd, paths, ds1, location):
|
||||||
# should get all tracked files, relative to the work path unless in a
|
# should get all tracked files, relative to the work path unless in a
|
||||||
# subdir, then those should be a limited set of files, relative to the
|
# subdir, then those should be a limited set of files, relative to the
|
||||||
# subdir
|
# subdir
|
||||||
run = runner(command=yadm_cmd("list"))
|
run = runner(command=yadm_cmd('list'))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
returned_files = set(run.out.splitlines())
|
returned_files = set(run.out.splitlines())
|
||||||
if location == "subdir":
|
if location == 'subdir':
|
||||||
basepath = os.path.basename(os.getcwd())
|
basepath = os.path.basename(os.getcwd())
|
||||||
# only expect files within the subdir
|
# only expect files within the subdir
|
||||||
# names should be relative to subdir
|
# names should be relative to subdir
|
||||||
index = len(basepath) + 1
|
expected_files = {
|
||||||
expected_files = {e.path[index:] for e in ds1 if e.tracked and e.path.startswith(basepath)}
|
e.path[len(basepath)+1:]
|
||||||
|
for e in ds1 if e.tracked and e.path.startswith(basepath)
|
||||||
|
}
|
||||||
assert returned_files == expected_files
|
assert returned_files == expected_files
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
"""Test perms"""
|
"""Test perms"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("autoperms", ["notest", "unset", "true", "false"])
|
@pytest.mark.parametrize('autoperms', ['notest', 'unset', 'true', 'false'])
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
def test_perms(runner, yadm_cmd, paths, ds1, autoperms):
|
def test_perms(runner, yadm_cmd, paths, ds1, autoperms):
|
||||||
"""Test perms"""
|
"""Test perms"""
|
||||||
# set the value of auto-perms
|
# set the value of auto-perms
|
||||||
if autoperms != "notest":
|
if autoperms != 'notest':
|
||||||
if autoperms != "unset":
|
if autoperms != 'unset':
|
||||||
os.system(" ".join(yadm_cmd("config", "yadm.auto-perms", autoperms)))
|
os.system(' '.join(
|
||||||
|
yadm_cmd('config', 'yadm.auto-perms', autoperms)))
|
||||||
|
|
||||||
# privatepaths will hold all paths that should become secured
|
# privatepaths will hold all paths that should become secured
|
||||||
privatepaths = [paths.work.join(".ssh"), paths.work.join(".gnupg")]
|
privatepaths = [paths.work.join('.ssh'), paths.work.join('.gnupg')]
|
||||||
privatepaths += [paths.work.join(private.path) for private in ds1.private]
|
privatepaths += [paths.work.join(private.path) for private in ds1.private]
|
||||||
|
|
||||||
# create an archive file
|
# create an archive file
|
||||||
|
@ -23,71 +23,82 @@ def test_perms(runner, yadm_cmd, paths, ds1, autoperms):
|
||||||
privatepaths.append(paths.archive)
|
privatepaths.append(paths.archive)
|
||||||
|
|
||||||
# create encrypted file test data
|
# create encrypted file test data
|
||||||
efile1 = paths.work.join("efile1")
|
efile1 = paths.work.join('efile1')
|
||||||
efile1.write("efile1")
|
efile1.write('efile1')
|
||||||
efile2 = paths.work.join("efile2")
|
efile2 = paths.work.join('efile2')
|
||||||
efile2.write("efile2")
|
efile2.write('efile2')
|
||||||
paths.encrypt.write("efile1\nefile2\n!efile1\n")
|
paths.encrypt.write('efile1\nefile2\n!efile1\n')
|
||||||
insecurepaths = [efile1]
|
insecurepaths = [efile1]
|
||||||
privatepaths.append(efile2)
|
privatepaths.append(efile2)
|
||||||
|
|
||||||
# assert these paths begin unsecured
|
# assert these paths begin unsecured
|
||||||
for private in privatepaths + insecurepaths:
|
for private in privatepaths + insecurepaths:
|
||||||
assert not oct(private.stat().mode).endswith("00"), "Path started secured"
|
assert not oct(private.stat().mode).endswith('00'), (
|
||||||
|
'Path started secured')
|
||||||
|
|
||||||
cmd = "perms"
|
cmd = 'perms'
|
||||||
if autoperms != "notest":
|
if autoperms != 'notest':
|
||||||
cmd = "status"
|
cmd = 'status'
|
||||||
run = runner(yadm_cmd(cmd), env={"HOME": paths.work})
|
run = runner(yadm_cmd(cmd), env={'HOME': paths.work})
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
if cmd == "perms":
|
if cmd == 'perms':
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
|
|
||||||
# these paths should be secured if processing perms
|
# these paths should be secured if processing perms
|
||||||
for private in privatepaths:
|
for private in privatepaths:
|
||||||
if autoperms == "false":
|
if autoperms == 'false':
|
||||||
assert not oct(private.stat().mode).endswith("00"), "Path should not be secured"
|
assert not oct(private.stat().mode).endswith('00'), (
|
||||||
|
'Path should not be secured')
|
||||||
else:
|
else:
|
||||||
assert oct(private.stat().mode).endswith("00"), "Path has not been secured"
|
assert oct(private.stat().mode).endswith('00'), (
|
||||||
|
'Path has not been secured')
|
||||||
|
|
||||||
# these paths should never be secured
|
# these paths should never be secured
|
||||||
for private in insecurepaths:
|
for private in insecurepaths:
|
||||||
assert not oct(private.stat().mode).endswith("00"), "Path should not be secured"
|
assert not oct(private.stat().mode).endswith('00'), (
|
||||||
|
'Path should not be secured')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("sshperms", [None, "true", "false"])
|
@pytest.mark.parametrize('sshperms', [None, 'true', 'false'])
|
||||||
@pytest.mark.parametrize("gpgperms", [None, "true", "false"])
|
@pytest.mark.parametrize('gpgperms', [None, 'true', 'false'])
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
def test_perms_control(runner, yadm_cmd, paths, ds1, sshperms, gpgperms):
|
def test_perms_control(runner, yadm_cmd, paths, ds1, sshperms, gpgperms):
|
||||||
"""Test fine control of perms"""
|
"""Test fine control of perms"""
|
||||||
# set the value of ssh-perms
|
# set the value of ssh-perms
|
||||||
if sshperms:
|
if sshperms:
|
||||||
os.system(" ".join(yadm_cmd("config", "yadm.ssh-perms", sshperms)))
|
os.system(' '.join(yadm_cmd('config', 'yadm.ssh-perms', sshperms)))
|
||||||
|
|
||||||
# set the value of gpg-perms
|
# set the value of gpg-perms
|
||||||
if gpgperms:
|
if gpgperms:
|
||||||
os.system(" ".join(yadm_cmd("config", "yadm.gpg-perms", gpgperms)))
|
os.system(' '.join(yadm_cmd('config', 'yadm.gpg-perms', gpgperms)))
|
||||||
|
|
||||||
# privatepaths will hold all paths that should become secured
|
# privatepaths will hold all paths that should become secured
|
||||||
privatepaths = [paths.work.join(".ssh"), paths.work.join(".gnupg")]
|
privatepaths = [paths.work.join('.ssh'), paths.work.join('.gnupg')]
|
||||||
privatepaths += [paths.work.join(private.path) for private in ds1.private]
|
privatepaths += [paths.work.join(private.path) for private in ds1.private]
|
||||||
|
|
||||||
# assert these paths begin unsecured
|
# assert these paths begin unsecured
|
||||||
for private in privatepaths:
|
for private in privatepaths:
|
||||||
assert not oct(private.stat().mode).endswith("00"), "Path started secured"
|
assert not oct(private.stat().mode).endswith('00'), (
|
||||||
|
'Path started secured')
|
||||||
|
|
||||||
run = runner(yadm_cmd("perms"), env={"HOME": paths.work})
|
run = runner(yadm_cmd('perms'), env={'HOME': paths.work})
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
|
|
||||||
# these paths should be secured if processing perms
|
# these paths should be secured if processing perms
|
||||||
for private in privatepaths:
|
for private in privatepaths:
|
||||||
if (sshperms == "false" and "ssh" in str(private)) or (gpgperms == "false" and "gnupg" in str(private)):
|
if (
|
||||||
assert not oct(private.stat().mode).endswith("00"), "Path should not be secured"
|
(sshperms == 'false' and 'ssh' in str(private))
|
||||||
|
or
|
||||||
|
(gpgperms == 'false' and 'gnupg' in str(private))
|
||||||
|
):
|
||||||
|
assert not oct(private.stat().mode).endswith('00'), (
|
||||||
|
'Path should not be secured')
|
||||||
else:
|
else:
|
||||||
assert oct(private.stat().mode).endswith("00"), "Path has not been secured"
|
assert oct(private.stat().mode).endswith('00'), (
|
||||||
|
'Path has not been secured')
|
||||||
|
|
||||||
# verify permissions aren't changed for the worktree
|
# verify permissions aren't changed for the worktree
|
||||||
assert oct(paths.work.stat().mode).endswith("0755")
|
assert oct(paths.work.stat().mode).endswith('0755')
|
||||||
|
|
|
@ -1,83 +1,65 @@
|
||||||
"""Syntax checks"""
|
"""Syntax checks"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def test_yadm_syntax(runner, yadm):
|
def test_yadm_syntax(runner, yadm):
|
||||||
"""Is syntactically valid"""
|
"""Is syntactically valid"""
|
||||||
run = runner(command=["bash", "-n", yadm])
|
run = runner(command=['bash', '-n', yadm])
|
||||||
assert run.success
|
assert run.success
|
||||||
|
|
||||||
|
|
||||||
def test_shellcheck(pytestconfig, runner, yadm, shellcheck_version):
|
def test_shellcheck(pytestconfig, runner, yadm, shellcheck_version):
|
||||||
"""Passes shellcheck"""
|
"""Passes shellcheck"""
|
||||||
if not pytestconfig.getoption("--force-linters"):
|
if not pytestconfig.getoption("--force-linters"):
|
||||||
run = runner(command=["shellcheck", "-V"], report=False)
|
run = runner(command=['shellcheck', '-V'], report=False)
|
||||||
if f"version: {shellcheck_version}" not in run.out:
|
if f'version: {shellcheck_version}' not in run.out:
|
||||||
pytest.skip("Unsupported shellcheck version")
|
pytest.skip('Unsupported shellcheck version')
|
||||||
run = runner(command=["shellcheck", "-s", "bash", yadm])
|
run = runner(command=['shellcheck', '-s', 'bash', yadm])
|
||||||
assert run.success
|
assert run.success
|
||||||
|
|
||||||
|
|
||||||
def test_pylint(pytestconfig, runner, pylint_version):
|
def test_pylint(pytestconfig, runner, pylint_version):
|
||||||
"""Passes pylint"""
|
"""Passes pylint"""
|
||||||
if not pytestconfig.getoption("--force-linters"):
|
if not pytestconfig.getoption("--force-linters"):
|
||||||
run = runner(command=["pylint", "--version"], report=False)
|
run = runner(command=['pylint', '--version'], report=False)
|
||||||
if f"pylint {pylint_version}" not in run.out:
|
if f'pylint {pylint_version}' not in run.out:
|
||||||
pytest.skip("Unsupported pylint version")
|
pytest.skip('Unsupported pylint version')
|
||||||
pyfiles = []
|
pyfiles = list()
|
||||||
for tfile in os.listdir("test"):
|
for tfile in os.listdir('test'):
|
||||||
if tfile.endswith(".py"):
|
if tfile.endswith('.py'):
|
||||||
pyfiles.append(f"test/{tfile}")
|
pyfiles.append(f'test/{tfile}')
|
||||||
run = runner(command=["pylint"] + pyfiles)
|
run = runner(command=['pylint'] + pyfiles)
|
||||||
assert run.success
|
|
||||||
|
|
||||||
|
|
||||||
def test_isort(pytestconfig, runner, isort_version):
|
|
||||||
"""Passes isort"""
|
|
||||||
if not pytestconfig.getoption("--force-linters"):
|
|
||||||
run = runner(command=["isort", "--version"], report=False)
|
|
||||||
if isort_version not in run.out:
|
|
||||||
pytest.skip("Unsupported isort version")
|
|
||||||
run = runner(command=["isort", "-c", "test"])
|
|
||||||
assert run.success
|
assert run.success
|
||||||
|
|
||||||
|
|
||||||
def test_flake8(pytestconfig, runner, flake8_version):
|
def test_flake8(pytestconfig, runner, flake8_version):
|
||||||
"""Passes flake8"""
|
"""Passes flake8"""
|
||||||
if not pytestconfig.getoption("--force-linters"):
|
if not pytestconfig.getoption("--force-linters"):
|
||||||
run = runner(command=["flake8", "--version"], report=False)
|
run = runner(command=['flake8', '--version'], report=False)
|
||||||
if not run.out.startswith(flake8_version):
|
if not run.out.startswith(flake8_version):
|
||||||
pytest.skip("Unsupported flake8 version")
|
pytest.skip('Unsupported flake8 version')
|
||||||
run = runner(command=["flake8", "test"])
|
run = runner(command=['flake8', 'test'])
|
||||||
assert run.success
|
|
||||||
|
|
||||||
|
|
||||||
def test_black(pytestconfig, runner, black_version):
|
|
||||||
"""Passes black"""
|
|
||||||
if not pytestconfig.getoption("--force-linters"):
|
|
||||||
run = runner(command=["black", "--version"], report=False)
|
|
||||||
if black_version not in run.out:
|
|
||||||
pytest.skip("Unsupported black version")
|
|
||||||
run = runner(command=["black", "--check", "test"])
|
|
||||||
assert run.success
|
assert run.success
|
||||||
|
|
||||||
|
|
||||||
def test_yamllint(pytestconfig, runner, yamllint_version):
|
def test_yamllint(pytestconfig, runner, yamllint_version):
|
||||||
"""Passes yamllint"""
|
"""Passes yamllint"""
|
||||||
if not pytestconfig.getoption("--force-linters"):
|
if not pytestconfig.getoption("--force-linters"):
|
||||||
run = runner(command=["yamllint", "--version"], report=False)
|
run = runner(command=['yamllint', '--version'], report=False)
|
||||||
if not run.out.strip().endswith(yamllint_version):
|
if not run.out.strip().endswith(yamllint_version):
|
||||||
pytest.skip("Unsupported yamllint version")
|
pytest.skip('Unsupported yamllint version')
|
||||||
run = runner(command=["yamllint", "-s", "$(find . -name \\*.yml)"], shell=True)
|
run = runner(
|
||||||
|
command=['yamllint', '-s', '$(find . -name \\*.yml)'],
|
||||||
|
shell=True)
|
||||||
assert run.success
|
assert run.success
|
||||||
|
|
||||||
|
|
||||||
def test_man(runner):
|
def test_man(runner):
|
||||||
"""Check for warnings from man"""
|
"""Check for warnings from man"""
|
||||||
run = runner(command=["man.REAL", "--warnings", "./yadm.1"])
|
run = runner(
|
||||||
|
command=['man', '--warnings', './yadm.1'])
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "yadm - Yet Another Dotfiles Manager" in run.out
|
assert 'yadm - Yet Another Dotfiles Manager' in run.out
|
||||||
|
|
|
@ -8,14 +8,14 @@ def test_bootstrap_missing(runner, paths):
|
||||||
|
|
||||||
def test_bootstrap_no_exec(runner, paths):
|
def test_bootstrap_no_exec(runner, paths):
|
||||||
"""Test result of bootstrap_available, when bootstrap not executable"""
|
"""Test result of bootstrap_available, when bootstrap not executable"""
|
||||||
paths.bootstrap.write("")
|
paths.bootstrap.write('')
|
||||||
paths.bootstrap.chmod(0o644)
|
paths.bootstrap.chmod(0o644)
|
||||||
run_test(runner, paths, False)
|
run_test(runner, paths, False)
|
||||||
|
|
||||||
|
|
||||||
def test_bootstrap_exec(runner, paths):
|
def test_bootstrap_exec(runner, paths):
|
||||||
"""Test result of bootstrap_available, when bootstrap executable"""
|
"""Test result of bootstrap_available, when bootstrap executable"""
|
||||||
paths.bootstrap.write("")
|
paths.bootstrap.write('')
|
||||||
paths.bootstrap.chmod(0o775)
|
paths.bootstrap.chmod(0o775)
|
||||||
run_test(runner, paths, True)
|
run_test(runner, paths, True)
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ def run_test(runner, paths, success):
|
||||||
YADM_BOOTSTRAP='{paths.bootstrap}'
|
YADM_BOOTSTRAP='{paths.bootstrap}'
|
||||||
bootstrap_available
|
bootstrap_available
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success == success
|
assert run.success == success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
|
|
|
@ -2,20 +2,20 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("label", ["", "default", "other"])
|
@pytest.mark.parametrize('label', ['', 'default', 'other'])
|
||||||
@pytest.mark.parametrize("awk", [True, False], ids=["awk", "no-awk"])
|
@pytest.mark.parametrize('awk', [True, False], ids=['awk', 'no-awk'])
|
||||||
def test_kind_default(runner, yadm, awk, label):
|
def test_kind_default(runner, yadm, awk, label):
|
||||||
"""Test kind: default"""
|
"""Test kind: default"""
|
||||||
|
|
||||||
expected = "template_default"
|
expected = 'template_default'
|
||||||
awk_avail = "true"
|
awk_avail = 'true'
|
||||||
|
|
||||||
if not awk:
|
if not awk:
|
||||||
awk_avail = "false"
|
awk_avail = 'false'
|
||||||
expected = ""
|
expected = ''
|
||||||
|
|
||||||
if label == "other":
|
if label == 'other':
|
||||||
expected = ""
|
expected = ''
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
|
@ -23,30 +23,30 @@ def test_kind_default(runner, yadm, awk, label):
|
||||||
template="$(choose_template_cmd "{label}")"
|
template="$(choose_template_cmd "{label}")"
|
||||||
echo "TEMPLATE:$template"
|
echo "TEMPLATE:$template"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert f"TEMPLATE:{expected}\n" in run.out
|
assert f'TEMPLATE:{expected}\n' in run.out
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("label", ["envtpl", "j2cli", "j2", "other"])
|
@pytest.mark.parametrize('label', ['envtpl', 'j2cli', 'j2', 'other'])
|
||||||
@pytest.mark.parametrize("envtpl", [True, False], ids=["envtpl", "no-envtpl"])
|
@pytest.mark.parametrize('envtpl', [True, False], ids=['envtpl', 'no-envtpl'])
|
||||||
@pytest.mark.parametrize("j2cli", [True, False], ids=["j2cli", "no-j2cli"])
|
@pytest.mark.parametrize('j2cli', [True, False], ids=['j2cli', 'no-j2cli'])
|
||||||
def test_kind_j2cli_envtpl(runner, yadm, envtpl, j2cli, label):
|
def test_kind_j2cli_envtpl(runner, yadm, envtpl, j2cli, label):
|
||||||
"""Test kind: j2 (both j2cli & envtpl)
|
"""Test kind: j2 (both j2cli & envtpl)
|
||||||
|
|
||||||
j2cli is preferred over envtpl if available.
|
j2cli is preferred over envtpl if available.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
envtpl_avail = "true" if envtpl else "false"
|
envtpl_avail = 'true' if envtpl else 'false'
|
||||||
j2cli_avail = "true" if j2cli else "false"
|
j2cli_avail = 'true' if j2cli else 'false'
|
||||||
|
|
||||||
if label in ("j2cli", "j2") and j2cli:
|
if label in ('j2cli', 'j2') and j2cli:
|
||||||
expected = "template_j2cli"
|
expected = 'template_j2cli'
|
||||||
elif label in ("envtpl", "j2") and envtpl:
|
elif label in ('envtpl', 'j2') and envtpl:
|
||||||
expected = "template_envtpl"
|
expected = 'template_envtpl'
|
||||||
else:
|
else:
|
||||||
expected = ""
|
expected = ''
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
|
@ -55,7 +55,7 @@ def test_kind_j2cli_envtpl(runner, yadm, envtpl, j2cli, label):
|
||||||
template="$(choose_template_cmd "{label}")"
|
template="$(choose_template_cmd "{label}")"
|
||||||
echo "TEMPLATE:$template"
|
echo "TEMPLATE:$template"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert f"TEMPLATE:{expected}\n" in run.out
|
assert f'TEMPLATE:{expected}\n' in run.out
|
||||||
|
|
|
@ -2,86 +2,78 @@
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
ARCHIVE = "archive"
|
ARCHIVE = 'archive'
|
||||||
BOOTSTRAP = "bootstrap"
|
BOOTSTRAP = 'bootstrap'
|
||||||
CONFIG = "config"
|
CONFIG = 'config'
|
||||||
ENCRYPT = "encrypt"
|
ENCRYPT = 'encrypt'
|
||||||
HOME = "/testhome"
|
HOME = '/testhome'
|
||||||
REPO = "repo.git"
|
REPO = 'repo.git'
|
||||||
YDIR = ".config/yadm"
|
YDIR = '.config/yadm'
|
||||||
YDATA = ".local/share/yadm"
|
YDATA = '.local/share/yadm'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"override, expect",
|
'override, expect', [
|
||||||
[
|
|
||||||
(None, {}),
|
(None, {}),
|
||||||
("-Y", {"yadm": "YADM_DIR"}),
|
('-Y', {'yadm': 'YADM_DIR'}),
|
||||||
("--yadm-data", {"data": "YADM_DATA"}),
|
('--yadm-data', {'data': 'YADM_DATA'}),
|
||||||
("--yadm-repo", {"repo": "YADM_REPO", "git": "GIT_DIR"}),
|
('--yadm-repo', {'repo': 'YADM_REPO', 'git': 'GIT_DIR'}),
|
||||||
("--yadm-config", {"config": "YADM_CONFIG"}),
|
('--yadm-config', {'config': 'YADM_CONFIG'}),
|
||||||
("--yadm-encrypt", {"encrypt": "YADM_ENCRYPT"}),
|
('--yadm-encrypt', {'encrypt': 'YADM_ENCRYPT'}),
|
||||||
("--yadm-archive", {"archive": "YADM_ARCHIVE"}),
|
('--yadm-archive', {'archive': 'YADM_ARCHIVE'}),
|
||||||
("--yadm-bootstrap", {"bootstrap": "YADM_BOOTSTRAP"}),
|
('--yadm-bootstrap', {'bootstrap': 'YADM_BOOTSTRAP'}),
|
||||||
],
|
], ids=[
|
||||||
ids=[
|
'default',
|
||||||
"default",
|
'override yadm dir',
|
||||||
"override yadm dir",
|
'override yadm data',
|
||||||
"override yadm data",
|
'override repo',
|
||||||
"override repo",
|
'override config',
|
||||||
"override config",
|
'override encrypt',
|
||||||
"override encrypt",
|
'override archive',
|
||||||
"override archive",
|
'override bootstrap',
|
||||||
"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"""
|
"""Test configure_paths"""
|
||||||
if path.startswith("/"):
|
opath = 'override'
|
||||||
expected_path = path
|
matches = match_map()
|
||||||
else:
|
args = []
|
||||||
expected_path = str(paths.root.join(path))
|
if override == '-Y':
|
||||||
|
matches = match_map('/' + opath)
|
||||||
|
if override == '--yadm-data':
|
||||||
|
matches = match_map(None, '/' + opath)
|
||||||
|
|
||||||
args = [override, path] if override else []
|
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)
|
||||||
|
|
||||||
if override == "-Y":
|
run_test(runner, paths, args, matches.values(), 0)
|
||||||
matches = match_map(expected_path)
|
|
||||||
elif override == "--yadm-data":
|
|
||||||
matches = match_map(None, expected_path)
|
|
||||||
else:
|
|
||||||
matches = match_map()
|
|
||||||
|
|
||||||
for ekey in expect.keys():
|
|
||||||
matches[ekey] = f'{expect[ekey]}="{expected_path}"'
|
|
||||||
|
|
||||||
run_test(runner, paths, args, matches.values(), cwd=str(paths.root))
|
|
||||||
|
|
||||||
|
|
||||||
def match_map(yadm_dir=None, yadm_data=None):
|
def match_map(yadm_dir=None, yadm_data=None):
|
||||||
"""Create a dictionary of matches, relative to yadm_dir"""
|
"""Create a dictionary of matches, relative to yadm_dir"""
|
||||||
if not yadm_dir:
|
if not yadm_dir:
|
||||||
yadm_dir = "/".join([HOME, YDIR])
|
yadm_dir = '/'.join([HOME, YDIR])
|
||||||
if not yadm_data:
|
if not yadm_data:
|
||||||
yadm_data = "/".join([HOME, YDATA])
|
yadm_data = '/'.join([HOME, YDATA])
|
||||||
return {
|
return {
|
||||||
"yadm": f'YADM_DIR="{yadm_dir}"',
|
'yadm': f'YADM_DIR="{yadm_dir}"',
|
||||||
"repo": f'YADM_REPO="{yadm_data}/{REPO}"',
|
'repo': f'YADM_REPO="{yadm_data}/{REPO}"',
|
||||||
"config": f'YADM_CONFIG="{yadm_dir}/{CONFIG}"',
|
'config': f'YADM_CONFIG="{yadm_dir}/{CONFIG}"',
|
||||||
"encrypt": f'YADM_ENCRYPT="{yadm_dir}/{ENCRYPT}"',
|
'encrypt': f'YADM_ENCRYPT="{yadm_dir}/{ENCRYPT}"',
|
||||||
"archive": f'YADM_ARCHIVE="{yadm_data}/{ARCHIVE}"',
|
'archive': f'YADM_ARCHIVE="{yadm_data}/{ARCHIVE}"',
|
||||||
"bootstrap": f'YADM_BOOTSTRAP="{yadm_dir}/{BOOTSTRAP}"',
|
'bootstrap': f'YADM_BOOTSTRAP="{yadm_dir}/{BOOTSTRAP}"',
|
||||||
"git": f'GIT_DIR="{yadm_data}/{REPO}"',
|
'git': f'GIT_DIR="{yadm_data}/{REPO}"',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def run_test(runner, paths, args, expected_matches, cwd=None):
|
def run_test(runner, paths, args, expected_matches, expected_code=0):
|
||||||
"""Run proces global args, and run configure_paths"""
|
"""Run proces global args, and run configure_paths"""
|
||||||
argstring = " ".join(['"' + a + '"' for a in args])
|
argstring = ' '.join(['"'+a+'"' for a in args])
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 HOME="{HOME}" source {paths.pgm}
|
YADM_TEST=1 HOME="{HOME}" source {paths.pgm}
|
||||||
process_global_args {argstring}
|
process_global_args {argstring}
|
||||||
|
@ -91,8 +83,9 @@ def run_test(runner, paths, args, expected_matches, cwd=None):
|
||||||
configure_paths
|
configure_paths
|
||||||
declare -p | grep -E '(YADM|GIT)_'
|
declare -p | grep -E '(YADM|GIT)_'
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script, cwd=cwd)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.code == expected_code
|
||||||
assert run.err == ""
|
assert run.success == (run.code == 0)
|
||||||
|
assert (run.err if run.success else run.out) == ''
|
||||||
for match in expected_matches:
|
for match in expected_matches:
|
||||||
assert match in run.out
|
assert match in run.out if run.success else run.err
|
||||||
|
|
|
@ -1,42 +1,43 @@
|
||||||
"""Unit tests: copy_perms"""
|
"""Unit tests: copy_perms"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
OCTAL = "7654"
|
OCTAL = '7654'
|
||||||
NON_OCTAL = "9876"
|
NON_OCTAL = '9876'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stat_broken", [True, False], ids=["normal", "stat broken"])
|
@pytest.mark.parametrize(
|
||||||
|
'stat_broken', [True, False], ids=['normal', 'stat broken'])
|
||||||
def test_copy_perms(runner, yadm, tmpdir, stat_broken):
|
def test_copy_perms(runner, yadm, tmpdir, stat_broken):
|
||||||
"""Test function copy_perms"""
|
"""Test function copy_perms"""
|
||||||
src_mode = 0o754
|
src_mode = 0o754
|
||||||
dst_mode = 0o644
|
dst_mode = 0o644
|
||||||
source = tmpdir.join("source")
|
source = tmpdir.join('source')
|
||||||
source.write("test", ensure=True)
|
source.write('test', ensure=True)
|
||||||
source.chmod(src_mode)
|
source.chmod(src_mode)
|
||||||
|
|
||||||
dest = tmpdir.join("dest")
|
dest = tmpdir.join('dest')
|
||||||
dest.write("test", ensure=True)
|
dest.write('test', ensure=True)
|
||||||
dest.chmod(dst_mode)
|
dest.chmod(dst_mode)
|
||||||
|
|
||||||
override_stat = ""
|
override_stat = ''
|
||||||
if stat_broken:
|
if stat_broken:
|
||||||
override_stat = "function stat() { echo broken; }"
|
override_stat = 'function stat() { echo broken; }'
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
{override_stat}
|
{override_stat}
|
||||||
copy_perms "{source}" "{dest}"
|
copy_perms "{source}" "{dest}"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
expected = dst_mode if stat_broken else src_mode
|
expected = dst_mode if stat_broken else src_mode
|
||||||
assert oct(os.stat(dest).st_mode)[-3:] == oct(expected)[-3:]
|
assert oct(os.stat(dest).st_mode)[-3:] == oct(expected)[-3:]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stat_output", [OCTAL, NON_OCTAL], ids=["octal", "non-octal"])
|
@pytest.mark.parametrize(
|
||||||
|
'stat_output', [OCTAL, NON_OCTAL], ids=['octal', 'non-octal'])
|
||||||
def test_get_mode(runner, yadm, stat_output):
|
def test_get_mode(runner, yadm, stat_output):
|
||||||
"""Test function get_mode"""
|
"""Test function get_mode"""
|
||||||
script = f"""
|
script = f"""
|
||||||
|
@ -45,8 +46,8 @@ def test_get_mode(runner, yadm, stat_output):
|
||||||
mode=$(get_mode abc)
|
mode=$(get_mode abc)
|
||||||
echo "MODE:$mode"
|
echo "MODE:$mode"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
expected = OCTAL if stat_output == OCTAL else ""
|
expected = OCTAL if stat_output == OCTAL else ""
|
||||||
assert f"MODE:{expected}\n" in run.out
|
assert f'MODE:{expected}\n' in run.out
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("condition", ["default", "override"])
|
@pytest.mark.parametrize('condition', ['default', 'override'])
|
||||||
def test_get_cipher(runner, paths, condition):
|
def test_get_cipher(runner, paths, condition):
|
||||||
"""Test _get_cipher()"""
|
"""Test _get_cipher()"""
|
||||||
|
|
||||||
if condition == "override":
|
if condition == 'override':
|
||||||
paths.config.write("[yadm]\n\tcipher = override-cipher")
|
paths.config.write('[yadm]\n\tcipher = override-cipher')
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {paths.pgm}
|
YADM_TEST=1 source {paths.pgm}
|
||||||
|
@ -19,18 +19,18 @@ def test_get_cipher(runner, paths, condition):
|
||||||
echo "output_archive:$output_archive"
|
echo "output_archive:$output_archive"
|
||||||
echo "yadm_cipher:$yadm_cipher"
|
echo "yadm_cipher:$yadm_cipher"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "output_archive:test-archive" in run.out
|
assert 'output_archive:test-archive' in run.out
|
||||||
if condition == "override":
|
if condition == 'override':
|
||||||
assert "yadm_cipher:override-cipher" in run.out
|
assert 'yadm_cipher:override-cipher' in run.out
|
||||||
else:
|
else:
|
||||||
assert "yadm_cipher:gpg" in run.out
|
assert 'yadm_cipher:gpg' in run.out
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("cipher", ["gpg", "openssl", "bad"])
|
@pytest.mark.parametrize('cipher', ['gpg', 'openssl', 'bad'])
|
||||||
@pytest.mark.parametrize("mode", ["_encrypt_to", "_decrypt_from"])
|
@pytest.mark.parametrize('mode', ['_encrypt_to', '_decrypt_from'])
|
||||||
def test_encrypt_decrypt(runner, paths, cipher, mode):
|
def test_encrypt_decrypt(runner, paths, cipher, mode):
|
||||||
"""Test _encrypt_to() & _decrypt_from"""
|
"""Test _encrypt_to() & _decrypt_from"""
|
||||||
|
|
||||||
|
@ -49,24 +49,24 @@ def test_encrypt_decrypt(runner, paths, cipher, mode):
|
||||||
GPG_PROGRAM=mock_gpg
|
GPG_PROGRAM=mock_gpg
|
||||||
{mode} {paths.archive}
|
{mode} {paths.archive}
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
|
|
||||||
if cipher != "bad":
|
if cipher != 'bad':
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.out.startswith(cipher)
|
assert run.out.startswith(cipher)
|
||||||
assert str(paths.archive) in run.out
|
assert str(paths.archive) in run.out
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
else:
|
else:
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert "Unknown cipher" in run.err
|
assert 'Unknown cipher' in run.err
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("condition", ["default", "override"])
|
@pytest.mark.parametrize('condition', ['default', 'override'])
|
||||||
def test_get_openssl_ciphername(runner, paths, condition):
|
def test_get_openssl_ciphername(runner, paths, condition):
|
||||||
"""Test _get_openssl_ciphername()"""
|
"""Test _get_openssl_ciphername()"""
|
||||||
|
|
||||||
if condition == "override":
|
if condition == 'override':
|
||||||
paths.config.write("[yadm]\n\topenssl-ciphername = override-cipher")
|
paths.config.write('[yadm]\n\topenssl-ciphername = override-cipher')
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {paths.pgm}
|
YADM_TEST=1 source {paths.pgm}
|
||||||
|
@ -76,21 +76,21 @@ def test_get_openssl_ciphername(runner, paths, condition):
|
||||||
result=$(_get_openssl_ciphername)
|
result=$(_get_openssl_ciphername)
|
||||||
echo "result:$result"
|
echo "result:$result"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
if condition == "override":
|
if condition == 'override':
|
||||||
assert run.out.strip() == "result:override-cipher"
|
assert run.out.strip() == 'result:override-cipher'
|
||||||
else:
|
else:
|
||||||
assert run.out.strip() == "result:aes-256-cbc"
|
assert run.out.strip() == 'result:aes-256-cbc'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("condition", ["old", "not-old"])
|
@pytest.mark.parametrize('condition', ['old', 'not-old'])
|
||||||
def test_set_openssl_options(runner, paths, condition):
|
def test_set_openssl_options(runner, paths, condition):
|
||||||
"""Test _set_openssl_options()"""
|
"""Test _set_openssl_options()"""
|
||||||
|
|
||||||
if condition == "old":
|
if condition == 'old':
|
||||||
paths.config.write("[yadm]\n\topenssl-old = true")
|
paths.config.write('[yadm]\n\topenssl-old = true')
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {paths.pgm}
|
YADM_TEST=1 source {paths.pgm}
|
||||||
|
@ -101,20 +101,20 @@ def test_set_openssl_options(runner, paths, condition):
|
||||||
_set_openssl_options
|
_set_openssl_options
|
||||||
echo "result:${{OPENSSL_OPTS[@]}}"
|
echo "result:${{OPENSSL_OPTS[@]}}"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
if condition == "old":
|
if condition == 'old':
|
||||||
assert "-testcipher -salt -md md5" in run.out
|
assert '-testcipher -salt -md md5' in run.out
|
||||||
else:
|
else:
|
||||||
assert "-testcipher -salt -pbkdf2 -iter 100000 -md sha512" in run.out
|
assert '-testcipher -salt -pbkdf2 -iter 100000 -md sha512' in run.out
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("recipient", ["ASK", "present", ""])
|
@pytest.mark.parametrize('recipient', ['ASK', 'present', ''])
|
||||||
def test_set_gpg_options(runner, paths, recipient):
|
def test_set_gpg_options(runner, paths, recipient):
|
||||||
"""Test _set_gpg_options()"""
|
"""Test _set_gpg_options()"""
|
||||||
|
|
||||||
paths.config.write(f"[yadm]\n\tgpg-recipient = {recipient}")
|
paths.config.write(f'[yadm]\n\tgpg-recipient = {recipient}')
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {paths.pgm}
|
YADM_TEST=1 source {paths.pgm}
|
||||||
|
@ -124,12 +124,12 @@ def test_set_gpg_options(runner, paths, recipient):
|
||||||
_set_gpg_options
|
_set_gpg_options
|
||||||
echo "result:${{GPG_OPTS[@]}}"
|
echo "result:${{GPG_OPTS[@]}}"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
if recipient == "ASK":
|
if recipient == 'ASK':
|
||||||
assert run.out.strip() == "result:--no-default-recipient -e"
|
assert run.out.strip() == 'result:--no-default-recipient -e'
|
||||||
elif recipient != "":
|
elif recipient != '':
|
||||||
assert run.out.strip() == f"result:-e -r {recipient}"
|
assert run.out.strip() == f'result:-e -r {recipient}'
|
||||||
else:
|
else:
|
||||||
assert run.out.strip() == "result:-c"
|
assert run.out.strip() == 'result:-c'
|
||||||
|
|
|
@ -2,28 +2,38 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("exclude", ["missing", "outdated", "up-to-date"])
|
@pytest.mark.parametrize(
|
||||||
@pytest.mark.parametrize("encrypt_exists", [True, False], ids=["encrypt", "no-encrypt"])
|
'exclude', ['missing', 'outdated', 'up-to-date'])
|
||||||
@pytest.mark.parametrize("auto_exclude", [True, False], ids=["enabled", "disabled"])
|
@pytest.mark.parametrize(
|
||||||
def test_exclude_encrypted(runner, tmpdir, yadm, encrypt_exists, auto_exclude, exclude):
|
'encrypt_exists', [True, False], ids=['encrypt', 'no-encrypt'])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'auto_exclude', [True, False], ids=['enabled', 'disabled'])
|
||||||
|
def test_exclude_encrypted(
|
||||||
|
runner, tmpdir, yadm, encrypt_exists, auto_exclude, exclude):
|
||||||
"""Test exclude_encrypted()"""
|
"""Test exclude_encrypted()"""
|
||||||
|
|
||||||
header = "# yadm-auto-excludes\n# This section is managed by yadm.\n# Any edits below will be lost.\n"
|
header = (
|
||||||
|
"# yadm-auto-excludes\n"
|
||||||
|
"# This section is managed by yadm.\n"
|
||||||
|
"# Any edits below will be lost.\n"
|
||||||
|
)
|
||||||
|
|
||||||
config_function = 'function config() { echo "false";}'
|
config_function = 'function config() { echo "false";}'
|
||||||
if auto_exclude:
|
if auto_exclude:
|
||||||
config_function = "function config() { return; }"
|
config_function = 'function config() { return; }'
|
||||||
|
|
||||||
encrypt_file = tmpdir.join("encrypt_file")
|
encrypt_file = tmpdir.join('encrypt_file')
|
||||||
repo_dir = tmpdir.join("repodir")
|
repo_dir = tmpdir.join('repodir')
|
||||||
exclude_file = repo_dir.join("info/exclude")
|
exclude_file = repo_dir.join('info/exclude')
|
||||||
|
|
||||||
if encrypt_exists:
|
if encrypt_exists:
|
||||||
encrypt_file.write("test-encrypt-data\n", ensure=True)
|
encrypt_file.write('test-encrypt-data\n', ensure=True)
|
||||||
if exclude == "outdated":
|
if exclude == 'outdated':
|
||||||
exclude_file.write(f"original-exclude\n{header}outdated\n", ensure=True)
|
exclude_file.write(
|
||||||
elif exclude == "up-to-date":
|
f'original-exclude\n{header}outdated\n', ensure=True)
|
||||||
exclude_file.write(f"original-exclude\n{header}test-encrypt-data\n", ensure=True)
|
elif exclude == 'up-to-date':
|
||||||
|
exclude_file.write(
|
||||||
|
f'original-exclude\n{header}test-encrypt-data\n', ensure=True)
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
|
@ -33,22 +43,24 @@ def test_exclude_encrypted(runner, tmpdir, yadm, encrypt_exists, auto_exclude, e
|
||||||
YADM_REPO="{repo_dir}"
|
YADM_REPO="{repo_dir}"
|
||||||
exclude_encrypted
|
exclude_encrypted
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
|
|
||||||
if auto_exclude:
|
if auto_exclude:
|
||||||
if encrypt_exists:
|
if encrypt_exists:
|
||||||
assert exclude_file.exists()
|
assert exclude_file.exists()
|
||||||
if exclude == "missing":
|
if exclude == 'missing':
|
||||||
assert exclude_file.read() == f"{header}test-encrypt-data\n"
|
assert exclude_file.read() == f'{header}test-encrypt-data\n'
|
||||||
else:
|
else:
|
||||||
assert exclude_file.read() == ("original-exclude\n" f"{header}test-encrypt-data\n")
|
assert exclude_file.read() == (
|
||||||
if exclude != "up-to-date":
|
'original-exclude\n'
|
||||||
assert f"Updating {exclude_file}" in run.out
|
f'{header}test-encrypt-data\n')
|
||||||
|
if exclude != 'up-to-date':
|
||||||
|
assert f'Updating {exclude_file}' in run.out
|
||||||
else:
|
else:
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
else:
|
else:
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
else:
|
else:
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
|
|
40
test/test_unit_is_valid_branch_name.py
Normal file
40
test/test_unit_is_valid_branch_name.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
"""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
|
|
@ -3,24 +3,25 @@ import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"legacy_path",
|
'legacy_path', [
|
||||||
[
|
|
||||||
None,
|
None,
|
||||||
"repo.git",
|
'repo.git',
|
||||||
"files.gpg",
|
'files.gpg',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize("override", [True, False], ids=["override", "no-override"])
|
@pytest.mark.parametrize(
|
||||||
@pytest.mark.parametrize("upgrade", [True, False], ids=["upgrade", "no-upgrade"])
|
'override', [True, False], ids=['override', 'no-override'])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'upgrade', [True, False], ids=['upgrade', 'no-upgrade'])
|
||||||
def test_legacy_warning(tmpdir, runner, yadm, upgrade, override, legacy_path):
|
def test_legacy_warning(tmpdir, runner, yadm, upgrade, override, legacy_path):
|
||||||
"""Use issue_legacy_path_warning"""
|
"""Use issue_legacy_path_warning"""
|
||||||
home = tmpdir.mkdir("home")
|
home = tmpdir.mkdir('home')
|
||||||
|
|
||||||
if legacy_path:
|
if legacy_path:
|
||||||
home.ensure(f".config/yadm/{str(legacy_path)}")
|
home.ensure(f'.config/yadm/{str(legacy_path)}')
|
||||||
|
|
||||||
override = "YADM_OVERRIDE_REPO=override" if override else ""
|
override = 'YADM_OVERRIDE_REPO=override' if override else ''
|
||||||
main_args = 'MAIN_ARGS=("upgrade")' if upgrade else ""
|
main_args = 'MAIN_ARGS=("upgrade")' if upgrade else ''
|
||||||
script = f"""
|
script = f"""
|
||||||
XDG_CONFIG_HOME=
|
XDG_CONFIG_HOME=
|
||||||
XDG_DATA_HOME=
|
XDG_DATA_HOME=
|
||||||
|
@ -31,10 +32,10 @@ def test_legacy_warning(tmpdir, runner, yadm, upgrade, override, legacy_path):
|
||||||
set_yadm_dirs
|
set_yadm_dirs
|
||||||
issue_legacy_path_warning
|
issue_legacy_path_warning
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
if legacy_path and (not upgrade) and (not override):
|
if legacy_path and (not upgrade) and (not override):
|
||||||
assert "Legacy paths have been detected" in run.err
|
assert 'Legacy paths have been detected' in run.err
|
||||||
else:
|
else:
|
||||||
assert "Legacy paths have been detected" not in run.err
|
assert 'Legacy paths have been detected' not in run.err
|
||||||
|
|
|
@ -7,137 +7,136 @@ def test_not_called(runner, paths):
|
||||||
"""Test parse_encrypt (not called)"""
|
"""Test parse_encrypt (not called)"""
|
||||||
run = run_parse_encrypt(runner, paths, skip_parse=True)
|
run = run_parse_encrypt(runner, paths, skip_parse=True)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "EIF:unparsed" in run.out, "EIF should be unparsed"
|
assert 'EIF:unparsed' in run.out, 'EIF should be unparsed'
|
||||||
assert "EIF_COUNT:1" in run.out, "Only value of EIF should be unparsed"
|
assert 'EIF_COUNT:1' in run.out, 'Only value of EIF should be unparsed'
|
||||||
|
|
||||||
|
|
||||||
def test_short_circuit(runner, paths):
|
def test_short_circuit(runner, paths):
|
||||||
"""Test parse_encrypt (short-circuit)"""
|
"""Test parse_encrypt (short-circuit)"""
|
||||||
run = run_parse_encrypt(runner, paths, twice=True)
|
run = run_parse_encrypt(runner, paths, twice=True)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "PARSE_ENCRYPT_SHORT=parse_encrypt() not reprocessed" in run.out, "parse_encrypt() should short-circuit"
|
assert 'PARSE_ENCRYPT_SHORT=parse_encrypt() not reprocessed' in run.out, (
|
||||||
|
'parse_encrypt() should short-circuit')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"encrypt",
|
'encrypt', [
|
||||||
[
|
('missing'),
|
||||||
("missing"),
|
('empty'),
|
||||||
("empty"),
|
])
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_empty(runner, paths, encrypt):
|
def test_empty(runner, paths, encrypt):
|
||||||
"""Test parse_encrypt (file missing/empty)"""
|
"""Test parse_encrypt (file missing/empty)"""
|
||||||
|
|
||||||
# write encrypt file
|
# write encrypt file
|
||||||
if encrypt == "missing":
|
if encrypt == 'missing':
|
||||||
assert not paths.encrypt.exists(), "Encrypt should be missing"
|
assert not paths.encrypt.exists(), 'Encrypt should be missing'
|
||||||
else:
|
else:
|
||||||
paths.encrypt.write("")
|
paths.encrypt.write('')
|
||||||
assert paths.encrypt.exists(), "Encrypt should exist"
|
assert paths.encrypt.exists(), 'Encrypt should exist'
|
||||||
assert paths.encrypt.size() == 0, "Encrypt should be empty"
|
assert paths.encrypt.size() == 0, 'Encrypt should be empty'
|
||||||
|
|
||||||
# run parse_encrypt
|
# run parse_encrypt
|
||||||
run = run_parse_encrypt(runner, paths)
|
run = run_parse_encrypt(runner, paths)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
|
|
||||||
# validate parsing result
|
# validate parsing result
|
||||||
assert "EIF_COUNT:0" in run.out, "EIF should be empty"
|
assert 'EIF_COUNT:0' in run.out, 'EIF should be empty'
|
||||||
|
|
||||||
|
|
||||||
def create_test_encrypt_data(paths):
|
def create_test_encrypt_data(paths):
|
||||||
"""Generate test data for testing encrypt"""
|
"""Generate test data for testing encrypt"""
|
||||||
|
|
||||||
edata = ""
|
edata = ''
|
||||||
expected = set()
|
expected = set()
|
||||||
|
|
||||||
# empty line
|
# empty line
|
||||||
edata += "\n"
|
edata += '\n'
|
||||||
|
|
||||||
# simple comments
|
# simple comments
|
||||||
edata += "# a simple comment\n"
|
edata += '# a simple comment\n'
|
||||||
edata += " # a comment with leading space\n"
|
edata += ' # a comment with leading space\n'
|
||||||
|
|
||||||
# unreferenced directory
|
# unreferenced directory
|
||||||
paths.work.join("unreferenced").mkdir()
|
paths.work.join('unreferenced').mkdir()
|
||||||
|
|
||||||
# simple files
|
# simple files
|
||||||
edata += "simple_file\n"
|
edata += 'simple_file\n'
|
||||||
edata += "simple.file\n"
|
edata += 'simple.file\n'
|
||||||
paths.work.join("simple_file").write("")
|
paths.work.join('simple_file').write('')
|
||||||
paths.work.join("simple.file").write("")
|
paths.work.join('simple.file').write('')
|
||||||
paths.work.join("simple_file2").write("")
|
paths.work.join('simple_file2').write('')
|
||||||
paths.work.join("simple.file2").write("")
|
paths.work.join('simple.file2').write('')
|
||||||
expected.add("simple_file")
|
expected.add('simple_file')
|
||||||
expected.add("simple.file")
|
expected.add('simple.file')
|
||||||
|
|
||||||
# simple files in directories
|
# simple files in directories
|
||||||
edata += "simple_dir/simple_file\n"
|
edata += 'simple_dir/simple_file\n'
|
||||||
paths.work.join("simple_dir/simple_file").write("", ensure=True)
|
paths.work.join('simple_dir/simple_file').write('', ensure=True)
|
||||||
paths.work.join("simple_dir/simple_file2").write("", ensure=True)
|
paths.work.join('simple_dir/simple_file2').write('', ensure=True)
|
||||||
expected.add("simple_dir/simple_file")
|
expected.add('simple_dir/simple_file')
|
||||||
|
|
||||||
# paths with spaces
|
# paths with spaces
|
||||||
edata += "with space/with space\n"
|
edata += 'with space/with space\n'
|
||||||
paths.work.join("with space/with space").write("", ensure=True)
|
paths.work.join('with space/with space').write('', ensure=True)
|
||||||
paths.work.join("with space/with space2").write("", ensure=True)
|
paths.work.join('with space/with space2').write('', ensure=True)
|
||||||
expected.add("with space/with space")
|
expected.add('with space/with space')
|
||||||
|
|
||||||
# hidden files
|
# hidden files
|
||||||
edata += ".hidden\n"
|
edata += '.hidden\n'
|
||||||
paths.work.join(".hidden").write("")
|
paths.work.join('.hidden').write('')
|
||||||
expected.add(".hidden")
|
expected.add('.hidden')
|
||||||
|
|
||||||
# hidden files in directories
|
# hidden files in directories
|
||||||
edata += ".hidden_dir/.hidden_file\n"
|
edata += '.hidden_dir/.hidden_file\n'
|
||||||
paths.work.join(".hidden_dir/.hidden_file").write("", ensure=True)
|
paths.work.join('.hidden_dir/.hidden_file').write('', ensure=True)
|
||||||
expected.add(".hidden_dir/.hidden_file")
|
expected.add('.hidden_dir/.hidden_file')
|
||||||
|
|
||||||
# wildcards
|
# wildcards
|
||||||
edata += "wild*\n"
|
edata += 'wild*\n'
|
||||||
paths.work.join("wildcard1").write("", ensure=True)
|
paths.work.join('wildcard1').write('', ensure=True)
|
||||||
paths.work.join("wildcard2").write("", ensure=True)
|
paths.work.join('wildcard2').write('', ensure=True)
|
||||||
expected.add("wildcard1")
|
expected.add('wildcard1')
|
||||||
expected.add("wildcard2")
|
expected.add('wildcard2')
|
||||||
|
|
||||||
edata += "dirwild*\n"
|
edata += 'dirwild*\n'
|
||||||
paths.work.join("dirwildcard/file1").write("", ensure=True)
|
paths.work.join('dirwildcard/file1').write('', ensure=True)
|
||||||
paths.work.join("dirwildcard/file2").write("", ensure=True)
|
paths.work.join('dirwildcard/file2').write('', ensure=True)
|
||||||
expected.add("dirwildcard")
|
expected.add('dirwildcard')
|
||||||
|
|
||||||
# excludes
|
# excludes
|
||||||
edata += "exclude*\n"
|
edata += 'exclude*\n'
|
||||||
edata += "ex ex/*\n"
|
edata += 'ex ex/*\n'
|
||||||
paths.work.join("exclude_file1").write("")
|
paths.work.join('exclude_file1').write('')
|
||||||
paths.work.join("exclude_file2.ex").write("")
|
paths.work.join('exclude_file2.ex').write('')
|
||||||
paths.work.join("exclude_file3.ex3").write("")
|
paths.work.join('exclude_file3.ex3').write('')
|
||||||
expected.add("exclude_file1")
|
expected.add('exclude_file1')
|
||||||
expected.add("exclude_file3.ex3")
|
expected.add('exclude_file3.ex3')
|
||||||
edata += "!*.ex\n"
|
edata += '!*.ex\n'
|
||||||
edata += "!ex ex/*.txt\n"
|
edata += '!ex ex/*.txt\n'
|
||||||
paths.work.join("ex ex/file4").write("", ensure=True)
|
paths.work.join('ex ex/file4').write('', ensure=True)
|
||||||
paths.work.join("ex ex/file5.txt").write("", ensure=True)
|
paths.work.join('ex ex/file5.txt').write('', ensure=True)
|
||||||
paths.work.join("ex ex/file6.text").write("", ensure=True)
|
paths.work.join('ex ex/file6.text').write('', ensure=True)
|
||||||
expected.add("ex ex/file4")
|
expected.add('ex ex/file4')
|
||||||
expected.add("ex ex/file6.text")
|
expected.add('ex ex/file6.text')
|
||||||
|
|
||||||
# double star
|
# double star
|
||||||
edata += "doublestar/**/file*\n"
|
edata += 'doublestar/**/file*\n'
|
||||||
edata += "!**/file3\n"
|
edata += '!**/file3\n'
|
||||||
paths.work.join("doublestar/a/b/file1").write("", ensure=True)
|
paths.work.join('doublestar/a/b/file1').write('', ensure=True)
|
||||||
paths.work.join("doublestar/c/d/file2").write("", ensure=True)
|
paths.work.join('doublestar/c/d/file2').write('', ensure=True)
|
||||||
paths.work.join("doublestar/e/f/file3").write("", ensure=True)
|
paths.work.join('doublestar/e/f/file3').write('', ensure=True)
|
||||||
paths.work.join("doublestar/g/h/nomatch").write("", ensure=True)
|
paths.work.join('doublestar/g/h/nomatch').write('', ensure=True)
|
||||||
expected.add("doublestar/a/b/file1")
|
expected.add('doublestar/a/b/file1')
|
||||||
expected.add("doublestar/c/d/file2")
|
expected.add('doublestar/c/d/file2')
|
||||||
# doublestar/e/f/file3 is excluded
|
# doublestar/e/f/file3 is excluded
|
||||||
|
|
||||||
return edata, expected
|
return edata, expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("ds1_repo_copy")
|
@pytest.mark.usefixtures('ds1_repo_copy')
|
||||||
def test_file_parse_encrypt(runner, paths):
|
def test_file_parse_encrypt(runner, paths):
|
||||||
"""Test parse_encrypt
|
"""Test parse_encrypt
|
||||||
|
|
||||||
|
@ -148,35 +147,39 @@ def test_file_parse_encrypt(runner, paths):
|
||||||
edata, expected = create_test_encrypt_data(paths)
|
edata, expected = create_test_encrypt_data(paths)
|
||||||
|
|
||||||
# write encrypt file
|
# write encrypt file
|
||||||
print(f"ENCRYPT:\n---\n{edata}---\n")
|
print(f'ENCRYPT:\n---\n{edata}---\n')
|
||||||
paths.encrypt.write(edata)
|
paths.encrypt.write(edata)
|
||||||
assert paths.encrypt.isfile()
|
assert paths.encrypt.isfile()
|
||||||
|
|
||||||
# run parse_encrypt
|
# run parse_encrypt
|
||||||
run = run_parse_encrypt(runner, paths)
|
run = run_parse_encrypt(runner, paths)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
|
|
||||||
assert f"EIF_COUNT:{len(expected)}" in run.out, "EIF count wrong"
|
assert f'EIF_COUNT:{len(expected)}' in run.out, 'EIF count wrong'
|
||||||
for expected_file in expected:
|
for expected_file in expected:
|
||||||
assert f"EIF:{expected_file}\n" in run.out
|
assert f'EIF:{expected_file}\n' in run.out
|
||||||
|
|
||||||
sorted_expectations = "\n".join([f"EIF:{exp}" for exp in sorted(expected)])
|
sorted_expectations = '\n'.join(
|
||||||
|
[f'EIF:{exp}' for exp in sorted(expected)])
|
||||||
assert sorted_expectations in run.out
|
assert sorted_expectations in run.out
|
||||||
|
|
||||||
|
|
||||||
def run_parse_encrypt(runner, paths, skip_parse=False, twice=False):
|
def run_parse_encrypt(
|
||||||
|
runner, paths,
|
||||||
|
skip_parse=False,
|
||||||
|
twice=False):
|
||||||
"""Run parse_encrypt
|
"""Run parse_encrypt
|
||||||
|
|
||||||
A count of ENCRYPT_INCLUDE_FILES will be reported as EIF_COUNT:X. All
|
A count of ENCRYPT_INCLUDE_FILES will be reported as EIF_COUNT:X. All
|
||||||
values of ENCRYPT_INCLUDE_FILES will be reported as individual EIF:value
|
values of ENCRYPT_INCLUDE_FILES will be reported as individual EIF:value
|
||||||
lines.
|
lines.
|
||||||
"""
|
"""
|
||||||
parse_cmd = "parse_encrypt"
|
parse_cmd = 'parse_encrypt'
|
||||||
if skip_parse:
|
if skip_parse:
|
||||||
parse_cmd = ""
|
parse_cmd = ''
|
||||||
if twice:
|
if twice:
|
||||||
parse_cmd = "parse_encrypt; parse_encrypt"
|
parse_cmd = 'parse_encrypt; parse_encrypt'
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {paths.pgm}
|
YADM_TEST=1 source {paths.pgm}
|
||||||
YADM_ENCRYPT={paths.encrypt}
|
YADM_ENCRYPT={paths.encrypt}
|
||||||
|
@ -194,5 +197,5 @@ def run_parse_encrypt(runner, paths, skip_parse=False, twice=False):
|
||||||
echo "EIF:$value"
|
echo "EIF:$value"
|
||||||
done
|
done
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
return run
|
return run
|
||||||
|
|
|
@ -3,15 +3,15 @@ import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"gnupghome",
|
'gnupghome',
|
||||||
[True, False],
|
[True, False],
|
||||||
ids=["gnupghome-set", "gnupghome-unset"],
|
ids=['gnupghome-set', 'gnupghome-unset'],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize("param", ["all", "gnupg"])
|
@pytest.mark.parametrize('param', ['all', 'gnupg'])
|
||||||
def test_relative_path(runner, paths, gnupghome, param):
|
def test_relative_path(runner, paths, gnupghome, param):
|
||||||
"""Test translate_to_relative"""
|
"""Test translate_to_relative"""
|
||||||
|
|
||||||
alt_gnupghome = "alt/gnupghome"
|
alt_gnupghome = 'alt/gnupghome'
|
||||||
env_gnupghome = paths.work.join(alt_gnupghome)
|
env_gnupghome = paths.work.join(alt_gnupghome)
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
|
@ -22,13 +22,13 @@ def test_relative_path(runner, paths, gnupghome, param):
|
||||||
|
|
||||||
env = {}
|
env = {}
|
||||||
if gnupghome:
|
if gnupghome:
|
||||||
env["GNUPGHOME"] = env_gnupghome
|
env['GNUPGHOME'] = env_gnupghome
|
||||||
|
|
||||||
expected = alt_gnupghome if gnupghome else ".gnupg"
|
expected = alt_gnupghome if gnupghome else '.gnupg'
|
||||||
if param == "all":
|
if param == 'all':
|
||||||
expected = f".ssh {expected}"
|
expected = f'.ssh {expected}'
|
||||||
|
|
||||||
run = runner(command=["bash"], inp=script, env=env)
|
run = runner(command=['bash'], inp=script, env=env)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out.strip() == expected
|
assert run.out.strip() == expected
|
||||||
|
|
|
@ -2,16 +2,18 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("condition", ["lsb_release", "os-release", "os-release-quotes", "missing"])
|
@pytest.mark.parametrize(
|
||||||
|
'condition', ['lsb_release', 'os-release', 'os-release-quotes', 'missing'])
|
||||||
def test_query_distro(runner, yadm, tst_distro, tmp_path, condition):
|
def test_query_distro(runner, yadm, tst_distro, tmp_path, condition):
|
||||||
"""Match lsb_release -si when present"""
|
"""Match lsb_release -si when present"""
|
||||||
test_release = "testrelease"
|
test_release = 'testrelease'
|
||||||
lsb_release = ""
|
lsb_release = ''
|
||||||
os_release = tmp_path.joinpath("os-release")
|
os_release = tmp_path.joinpath('os-release')
|
||||||
if "os-release" in condition:
|
if 'os-release' in condition:
|
||||||
quotes = '"' if "quotes" in condition else ""
|
quotes = '"' if 'quotes' in condition else ''
|
||||||
os_release.write_text(f"testing\nID={quotes}{test_release}{quotes}\nrelease")
|
os_release.write_text(
|
||||||
if condition != "lsb_release":
|
f"testing\nID={quotes}{test_release}{quotes}\nrelease")
|
||||||
|
if condition != 'lsb_release':
|
||||||
lsb_release = 'LSB_RELEASE_PROGRAM="missing_lsb_release"'
|
lsb_release = 'LSB_RELEASE_PROGRAM="missing_lsb_release"'
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
|
@ -19,12 +21,12 @@ def test_query_distro(runner, yadm, tst_distro, tmp_path, condition):
|
||||||
OS_RELEASE="{os_release}"
|
OS_RELEASE="{os_release}"
|
||||||
query_distro
|
query_distro
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
if condition == "lsb_release":
|
if condition == 'lsb_release':
|
||||||
assert run.out.rstrip() == tst_distro
|
assert run.out.rstrip() == tst_distro
|
||||||
elif "os-release" in condition:
|
elif 'os-release' in condition:
|
||||||
assert run.out.rstrip() == test_release
|
assert run.out.rstrip() == test_release
|
||||||
else:
|
else:
|
||||||
assert run.out.rstrip() == ""
|
assert run.out.rstrip() == ''
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
"""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() == ""
|
|
|
@ -30,13 +30,13 @@ def test_dont_record_zeros(runner, yadm):
|
||||||
record_score "0" "testtgt" "testsrc"
|
record_score "0" "testtgt" "testsrc"
|
||||||
{REPORT_RESULTS}
|
{REPORT_RESULTS}
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "SIZE:0\n" in run.out
|
assert 'SIZE:0\n' in run.out
|
||||||
assert "SCORES:\n" in run.out
|
assert 'SCORES:\n' in run.out
|
||||||
assert "TARGETS:\n" in run.out
|
assert 'TARGETS:\n' in run.out
|
||||||
assert "SOURCES:\n" in run.out
|
assert 'SOURCES:\n' in run.out
|
||||||
|
|
||||||
|
|
||||||
def test_new_scores(runner, yadm):
|
def test_new_scores(runner, yadm):
|
||||||
|
@ -50,29 +50,29 @@ def test_new_scores(runner, yadm):
|
||||||
record_score "4" "tgt_three" "src_three"
|
record_score "4" "tgt_three" "src_three"
|
||||||
{REPORT_RESULTS}
|
{REPORT_RESULTS}
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "SIZE:3\n" in run.out
|
assert 'SIZE:3\n' in run.out
|
||||||
assert "SCORES:1 2 4\n" in run.out
|
assert 'SCORES:1 2 4\n' in run.out
|
||||||
assert "TARGETS:tgt_one tgt_two tgt_three\n" in run.out
|
assert 'TARGETS:tgt_one tgt_two tgt_three\n' in run.out
|
||||||
assert "SOURCES:src_one src_two src_three\n" in run.out
|
assert 'SOURCES:src_one src_two src_three\n' in run.out
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("difference", ["lower", "equal", "higher"])
|
@pytest.mark.parametrize('difference', ['lower', 'equal', 'higher'])
|
||||||
def test_existing_scores(runner, yadm, difference):
|
def test_existing_scores(runner, yadm, difference):
|
||||||
"""Test existing scores"""
|
"""Test existing scores"""
|
||||||
|
|
||||||
expected_score = "2"
|
expected_score = '2'
|
||||||
expected_src = "existing_src"
|
expected_src = 'existing_src'
|
||||||
if difference == "lower":
|
if difference == 'lower':
|
||||||
score = "1"
|
score = '1'
|
||||||
elif difference == "equal":
|
elif difference == 'equal':
|
||||||
score = "2"
|
score = '2'
|
||||||
else:
|
else:
|
||||||
score = "4"
|
score = '4'
|
||||||
expected_score = "4"
|
expected_score = '4'
|
||||||
expected_src = "new_src"
|
expected_src = 'new_src'
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
|
@ -83,13 +83,13 @@ def test_existing_scores(runner, yadm, difference):
|
||||||
record_score "{score}" "testtgt" "new_src"
|
record_score "{score}" "testtgt" "new_src"
|
||||||
{REPORT_RESULTS}
|
{REPORT_RESULTS}
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "SIZE:1\n" in run.out
|
assert 'SIZE:1\n' in run.out
|
||||||
assert f"SCORES:{expected_score}\n" in run.out
|
assert f'SCORES:{expected_score}\n' in run.out
|
||||||
assert "TARGETS:testtgt\n" in run.out
|
assert 'TARGETS:testtgt\n' in run.out
|
||||||
assert f"SOURCES:{expected_src}\n" in run.out
|
assert f'SOURCES:{expected_src}\n' in run.out
|
||||||
|
|
||||||
|
|
||||||
def test_existing_template(runner, yadm):
|
def test_existing_template(runner, yadm):
|
||||||
|
@ -105,19 +105,19 @@ def test_existing_template(runner, yadm):
|
||||||
record_score "2" "testtgt" "new_src"
|
record_score "2" "testtgt" "new_src"
|
||||||
{REPORT_RESULTS}
|
{REPORT_RESULTS}
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "SIZE:1\n" in run.out
|
assert 'SIZE:1\n' in run.out
|
||||||
assert "SCORES:1\n" in run.out
|
assert 'SCORES:1\n' in run.out
|
||||||
assert "TARGETS:testtgt\n" in run.out
|
assert 'TARGETS:testtgt\n' in run.out
|
||||||
assert "SOURCES:\n" in run.out
|
assert 'SOURCES:\n' in run.out
|
||||||
|
|
||||||
|
|
||||||
def test_config_first(runner, yadm):
|
def test_config_first(runner, yadm):
|
||||||
"""Verify YADM_CONFIG is always processed first"""
|
"""Verify YADM_CONFIG is always processed first"""
|
||||||
|
|
||||||
config = "yadm_config_file"
|
config = 'yadm_config_file'
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
{INIT_VARS}
|
{INIT_VARS}
|
||||||
|
@ -130,12 +130,12 @@ def test_config_first(runner, yadm):
|
||||||
echo "CMD_VALUE:${{alt_template_cmds[@]}}"
|
echo "CMD_VALUE:${{alt_template_cmds[@]}}"
|
||||||
echo "CMD_INDEX:${{!alt_template_cmds[@]}}"
|
echo "CMD_INDEX:${{!alt_template_cmds[@]}}"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "SIZE:3\n" in run.out
|
assert 'SIZE:3\n' in run.out
|
||||||
assert "SCORES:2 1 3\n" in run.out
|
assert 'SCORES:2 1 3\n' in run.out
|
||||||
assert f"TARGETS:{config} tgt_before tgt_tmp tgt_after\n" in run.out
|
assert f'TARGETS:{config} tgt_before tgt_tmp tgt_after\n' in run.out
|
||||||
assert "SOURCES:src_config src_before src_tmp src_after\n" in run.out
|
assert 'SOURCES:src_config src_before src_tmp src_after\n' in run.out
|
||||||
assert "CMD_VALUE:cmd_tmp\n" in run.out
|
assert 'CMD_VALUE:cmd_tmp\n' in run.out
|
||||||
assert "CMD_INDEX:2\n" in run.out
|
assert 'CMD_INDEX:2\n' in run.out
|
||||||
|
|
|
@ -25,13 +25,13 @@ def test_new_template(runner, yadm):
|
||||||
record_template "tgt_three" "cmd_three" "src_three"
|
record_template "tgt_three" "cmd_three" "src_three"
|
||||||
{REPORT_RESULTS}
|
{REPORT_RESULTS}
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "SIZE:3\n" in run.out
|
assert 'SIZE:3\n' in run.out
|
||||||
assert "TARGETS:tgt_one tgt_two tgt_three\n" in run.out
|
assert 'TARGETS:tgt_one tgt_two tgt_three\n' in run.out
|
||||||
assert "CMDS:cmd_one cmd_two cmd_three\n" in run.out
|
assert 'CMDS:cmd_one cmd_two cmd_three\n' in run.out
|
||||||
assert "SOURCES:src_one src_two src_three\n" in run.out
|
assert 'SOURCES:src_one src_two src_three\n' in run.out
|
||||||
|
|
||||||
|
|
||||||
def test_existing_template(runner, yadm):
|
def test_existing_template(runner, yadm):
|
||||||
|
@ -46,10 +46,10 @@ def test_existing_template(runner, yadm):
|
||||||
record_template "testtgt" "new_cmd" "new_src"
|
record_template "testtgt" "new_cmd" "new_src"
|
||||||
{REPORT_RESULTS}
|
{REPORT_RESULTS}
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "SIZE:1\n" in run.out
|
assert 'SIZE:1\n' in run.out
|
||||||
assert "TARGETS:testtgt\n" in run.out
|
assert 'TARGETS:testtgt\n' in run.out
|
||||||
assert "CMDS:new_cmd\n" in run.out
|
assert 'CMDS:new_cmd\n' in run.out
|
||||||
assert "SOURCES:new_src\n" in run.out
|
assert 'SOURCES:new_src\n' in run.out
|
||||||
|
|
|
@ -3,7 +3,7 @@ import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"base,full_path,expected",
|
'base,full_path,expected',
|
||||||
[
|
[
|
||||||
("/A/B/C", "/A", "../.."),
|
("/A/B/C", "/A", "../.."),
|
||||||
("/A/B/C", "/A/B", ".."),
|
("/A/B/C", "/A/B", ".."),
|
||||||
|
@ -25,7 +25,7 @@ def test_relative_path(runner, paths, base, full_path, expected):
|
||||||
relative_path "{base}" "{full_path}"
|
relative_path "{base}" "{full_path}"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out.strip() == expected
|
assert run.out.strip() == expected
|
||||||
|
|
|
@ -1,24 +1,23 @@
|
||||||
"""Unit tests: remove_stale_links"""
|
"""Unit tests: remove_stale_links"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("linked", [True, False])
|
@pytest.mark.parametrize('linked', [True, False])
|
||||||
@pytest.mark.parametrize("kind", ["file", "symlink"])
|
@pytest.mark.parametrize('kind', ['file', 'symlink'])
|
||||||
def test_remove_stale_links(runner, yadm, tmpdir, kind, linked):
|
def test_remove_stale_links(runner, yadm, tmpdir, kind, linked):
|
||||||
"""Test remove_stale_links()"""
|
"""Test remove_stale_links()"""
|
||||||
|
|
||||||
source_file = tmpdir.join("source_file")
|
source_file = tmpdir.join('source_file')
|
||||||
source_file.write("source file", ensure=True)
|
source_file.write('source file', ensure=True)
|
||||||
link = tmpdir.join("link")
|
link = tmpdir.join('link')
|
||||||
|
|
||||||
if kind == "file":
|
if kind == 'file':
|
||||||
link.write("link file", ensure=True)
|
link.write('link file', ensure=True)
|
||||||
else:
|
else:
|
||||||
os.system(f"ln -s {source_file} {link}")
|
os.system(f'ln -s {source_file} {link}')
|
||||||
|
|
||||||
alt_linked = ""
|
alt_linked = ''
|
||||||
if linked:
|
if linked:
|
||||||
alt_linked = source_file
|
alt_linked = source_file
|
||||||
|
|
||||||
|
@ -30,9 +29,9 @@ def test_remove_stale_links(runner, yadm, tmpdir, kind, linked):
|
||||||
remove_stale_links
|
remove_stale_links
|
||||||
"""
|
"""
|
||||||
|
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
if kind == "symlink" and not linked:
|
if kind == 'symlink' and not linked:
|
||||||
assert f"rm -f {link}" in run.out
|
assert f'rm -f {link}' in run.out
|
||||||
else:
|
else:
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("valid", [True, False], ids=["valid", "no_valid"])
|
@pytest.mark.parametrize('valid', [True, False], ids=['valid', 'no_valid'])
|
||||||
@pytest.mark.parametrize("previous", [True, False], ids=["prev", "no_prev"])
|
@pytest.mark.parametrize('previous', [True, False], ids=['prev', 'no_prev'])
|
||||||
def test_report_invalid_alts(runner, yadm, valid, previous):
|
def test_report_invalid_alts(runner, yadm, valid, previous):
|
||||||
"""Use report_invalid_alts"""
|
"""Use report_invalid_alts"""
|
||||||
|
|
||||||
lwi = ""
|
lwi = ''
|
||||||
alts = "INVALID_ALT=()"
|
alts = 'INVALID_ALT=()'
|
||||||
if previous:
|
if previous:
|
||||||
lwi = "LEGACY_WARNING_ISSUED=1"
|
lwi = 'LEGACY_WARNING_ISSUED=1'
|
||||||
if not valid:
|
if not valid:
|
||||||
alts = 'INVALID_ALT=("file##invalid")'
|
alts = 'INVALID_ALT=("file##invalid")'
|
||||||
|
|
||||||
|
@ -20,11 +20,11 @@ def test_report_invalid_alts(runner, yadm, valid, previous):
|
||||||
{alts}
|
{alts}
|
||||||
report_invalid_alts
|
report_invalid_alts
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.out == ""
|
assert run.out == ''
|
||||||
if not valid and not previous:
|
if not valid and not previous:
|
||||||
assert "WARNING" in run.err
|
assert 'WARNING' in run.err
|
||||||
assert "file##invalid" in run.err
|
assert 'file##invalid' in run.err
|
||||||
else:
|
else:
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
|
|
|
@ -2,40 +2,32 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
CONDITION = {
|
CONDITION = {
|
||||||
"default": {
|
'default': {
|
||||||
"labels": ["default"],
|
'labels': ['default'],
|
||||||
"modifier": 0,
|
'modifier': 0,
|
||||||
},
|
},
|
||||||
"arch": {
|
'system': {
|
||||||
"labels": ["a", "arch"],
|
'labels': ['o', 'os'],
|
||||||
"modifier": 1,
|
'modifier': 1,
|
||||||
},
|
},
|
||||||
"system": {
|
'distro': {
|
||||||
"labels": ["o", "os"],
|
'labels': ['d', 'distro'],
|
||||||
"modifier": 2,
|
'modifier': 2,
|
||||||
},
|
},
|
||||||
"distro": {
|
'class': {
|
||||||
"labels": ["d", "distro"],
|
'labels': ['c', 'class'],
|
||||||
"modifier": 4,
|
'modifier': 4,
|
||||||
},
|
},
|
||||||
"distro_family": {
|
'hostname': {
|
||||||
"labels": ["f", "distro_family"],
|
'labels': ['h', 'hostname'],
|
||||||
"modifier": 8,
|
'modifier': 8,
|
||||||
},
|
},
|
||||||
"class": {
|
'user': {
|
||||||
"labels": ["c", "class"],
|
'labels': ['u', 'user'],
|
||||||
"modifier": 16,
|
'modifier': 16,
|
||||||
},
|
},
|
||||||
"hostname": {
|
}
|
||||||
"labels": ["h", "hostname"],
|
TEMPLATE_LABELS = ['t', 'template', 'yadm']
|
||||||
"modifier": 32,
|
|
||||||
},
|
|
||||||
"user": {
|
|
||||||
"labels": ["u", "user"],
|
|
||||||
"modifier": 64,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
TEMPLATE_LABELS = ["t", "template", "yadm"]
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_score(filename):
|
def calculate_score(filename):
|
||||||
|
@ -43,48 +35,42 @@ def calculate_score(filename):
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
score = 0
|
score = 0
|
||||||
|
|
||||||
_, conditions = filename.split("##", 1)
|
_, conditions = filename.split('##', 1)
|
||||||
|
|
||||||
for condition in conditions.split(","):
|
for condition in conditions.split(','):
|
||||||
label = condition
|
label = condition
|
||||||
value = None
|
value = None
|
||||||
if "." in condition:
|
if '.' in condition:
|
||||||
label, value = condition.split(".", 1)
|
label, value = condition.split('.', 1)
|
||||||
if label in CONDITION["default"]["labels"]:
|
if label in CONDITION['default']['labels']:
|
||||||
score += 1000
|
score += 1000
|
||||||
elif label in CONDITION["arch"]["labels"]:
|
elif label in CONDITION['system']['labels']:
|
||||||
if value == "testarch":
|
if value == 'testsystem':
|
||||||
score += 1000 + CONDITION["arch"]["modifier"]
|
score += 1000 + CONDITION['system']['modifier']
|
||||||
else:
|
else:
|
||||||
score = 0
|
score = 0
|
||||||
break
|
break
|
||||||
elif label in CONDITION["system"]["labels"]:
|
elif label in CONDITION['distro']['labels']:
|
||||||
if value == "testsystem":
|
if value == 'testdistro':
|
||||||
score += 1000 + CONDITION["system"]["modifier"]
|
score += 1000 + CONDITION['distro']['modifier']
|
||||||
else:
|
else:
|
||||||
score = 0
|
score = 0
|
||||||
break
|
break
|
||||||
elif label in CONDITION["distro"]["labels"]:
|
elif label in CONDITION['class']['labels']:
|
||||||
if value == "testdistro":
|
if value == 'testclass':
|
||||||
score += 1000 + CONDITION["distro"]["modifier"]
|
score += 1000 + CONDITION['class']['modifier']
|
||||||
else:
|
else:
|
||||||
score = 0
|
score = 0
|
||||||
break
|
break
|
||||||
elif label in CONDITION["class"]["labels"]:
|
elif label in CONDITION['hostname']['labels']:
|
||||||
if value == "testclass":
|
if value == 'testhost':
|
||||||
score += 1000 + CONDITION["class"]["modifier"]
|
score += 1000 + CONDITION['hostname']['modifier']
|
||||||
else:
|
else:
|
||||||
score = 0
|
score = 0
|
||||||
break
|
break
|
||||||
elif label in CONDITION["hostname"]["labels"]:
|
elif label in CONDITION['user']['labels']:
|
||||||
if value == "testhost":
|
if value == 'testuser':
|
||||||
score += 1000 + CONDITION["hostname"]["modifier"]
|
score += 1000 + CONDITION['user']['modifier']
|
||||||
else:
|
|
||||||
score = 0
|
|
||||||
break
|
|
||||||
elif label in CONDITION["user"]["labels"]:
|
|
||||||
if value == "testuser":
|
|
||||||
score += 1000 + CONDITION["user"]["modifier"]
|
|
||||||
else:
|
else:
|
||||||
score = 0
|
score = 0
|
||||||
break
|
break
|
||||||
|
@ -94,121 +80,130 @@ def calculate_score(filename):
|
||||||
return score
|
return score
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("default", ["default", None], ids=["default", "no-default"])
|
@pytest.mark.parametrize(
|
||||||
@pytest.mark.parametrize("arch", ["arch", None], ids=["arch", "no-arch"])
|
'default', ['default', None], ids=['default', 'no-default'])
|
||||||
@pytest.mark.parametrize("system", ["system", None], ids=["system", "no-system"])
|
@pytest.mark.parametrize(
|
||||||
@pytest.mark.parametrize("distro", ["distro", None], ids=["distro", "no-distro"])
|
'system', ['system', None], ids=['system', 'no-system'])
|
||||||
@pytest.mark.parametrize("cla", ["class", None], ids=["class", "no-class"])
|
@pytest.mark.parametrize(
|
||||||
@pytest.mark.parametrize("host", ["hostname", None], ids=["hostname", "no-host"])
|
'distro', ['distro', None], ids=['distro', 'no-distro'])
|
||||||
@pytest.mark.parametrize("user", ["user", None], ids=["user", "no-user"])
|
@pytest.mark.parametrize(
|
||||||
def test_score_values(runner, yadm, default, arch, system, distro, cla, host, user):
|
'cla', ['class', None], ids=['class', 'no-class'])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'host', ['hostname', None], ids=['hostname', 'no-host'])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'user', ['user', None], ids=['user', 'no-user'])
|
||||||
|
def test_score_values(
|
||||||
|
runner, yadm, default, system, distro, cla, host, user):
|
||||||
"""Test score results"""
|
"""Test score results"""
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
local_class = "testclass"
|
local_class = 'testclass'
|
||||||
local_arch = "testarch"
|
local_system = 'testsystem'
|
||||||
local_system = "testsystem"
|
local_distro = 'testdistro'
|
||||||
local_distro = "testdistro"
|
local_host = 'testhost'
|
||||||
local_host = "testhost"
|
local_user = 'testuser'
|
||||||
local_user = "testuser"
|
filenames = {'filename##': 0}
|
||||||
filenames = {"filename##": 0}
|
|
||||||
|
|
||||||
if default:
|
if default:
|
||||||
for filename in list(filenames):
|
for filename in list(filenames):
|
||||||
for label in CONDITION[default]["labels"]:
|
for label in CONDITION[default]['labels']:
|
||||||
newfile = filename
|
newfile = filename
|
||||||
if not newfile.endswith("##"):
|
if not newfile.endswith('##'):
|
||||||
newfile += ","
|
newfile += ','
|
||||||
newfile += label
|
newfile += label
|
||||||
filenames[newfile] = calculate_score(newfile)
|
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:
|
if system:
|
||||||
for filename in list(filenames):
|
for filename in list(filenames):
|
||||||
for match in [True, False]:
|
for match in [True, False]:
|
||||||
for label in CONDITION[system]["labels"]:
|
for label in CONDITION[system]['labels']:
|
||||||
newfile = filename
|
newfile = filename
|
||||||
if not newfile.endswith("##"):
|
if not newfile.endswith('##'):
|
||||||
newfile += ","
|
newfile += ','
|
||||||
newfile += ".".join([label, local_system if match else "badsys"])
|
newfile += '.'.join([
|
||||||
|
label,
|
||||||
|
local_system if match else 'badsys'
|
||||||
|
])
|
||||||
filenames[newfile] = calculate_score(newfile)
|
filenames[newfile] = calculate_score(newfile)
|
||||||
if distro:
|
if distro:
|
||||||
for filename in list(filenames):
|
for filename in list(filenames):
|
||||||
for match in [True, False]:
|
for match in [True, False]:
|
||||||
for label in CONDITION[distro]["labels"]:
|
for label in CONDITION[distro]['labels']:
|
||||||
newfile = filename
|
newfile = filename
|
||||||
if not newfile.endswith("##"):
|
if not newfile.endswith('##'):
|
||||||
newfile += ","
|
newfile += ','
|
||||||
newfile += ".".join([label, local_distro if match else "baddistro"])
|
newfile += '.'.join([
|
||||||
|
label,
|
||||||
|
local_distro if match else 'baddistro'
|
||||||
|
])
|
||||||
filenames[newfile] = calculate_score(newfile)
|
filenames[newfile] = calculate_score(newfile)
|
||||||
if cla:
|
if cla:
|
||||||
for filename in list(filenames):
|
for filename in list(filenames):
|
||||||
for match in [True, False]:
|
for match in [True, False]:
|
||||||
for label in CONDITION[cla]["labels"]:
|
for label in CONDITION[cla]['labels']:
|
||||||
newfile = filename
|
newfile = filename
|
||||||
if not newfile.endswith("##"):
|
if not newfile.endswith('##'):
|
||||||
newfile += ","
|
newfile += ','
|
||||||
newfile += ".".join([label, local_class if match else "badclass"])
|
newfile += '.'.join([
|
||||||
|
label,
|
||||||
|
local_class if match else 'badclass'
|
||||||
|
])
|
||||||
filenames[newfile] = calculate_score(newfile)
|
filenames[newfile] = calculate_score(newfile)
|
||||||
if host:
|
if host:
|
||||||
for filename in list(filenames):
|
for filename in list(filenames):
|
||||||
for match in [True, False]:
|
for match in [True, False]:
|
||||||
for label in CONDITION[host]["labels"]:
|
for label in CONDITION[host]['labels']:
|
||||||
newfile = filename
|
newfile = filename
|
||||||
if not newfile.endswith("##"):
|
if not newfile.endswith('##'):
|
||||||
newfile += ","
|
newfile += ','
|
||||||
newfile += ".".join([label, local_host if match else "badhost"])
|
newfile += '.'.join([
|
||||||
|
label,
|
||||||
|
local_host if match else 'badhost'
|
||||||
|
])
|
||||||
filenames[newfile] = calculate_score(newfile)
|
filenames[newfile] = calculate_score(newfile)
|
||||||
if user:
|
if user:
|
||||||
for filename in list(filenames):
|
for filename in list(filenames):
|
||||||
for match in [True, False]:
|
for match in [True, False]:
|
||||||
for label in CONDITION[user]["labels"]:
|
for label in CONDITION[user]['labels']:
|
||||||
newfile = filename
|
newfile = filename
|
||||||
if not newfile.endswith("##"):
|
if not newfile.endswith('##'):
|
||||||
newfile += ","
|
newfile += ','
|
||||||
newfile += ".".join([label, local_user if match else "baduser"])
|
newfile += '.'.join([
|
||||||
|
label,
|
||||||
|
local_user if match else 'baduser'
|
||||||
|
])
|
||||||
filenames[newfile] = calculate_score(newfile)
|
filenames[newfile] = calculate_score(newfile)
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
score=0
|
score=0
|
||||||
local_class={local_class}
|
local_class={local_class}
|
||||||
local_classes=({local_class})
|
|
||||||
local_arch={local_arch}
|
|
||||||
local_system={local_system}
|
local_system={local_system}
|
||||||
local_distro={local_distro}
|
local_distro={local_distro}
|
||||||
local_host={local_host}
|
local_host={local_host}
|
||||||
local_user={local_user}
|
local_user={local_user}
|
||||||
"""
|
"""
|
||||||
expected = ""
|
expected = ''
|
||||||
for filename, score in filenames.items():
|
for filename in filenames:
|
||||||
script += f"""
|
script += f"""
|
||||||
score_file "{filename}"
|
score_file "{filename}"
|
||||||
echo "{filename}"
|
echo "{filename}"
|
||||||
echo "$score"
|
echo "$score"
|
||||||
"""
|
"""
|
||||||
expected += filename + "\n"
|
expected += filename + '\n'
|
||||||
expected += str(score) + "\n"
|
expected += str(filenames[filename]) + '\n'
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out == expected
|
assert run.out == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("ext", [None, "e", "extension"])
|
@pytest.mark.parametrize('ext', [None, 'e', 'extension'])
|
||||||
def test_extensions(runner, yadm, ext):
|
def test_extensions(runner, yadm, ext):
|
||||||
"""Verify extensions do not effect scores"""
|
"""Verify extensions do not effect scores"""
|
||||||
local_user = "testuser"
|
local_user = 'testuser'
|
||||||
filename = f"filename##u.{local_user}"
|
filename = f'filename##u.{local_user}'
|
||||||
if ext:
|
if ext:
|
||||||
filename += f",{ext}.xyz"
|
filename += f',{ext}.xyz'
|
||||||
expected = ""
|
expected = ''
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
score=0
|
score=0
|
||||||
|
@ -217,64 +212,65 @@ def test_extensions(runner, yadm, ext):
|
||||||
echo "$score"
|
echo "$score"
|
||||||
"""
|
"""
|
||||||
expected = f'{1000 + CONDITION["user"]["modifier"]}\n'
|
expected = f'{1000 + CONDITION["user"]["modifier"]}\n'
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out == expected
|
assert run.out == expected
|
||||||
|
|
||||||
|
|
||||||
def test_score_values_templates(runner, yadm):
|
def test_score_values_templates(runner, yadm):
|
||||||
"""Test score results"""
|
"""Test score results"""
|
||||||
local_class = "testclass"
|
local_class = 'testclass'
|
||||||
local_arch = "arch"
|
local_system = 'testsystem'
|
||||||
local_system = "testsystem"
|
local_distro = 'testdistro'
|
||||||
local_distro = "testdistro"
|
local_host = 'testhost'
|
||||||
local_host = "testhost"
|
local_user = 'testuser'
|
||||||
local_user = "testuser"
|
filenames = {'filename##': 0}
|
||||||
filenames = {"filename##": 0}
|
|
||||||
|
|
||||||
for filename in list(filenames):
|
for filename in list(filenames):
|
||||||
for label in TEMPLATE_LABELS:
|
for label in TEMPLATE_LABELS:
|
||||||
newfile = filename
|
newfile = filename
|
||||||
if not newfile.endswith("##"):
|
if not newfile.endswith('##'):
|
||||||
newfile += ","
|
newfile += ','
|
||||||
newfile += ".".join([label, "testtemplate"])
|
newfile += '.'.join([label, 'testtemplate'])
|
||||||
filenames[newfile] = calculate_score(newfile)
|
filenames[newfile] = calculate_score(newfile)
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
score=0
|
score=0
|
||||||
local_class={local_class}
|
local_class={local_class}
|
||||||
local_arch={local_arch}
|
|
||||||
local_system={local_system}
|
local_system={local_system}
|
||||||
local_distro={local_distro}
|
local_distro={local_distro}
|
||||||
local_host={local_host}
|
local_host={local_host}
|
||||||
local_user={local_user}
|
local_user={local_user}
|
||||||
"""
|
"""
|
||||||
expected = ""
|
expected = ''
|
||||||
for filename, score in filenames.items():
|
for filename in filenames:
|
||||||
script += f"""
|
script += f"""
|
||||||
score_file "{filename}"
|
score_file "{filename}"
|
||||||
echo "{filename}"
|
echo "{filename}"
|
||||||
echo "$score"
|
echo "$score"
|
||||||
"""
|
"""
|
||||||
expected += filename + "\n"
|
expected += filename + '\n'
|
||||||
expected += str(score) + "\n"
|
expected += str(filenames[filename]) + '\n'
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out == expected
|
assert run.out == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("cmd_generated", [True, False], ids=["supported-template", "unsupported-template"])
|
@pytest.mark.parametrize(
|
||||||
|
'cmd_generated',
|
||||||
|
[True, False],
|
||||||
|
ids=['supported-template', 'unsupported-template'])
|
||||||
def test_template_recording(runner, yadm, cmd_generated):
|
def test_template_recording(runner, yadm, cmd_generated):
|
||||||
"""Template should be recorded if choose_template_cmd outputs a command"""
|
"""Template should be recorded if choose_template_cmd outputs a command"""
|
||||||
|
|
||||||
mock = "function choose_template_cmd() { return; }"
|
mock = 'function choose_template_cmd() { return; }'
|
||||||
expected = ""
|
expected = ''
|
||||||
if cmd_generated:
|
if cmd_generated:
|
||||||
mock = 'function choose_template_cmd() { echo "test_cmd"; }'
|
mock = 'function choose_template_cmd() { echo "test_cmd"; }'
|
||||||
expected = "template recorded"
|
expected = 'template recorded'
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
|
@ -282,41 +278,7 @@ def test_template_recording(runner, yadm, cmd_generated):
|
||||||
{mock}
|
{mock}
|
||||||
score_file "testfile##template.kind"
|
score_file "testfile##template.kind"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert run.out.rstrip() == expected
|
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, score in filenames.items():
|
|
||||||
script += f"""
|
|
||||||
score_file "{filename}"
|
|
||||||
echo "{filename}"
|
|
||||||
echo "$score"
|
|
||||||
"""
|
|
||||||
expected += filename + "\n"
|
|
||||||
expected += str(score) + "\n"
|
|
||||||
run = runner(command=["bash"], inp=script)
|
|
||||||
assert run.success
|
|
||||||
assert run.err == ""
|
|
||||||
assert run.out == expected
|
|
||||||
|
|
|
@ -4,26 +4,24 @@ import utils
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"override",
|
'override', [
|
||||||
[
|
|
||||||
False,
|
False,
|
||||||
"class",
|
'class',
|
||||||
"arch",
|
'os',
|
||||||
"os",
|
'hostname',
|
||||||
"hostname",
|
'user',
|
||||||
"user",
|
],
|
||||||
],
|
|
||||||
ids=[
|
ids=[
|
||||||
"no-override",
|
'no-override',
|
||||||
"override-class",
|
'override-class',
|
||||||
"override-arch",
|
'override-os',
|
||||||
"override-os",
|
'override-hostname',
|
||||||
"override-hostname",
|
'override-user',
|
||||||
"override-user",
|
]
|
||||||
],
|
)
|
||||||
)
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
@pytest.mark.usefixtures("ds1_copy")
|
def test_set_local_alt_values(
|
||||||
def test_set_local_alt_values(runner, yadm, paths, tst_arch, tst_sys, tst_host, tst_user, override):
|
runner, yadm, paths, tst_sys, tst_host, tst_user, override):
|
||||||
"""Use issue_legacy_path_warning"""
|
"""Use issue_legacy_path_warning"""
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm} &&
|
YADM_TEST=1 source {yadm} &&
|
||||||
|
@ -31,62 +29,50 @@ def test_set_local_alt_values(runner, yadm, paths, tst_arch, tst_sys, tst_host,
|
||||||
YADM_DIR={paths.yadm} YADM_DATA={paths.data} configure_paths &&
|
YADM_DIR={paths.yadm} YADM_DATA={paths.data} configure_paths &&
|
||||||
set_local_alt_values
|
set_local_alt_values
|
||||||
echo "class='$local_class'"
|
echo "class='$local_class'"
|
||||||
echo "arch='$local_arch'"
|
|
||||||
echo "os='$local_system'"
|
echo "os='$local_system'"
|
||||||
echo "host='$local_host'"
|
echo "host='$local_host'"
|
||||||
echo "user='$local_user'"
|
echo "user='$local_user'"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if override == "class":
|
if override:
|
||||||
utils.set_local(paths, override, "first")
|
utils.set_local(paths, override, 'override')
|
||||||
utils.set_local(paths, override, "override", add=True)
|
|
||||||
elif override:
|
|
||||||
utils.set_local(paths, override, "override")
|
|
||||||
|
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
|
|
||||||
if override == "class":
|
if override == 'class':
|
||||||
assert "class='override'" in run.out
|
assert "class='override'" in run.out
|
||||||
else:
|
else:
|
||||||
assert "class=''" in run.out
|
assert "class=''" in run.out
|
||||||
|
|
||||||
if override == "arch":
|
if override == 'os':
|
||||||
assert "arch='override'" in run.out
|
|
||||||
else:
|
|
||||||
assert f"arch='{tst_arch}'" in run.out
|
|
||||||
|
|
||||||
if override == "os":
|
|
||||||
assert "os='override'" in run.out
|
assert "os='override'" in run.out
|
||||||
else:
|
else:
|
||||||
assert f"os='{tst_sys}'" in run.out
|
assert f"os='{tst_sys}'" in run.out
|
||||||
|
|
||||||
if override == "hostname":
|
if override == 'hostname':
|
||||||
assert "host='override'" in run.out
|
assert "host='override'" in run.out
|
||||||
else:
|
else:
|
||||||
assert f"host='{tst_host}'" in run.out
|
assert f"host='{tst_host}'" in run.out
|
||||||
|
|
||||||
if override == "user":
|
if override == 'user':
|
||||||
assert "user='override'" in run.out
|
assert "user='override'" in run.out
|
||||||
else:
|
else:
|
||||||
assert f"user='{tst_user}'" in run.out
|
assert f"user='{tst_user}'" in run.out
|
||||||
|
|
||||||
|
|
||||||
def test_distro_and_family(runner, yadm):
|
def test_distro(runner, yadm):
|
||||||
"""Assert that local_distro/local_distro_family are set"""
|
"""Assert that local_distro is set"""
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
function config() {{ echo "$1"; }}
|
function config() {{ echo "$1"; }}
|
||||||
function query_distro() {{ echo "testdistro"; }}
|
function query_distro() {{ echo "testdistro"; }}
|
||||||
function query_distro_family() {{ echo "testfamily"; }}
|
|
||||||
set_local_alt_values
|
set_local_alt_values
|
||||||
echo "distro='$local_distro'"
|
echo "distro='$local_distro'"
|
||||||
echo "distro_family='$local_distro_family'"
|
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "distro='testdistro'" in run.out
|
assert run.out.strip() == "distro='testdistro'"
|
||||||
assert "distro_family='testfamily'" in run.out
|
|
||||||
|
|
|
@ -4,27 +4,25 @@ import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"proc_value, expected_os",
|
'proc_value, expected_os', [
|
||||||
[
|
('missing', 'uname'),
|
||||||
("missing", "uname"),
|
('has microsoft inside', 'WSL'), # case insensitive
|
||||||
("has microsoft inside", "WSL"), # case insensitive
|
('has Microsoft inside', 'WSL'), # case insensitive
|
||||||
("has Microsoft inside", "WSL"), # case insensitive
|
('another value', 'uname'),
|
||||||
("another value", "uname"),
|
], ids=[
|
||||||
],
|
'/proc/version missing',
|
||||||
ids=[
|
'/proc/version includes ms',
|
||||||
"/proc/version missing",
|
'/proc/version excludes Ms',
|
||||||
"/proc/version includes ms",
|
'another value',
|
||||||
"/proc/version excludes Ms",
|
])
|
||||||
"another value",
|
def test_set_operating_system(
|
||||||
],
|
runner, paths, tst_sys, proc_value, expected_os):
|
||||||
)
|
|
||||||
def test_set_operating_system(runner, paths, tst_sys, proc_value, expected_os):
|
|
||||||
"""Run set_operating_system and test result"""
|
"""Run set_operating_system and test result"""
|
||||||
|
|
||||||
# Normally /proc/version (set in PROC_VERSION) is inspected to identify
|
# Normally /proc/version (set in PROC_VERSION) is inspected to identify
|
||||||
# WSL. During testing, we will override that value.
|
# WSL. During testing, we will override that value.
|
||||||
proc_version = paths.root.join("proc_version")
|
proc_version = paths.root.join('proc_version')
|
||||||
if proc_value != "missing":
|
if proc_value != 'missing':
|
||||||
proc_version.write(proc_value)
|
proc_version.write(proc_value)
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {paths.pgm}
|
YADM_TEST=1 source {paths.pgm}
|
||||||
|
@ -32,9 +30,9 @@ def test_set_operating_system(runner, paths, tst_sys, proc_value, expected_os):
|
||||||
set_operating_system
|
set_operating_system
|
||||||
echo $OPERATING_SYSTEM
|
echo $OPERATING_SYSTEM
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
if expected_os == "uname":
|
if expected_os == 'uname':
|
||||||
expected_os = tst_sys
|
expected_os = tst_sys
|
||||||
assert run.out.rstrip() == expected_os
|
assert run.out.rstrip() == expected_os
|
||||||
|
|
|
@ -3,20 +3,25 @@ import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"condition",
|
'condition', [
|
||||||
["basic", "override", "override_data", "xdg_config_home", "xdg_data_home"],
|
'basic',
|
||||||
)
|
'override',
|
||||||
|
'override_data',
|
||||||
|
'xdg_config_home',
|
||||||
|
'xdg_data_home'
|
||||||
|
],
|
||||||
|
)
|
||||||
def test_set_yadm_dirs(runner, yadm, condition):
|
def test_set_yadm_dirs(runner, yadm, condition):
|
||||||
"""Test set_yadm_dirs"""
|
"""Test set_yadm_dirs"""
|
||||||
setup = ""
|
setup = ''
|
||||||
if condition == "override":
|
if condition == 'override':
|
||||||
setup = "YADM_DIR=/override"
|
setup = 'YADM_DIR=/override'
|
||||||
elif condition == "override_data":
|
elif condition == 'override_data':
|
||||||
setup = "YADM_DATA=/override"
|
setup = 'YADM_DATA=/override'
|
||||||
elif condition == "xdg_config_home":
|
elif condition == 'xdg_config_home':
|
||||||
setup = "XDG_CONFIG_HOME=/xdg"
|
setup = 'XDG_CONFIG_HOME=/xdg'
|
||||||
elif condition == "xdg_data_home":
|
elif condition == 'xdg_data_home':
|
||||||
setup = "XDG_DATA_HOME=/xdg"
|
setup = 'XDG_DATA_HOME=/xdg'
|
||||||
script = f"""
|
script = f"""
|
||||||
HOME=/testhome
|
HOME=/testhome
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
|
@ -27,17 +32,17 @@ def test_set_yadm_dirs(runner, yadm, condition):
|
||||||
echo "YADM_DIR=$YADM_DIR"
|
echo "YADM_DIR=$YADM_DIR"
|
||||||
echo "YADM_DATA=$YADM_DATA"
|
echo "YADM_DATA=$YADM_DATA"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
if condition == "basic":
|
if condition == 'basic':
|
||||||
assert "YADM_DIR=/testhome/.config/yadm" in run.out
|
assert 'YADM_DIR=/testhome/.config/yadm' in run.out
|
||||||
assert "YADM_DATA=/testhome/.local/share/yadm" in run.out
|
assert 'YADM_DATA=/testhome/.local/share/yadm' in run.out
|
||||||
elif condition == "override":
|
elif condition == 'override':
|
||||||
assert "YADM_DIR=/override" in run.out
|
assert 'YADM_DIR=/override' in run.out
|
||||||
elif condition == "override_data":
|
elif condition == 'override_data':
|
||||||
assert "YADM_DATA=/override" in run.out
|
assert 'YADM_DATA=/override' in run.out
|
||||||
elif condition == "xdg_config_home":
|
elif condition == 'xdg_config_home':
|
||||||
assert "YADM_DIR=/xdg/yadm" in run.out
|
assert 'YADM_DIR=/xdg/yadm' in run.out
|
||||||
elif condition == "xdg_data_home":
|
elif condition == 'xdg_data_home':
|
||||||
assert "YADM_DATA=/xdg/yadm" in run.out
|
assert 'YADM_DATA=/xdg/yadm' in run.out
|
||||||
|
|
|
@ -1,29 +1,21 @@
|
||||||
"""Unit tests: template_default"""
|
"""Unit tests: template_default"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
FILE_MODE = 0o754
|
FILE_MODE = 0o754
|
||||||
|
|
||||||
# these values are also testing the handling of bizarre characters
|
# these values are also testing the handling of bizarre characters
|
||||||
LOCAL_CLASS = "default_Test+@-!^Class"
|
LOCAL_CLASS = "default_Test+@-!^Class"
|
||||||
LOCAL_CLASS2 = "default_Test+@-|^2nd_Class withSpace"
|
|
||||||
LOCAL_ARCH = "default_Test+@-!^Arch"
|
|
||||||
LOCAL_SYSTEM = "default_Test+@-!^System"
|
LOCAL_SYSTEM = "default_Test+@-!^System"
|
||||||
LOCAL_HOST = "default_Test+@-!^Host"
|
LOCAL_HOST = "default_Test+@-!^Host"
|
||||||
LOCAL_USER = "default_Test+@-!^User"
|
LOCAL_USER = "default_Test+@-!^User"
|
||||||
LOCAL_DISTRO = "default_Test+@-!^Distro"
|
LOCAL_DISTRO = "default_Test+@-!^Distro"
|
||||||
LOCAL_DISTRO_FAMILY = "default_Test+@-!^Family"
|
TEMPLATE = f'''
|
||||||
ENV_VAR = "default_Test+@-!^Env"
|
|
||||||
TEMPLATE = f"""
|
|
||||||
start of template
|
start of template
|
||||||
default class = >{{{{yadm.class}}}}<
|
default class = >{{{{yadm.class}}}}<
|
||||||
default arch = >{{{{yadm.arch}}}}<
|
default os = >{{{{yadm.os}}}}<
|
||||||
default os = >{{{{yadm.os}}}}<
|
default host = >{{{{yadm.hostname}}}}<
|
||||||
default host = >{{{{yadm.hostname}}}}<
|
default user = >{{{{yadm.user}}}}<
|
||||||
default user = >{{{{yadm.user}}}}<
|
default distro = >{{{{yadm.distro}}}}<
|
||||||
default distro = >{{{{yadm.distro}}}}<
|
|
||||||
default distro_family = >{{{{yadm.distro_family}}}}<
|
|
||||||
classes = >{{{{yadm.classes}}}}<
|
|
||||||
{{% if yadm.class == "else1" %}}
|
{{% if yadm.class == "else1" %}}
|
||||||
wrong else 1
|
wrong else 1
|
||||||
{{% else %}}
|
{{% else %}}
|
||||||
|
@ -32,30 +24,15 @@ Included section from else
|
||||||
{{% if yadm.class == "wrongclass1" %}}
|
{{% if yadm.class == "wrongclass1" %}}
|
||||||
wrong class 1
|
wrong class 1
|
||||||
{{% endif %}}
|
{{% endif %}}
|
||||||
{{% if yadm.class != "wronglcass" %}}
|
|
||||||
Included section from !=
|
|
||||||
{{% endif\t\t %}}
|
|
||||||
{{% if yadm.class == "{LOCAL_CLASS}" %}}
|
{{% if yadm.class == "{LOCAL_CLASS}" %}}
|
||||||
Included section for class = {{{{yadm.class}}}} ({{{{yadm.class}}}} repeated)
|
Included section for class = {{{{yadm.class}}}} ({{{{yadm.class}}}} repeated)
|
||||||
Multiple lines
|
Multiple lines
|
||||||
{{% else %}}
|
{{% else %}}
|
||||||
Should not be included...
|
Should not be included...
|
||||||
{{% endif %}}
|
{{% endif %}}
|
||||||
{{% if yadm.class == "{LOCAL_CLASS2}" %}}
|
|
||||||
Included section for second class
|
|
||||||
{{% endif %}}
|
|
||||||
{{% if yadm.class == "wrongclass2" %}}
|
{{% if yadm.class == "wrongclass2" %}}
|
||||||
wrong class 2
|
wrong class 2
|
||||||
{{% endif %}}
|
{{% 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" %}}
|
{{% if yadm.os == "wrongos1" %}}
|
||||||
wrong os 1
|
wrong os 1
|
||||||
{{% endif %}}
|
{{% endif %}}
|
||||||
|
@ -92,72 +69,44 @@ Included section for distro = {{{{yadm.distro}}}} ({{{{yadm.distro}}}} again)
|
||||||
{{% if yadm.distro == "wrongdistro2" %}}
|
{{% if yadm.distro == "wrongdistro2" %}}
|
||||||
wrong distro 2
|
wrong distro 2
|
||||||
{{% endif %}}
|
{{% 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 %}}
|
|
||||||
{{% if env.VAR == "{ENV_VAR}" %}}
|
|
||||||
Included section for env.VAR = {{{{env.VAR}}}} ({{{{env.VAR}}}} again)
|
|
||||||
{{% endif %}}
|
|
||||||
{{% if env.VAR == "wrongenvvar" %}}
|
|
||||||
wrong env.VAR
|
|
||||||
{{% endif %}}
|
|
||||||
yadm.no_such_var="{{{{ yadm.no_such_var }}}}" and env.NO_SUCH_VAR="{{{{ env.NO_SUCH_VAR }}}}"
|
|
||||||
end of template
|
end of template
|
||||||
"""
|
'''
|
||||||
EXPECTED = f"""
|
EXPECTED = f'''
|
||||||
start of template
|
start of template
|
||||||
default class = >{LOCAL_CLASS}<
|
default class = >{LOCAL_CLASS}<
|
||||||
default arch = >{LOCAL_ARCH}<
|
default os = >{LOCAL_SYSTEM}<
|
||||||
default os = >{LOCAL_SYSTEM}<
|
default host = >{LOCAL_HOST}<
|
||||||
default host = >{LOCAL_HOST}<
|
default user = >{LOCAL_USER}<
|
||||||
default user = >{LOCAL_USER}<
|
default distro = >{LOCAL_DISTRO}<
|
||||||
default distro = >{LOCAL_DISTRO}<
|
|
||||||
default distro_family = >{LOCAL_DISTRO_FAMILY}<
|
|
||||||
classes = >{LOCAL_CLASS2}
|
|
||||||
{LOCAL_CLASS}<
|
|
||||||
Included section from else
|
Included section from else
|
||||||
Included section from !=
|
|
||||||
Included section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated)
|
Included section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated)
|
||||||
Multiple lines
|
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 os = {LOCAL_SYSTEM} ({LOCAL_SYSTEM} repeated)
|
||||||
Included section for host = {LOCAL_HOST} ({LOCAL_HOST} again)
|
Included section for host = {LOCAL_HOST} ({LOCAL_HOST} again)
|
||||||
Included section for user = {LOCAL_USER} ({LOCAL_USER} repeated)
|
Included section for user = {LOCAL_USER} ({LOCAL_USER} repeated)
|
||||||
Included section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again)
|
Included section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again)
|
||||||
Included section for distro_family = \
|
|
||||||
{LOCAL_DISTRO_FAMILY} ({LOCAL_DISTRO_FAMILY} again)
|
|
||||||
Included section for env.VAR = {ENV_VAR} ({ENV_VAR} again)
|
|
||||||
yadm.no_such_var="" and env.NO_SUCH_VAR=""
|
|
||||||
end of template
|
end of template
|
||||||
"""
|
'''
|
||||||
|
|
||||||
INCLUDE_BASIC = "basic\n"
|
INCLUDE_BASIC = 'basic\n'
|
||||||
INCLUDE_VARIABLES = """\
|
INCLUDE_VARIABLES = '''\
|
||||||
included <{{ yadm.class }}> file
|
included <{{ yadm.class }}> file
|
||||||
|
|
||||||
empty line above
|
empty line above
|
||||||
"""
|
'''
|
||||||
INCLUDE_NESTED = "no newline at the end"
|
INCLUDE_NESTED = 'no newline at the end'
|
||||||
|
|
||||||
TEMPLATE_INCLUDE = """\
|
TEMPLATE_INCLUDE = '''\
|
||||||
The first line
|
The first line
|
||||||
{% include empty %}
|
{% include empty %}
|
||||||
An empty file removes the line above
|
An empty file removes the line above
|
||||||
{%include basic%}
|
{%include basic%}
|
||||||
{% include "./variables.{{ yadm.os }}" %}
|
{% include "./variables.{{ yadm.os }}" %}
|
||||||
{% include dir/nested %}
|
{% include dir/nested %}
|
||||||
Include basic again:
|
Include basic again:
|
||||||
{% include basic %}
|
{% include basic %}
|
||||||
"""
|
'''
|
||||||
EXPECTED_INCLUDE = f"""\
|
EXPECTED_INCLUDE = f'''\
|
||||||
The first line
|
The first line
|
||||||
An empty file removes the line above
|
An empty file removes the line above
|
||||||
basic
|
basic
|
||||||
|
@ -167,75 +116,30 @@ empty line above
|
||||||
no newline at the end
|
no newline at the end
|
||||||
Include basic again:
|
Include basic again:
|
||||||
basic
|
basic
|
||||||
"""
|
'''
|
||||||
|
|
||||||
TEMPLATE_NESTED_IFS = """\
|
|
||||||
{% if yadm.user == "me" %}
|
|
||||||
print1
|
|
||||||
{% if yadm.user == "me" %}
|
|
||||||
print2
|
|
||||||
{% else %}
|
|
||||||
no print1
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
{% if yadm.user == "me" %}
|
|
||||||
no print2
|
|
||||||
{% else %}
|
|
||||||
no print3
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% if yadm.user != "me" %}
|
|
||||||
no print4
|
|
||||||
{% if yadm.user == "me" %}
|
|
||||||
no print5
|
|
||||||
{% else %}
|
|
||||||
no print6
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
{% if yadm.user == "me" %}
|
|
||||||
print3
|
|
||||||
{% else %}
|
|
||||||
no print7
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
"""
|
|
||||||
EXPECTED_NESTED_IFS = """\
|
|
||||||
print1
|
|
||||||
print2
|
|
||||||
print3
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def test_template_default(runner, yadm, tmpdir):
|
def test_template_default(runner, yadm, tmpdir):
|
||||||
"""Test template_default"""
|
"""Test template_default"""
|
||||||
|
|
||||||
input_file = tmpdir.join("input")
|
input_file = tmpdir.join('input')
|
||||||
input_file.write(TEMPLATE, ensure=True)
|
input_file.write(TEMPLATE, ensure=True)
|
||||||
input_file.chmod(FILE_MODE)
|
input_file.chmod(FILE_MODE)
|
||||||
output_file = tmpdir.join("output")
|
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"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
set_awk
|
set_awk
|
||||||
local_class="{LOCAL_CLASS}"
|
local_class="{LOCAL_CLASS}"
|
||||||
local_classes=("{LOCAL_CLASS2}" "{LOCAL_CLASS}")
|
|
||||||
local_arch="{LOCAL_ARCH}"
|
|
||||||
local_system="{LOCAL_SYSTEM}"
|
local_system="{LOCAL_SYSTEM}"
|
||||||
local_host="{LOCAL_HOST}"
|
local_host="{LOCAL_HOST}"
|
||||||
local_user="{LOCAL_USER}"
|
local_user="{LOCAL_USER}"
|
||||||
local_distro="{LOCAL_DISTRO}"
|
local_distro="{LOCAL_DISTRO}"
|
||||||
local_distro_family="{LOCAL_DISTRO_FAMILY}"
|
|
||||||
template_default "{input_file}" "{output_file}"
|
template_default "{input_file}" "{output_file}"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script, env={"VAR": ENV_VAR})
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert output_file.read() == EXPECTED
|
assert output_file.read() == EXPECTED
|
||||||
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
||||||
|
|
||||||
|
@ -243,19 +147,19 @@ def test_template_default(runner, yadm, tmpdir):
|
||||||
def test_source(runner, yadm, tmpdir):
|
def test_source(runner, yadm, tmpdir):
|
||||||
"""Test yadm.source"""
|
"""Test yadm.source"""
|
||||||
|
|
||||||
input_file = tmpdir.join("input")
|
input_file = tmpdir.join('input')
|
||||||
input_file.write("{{yadm.source}}", ensure=True)
|
input_file.write('{{yadm.source}}', ensure=True)
|
||||||
input_file.chmod(FILE_MODE)
|
input_file.chmod(FILE_MODE)
|
||||||
output_file = tmpdir.join("output")
|
output_file = tmpdir.join('output')
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
set_awk
|
set_awk
|
||||||
template_default "{input_file}" "{output_file}"
|
template_default "{input_file}" "{output_file}"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert output_file.read().strip() == str(input_file)
|
assert output_file.read().strip() == str(input_file)
|
||||||
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
||||||
|
|
||||||
|
@ -263,22 +167,22 @@ def test_source(runner, yadm, tmpdir):
|
||||||
def test_include(runner, yadm, tmpdir):
|
def test_include(runner, yadm, tmpdir):
|
||||||
"""Test include"""
|
"""Test include"""
|
||||||
|
|
||||||
empty_file = tmpdir.join("empty")
|
empty_file = tmpdir.join('empty')
|
||||||
empty_file.write("", ensure=True)
|
empty_file.write('', ensure=True)
|
||||||
|
|
||||||
basic_file = tmpdir.join("basic")
|
basic_file = tmpdir.join('basic')
|
||||||
basic_file.write(INCLUDE_BASIC)
|
basic_file.write(INCLUDE_BASIC)
|
||||||
|
|
||||||
variables_file = tmpdir.join(f"variables.{LOCAL_SYSTEM}")
|
variables_file = tmpdir.join(f'variables.{LOCAL_SYSTEM}')
|
||||||
variables_file.write(INCLUDE_VARIABLES)
|
variables_file.write(INCLUDE_VARIABLES)
|
||||||
|
|
||||||
nested_file = tmpdir.join("dir").join("nested")
|
nested_file = tmpdir.join('dir').join('nested')
|
||||||
nested_file.write(INCLUDE_NESTED, ensure=True)
|
nested_file.write(INCLUDE_NESTED, ensure=True)
|
||||||
|
|
||||||
input_file = tmpdir.join("input")
|
input_file = tmpdir.join('input')
|
||||||
input_file.write(TEMPLATE_INCLUDE)
|
input_file.write(TEMPLATE_INCLUDE)
|
||||||
input_file.chmod(FILE_MODE)
|
input_file.chmod(FILE_MODE)
|
||||||
output_file = tmpdir.join("output")
|
output_file = tmpdir.join('output')
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
|
@ -287,45 +191,8 @@ def test_include(runner, yadm, tmpdir):
|
||||||
local_system="{LOCAL_SYSTEM}"
|
local_system="{LOCAL_SYSTEM}"
|
||||||
template_default "{input_file}" "{output_file}"
|
template_default "{input_file}" "{output_file}"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert output_file.read() == EXPECTED_INCLUDE
|
assert output_file.read() == EXPECTED_INCLUDE
|
||||||
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
||||||
|
|
||||||
|
|
||||||
def test_nested_ifs(runner, yadm, tmpdir):
|
|
||||||
"""Test nested if statements"""
|
|
||||||
|
|
||||||
input_file = tmpdir.join("input")
|
|
||||||
input_file.write(TEMPLATE_NESTED_IFS, ensure=True)
|
|
||||||
output_file = tmpdir.join("output")
|
|
||||||
|
|
||||||
script = f"""
|
|
||||||
YADM_TEST=1 source {yadm}
|
|
||||||
set_awk
|
|
||||||
local_user="me"
|
|
||||||
template_default "{input_file}" "{output_file}"
|
|
||||||
"""
|
|
||||||
run = runner(command=["bash"], inp=script)
|
|
||||||
assert run.success
|
|
||||||
assert run.err == ""
|
|
||||||
assert output_file.read() == EXPECTED_NESTED_IFS
|
|
||||||
|
|
||||||
|
|
||||||
def test_env(runner, yadm, tmpdir):
|
|
||||||
"""Test env"""
|
|
||||||
|
|
||||||
input_file = tmpdir.join("input")
|
|
||||||
input_file.write("{{env.PWD}}", ensure=True)
|
|
||||||
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,50 +4,31 @@ import os
|
||||||
FILE_MODE = 0o754
|
FILE_MODE = 0o754
|
||||||
|
|
||||||
LOCAL_CLASS = "esh_Test+@-!^Class"
|
LOCAL_CLASS = "esh_Test+@-!^Class"
|
||||||
LOCAL_CLASS2 = "esh_Test+@-|^2nd_Class withSpace"
|
|
||||||
LOCAL_ARCH = "esh_Test+@-!^Arch"
|
|
||||||
LOCAL_SYSTEM = "esh_Test+@-!^System"
|
LOCAL_SYSTEM = "esh_Test+@-!^System"
|
||||||
LOCAL_HOST = "esh_Test+@-!^Host"
|
LOCAL_HOST = "esh_Test+@-!^Host"
|
||||||
LOCAL_USER = "esh_Test+@-!^User"
|
LOCAL_USER = "esh_Test+@-!^User"
|
||||||
LOCAL_DISTRO = "esh_Test+@-!^Distro"
|
LOCAL_DISTRO = "esh_Test+@-!^Distro"
|
||||||
LOCAL_DISTRO_FAMILY = "esh_Test+@-!^Family"
|
TEMPLATE = f'''
|
||||||
TEMPLATE = f"""
|
|
||||||
start of template
|
start of template
|
||||||
esh class = ><%=$YADM_CLASS%><
|
esh class = ><%=$YADM_CLASS%><
|
||||||
esh arch = ><%=$YADM_ARCH%><
|
esh os = ><%=$YADM_OS%><
|
||||||
esh os = ><%=$YADM_OS%><
|
esh host = ><%=$YADM_HOSTNAME%><
|
||||||
esh host = ><%=$YADM_HOSTNAME%><
|
esh user = ><%=$YADM_USER%><
|
||||||
esh user = ><%=$YADM_USER%><
|
esh distro = ><%=$YADM_DISTRO%><
|
||||||
esh distro = ><%=$YADM_DISTRO%><
|
|
||||||
esh distro_family = ><%=$YADM_DISTRO_FAMILY%><
|
|
||||||
esh classes = ><%=$YADM_CLASSES%><
|
|
||||||
<% if [ "$YADM_CLASS" = "wrongclass1" ]; then -%>
|
<% if [ "$YADM_CLASS" = "wrongclass1" ]; then -%>
|
||||||
wrong class 1
|
wrong class 1
|
||||||
<% fi -%>
|
<% fi -%>
|
||||||
<% if [ "$YADM_CLASS" = "{LOCAL_CLASS}" ]; then -%>
|
<% if [ "$YADM_CLASS" = "{LOCAL_CLASS}" ]; then -%>
|
||||||
Included esh section for class = <%=$YADM_CLASS%> (<%=$YADM_CLASS%> repeated)
|
Included section for class = <%=$YADM_CLASS%> (<%=$YADM_CLASS%> repeated)
|
||||||
<% fi -%>
|
<% fi -%>
|
||||||
<% if [ "$YADM_CLASS" = "wrongclass2" ]; then -%>
|
<% if [ "$YADM_CLASS" = "wrongclass2" ]; then -%>
|
||||||
wrong class 2
|
wrong class 2
|
||||||
<% fi -%>
|
<% 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 -%>
|
<% if [ "$YADM_OS" = "wrongos1" ]; then -%>
|
||||||
wrong os 1
|
wrong os 1
|
||||||
<% fi -%>
|
<% fi -%>
|
||||||
<% if [ "$YADM_OS" = "{LOCAL_SYSTEM}" ]; then -%>
|
<% if [ "$YADM_OS" = "{LOCAL_SYSTEM}" ]; then -%>
|
||||||
Included esh section for os = <%=$YADM_OS%> (<%=$YADM_OS%> repeated)
|
Included section for os = <%=$YADM_OS%> (<%=$YADM_OS%> repeated)
|
||||||
<% fi -%>
|
<% fi -%>
|
||||||
<% if [ "$YADM_OS" = "wrongos2" ]; then -%>
|
<% if [ "$YADM_OS" = "wrongos2" ]; then -%>
|
||||||
wrong os 2
|
wrong os 2
|
||||||
|
@ -56,7 +37,7 @@ wrong os 2
|
||||||
wrong host 1
|
wrong host 1
|
||||||
<% fi -%>
|
<% fi -%>
|
||||||
<% if [ "$YADM_HOSTNAME" = "{LOCAL_HOST}" ]; then -%>
|
<% if [ "$YADM_HOSTNAME" = "{LOCAL_HOST}" ]; then -%>
|
||||||
Included esh section for host = <%=$YADM_HOSTNAME%> (<%=$YADM_HOSTNAME%> again)
|
Included section for host = <%=$YADM_HOSTNAME%> (<%=$YADM_HOSTNAME%> again)
|
||||||
<% fi -%>
|
<% fi -%>
|
||||||
<% if [ "$YADM_HOSTNAME" = "wronghost2" ]; then -%>
|
<% if [ "$YADM_HOSTNAME" = "wronghost2" ]; then -%>
|
||||||
wrong host 2
|
wrong host 2
|
||||||
|
@ -65,7 +46,7 @@ wrong host 2
|
||||||
wrong user 1
|
wrong user 1
|
||||||
<% fi -%>
|
<% fi -%>
|
||||||
<% if [ "$YADM_USER" = "{LOCAL_USER}" ]; then -%>
|
<% if [ "$YADM_USER" = "{LOCAL_USER}" ]; then -%>
|
||||||
Included esh section for user = <%=$YADM_USER%> (<%=$YADM_USER%> repeated)
|
Included section for user = <%=$YADM_USER%> (<%=$YADM_USER%> repeated)
|
||||||
<% fi -%>
|
<% fi -%>
|
||||||
<% if [ "$YADM_USER" = "wronguser2" ]; then -%>
|
<% if [ "$YADM_USER" = "wronguser2" ]; then -%>
|
||||||
wrong user 2
|
wrong user 2
|
||||||
|
@ -74,76 +55,49 @@ wrong user 2
|
||||||
wrong distro 1
|
wrong distro 1
|
||||||
<% fi -%>
|
<% fi -%>
|
||||||
<% if [ "$YADM_DISTRO" = "{LOCAL_DISTRO}" ]; then -%>
|
<% if [ "$YADM_DISTRO" = "{LOCAL_DISTRO}" ]; then -%>
|
||||||
Included esh section for distro = <%=$YADM_DISTRO%> (<%=$YADM_DISTRO%> again)
|
Included section for distro = <%=$YADM_DISTRO%> (<%=$YADM_DISTRO%> again)
|
||||||
<% fi -%>
|
<% fi -%>
|
||||||
<% if [ "$YADM_DISTRO" = "wrongdistro2" ]; then -%>
|
<% if [ "$YADM_DISTRO" = "wrongdistro2" ]; then -%>
|
||||||
wrong distro 2
|
wrong distro 2
|
||||||
<% fi -%>
|
<% 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
|
end of template
|
||||||
"""
|
'''
|
||||||
EXPECTED = f"""
|
EXPECTED = f'''
|
||||||
start of template
|
start of template
|
||||||
esh class = >{LOCAL_CLASS}<
|
esh class = >{LOCAL_CLASS}<
|
||||||
esh arch = >{LOCAL_ARCH}<
|
esh os = >{LOCAL_SYSTEM}<
|
||||||
esh os = >{LOCAL_SYSTEM}<
|
esh host = >{LOCAL_HOST}<
|
||||||
esh host = >{LOCAL_HOST}<
|
esh user = >{LOCAL_USER}<
|
||||||
esh user = >{LOCAL_USER}<
|
esh distro = >{LOCAL_DISTRO}<
|
||||||
esh distro = >{LOCAL_DISTRO}<
|
Included section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated)
|
||||||
esh distro_family = >{LOCAL_DISTRO_FAMILY}<
|
Included section for os = {LOCAL_SYSTEM} ({LOCAL_SYSTEM} repeated)
|
||||||
esh classes = >{LOCAL_CLASS2} {LOCAL_CLASS}<
|
Included section for host = {LOCAL_HOST} ({LOCAL_HOST} again)
|
||||||
Included esh section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated)
|
Included section for user = {LOCAL_USER} ({LOCAL_USER} repeated)
|
||||||
Included esh section for second class
|
Included section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again)
|
||||||
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
|
end of template
|
||||||
"""
|
'''
|
||||||
|
|
||||||
|
|
||||||
def test_template_esh(runner, yadm, tmpdir):
|
def test_template_esh(runner, yadm, tmpdir):
|
||||||
"""Test processing by esh"""
|
"""Test processing by esh"""
|
||||||
# pylint: disable=duplicate-code
|
|
||||||
|
|
||||||
input_file = tmpdir.join("input")
|
input_file = tmpdir.join('input')
|
||||||
input_file.write(TEMPLATE, ensure=True)
|
input_file.write(TEMPLATE, ensure=True)
|
||||||
input_file.chmod(FILE_MODE)
|
input_file.chmod(FILE_MODE)
|
||||||
output_file = tmpdir.join("output")
|
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"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
local_class="{LOCAL_CLASS}"
|
local_class="{LOCAL_CLASS}"
|
||||||
local_classes=("{LOCAL_CLASS2}" "{LOCAL_CLASS}")
|
|
||||||
local_arch="{LOCAL_ARCH}"
|
|
||||||
local_system="{LOCAL_SYSTEM}"
|
local_system="{LOCAL_SYSTEM}"
|
||||||
local_host="{LOCAL_HOST}"
|
local_host="{LOCAL_HOST}"
|
||||||
local_user="{LOCAL_USER}"
|
local_user="{LOCAL_USER}"
|
||||||
local_distro="{LOCAL_DISTRO}"
|
local_distro="{LOCAL_DISTRO}"
|
||||||
local_distro_family="{LOCAL_DISTRO_FAMILY}"
|
|
||||||
template_esh "{input_file}" "{output_file}"
|
template_esh "{input_file}" "{output_file}"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert output_file.read().strip() == str(EXPECTED).strip()
|
assert output_file.read().strip() == str(EXPECTED).strip()
|
||||||
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
||||||
|
|
||||||
|
@ -151,17 +105,17 @@ def test_template_esh(runner, yadm, tmpdir):
|
||||||
def test_source(runner, yadm, tmpdir):
|
def test_source(runner, yadm, tmpdir):
|
||||||
"""Test YADM_SOURCE"""
|
"""Test YADM_SOURCE"""
|
||||||
|
|
||||||
input_file = tmpdir.join("input")
|
input_file = tmpdir.join('input')
|
||||||
input_file.write("<%= $YADM_SOURCE %>", ensure=True)
|
input_file.write('<%= $YADM_SOURCE %>', ensure=True)
|
||||||
input_file.chmod(FILE_MODE)
|
input_file.chmod(FILE_MODE)
|
||||||
output_file = tmpdir.join("output")
|
output_file = tmpdir.join('output')
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
template_esh "{input_file}" "{output_file}"
|
template_esh "{input_file}" "{output_file}"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert output_file.read().strip() == str(input_file)
|
assert output_file.read().strip() == str(input_file)
|
||||||
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
||||||
|
|
|
@ -1,55 +1,35 @@
|
||||||
"""Unit tests: template_j2cli & template_envtpl"""
|
"""Unit tests: template_j2cli & template_envtpl"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
FILE_MODE = 0o754
|
FILE_MODE = 0o754
|
||||||
|
|
||||||
LOCAL_CLASS = "j2_Test+@-!^Class"
|
LOCAL_CLASS = "j2_Test+@-!^Class"
|
||||||
LOCAL_CLASS2 = "j2_Test+@-|^2nd_Class withSpace"
|
|
||||||
LOCAL_ARCH = "j2_Test+@-!^Arch"
|
|
||||||
LOCAL_SYSTEM = "j2_Test+@-!^System"
|
LOCAL_SYSTEM = "j2_Test+@-!^System"
|
||||||
LOCAL_HOST = "j2_Test+@-!^Host"
|
LOCAL_HOST = "j2_Test+@-!^Host"
|
||||||
LOCAL_USER = "j2_Test+@-!^User"
|
LOCAL_USER = "j2_Test+@-!^User"
|
||||||
LOCAL_DISTRO = "j2_Test+@-!^Distro"
|
LOCAL_DISTRO = "j2_Test+@-!^Distro"
|
||||||
LOCAL_DISTRO_FAMILY = "j2_Test+@-!^Family"
|
TEMPLATE = f'''
|
||||||
TEMPLATE = f"""
|
|
||||||
start of template
|
start of template
|
||||||
j2 class = >{{{{YADM_CLASS}}}}<
|
j2 class = >{{{{YADM_CLASS}}}}<
|
||||||
j2 arch = >{{{{YADM_ARCH}}}}<
|
j2 os = >{{{{YADM_OS}}}}<
|
||||||
j2 os = >{{{{YADM_OS}}}}<
|
j2 host = >{{{{YADM_HOSTNAME}}}}<
|
||||||
j2 host = >{{{{YADM_HOSTNAME}}}}<
|
j2 user = >{{{{YADM_USER}}}}<
|
||||||
j2 user = >{{{{YADM_USER}}}}<
|
j2 distro = >{{{{YADM_DISTRO}}}}<
|
||||||
j2 distro = >{{{{YADM_DISTRO}}}}<
|
|
||||||
j2 distro_family = >{{{{YADM_DISTRO_FAMILY}}}}<
|
|
||||||
j2 classes = >{{{{YADM_CLASSES}}}}<
|
|
||||||
{{%- if YADM_CLASS == "wrongclass1" %}}
|
{{%- if YADM_CLASS == "wrongclass1" %}}
|
||||||
wrong class 1
|
wrong class 1
|
||||||
{{%- endif %}}
|
{{%- endif %}}
|
||||||
{{%- if YADM_CLASS == "{LOCAL_CLASS}" %}}
|
{{%- if YADM_CLASS == "{LOCAL_CLASS}" %}}
|
||||||
Included j2 section for class = \
|
Included section for class = {{{{YADM_CLASS}}}} ({{{{YADM_CLASS}}}} repeated)
|
||||||
{{{{YADM_CLASS}}}} ({{{{YADM_CLASS}}}} repeated)
|
|
||||||
{{%- endif %}}
|
{{%- endif %}}
|
||||||
{{%- if YADM_CLASS == "wrongclass2" %}}
|
{{%- if YADM_CLASS == "wrongclass2" %}}
|
||||||
wrong class 2
|
wrong class 2
|
||||||
{{%- endif %}}
|
{{%- 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" %}}
|
{{%- if YADM_OS == "wrongos1" %}}
|
||||||
wrong os 1
|
wrong os 1
|
||||||
{{%- endif %}}
|
{{%- endif %}}
|
||||||
{{%- if YADM_OS == "{LOCAL_SYSTEM}" %}}
|
{{%- if YADM_OS == "{LOCAL_SYSTEM}" %}}
|
||||||
Included j2 section for os = {{{{YADM_OS}}}} ({{{{YADM_OS}}}} repeated)
|
Included section for os = {{{{YADM_OS}}}} ({{{{YADM_OS}}}} repeated)
|
||||||
{{%- endif %}}
|
{{%- endif %}}
|
||||||
{{%- if YADM_OS == "wrongos2" %}}
|
{{%- if YADM_OS == "wrongos2" %}}
|
||||||
wrong os 2
|
wrong os 2
|
||||||
|
@ -58,8 +38,7 @@ wrong os 2
|
||||||
wrong host 1
|
wrong host 1
|
||||||
{{%- endif %}}
|
{{%- endif %}}
|
||||||
{{%- if YADM_HOSTNAME == "{LOCAL_HOST}" %}}
|
{{%- if YADM_HOSTNAME == "{LOCAL_HOST}" %}}
|
||||||
Included j2 section for host = \
|
Included section for host = {{{{YADM_HOSTNAME}}}} ({{{{YADM_HOSTNAME}}}} again)
|
||||||
{{{{YADM_HOSTNAME}}}} ({{{{YADM_HOSTNAME}}}} again)
|
|
||||||
{{%- endif %}}
|
{{%- endif %}}
|
||||||
{{%- if YADM_HOSTNAME == "wronghost2" %}}
|
{{%- if YADM_HOSTNAME == "wronghost2" %}}
|
||||||
wrong host 2
|
wrong host 2
|
||||||
|
@ -68,7 +47,7 @@ wrong host 2
|
||||||
wrong user 1
|
wrong user 1
|
||||||
{{%- endif %}}
|
{{%- endif %}}
|
||||||
{{%- if YADM_USER == "{LOCAL_USER}" %}}
|
{{%- if YADM_USER == "{LOCAL_USER}" %}}
|
||||||
Included j2 section for user = {{{{YADM_USER}}}} ({{{{YADM_USER}}}} repeated)
|
Included section for user = {{{{YADM_USER}}}} ({{{{YADM_USER}}}} repeated)
|
||||||
{{%- endif %}}
|
{{%- endif %}}
|
||||||
{{%- if YADM_USER == "wronguser2" %}}
|
{{%- if YADM_USER == "wronguser2" %}}
|
||||||
wrong user 2
|
wrong user 2
|
||||||
|
@ -77,98 +56,69 @@ wrong user 2
|
||||||
wrong distro 1
|
wrong distro 1
|
||||||
{{%- endif %}}
|
{{%- endif %}}
|
||||||
{{%- if YADM_DISTRO == "{LOCAL_DISTRO}" %}}
|
{{%- if YADM_DISTRO == "{LOCAL_DISTRO}" %}}
|
||||||
Included j2 section for distro = \
|
Included section for distro = {{{{YADM_DISTRO}}}} ({{{{YADM_DISTRO}}}} again)
|
||||||
{{{{YADM_DISTRO}}}} ({{{{YADM_DISTRO}}}} again)
|
|
||||||
{{%- endif %}}
|
{{%- endif %}}
|
||||||
{{%- if YADM_DISTRO == "wrongdistro2" %}}
|
{{%- if YADM_DISTRO == "wrongdistro2" %}}
|
||||||
wrong distro 2
|
wrong distro 2
|
||||||
{{%- endif %}}
|
{{%- 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
|
end of template
|
||||||
"""
|
'''
|
||||||
EXPECTED = f"""
|
EXPECTED = f'''
|
||||||
start of template
|
start of template
|
||||||
j2 class = >{LOCAL_CLASS}<
|
j2 class = >{LOCAL_CLASS}<
|
||||||
j2 arch = >{LOCAL_ARCH}<
|
j2 os = >{LOCAL_SYSTEM}<
|
||||||
j2 os = >{LOCAL_SYSTEM}<
|
j2 host = >{LOCAL_HOST}<
|
||||||
j2 host = >{LOCAL_HOST}<
|
j2 user = >{LOCAL_USER}<
|
||||||
j2 user = >{LOCAL_USER}<
|
j2 distro = >{LOCAL_DISTRO}<
|
||||||
j2 distro = >{LOCAL_DISTRO}<
|
Included section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated)
|
||||||
j2 distro_family = >{LOCAL_DISTRO_FAMILY}<
|
Included section for os = {LOCAL_SYSTEM} ({LOCAL_SYSTEM} repeated)
|
||||||
j2 classes = >{LOCAL_CLASS2}
|
Included section for host = {LOCAL_HOST} ({LOCAL_HOST} again)
|
||||||
{LOCAL_CLASS}<
|
Included section for user = {LOCAL_USER} ({LOCAL_USER} repeated)
|
||||||
Included j2 section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated)
|
Included section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again)
|
||||||
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
|
end of template
|
||||||
"""
|
'''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("processor", ("j2cli", "envtpl"))
|
@pytest.mark.parametrize('processor', ('j2cli', 'envtpl'))
|
||||||
def test_template_j2(runner, yadm, tmpdir, processor):
|
def test_template_j2(runner, yadm, tmpdir, processor):
|
||||||
"""Test processing by j2cli & envtpl"""
|
"""Test processing by j2cli & envtpl"""
|
||||||
# pylint: disable=duplicate-code
|
|
||||||
|
|
||||||
input_file = tmpdir.join("input")
|
input_file = tmpdir.join('input')
|
||||||
input_file.write(TEMPLATE, ensure=True)
|
input_file.write(TEMPLATE, ensure=True)
|
||||||
input_file.chmod(FILE_MODE)
|
input_file.chmod(FILE_MODE)
|
||||||
output_file = tmpdir.join("output")
|
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"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
local_class="{LOCAL_CLASS}"
|
local_class="{LOCAL_CLASS}"
|
||||||
local_classes=("{LOCAL_CLASS2}" "{LOCAL_CLASS}")
|
|
||||||
local_arch="{LOCAL_ARCH}"
|
|
||||||
local_system="{LOCAL_SYSTEM}"
|
local_system="{LOCAL_SYSTEM}"
|
||||||
local_host="{LOCAL_HOST}"
|
local_host="{LOCAL_HOST}"
|
||||||
local_user="{LOCAL_USER}"
|
local_user="{LOCAL_USER}"
|
||||||
local_distro="{LOCAL_DISTRO}"
|
local_distro="{LOCAL_DISTRO}"
|
||||||
local_distro_family="{LOCAL_DISTRO_FAMILY}"
|
|
||||||
template_{processor} "{input_file}" "{output_file}"
|
template_{processor} "{input_file}" "{output_file}"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert output_file.read() == EXPECTED
|
assert output_file.read() == EXPECTED
|
||||||
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("processor", ("j2cli", "envtpl"))
|
@pytest.mark.parametrize('processor', ('j2cli', 'envtpl'))
|
||||||
def test_source(runner, yadm, tmpdir, processor):
|
def test_source(runner, yadm, tmpdir, processor):
|
||||||
"""Test YADM_SOURCE"""
|
"""Test YADM_SOURCE"""
|
||||||
|
|
||||||
input_file = tmpdir.join("input")
|
input_file = tmpdir.join('input')
|
||||||
input_file.write("{{YADM_SOURCE}}", ensure=True)
|
input_file.write('{{YADM_SOURCE}}', ensure=True)
|
||||||
input_file.chmod(FILE_MODE)
|
input_file.chmod(FILE_MODE)
|
||||||
output_file = tmpdir.join("output")
|
output_file = tmpdir.join('output')
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
template_{processor} "{input_file}" "{output_file}"
|
template_{processor} "{input_file}" "{output_file}"
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert output_file.read().strip() == str(input_file)
|
assert output_file.read().strip() == str(input_file)
|
||||||
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
assert os.stat(output_file).st_mode == os.stat(input_file).st_mode
|
||||||
|
|
|
@ -2,21 +2,21 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("condition", ["override", "equal", "existing_repo"])
|
@pytest.mark.parametrize('condition', ['override', 'equal', 'existing_repo'])
|
||||||
def test_upgrade_errors(tmpdir, runner, yadm, condition):
|
def test_upgrade_errors(tmpdir, runner, yadm, condition):
|
||||||
"""Test upgrade() error conditions"""
|
"""Test upgrade() error conditions"""
|
||||||
|
|
||||||
home = tmpdir.mkdir("home")
|
home = tmpdir.mkdir('home')
|
||||||
yadm_dir = home.join(".config/yadm")
|
yadm_dir = home.join('.config/yadm')
|
||||||
yadm_data = home.join(".local/share/yadm")
|
yadm_data = home.join('.local/share/yadm')
|
||||||
override = ""
|
override = ''
|
||||||
if condition == "override":
|
if condition == 'override':
|
||||||
override = "override"
|
override = 'override'
|
||||||
if condition == "equal":
|
if condition == 'equal':
|
||||||
yadm_data = yadm_dir
|
yadm_data = yadm_dir
|
||||||
if condition == "existing_repo":
|
if condition == 'existing_repo':
|
||||||
yadm_dir.ensure_dir("repo.git")
|
yadm_dir.ensure_dir('repo.git')
|
||||||
yadm_data.ensure_dir("repo.git")
|
yadm_data.ensure_dir('repo.git')
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
|
@ -27,16 +27,17 @@ def test_upgrade_errors(tmpdir, runner, yadm, condition):
|
||||||
YADM_OVERRIDE_REPO="{override}"
|
YADM_OVERRIDE_REPO="{override}"
|
||||||
upgrade
|
upgrade
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert "Unable to upgrade" in run.err
|
assert 'Unable to upgrade' in run.err
|
||||||
if condition in ["override", "equal"]:
|
if condition in ['override', 'equal']:
|
||||||
assert "Paths have been overridden" in run.err
|
assert 'Paths have been overridden' in run.err
|
||||||
elif condition == "existing_repo":
|
elif condition == 'existing_repo':
|
||||||
assert "already exists" in run.err
|
assert 'already exists' in run.err
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("condition", ["no-paths", "untracked", "tracked", "submodules"])
|
@pytest.mark.parametrize(
|
||||||
|
'condition', ['no-paths', 'untracked', 'tracked', 'submodules'])
|
||||||
def test_upgrade(tmpdir, runner, yadm, condition):
|
def test_upgrade(tmpdir, runner, yadm, condition):
|
||||||
"""Test upgrade()
|
"""Test upgrade()
|
||||||
|
|
||||||
|
@ -44,21 +45,21 @@ def test_upgrade(tmpdir, runner, yadm, condition):
|
||||||
mock for git. echo will return true, simulating a positive result from "git
|
mock for git. echo will return true, simulating a positive result from "git
|
||||||
ls-files". Also echo will report the parameters for "git mv".
|
ls-files". Also echo will report the parameters for "git mv".
|
||||||
"""
|
"""
|
||||||
legacy_paths = ("config", "encrypt", "bootstrap", "hooks/pre_cmd")
|
legacy_paths = ('config', 'encrypt', 'bootstrap', 'hooks/pre_cmd')
|
||||||
home = tmpdir.mkdir("home")
|
home = tmpdir.mkdir('home')
|
||||||
yadm_dir = home.join(".config/yadm")
|
yadm_dir = home.join('.config/yadm')
|
||||||
yadm_data = home.join(".local/share/yadm")
|
yadm_data = home.join('.local/share/yadm')
|
||||||
yadm_legacy = home.join(".yadm")
|
yadm_legacy = home.join('.yadm')
|
||||||
|
|
||||||
if condition != "no-paths":
|
if condition != 'no-paths':
|
||||||
yadm_dir.join("repo.git/config").write("test-repo", ensure=True)
|
yadm_dir.join('repo.git/config').write('test-repo', ensure=True)
|
||||||
yadm_dir.join("files.gpg").write("files.gpg", ensure=True)
|
yadm_dir.join('files.gpg').write('files.gpg', ensure=True)
|
||||||
for path in legacy_paths:
|
for path in legacy_paths:
|
||||||
yadm_legacy.join(path).write(path, ensure=True)
|
yadm_legacy.join(path).write(path, ensure=True)
|
||||||
|
|
||||||
mock_git = ""
|
mock_git = ""
|
||||||
if condition != "no-paths":
|
if condition != 'no-paths':
|
||||||
mock_git = f"""
|
mock_git = f'''
|
||||||
function git() {{
|
function git() {{
|
||||||
echo "$@"
|
echo "$@"
|
||||||
if [[ "$*" = *"submodule status" ]]; then
|
if [[ "$*" = *"submodule status" ]]; then
|
||||||
|
@ -70,7 +71,7 @@ def test_upgrade(tmpdir, runner, yadm, condition):
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
}}
|
}}
|
||||||
"""
|
'''
|
||||||
|
|
||||||
script = f"""
|
script = f"""
|
||||||
YADM_TEST=1 source {yadm}
|
YADM_TEST=1 source {yadm}
|
||||||
|
@ -84,30 +85,38 @@ def test_upgrade(tmpdir, runner, yadm, condition):
|
||||||
function cd {{ echo "$@";}}
|
function cd {{ echo "$@";}}
|
||||||
upgrade
|
upgrade
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
if condition == "no-paths":
|
if condition == 'no-paths':
|
||||||
assert "Upgrade is not necessary" in run.out
|
assert 'Upgrade is not necessary' in run.out
|
||||||
else:
|
else:
|
||||||
for lpath, npath in [("repo.git", "repo.git"), ("files.gpg", "archive")]:
|
for (lpath, npath) in [
|
||||||
expected = f"Moving {yadm_dir.join(lpath)} " f"to {yadm_data.join(npath)}"
|
('repo.git', 'repo.git'), ('files.gpg', 'archive')]:
|
||||||
|
expected = (
|
||||||
|
f'Moving {yadm_dir.join(lpath)} '
|
||||||
|
f'to {yadm_data.join(npath)}')
|
||||||
assert expected in run.out
|
assert expected in run.out
|
||||||
for path in legacy_paths:
|
for path in legacy_paths:
|
||||||
expected = f"Moving {yadm_legacy.join(path)} " f"to {yadm_dir.join(path)}"
|
expected = (
|
||||||
|
f'Moving {yadm_legacy.join(path)} '
|
||||||
|
f'to {yadm_dir.join(path)}')
|
||||||
assert expected in run.out
|
assert expected in run.out
|
||||||
if condition == "untracked":
|
if condition == 'untracked':
|
||||||
assert "test-repo" in yadm_data.join("repo.git/config").read()
|
assert 'test-repo' in yadm_data.join('repo.git/config').read()
|
||||||
assert "files.gpg" in yadm_data.join("archive").read()
|
assert 'files.gpg' in yadm_data.join('archive').read()
|
||||||
for path in legacy_paths:
|
for path in legacy_paths:
|
||||||
assert path in yadm_dir.join(path).read()
|
assert path in yadm_dir.join(path).read()
|
||||||
elif condition in ["tracked", "submodules"]:
|
elif condition in ['tracked', 'submodules']:
|
||||||
expected = f'mv {yadm_dir.join("files.gpg")} ' f'{yadm_data.join("archive")}'
|
expected = (
|
||||||
|
f'mv {yadm_dir.join("files.gpg")} '
|
||||||
|
f'{yadm_data.join("archive")}')
|
||||||
assert expected in run.out
|
assert expected in run.out
|
||||||
assert "files tracked by yadm have been renamed" in run.out
|
assert 'files tracked by yadm have been renamed' in run.out
|
||||||
if condition == "submodules":
|
if condition == 'submodules':
|
||||||
assert "submodule deinit -- mymodule" in run.out
|
assert 'submodule deinit -- mymodule' in run.out
|
||||||
assert "submodule update --init --recursive -- mymodule" in run.out
|
assert 'submodule update --init --recursive -- mymodule' \
|
||||||
|
in run.out
|
||||||
else:
|
else:
|
||||||
assert "submodule deinit" not in run.out
|
assert 'submodule deinit' not in run.out
|
||||||
assert "submodule update --init --recursive" not in run.out
|
assert 'submodule update --init --recursive' not in run.out
|
||||||
|
|
|
@ -1,30 +1,28 @@
|
||||||
"""Unit tests: yadm.[git,gpg]-program"""
|
"""Unit tests: yadm.[git,gpg]-program"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"executable, success, value, match",
|
'executable, success, value, match', [
|
||||||
[
|
(None, True, 'program', None),
|
||||||
(None, True, "program", None),
|
('cat', True, 'cat', None),
|
||||||
("cat", True, "cat", None),
|
('badprogram', False, None, 'badprogram'),
|
||||||
("badprogram", False, None, "badprogram"),
|
], ids=[
|
||||||
],
|
'executable missing',
|
||||||
ids=[
|
'valid alternative',
|
||||||
"executable missing",
|
'invalid alternative',
|
||||||
"valid alternative",
|
])
|
||||||
"invalid alternative",
|
@pytest.mark.parametrize('program', ['git', 'gpg'])
|
||||||
],
|
def test_x_program(
|
||||||
)
|
runner, yadm_cmd, paths, program, executable, success, value, match):
|
||||||
@pytest.mark.parametrize("program", ["git", "gpg"])
|
|
||||||
def test_x_program(runner, yadm_cmd, paths, program, executable, success, value, match):
|
|
||||||
"""Set yadm.X-program, and test result of require_X"""
|
"""Set yadm.X-program, and test result of require_X"""
|
||||||
|
|
||||||
# set configuration
|
# set configuration
|
||||||
if executable:
|
if executable:
|
||||||
os.system(" ".join(yadm_cmd("config", f"yadm.{program}-program", executable)))
|
os.system(' '.join(yadm_cmd(
|
||||||
|
'config', f'yadm.{program}-program', executable)))
|
||||||
|
|
||||||
# test require_[git,gpg]
|
# test require_[git,gpg]
|
||||||
script = f"""
|
script = f"""
|
||||||
|
@ -34,11 +32,11 @@ def test_x_program(runner, yadm_cmd, paths, program, executable, success, value,
|
||||||
require_{program}
|
require_{program}
|
||||||
echo ${program.upper()}_PROGRAM
|
echo ${program.upper()}_PROGRAM
|
||||||
"""
|
"""
|
||||||
run = runner(command=["bash"], inp=script)
|
run = runner(command=['bash'], inp=script)
|
||||||
assert run.success == success
|
assert run.success == success
|
||||||
|
|
||||||
# [GIT,GPG]_PROGRAM set correctly
|
# [GIT,GPG]_PROGRAM set correctly
|
||||||
if value == "program":
|
if value == 'program':
|
||||||
assert run.out.rstrip() == program
|
assert run.out.rstrip() == program
|
||||||
elif value:
|
elif value:
|
||||||
assert run.out.rstrip() == value
|
assert run.out.rstrip() == value
|
||||||
|
@ -47,4 +45,4 @@ def test_x_program(runner, yadm_cmd, paths, program, executable, success, value,
|
||||||
if match:
|
if match:
|
||||||
assert match in run.err
|
assert match in run.err
|
||||||
else:
|
else:
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
|
|
|
@ -1,131 +1,129 @@
|
||||||
"""Test upgrade"""
|
"""Test upgrade"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"versions",
|
'versions', [
|
||||||
[
|
('1.12.0', '2.5.0'),
|
||||||
("1.12.0", "2.5.0"),
|
('1.12.0',),
|
||||||
("1.12.0",),
|
('2.5.0',),
|
||||||
("2.5.0",),
|
], ids=[
|
||||||
],
|
'1.12.0 -> 2.5.0 -> latest',
|
||||||
ids=[
|
'1.12.0 -> latest',
|
||||||
"1.12.0 -> 2.5.0 -> latest",
|
'2.5.0 -> latest',
|
||||||
"1.12.0 -> latest",
|
])
|
||||||
"2.5.0 -> latest",
|
@pytest.mark.parametrize(
|
||||||
],
|
'submodule', [False, True],
|
||||||
)
|
ids=['no submodule', 'with submodules'])
|
||||||
@pytest.mark.parametrize("submodule", [False, True], ids=["no submodule", "with submodules"])
|
|
||||||
def test_upgrade(tmpdir, runner, versions, submodule):
|
def test_upgrade(tmpdir, runner, versions, submodule):
|
||||||
"""Upgrade tests"""
|
"""Upgrade tests"""
|
||||||
# pylint: disable=too-many-statements
|
# pylint: disable=too-many-statements
|
||||||
home = tmpdir.mkdir("HOME")
|
home = tmpdir.mkdir('HOME')
|
||||||
env = {"HOME": str(home)}
|
env = {'HOME': str(home)}
|
||||||
runner(["git", "config", "--global", "init.defaultBranch", "master"], env=env)
|
|
||||||
runner(["git", "config", "--global", "protocol.file.allow", "always"], env=env)
|
|
||||||
|
|
||||||
if submodule:
|
if submodule:
|
||||||
ext_repo = tmpdir.mkdir("ext_repo")
|
ext_repo = tmpdir.mkdir('ext_repo')
|
||||||
ext_repo.join("afile").write("some data")
|
ext_repo.join('afile').write('some data')
|
||||||
|
|
||||||
for cmd in (("init",), ("add", "afile"), ("commit", "-m", "test")):
|
for cmd in (('init',), ('add', 'afile'), ('commit', '-m', 'test')):
|
||||||
run = runner(["git", "-C", str(ext_repo), *cmd])
|
run = runner(['git', '-C', str(ext_repo), *cmd])
|
||||||
assert run.success
|
assert run.success
|
||||||
|
|
||||||
os.environ.pop("XDG_CONFIG_HOME", None)
|
os.environ.pop('XDG_CONFIG_HOME', None)
|
||||||
os.environ.pop("XDG_DATA_HOME", None)
|
os.environ.pop('XDG_DATA_HOME', None)
|
||||||
|
|
||||||
def run_version(version, *args, check_stderr=True):
|
def run_version(version, *args, check_stderr=True):
|
||||||
yadm = f"yadm-{version}" if version else "/yadm/yadm"
|
yadm = 'yadm-%s' % version if version else '/yadm/yadm'
|
||||||
run = runner([yadm, *args], shell=True, cwd=str(home), env=env)
|
run = runner([yadm, *args], shell=True, cwd=str(home), env=env)
|
||||||
assert run.success
|
assert run.success
|
||||||
if check_stderr:
|
if check_stderr:
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
return run
|
return run
|
||||||
|
|
||||||
# Initialize the repo with the first version
|
# Initialize the repo with the first version
|
||||||
first = versions[0]
|
first = versions[0]
|
||||||
run_version(first, "init")
|
run_version(first, 'init')
|
||||||
|
|
||||||
home.join("file").write("some data")
|
home.join('file').write('some data')
|
||||||
run_version(first, "add", "file")
|
run_version(first, 'add', 'file')
|
||||||
run_version(first, "commit", "-m", '"First commit"')
|
run_version(first, 'commit', '-m', '"First commit"')
|
||||||
|
|
||||||
if submodule:
|
if submodule:
|
||||||
# When upgrading via 2.5.0 we can't have a submodule that's been added
|
# 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.
|
# after being cloned as 2.5.0 fails the upgrade in that case.
|
||||||
can_upgrade_cloned_submodule = "2.5.0" not in versions[1:]
|
can_upgraded_cloned_submodule = '2.5.0' not in versions[1:]
|
||||||
if can_upgrade_cloned_submodule:
|
if can_upgraded_cloned_submodule:
|
||||||
# Check out a repo and then add it as a submodule
|
# Check out a repo and then add it as a submodule
|
||||||
run = runner(["git", "-C", str(home), "clone", str(ext_repo), "b"])
|
run = runner(['git', '-C', str(home), 'clone', str(ext_repo), 'b'])
|
||||||
assert run.success
|
assert run.success
|
||||||
run_version(first, "submodule", "add", str(ext_repo), "b")
|
run_version(first, 'submodule', 'add', str(ext_repo), 'b')
|
||||||
|
|
||||||
# Add submodule without first checking it out
|
# Add submodule without first checking it out
|
||||||
run_version(first, "submodule", "add", str(ext_repo), "a", check_stderr=False)
|
run_version(first, 'submodule', 'add', str(ext_repo), 'a',
|
||||||
run_version(first, "submodule", "add", str(ext_repo), "c", check_stderr=False)
|
check_stderr=False)
|
||||||
|
run_version(first, 'submodule', 'add', str(ext_repo), 'c',
|
||||||
|
check_stderr=False)
|
||||||
|
|
||||||
run_version(first, "commit", "-m", '"Add submodules"')
|
run_version(first, 'commit', '-m', '"Add submodules"')
|
||||||
|
|
||||||
for path in (".yadm", ".config/yadm"):
|
for path in ('.yadm', '.config/yadm'):
|
||||||
yadm_dir = home.join(path)
|
yadm_dir = home.join(path)
|
||||||
if yadm_dir.exists():
|
if yadm_dir.exists():
|
||||||
break
|
break
|
||||||
|
|
||||||
yadm_dir.join("bootstrap").write("init stuff")
|
yadm_dir.join('bootstrap').write('init stuff')
|
||||||
run_version(first, "add", yadm_dir.join("bootstrap"))
|
run_version(first, 'add', yadm_dir.join('bootstrap'))
|
||||||
run_version(first, "commit", "-m", "bootstrap")
|
run_version(first, 'commit', '-m', 'bootstrap')
|
||||||
|
|
||||||
yadm_dir.join("encrypt").write("secret")
|
yadm_dir.join('encrypt').write('secret')
|
||||||
|
|
||||||
hooks_dir = yadm_dir.mkdir("hooks")
|
hooks_dir = yadm_dir.mkdir('hooks')
|
||||||
hooks_dir.join("pre_status").write("status")
|
hooks_dir.join('pre_status').write('status')
|
||||||
hooks_dir.join("post_commit").write("commit")
|
hooks_dir.join('post_commit').write('commit')
|
||||||
|
|
||||||
run_version(first, "config", "local.class", "test")
|
run_version(first, 'config', 'local.class', 'test')
|
||||||
run_version(first, "config", "foo.bar", "true")
|
run_version(first, 'config', 'foo.bar', 'true')
|
||||||
|
|
||||||
# Run upgrade with intermediate versions and latest
|
# Run upgrade with intermediate versions and latest
|
||||||
latest = None
|
latest = None
|
||||||
for version in versions[1:] + (latest,):
|
for version in versions[1:] + (latest,):
|
||||||
run = run_version(version, "upgrade", check_stderr=not submodule)
|
run = run_version(version, 'upgrade', check_stderr=not submodule)
|
||||||
if submodule:
|
if submodule:
|
||||||
lines = run.err.splitlines()
|
lines = run.err.splitlines()
|
||||||
if can_upgrade_cloned_submodule:
|
if can_upgraded_cloned_submodule:
|
||||||
assert "Migrating git directory of" in lines[0]
|
assert 'Migrating git directory of' in lines[0]
|
||||||
assert str(home.join("b/.git")) in lines[1]
|
assert str(home.join('b/.git')) in lines[1]
|
||||||
assert str(yadm_dir.join("repo.git/modules/b")) in lines[2]
|
assert str(yadm_dir.join('repo.git/modules/b')) in lines[2]
|
||||||
del lines[:3]
|
del lines[:3]
|
||||||
for line in lines:
|
for line in lines:
|
||||||
assert line.startswith("Submodule")
|
assert line.startswith('Submodule')
|
||||||
assert "registered for path" in line
|
assert 'registered for path' in line
|
||||||
|
|
||||||
# Verify result for the final upgrade
|
# Verify result for the final upgrade
|
||||||
run_version(latest, "status")
|
run_version(latest, 'status')
|
||||||
|
|
||||||
run = run_version(latest, "show", "HEAD:file")
|
run = run_version(latest, 'show', 'HEAD:file')
|
||||||
assert run.out == "some data"
|
assert run.out == 'some data'
|
||||||
|
|
||||||
if submodule:
|
if submodule:
|
||||||
if can_upgrade_cloned_submodule:
|
if can_upgraded_cloned_submodule:
|
||||||
assert home.join("b/afile").read() == "some data"
|
assert home.join('b/afile').read() == 'some data'
|
||||||
assert home.join("a/afile").read() == "some data"
|
assert home.join('a/afile').read() == 'some data'
|
||||||
assert home.join("c/afile").read() == "some data"
|
assert home.join('c/afile').read() == 'some data'
|
||||||
|
|
||||||
yadm_dir = home.join(".config/yadm")
|
yadm_dir = home.join('.config/yadm')
|
||||||
|
|
||||||
assert yadm_dir.join("bootstrap").read() == "init stuff"
|
assert yadm_dir.join('bootstrap').read() == 'init stuff'
|
||||||
assert yadm_dir.join("encrypt").read() == "secret"
|
assert yadm_dir.join('encrypt').read() == 'secret'
|
||||||
|
|
||||||
hooks_dir = yadm_dir.join("hooks")
|
hooks_dir = yadm_dir.join('hooks')
|
||||||
assert hooks_dir.join("pre_status").read() == "status"
|
assert hooks_dir.join('pre_status').read() == 'status'
|
||||||
assert hooks_dir.join("post_commit").read() == "commit"
|
assert hooks_dir.join('post_commit').read() == 'commit'
|
||||||
|
|
||||||
run = run_version(latest, "config", "local.class")
|
run = run_version(latest, 'config', 'local.class')
|
||||||
assert run.out.rstrip() == "test"
|
assert run.out.rstrip() == 'test'
|
||||||
|
|
||||||
run = run_version(latest, "config", "foo.bar")
|
run = run_version(latest, 'config', 'foo.bar')
|
||||||
assert run.out.rstrip() == "true"
|
assert run.out.rstrip() == 'true'
|
||||||
|
|
|
@ -1,36 +1,36 @@
|
||||||
"""Test version"""
|
"""Test version"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope='module')
|
||||||
def expected_version(yadm):
|
def expected_version(yadm):
|
||||||
"""
|
"""
|
||||||
Expected semantic version number. This is taken directly out of yadm,
|
Expected semantic version number. This is taken directly out of yadm,
|
||||||
searching for the VERSION= string.
|
searching for the VERSION= string.
|
||||||
"""
|
"""
|
||||||
with open(yadm, encoding="utf-8") as source_file:
|
yadm_version = re.findall(
|
||||||
yadm_version = re.findall(r"VERSION=([^\n]+)", source_file.read())
|
r'VERSION=([^\n]+)',
|
||||||
|
open(yadm).read())
|
||||||
if yadm_version:
|
if yadm_version:
|
||||||
return yadm_version[0]
|
return yadm_version[0]
|
||||||
pytest.fail(f"version not found in {yadm}")
|
pytest.fail(f'version not found in {yadm}')
|
||||||
return "not found"
|
return 'not found'
|
||||||
|
|
||||||
|
|
||||||
def test_semantic_version(expected_version):
|
def test_semantic_version(expected_version):
|
||||||
"""Version is semantic"""
|
"""Version is semantic"""
|
||||||
# semantic version conforms to MAJOR.MINOR.PATCH
|
# semantic version conforms to MAJOR.MINOR.PATCH
|
||||||
assert re.search(r"^\d+\.\d+\.\d+$", expected_version), "does not conform to MAJOR.MINOR.PATCH"
|
assert re.search(r'^\d+\.\d+\.\d+$', expected_version), (
|
||||||
|
'does not conform to MAJOR.MINOR.PATCH')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("cmd", ["--version", "version"])
|
@pytest.mark.parametrize('cmd', ['--version', 'version'])
|
||||||
def test_reported_version(runner, yadm_cmd, cmd, expected_version):
|
def test_reported_version(
|
||||||
"""Report correct version and bash/git versions"""
|
runner, yadm_cmd, cmd, expected_version):
|
||||||
|
"""Report correct version"""
|
||||||
run = runner(command=yadm_cmd(cmd))
|
run = runner(command=yadm_cmd(cmd))
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ""
|
assert run.err == ''
|
||||||
assert "bash version" in run.out
|
assert run.out == f'yadm {expected_version}\n'
|
||||||
assert "git version" in run.out
|
|
||||||
assert run.out.endswith(f"\nyadm version {expected_version}\n")
|
|
||||||
|
|
|
@ -3,42 +3,37 @@
|
||||||
This module holds values/functions common to multiple tests.
|
This module holds values/functions common to multiple tests.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
ALT_FILE1 = "test_alt"
|
ALT_FILE1 = 'test_alt'
|
||||||
ALT_FILE2 = "test alt/test alt"
|
ALT_FILE2 = 'test alt/test alt'
|
||||||
ALT_DIR = "test alt/test alt dir"
|
ALT_DIR = 'test alt/test alt dir'
|
||||||
|
|
||||||
# Directory based alternates must have a tracked contained file.
|
# Directory based alternates must have a tracked contained file.
|
||||||
# This will be the test contained file name
|
# This will be the test contained file name
|
||||||
CONTAINED = "contained_file"
|
CONTAINED = 'contained_file'
|
||||||
|
|
||||||
# These variables are used for making include files which will be processed
|
# These variables are used for making include files which will be processed
|
||||||
# within jinja templates
|
# within jinja templates
|
||||||
INCLUDE_FILE = "inc_file"
|
INCLUDE_FILE = 'inc_file'
|
||||||
INCLUDE_DIRS = ["", "test alt"]
|
INCLUDE_DIRS = ['', 'test alt']
|
||||||
INCLUDE_CONTENT = "8780846c02e34c930d0afd127906668f"
|
INCLUDE_CONTENT = '8780846c02e34c930d0afd127906668f'
|
||||||
|
|
||||||
|
|
||||||
def set_local(paths, variable, value, add=False):
|
def set_local(paths, variable, value):
|
||||||
"""Set local override"""
|
"""Set local override"""
|
||||||
add = "--add" if add else ""
|
os.system(
|
||||||
os.system(f"GIT_DIR={str(paths.repo)} " f'git config --local {add} "local.{variable}" "{value}"')
|
f'GIT_DIR={str(paths.repo)} '
|
||||||
|
f'git config --local "local.{variable}" "{value}"'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_alt_files(
|
def create_alt_files(paths, suffix,
|
||||||
paths,
|
preserve=False, tracked=True,
|
||||||
suffix,
|
encrypt=False, exclude=False,
|
||||||
preserve=False,
|
content=None, includefile=False,
|
||||||
tracked=True,
|
yadm_alt=False, yadm_dir=None):
|
||||||
encrypt=False,
|
|
||||||
exclude=False,
|
|
||||||
content=None,
|
|
||||||
includefile=False,
|
|
||||||
yadm_alt=False,
|
|
||||||
yadm_dir=None,
|
|
||||||
):
|
|
||||||
"""Create new files, and add to the repo
|
"""Create new files, and add to the repo
|
||||||
|
|
||||||
This is used for testing alternate files. In each case, a suffix is
|
This is used for testing alternate files. In each case, a suffix is
|
||||||
|
@ -46,7 +41,7 @@ def create_alt_files(
|
||||||
repo handling are dependent upon the function arguments.
|
repo handling are dependent upon the function arguments.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
basepath = yadm_dir.join("alt") if yadm_alt else paths.work
|
basepath = yadm_dir.join('alt') if yadm_alt else paths.work
|
||||||
|
|
||||||
if not preserve:
|
if not preserve:
|
||||||
for remove_path in (ALT_FILE1, ALT_FILE2, ALT_DIR):
|
for remove_path in (ALT_FILE1, ALT_FILE2, ALT_DIR):
|
||||||
|
@ -64,28 +59,28 @@ def create_alt_files(
|
||||||
# Do not test directory support for jinja alternates
|
# Do not test directory support for jinja alternates
|
||||||
test_paths = [new_file1, new_file2]
|
test_paths = [new_file1, new_file2]
|
||||||
test_names = [ALT_FILE1, ALT_FILE2]
|
test_names = [ALT_FILE1, ALT_FILE2]
|
||||||
if not re.match(r"##(t$|t\.|template|yadm)", suffix):
|
if not re.match(r'##(t$|t\.|template|yadm)', suffix):
|
||||||
test_paths += [new_dir]
|
test_paths += [new_dir]
|
||||||
test_names += [ALT_DIR]
|
test_names += [ALT_DIR]
|
||||||
|
|
||||||
for test_path in test_paths:
|
for test_path in test_paths:
|
||||||
if content:
|
if content:
|
||||||
test_path.write("\n" + content, mode="a", ensure=True)
|
test_path.write('\n' + content, mode='a', ensure=True)
|
||||||
assert test_path.exists()
|
assert test_path.exists()
|
||||||
|
|
||||||
_create_includefiles(includefile, test_paths, basepath)
|
_create_includefiles(includefile, test_paths, basepath)
|
||||||
_create_tracked(tracked, test_paths, paths)
|
_create_tracked(tracked, test_paths, paths)
|
||||||
|
|
||||||
prefix = ".config/yadm/alt/" if yadm_alt else ""
|
prefix = '.config/yadm/alt/' if yadm_alt else ''
|
||||||
_create_encrypt(encrypt, test_names, suffix, paths, exclude, prefix)
|
_create_encrypt(encrypt, test_names, suffix, paths, exclude, prefix)
|
||||||
|
|
||||||
|
|
||||||
def parse_alt_output(output, linked=True):
|
def parse_alt_output(output, linked=True):
|
||||||
"""Parse output of 'alt', and return list of linked files"""
|
"""Parse output of 'alt', and return list of linked files"""
|
||||||
regex = r"Creating (.+) from template (.+)$"
|
regex = r'Creating (.+) from template (.+)$'
|
||||||
if linked:
|
if linked:
|
||||||
regex = r"Linking (.+) to (.+)$"
|
regex = r'Linking (.+) to (.+)$'
|
||||||
parsed_list = {}
|
parsed_list = dict()
|
||||||
for line in output.splitlines():
|
for line in output.splitlines():
|
||||||
match = re.match(regex, line)
|
match = re.match(regex, line)
|
||||||
if match:
|
if match:
|
||||||
|
@ -99,7 +94,7 @@ def parse_alt_output(output, linked=True):
|
||||||
def _create_includefiles(includefile, test_paths, basepath):
|
def _create_includefiles(includefile, test_paths, basepath):
|
||||||
if includefile:
|
if includefile:
|
||||||
for dpath in INCLUDE_DIRS:
|
for dpath in INCLUDE_DIRS:
|
||||||
incfile = basepath.join(dpath + "/" + INCLUDE_FILE)
|
incfile = basepath.join(dpath + '/' + INCLUDE_FILE)
|
||||||
incfile.write(INCLUDE_CONTENT, ensure=True)
|
incfile.write(INCLUDE_CONTENT, ensure=True)
|
||||||
test_paths += [incfile]
|
test_paths += [incfile]
|
||||||
|
|
||||||
|
@ -114,6 +109,8 @@ def _create_tracked(tracked, test_paths, paths):
|
||||||
def _create_encrypt(encrypt, test_names, suffix, paths, exclude, prefix):
|
def _create_encrypt(encrypt, test_names, suffix, paths, exclude, prefix):
|
||||||
if encrypt:
|
if encrypt:
|
||||||
for encrypt_name in test_names:
|
for encrypt_name in test_names:
|
||||||
paths.encrypt.write(f"{prefix + encrypt_name + suffix}\n", mode="a")
|
paths.encrypt.write(
|
||||||
|
f'{prefix + encrypt_name + suffix}\n', mode='a')
|
||||||
if exclude:
|
if exclude:
|
||||||
paths.encrypt.write(f"!{prefix + encrypt_name + suffix}\n", mode="a")
|
paths.encrypt.write(
|
||||||
|
f'!{prefix + encrypt_name + suffix}\n', mode='a')
|
||||||
|
|
114
yadm.1
114
yadm.1
|
@ -1,5 +1,5 @@
|
||||||
.\" vim: set spell so=8:
|
.\" vim: set spell so=8:
|
||||||
.TH yadm 1 "8 November 2024" "3.3.0"
|
.TH yadm 1 "7 January 2021" "3.0.2"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
|
|
||||||
|
@ -116,12 +116,32 @@ if it exists.
|
||||||
.TP
|
.TP
|
||||||
.BI clone " url
|
.BI clone " url
|
||||||
Clone a remote repository for tracking dotfiles.
|
Clone a remote repository for tracking dotfiles.
|
||||||
After the contents of the remote repository have been fetched, a "check out" of
|
After the contents of the remote repository have been fetched, a "merge" of
|
||||||
the remote HEAD branch is attempted.
|
the remote HEAD branch is attempted.
|
||||||
If there are conflicting files already present in the
|
If there are conflicting files already present in the
|
||||||
.IR work-tree ,
|
.IR work-tree ,
|
||||||
the local version will be left unmodified and you'll have to review and resolve
|
this merge will fail and instead a "reset" of the remote HEAD branch
|
||||||
the difference.
|
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 repository is stored in
|
The repository is stored in
|
||||||
.IR $HOME/.local/share/yadm/repo.git .
|
.IR $HOME/.local/share/yadm/repo.git .
|
||||||
|
@ -141,6 +161,7 @@ exists). The options
|
||||||
.BR --bootstrap " or " --no-bootstrap
|
.BR --bootstrap " or " --no-bootstrap
|
||||||
will either force the bootstrap to be run, or prevent it from being run,
|
will either force the bootstrap to be run, or prevent it from being run,
|
||||||
without prompting the user.
|
without prompting the user.
|
||||||
|
.RE
|
||||||
.TP
|
.TP
|
||||||
.B config
|
.B config
|
||||||
This command manages configurations for yadm.
|
This command manages configurations for yadm.
|
||||||
|
@ -310,7 +331,7 @@ alias yadm='yadm --yadm-repo /alternate/path/to/repo'
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
The following is the full list of universal options.
|
The following is the full list of universal options.
|
||||||
Each option should be followed by a path.
|
Each option should be followed by a fully qualified path.
|
||||||
.TP
|
.TP
|
||||||
.B -Y,--yadm-dir
|
.B -Y,--yadm-dir
|
||||||
Override the yadm directory.
|
Override the yadm directory.
|
||||||
|
@ -427,7 +448,7 @@ Disable the permission changes to
|
||||||
This feature is enabled by default.
|
This feature is enabled by default.
|
||||||
|
|
||||||
.RE
|
.RE
|
||||||
The following five "local" configurations are not stored in the
|
The following four "local" configurations are not stored in the
|
||||||
.IR $HOME/.config/yadm/config,
|
.IR $HOME/.config/yadm/config,
|
||||||
they are stored in the local repository.
|
they are stored in the local repository.
|
||||||
|
|
||||||
|
@ -435,14 +456,6 @@ they are stored in the local repository.
|
||||||
.B local.class
|
.B local.class
|
||||||
Specify a class for the purpose of symlinking alternate files.
|
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 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
|
.TP
|
||||||
.B local.hostname
|
.B local.hostname
|
||||||
Override the hostname for the purpose of symlinking alternate files.
|
Override the hostname for the purpose of symlinking alternate files.
|
||||||
|
@ -486,11 +499,17 @@ Valid if the value matches the current user.
|
||||||
Current user is calculated by running
|
Current user is calculated by running
|
||||||
.BR "id -u -n" .
|
.BR "id -u -n" .
|
||||||
.TP
|
.TP
|
||||||
.BR hostname , " h
|
.BR distro , " d
|
||||||
Valid if the value matches the short hostname.
|
Valid if the value matches the distro.
|
||||||
Hostname is calculated by running
|
Distro is calculated by running
|
||||||
.BR "uname -n" ,
|
.B "lsb_release -si"
|
||||||
and trimming off any domain.
|
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" .
|
||||||
.TP
|
.TP
|
||||||
.BR class , " c
|
.BR class , " c
|
||||||
Valid if the value matches the
|
Valid if the value matches the
|
||||||
|
@ -501,27 +520,11 @@ Class must be manually set using
|
||||||
See the CONFIGURATION section for more details about setting
|
See the CONFIGURATION section for more details about setting
|
||||||
.BR local.class .
|
.BR local.class .
|
||||||
.TP
|
.TP
|
||||||
.BR distro , " d
|
.BR hostname , " h
|
||||||
Valid if the value matches the distro.
|
Valid if the value matches the short hostname.
|
||||||
Distro is calculated by running
|
Hostname is calculated by running
|
||||||
.B "lsb_release -si"
|
.BR "uname -n" ,
|
||||||
or by inspecting the ID from
|
and trimming off any domain.
|
||||||
.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
|
.TP
|
||||||
.B default
|
.B default
|
||||||
Valid when no other alternate is valid.
|
Valid when no other alternate is valid.
|
||||||
|
@ -595,7 +598,7 @@ If no "##default" version exists and no files have valid conditions, then no
|
||||||
link will be created.
|
link will be created.
|
||||||
|
|
||||||
Links are also created for directories named this way, as long as they have at
|
Links are also created for directories named this way, as long as they have at
|
||||||
least one yadm managed file within them (at the top level).
|
least one yadm managed file within them.
|
||||||
|
|
||||||
yadm will automatically create these links by default. This can be disabled
|
yadm will automatically create these links by default. This can be disabled
|
||||||
using the
|
using the
|
||||||
|
@ -614,9 +617,8 @@ command. The following sets the class to be "Work".
|
||||||
|
|
||||||
yadm config local.class Work
|
yadm config local.class Work
|
||||||
|
|
||||||
Similarly, the values of architecture, os, hostname, and user can be manually
|
Similarly, the values of os, hostname, and user can be manually overridden
|
||||||
overridden using the configuration options
|
using the configuration options
|
||||||
.BR local.arch ,
|
|
||||||
.BR local.os ,
|
.BR local.os ,
|
||||||
.BR local.hostname ,
|
.BR local.hostname ,
|
||||||
and
|
and
|
||||||
|
@ -664,18 +666,14 @@ to create or overwrite files.
|
||||||
|
|
||||||
During processing, the following variables are available in the template:
|
During processing, the following variables are available in the template:
|
||||||
|
|
||||||
Default Jinja or ESH Description
|
Default Jinja or ESH Description
|
||||||
------------- ------------- ----------------------------
|
------------- ------------- --------------------------
|
||||||
yadm.arch YADM_ARCH uname -m
|
yadm.class YADM_CLASS Locally defined yadm class
|
||||||
yadm.class YADM_CLASS Last locally defined class
|
yadm.distro YADM_DISTRO lsb_release -si
|
||||||
yadm.classes YADM_CLASSES All classes
|
yadm.hostname YADM_HOSTNAME uname -n (without domain)
|
||||||
yadm.distro YADM_DISTRO lsb_release -si
|
yadm.os YADM_OS uname -s
|
||||||
yadm.distro_family YADM_DISTRO_FAMILY ID_LIKE from /etc/os-release
|
yadm.user YADM_USER id -u -n
|
||||||
yadm.hostname YADM_HOSTNAME uname -n (without domain)
|
yadm.source YADM_SOURCE Template filename
|
||||||
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 :
|
.BR NOTE :
|
||||||
The OS for "Windows Subsystem for Linux" is reported as "WSL", even
|
The OS for "Windows Subsystem for Linux" is reported as "WSL", even
|
||||||
|
@ -746,7 +744,7 @@ and
|
||||||
.BR openssl (1)
|
.BR openssl (1)
|
||||||
are supported.
|
are supported.
|
||||||
gpg is used by default, but openssl can be configured with the
|
gpg is used by default, but openssl can be configured with the
|
||||||
.I yadm.cipher
|
.I yadm.cypher
|
||||||
configuration.
|
configuration.
|
||||||
|
|
||||||
To use this feature, a list of patterns must be created and saved as
|
To use this feature, a list of patterns must be created and saved as
|
||||||
|
@ -989,7 +987,7 @@ Commit a new set of encrypted files
|
||||||
|
|
||||||
Report issues or create pull requests at GitHub:
|
Report issues or create pull requests at GitHub:
|
||||||
|
|
||||||
https://github.com/yadm-dev/yadm/issues
|
https://github.com/TheLocehiliosan/yadm/issues
|
||||||
|
|
||||||
.SH AUTHOR
|
.SH AUTHOR
|
||||||
|
|
||||||
|
|
435
yadm.md
435
yadm.md
|
@ -1,4 +1,6 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## NAME
|
## NAME
|
||||||
yadm - Yet Another Dotfiles Manager
|
yadm - Yet Another Dotfiles Manager
|
||||||
|
|
||||||
|
@ -40,21 +42,21 @@
|
||||||
|
|
||||||
|
|
||||||
## DESCRIPTION
|
## DESCRIPTION
|
||||||
yadm is a tool for managing a collection of files across multiple com‐
|
yadm is a tool for managing a collection of files across multiple com-
|
||||||
puters, using a shared Git repository. In addition, yadm provides a
|
puters, using a shared Git repository. In addition, yadm provides a
|
||||||
feature to select alternate versions of files for particular systems.
|
feature to select alternate versions of files for particular systems.
|
||||||
Lastly, yadm supplies the ability to manage a subset of secure files,
|
Lastly, yadm supplies the ability to manage a subset of secure files,
|
||||||
which are encrypted before they are included in the repository.
|
which are encrypted before they are included in the repository.
|
||||||
|
|
||||||
|
|
||||||
## COMMANDS
|
## COMMANDS
|
||||||
git-command or git-alias
|
git-command or git-alias
|
||||||
Any command not internally handled by yadm is passed through to
|
Any command not internally handled by yadm is passed through to
|
||||||
git(1). Git commands or aliases are invoked with the yadm man‐
|
git(1). Git commands or aliases are invoked with the yadm man-
|
||||||
aged repository. The working directory for Git commands will be
|
aged repository. The working directory for Git commands will be
|
||||||
the configured work-tree (usually $HOME).
|
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.
|
mit, push, pull, etc.
|
||||||
|
|
||||||
The config command is not passed directly through. Instead use
|
The config command is not passed directly through. Instead use
|
||||||
|
@ -62,34 +64,46 @@
|
||||||
|
|
||||||
alt Create symbolic links and process templates for any managed
|
alt Create symbolic links and process templates for any managed
|
||||||
files matching the naming rules described in the ALTERNATES and
|
files matching the naming rules described in the ALTERNATES and
|
||||||
TEMPLATES sections. It is usually unnecessary to run this com‐
|
TEMPLATES sections. It is usually unnecessary to run this com-
|
||||||
mand, as yadm automatically processes alternates by default.
|
mand, as yadm automatically processes alternates by default.
|
||||||
This automatic behavior can be disabled by setting the configu‐
|
This automatic behavior can be disabled by setting the configu-
|
||||||
ration yadm.auto-alt to "false".
|
ration yadm.auto-alt to "false".
|
||||||
|
|
||||||
bootstrap
|
bootstrap
|
||||||
Execute $HOME/.config/yadm/bootstrap if it exists.
|
Execute $HOME/.config/yadm/bootstrap if it exists.
|
||||||
|
|
||||||
clone url
|
clone url
|
||||||
Clone a remote repository for tracking dotfiles. After the con‐
|
Clone a remote repository for tracking dotfiles. After the con-
|
||||||
tents of the remote repository have been fetched, a "check out"
|
tents of the remote repository have been fetched, a "merge" of
|
||||||
of the remote HEAD branch is attempted. If there are conflict‐
|
the remote HEAD branch is attempted. If there are conflicting
|
||||||
ing files already present in the work-tree, the local version
|
files already present in the work-tree, this merge will fail and
|
||||||
will be left unmodified and you'll have to review and resolve
|
instead a "reset" of the remote HEAD branch will be done, fol-
|
||||||
the difference.
|
lowed by a "stash". This "stash" operation will preserve the
|
||||||
|
original data.
|
||||||
|
|
||||||
The repository is stored in $HOME/.local/share/yadm/repo.git.
|
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.
|
||||||
By default, $HOME will be used as the work-tree, but this can be
|
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
|
overridden with the -w option. yadm can be forced to overwrite
|
||||||
an existing repository by providing the -f option. If you want
|
an existing repository by providing the -f option. If you want
|
||||||
to use a branch other than the remote HEAD branch you can spec‐
|
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
|
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‐
|
if the bootstrap program should be run (if it exists). The
|
||||||
tions --bootstrap or --no-bootstrap will either force the boot‐
|
options --bootstrap or --no-bootstrap will either force the
|
||||||
strap to be run, or prevent it from being run, without prompting
|
bootstrap to be run, or prevent it from being run, without
|
||||||
the user.
|
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
|
works exactly the way git-config(1) does. See the CONFIGURATION
|
||||||
section for more details.
|
section for more details.
|
||||||
|
|
||||||
|
@ -100,8 +114,8 @@
|
||||||
without extracting them.
|
without extracting them.
|
||||||
|
|
||||||
encrypt
|
encrypt
|
||||||
Encrypt all files matching the patterns found in $HOME/.con‐
|
Encrypt all files matching the patterns found in $HOME/.con-
|
||||||
fig/yadm/encrypt. See the ENCRYPTION section for more details.
|
fig/yadm/encrypt. See the ENCRYPTION section for more details.
|
||||||
|
|
||||||
enter Run a sub-shell with all Git variables set. Exit the sub-shell
|
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
|
the same way you leave your normal shell (usually with the
|
||||||
|
@ -114,7 +128,7 @@
|
||||||
of invoking your shell, that command will be run with all of the
|
of invoking your shell, that command will be run with all of the
|
||||||
Git variables exposed to the command's environment.
|
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:
|
tion:
|
||||||
|
|
||||||
(add-to-list 'tramp-methods
|
(add-to-list 'tramp-methods
|
||||||
|
@ -128,33 +142,33 @@
|
||||||
With this config, use (magit-status "/yadm::").
|
With this config, use (magit-status "/yadm::").
|
||||||
|
|
||||||
git-crypt options
|
git-crypt options
|
||||||
If git-crypt is installed, this command allows you to pass op‐
|
If git-crypt is installed, this command allows you to pass
|
||||||
tions directly to git-crypt, with the environment configured to
|
options directly to git-crypt, with the environment configured
|
||||||
use the yadm repository.
|
to use the yadm repository.
|
||||||
|
|
||||||
git-crypt enables transparent encryption and decryption of files
|
git-crypt enables transparent encryption and decryption of files
|
||||||
in a git repository. You can read https://github.com/AGWA/git-
|
in a git repository. You can read https://github.com/AGWA/git-
|
||||||
crypt for details.
|
crypt for details.
|
||||||
|
|
||||||
gitconfig
|
gitconfig
|
||||||
Pass options to the git config command. Since yadm already uses
|
Pass options to the git config command. Since yadm already uses
|
||||||
the config command to manage its own configurations, this com‐
|
the config command to manage its own configurations, this com-
|
||||||
mand is provided as a way to change configurations of the repos‐
|
mand is provided as a way to change configurations of the repos-
|
||||||
itory managed by yadm. One useful case might be to configure
|
itory managed by yadm. One useful case might be to configure
|
||||||
the repository so untracked files are shown in status commands.
|
the repository so untracked files are shown in status commands.
|
||||||
yadm initially configures its repository so that untracked files
|
yadm initially configures its repository so that untracked files
|
||||||
are not shown. If you wish use the default Git behavior (to
|
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.
|
figuration.
|
||||||
|
|
||||||
yadm gitconfig --unset status.showUntrackedFiles
|
yadm gitconfig --unset status.showUntrackedFiles
|
||||||
|
|
||||||
help Print a summary of yadm commands.
|
help Print a summary of yadm commands.
|
||||||
|
|
||||||
init Initialize a new, empty repository for tracking dotfiles. The
|
init Initialize a new, empty repository for tracking dotfiles. The
|
||||||
repository is stored in $HOME/.local/share/yadm/repo.git. By
|
repository is stored in $HOME/.local/share/yadm/repo.git. By
|
||||||
default, $HOME will be used as the work-tree, but this can be
|
default, $HOME will be used as the work-tree, but this can be
|
||||||
overridden with the -w option. yadm can be forced to overwrite
|
overridden with the -w option. yadm can be forced to overwrite
|
||||||
an existing repository by providing the -f option.
|
an existing repository by providing the -f option.
|
||||||
|
|
||||||
list Print a list of files managed by yadm. The -a option will cause
|
list Print a list of files managed by yadm. The -a option will cause
|
||||||
|
@ -166,16 +180,16 @@
|
||||||
configs, repo, and switches. The purpose of introspection is to
|
configs, repo, and switches. The purpose of introspection is to
|
||||||
support command line completion.
|
support command line completion.
|
||||||
|
|
||||||
perms Update permissions as described in the PERMISSIONS section. It
|
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
|
cally processes permissions by default. This automatic behavior
|
||||||
can be disabled by setting the configuration yadm.auto-perms to
|
can be disabled by setting the configuration yadm.auto-perms to
|
||||||
"false".
|
"false".
|
||||||
|
|
||||||
transcrypt options
|
transcrypt options
|
||||||
If transcrypt is installed, this command allows you to pass op‐
|
If transcrypt is installed, this command allows you to pass
|
||||||
tions directly to transcrypt, with the environment configured to
|
options directly to transcrypt, with the environment configured
|
||||||
use the yadm repository.
|
to use the yadm repository.
|
||||||
|
|
||||||
transcrypt enables transparent encryption and decryption of
|
transcrypt enables transparent encryption and decryption of
|
||||||
files in a git repository. You can read
|
files in a git repository. You can read
|
||||||
|
@ -184,26 +198,26 @@
|
||||||
upgrade
|
upgrade
|
||||||
Version 3 of yadm uses a different directory for storing data.
|
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
|
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
|
iest way to accomplish this is by running "yadm upgrade". This
|
||||||
command will start by moving your yadm repo to the new path.
|
command will start by moving your yadm repo to the new path.
|
||||||
Next it will move any archive data. If the archive is tracked
|
Next it will move any archive data. If the archive is tracked
|
||||||
within your yadm repo, this command will "stage" the renaming of
|
within your yadm repo, this command will "stage" the renaming of
|
||||||
that file in the repo's index.
|
that file in the repo's index.
|
||||||
|
|
||||||
Upgrading will attempt to de-initialize and re-initialize your
|
Upgrading will attempt to de-initialize and re-initialize your
|
||||||
submodules. If your submodules cannot be de-initialized, the up‐
|
submodules. If your submodules cannot be de-initialized, the
|
||||||
grade will fail. The most common reason submodules will fail to
|
upgrade will fail. The most common reason submodules will fail
|
||||||
de-initialize is because they have local modifications. If you
|
to de-initialize is because they have local modifications. If
|
||||||
are willing to lose the local modifications to those submodules,
|
you are willing to lose the local modifications to those submod-
|
||||||
you can use the -f option with the "upgrade" command to force
|
ules, you can use the -f option with the "upgrade" command to
|
||||||
the de-initialization.
|
force the de-initialization.
|
||||||
|
|
||||||
After running "yadm upgrade", you should run "yadm status" to
|
After running "yadm upgrade", you should run "yadm status" to
|
||||||
review changes which have been staged, and commit them to your
|
review changes which have been staged, and commit them to your
|
||||||
repository.
|
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.
|
mation.
|
||||||
|
|
||||||
version
|
version
|
||||||
|
@ -211,20 +225,20 @@
|
||||||
|
|
||||||
|
|
||||||
## OPTIONS
|
## OPTIONS
|
||||||
yadm supports a set of universal options that alter the paths it uses.
|
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
|
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
|
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
|
alias for the yadm command. For example, the following alias could be
|
||||||
used to override the repository directory.
|
used to override the repository directory.
|
||||||
|
|
||||||
alias yadm='yadm --yadm-repo /alternate/path/to/repo'
|
alias yadm='yadm --yadm-repo /alternate/path/to/repo'
|
||||||
|
|
||||||
The following is the full list of universal options. Each option
|
The following is the full list of universal options. Each option
|
||||||
should be followed by a path.
|
should be followed by a fully qualified path.
|
||||||
|
|
||||||
-Y,--yadm-dir
|
-Y,--yadm-dir
|
||||||
Override the yadm directory. yadm stores its configurations
|
Override the yadm directory. yadm stores its configurations
|
||||||
relative to this directory.
|
relative to this directory.
|
||||||
|
|
||||||
--yadm-data
|
--yadm-data
|
||||||
|
@ -259,24 +273,24 @@
|
||||||
The following is the full list of supported configurations:
|
The following is the full list of supported configurations:
|
||||||
|
|
||||||
yadm.alt-copy
|
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
|
bolic links. This might be desirable, because some systems may
|
||||||
not properly support symlinks.
|
not properly support symlinks.
|
||||||
|
|
||||||
yadm.auto-alt
|
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
|
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
|
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.
|
$HOME/.config/yadm/encrypt. This feature is enabled by default.
|
||||||
|
|
||||||
yadm.auto-perms
|
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
|
tion PERMISSIONS. If disabled, you may still run yadm perms
|
||||||
manually to update permissions. This feature is enabled by de‐
|
manually to update permissions. This feature is enabled by
|
||||||
fault.
|
default.
|
||||||
|
|
||||||
yadm.auto-private-dirs
|
yadm.auto-private-dirs
|
||||||
Disable the automatic creating of private directories described
|
Disable the automatic creating of private directories described
|
||||||
|
@ -285,67 +299,60 @@
|
||||||
yadm.cipher
|
yadm.cipher
|
||||||
Configure which encryption system is used by the encrypt/decrypt
|
Configure which encryption system is used by the encrypt/decrypt
|
||||||
commands. Valid options are "gpg" and "openssl". The default is
|
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.
|
TION.
|
||||||
|
|
||||||
yadm.git-program
|
yadm.git-program
|
||||||
Specify an alternate program to use instead of "git". By de‐
|
Specify an alternate program to use instead of "git". By
|
||||||
fault, the first "git" found in $PATH is used.
|
default, the first "git" found in $PATH is used.
|
||||||
|
|
||||||
yadm.gpg-perms
|
yadm.gpg-perms
|
||||||
Disable the permission changes to $HOME/.gnupg/*. This feature
|
Disable the permission changes to $HOME/.gnupg/*. This feature
|
||||||
is enabled by default.
|
is enabled by default.
|
||||||
|
|
||||||
yadm.gpg-program
|
yadm.gpg-program
|
||||||
Specify an alternate program to use instead of "gpg". By de‐
|
Specify an alternate program to use instead of "gpg". By
|
||||||
fault, the first "gpg" found in $PATH is used.
|
default, the first "gpg" found in $PATH is used.
|
||||||
|
|
||||||
yadm.gpg-recipient
|
yadm.gpg-recipient
|
||||||
Asymmetrically encrypt files with a gpg public/private key pair.
|
Asymmetrically encrypt files with a gpg public/private key pair.
|
||||||
Provide a "key ID" to specify which public key to encrypt with.
|
Provide a "key ID" to specify which public key to encrypt with.
|
||||||
The key must exist in your public keyrings. Multiple recipients
|
The key must exist in your public keyrings. Multiple recipients
|
||||||
can be specified (separated by space). If left blank or not
|
can be specified (separated by space). If left blank or not
|
||||||
provided, symmetric encryption is used instead. If set to
|
provided, symmetric encryption is used instead. If set to
|
||||||
"ASK", gpg will interactively ask for recipients. See the EN‐
|
"ASK", gpg will interactively ask for recipients. See the
|
||||||
CRYPTION section for more details. This feature is disabled by
|
ENCRYPTION section for more details. This feature is disabled
|
||||||
default.
|
by default.
|
||||||
|
|
||||||
yadm.openssl-ciphername
|
yadm.openssl-ciphername
|
||||||
Specify which cipher should be used by openssl. "aes-256-cbc"
|
Specify which cipher should be used by openssl. "aes-256-cbc"
|
||||||
is used by default.
|
is used by default.
|
||||||
|
|
||||||
yadm.openssl-old
|
yadm.openssl-old
|
||||||
Newer versions of openssl support the pbkdf2 key derivation
|
Newer versions of openssl support the pbkdf2 key derivation
|
||||||
function. This is used by default. If this configuration is set
|
function. This is used by default. If this configuration is set
|
||||||
to "true", openssl operations will use options compatible with
|
to "true", openssl operations will use options compatible with
|
||||||
older versions of openssl. If you change this option, you will
|
older versions of openssl. If you change this option, you will
|
||||||
need to recreate your encrypted archive.
|
need to recreate your encrypted archive.
|
||||||
|
|
||||||
yadm.openssl-program
|
yadm.openssl-program
|
||||||
Specify an alternate program to use instead of "openssl". By
|
Specify an alternate program to use instead of "openssl". By
|
||||||
default, the first "openssl" found in $PATH is used.
|
default, the first "openssl" found in $PATH is used.
|
||||||
|
|
||||||
yadm.ssh-perms
|
yadm.ssh-perms
|
||||||
Disable the permission changes to $HOME/.ssh/*. This feature is
|
Disable the permission changes to $HOME/.ssh/*. This feature is
|
||||||
enabled by default.
|
enabled by default.
|
||||||
|
|
||||||
The following five "local" configurations are not stored in the
|
The following four "local" configurations are not stored in the
|
||||||
$HOME/.config/yadm/config, they are stored in the local repository.
|
$HOME/.config/yadm/config, they are stored in the local repository.
|
||||||
|
|
||||||
|
|
||||||
local.class
|
local.class
|
||||||
Specify a class for the purpose of symlinking alternate files.
|
Specify a class for the purpose of symlinking alternate files.
|
||||||
By default, no class will be matched. The local host can be as‐
|
By default, no class will be matched.
|
||||||
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
|
local.hostname
|
||||||
Override the hostname for the purpose of symlinking alternate
|
Override the hostname for the purpose of symlinking alternate
|
||||||
files.
|
files.
|
||||||
|
|
||||||
local.os
|
local.os
|
||||||
|
@ -360,24 +367,24 @@
|
||||||
to have an automated way of choosing an alternate version of a file for
|
to have an automated way of choosing an alternate version of a file for
|
||||||
a different operating system, host, user, etc.
|
a different operating system, host, user, etc.
|
||||||
|
|
||||||
yadm will automatically create a symbolic link to the appropriate ver‐
|
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
|
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
|
suffix contains the conditions that must be met for that file to be
|
||||||
used.
|
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.
|
rated by commas.
|
||||||
|
|
||||||
##<condition>[,<condition>,...]
|
##<condition>[,<condition>,...]
|
||||||
|
|
||||||
Each condition is an attribute/value pair, separated by a period. Some
|
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
|
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
|
value can be omitted. Most attributes can be abbreviated as a single
|
||||||
letter.
|
letter.
|
||||||
|
|
||||||
<attribute>[.<value>]
|
<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:
|
dence:
|
||||||
|
|
||||||
|
|
||||||
|
@ -389,31 +396,23 @@
|
||||||
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.
|
calculated by running id -u -n.
|
||||||
|
|
||||||
hostname, h
|
distro, d
|
||||||
Valid if the value matches the short hostname. Hostname is cal‐
|
Valid if the value matches the distro. Distro is calculated by
|
||||||
culated by running uname -n, and trimming off any domain.
|
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.
|
||||||
|
|
||||||
class, c
|
class, c
|
||||||
Valid if the value matches the local.class configuration. Class
|
Valid if the value matches the local.class configuration. Class
|
||||||
must be manually set using yadm config local.class <class>. See
|
must be manually set using yadm config local.class <class>. See
|
||||||
the CONFIGURATION section for more details about setting lo‐
|
the CONFIGURATION section for more details about setting
|
||||||
cal.class.
|
local.class.
|
||||||
|
|
||||||
distro, d
|
hostname, h
|
||||||
Valid if the value matches the distro. Distro is calculated by
|
Valid if the value matches the short hostname. Hostname is cal-
|
||||||
running lsb_release -si or by inspecting the ID from /etc/os-re‐
|
culated by running uname -n, and trimming off any domain.
|
||||||
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
|
default
|
||||||
Valid when no other alternate is valid.
|
Valid when no other alternate is valid.
|
||||||
|
@ -424,6 +423,7 @@
|
||||||
certain extension to e.g. make editors highlight the content
|
certain extension to e.g. make editors highlight the content
|
||||||
properly.
|
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".
|
even though uname identifies as "Linux".
|
||||||
|
|
||||||
|
@ -434,17 +434,17 @@
|
||||||
most appropriate version.
|
most appropriate version.
|
||||||
|
|
||||||
The "most appropriate" version is determined by calculating a score for
|
The "most appropriate" version is determined by calculating a score for
|
||||||
each version of a file. A template is always scored higher than any
|
each version of a file. A template is always scored higher than any
|
||||||
symlink condition. The number of conditions is the next largest factor
|
symlink condition. The number of conditions is the next largest factor
|
||||||
in scoring. Files with more conditions will always be favored. Any in‐
|
in scoring. Files with more conditions will always be favored. Any
|
||||||
valid condition will disqualify that file completely.
|
invalid condition will disqualify that file completely.
|
||||||
|
|
||||||
If you don't care to have all versions of alternates stored in the same
|
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
|
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.
|
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:
|
lowing files are managed by yadm's repository:
|
||||||
|
|
||||||
- $HOME/path/example.txt##default
|
- $HOME/path/example.txt##default
|
||||||
|
@ -459,7 +459,7 @@
|
||||||
If running on a Macbook named "host2", yadm will create a symbolic link
|
If running on a Macbook named "host2", yadm will create a symbolic link
|
||||||
which looks like this:
|
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
|
name.host2
|
||||||
|
|
||||||
However, on another Mackbook named "host3", yadm will create a symbolic
|
However, on another Mackbook named "host3", yadm will create a symbolic
|
||||||
|
@ -467,7 +467,7 @@
|
||||||
|
|
||||||
$HOME/path/example.txt -> $HOME/path/example.txt##os.Darwin
|
$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.
|
generic version is chosen.
|
||||||
|
|
||||||
If running on a Linux server named "host4", the link will be:
|
If running on a Linux server named "host4", the link will be:
|
||||||
|
@ -482,13 +482,13 @@
|
||||||
|
|
||||||
$HOME/path/example.txt -> $HOME/path/example.txt##class.Work
|
$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.
|
then no link will be created.
|
||||||
|
|
||||||
Links are also created for directories named this way, as long as they
|
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).
|
have at least one yadm managed file within them.
|
||||||
|
|
||||||
yadm will automatically create these links by default. This can be dis‐
|
yadm will automatically create these links by default. This can be dis-
|
||||||
abled using the yadm.auto-alt configuration. Even if disabled, links
|
abled using the yadm.auto-alt configuration. Even if disabled, links
|
||||||
can be manually created by running yadm alt.
|
can be manually created by running yadm alt.
|
||||||
|
|
||||||
|
@ -500,13 +500,13 @@
|
||||||
|
|
||||||
yadm config local.class Work
|
yadm config local.class Work
|
||||||
|
|
||||||
Similarly, the values of architecture, os, hostname, and user can be
|
Similarly, the values of os, hostname, and user can be manually over-
|
||||||
manually overridden using the configuration options local.arch, lo‐
|
ridden using the configuration options local.os, local.hostname, and
|
||||||
cal.os, local.hostname, and local.user.
|
local.user.
|
||||||
|
|
||||||
|
|
||||||
## TEMPLATES
|
## 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
|
and the necessary dependencies for the template are available, then the
|
||||||
file will be processed to create or overwrite files.
|
file will be processed to create or overwrite files.
|
||||||
|
|
||||||
|
@ -514,52 +514,49 @@
|
||||||
|
|
||||||
default
|
default
|
||||||
This is yadm's built-in template processor. This processor is
|
This is yadm's built-in template processor. This processor is
|
||||||
very basic, with a Jinja-like syntax. The advantage of this
|
very basic, with a Jinja-like syntax. The advantage of this pro-
|
||||||
processor is that it only depends upon awk, which is available
|
cessor is that it only depends upon awk, which is available on
|
||||||
on most *nix systems. To use this processor, specify the value
|
most *nix systems. To use this processor, specify the value of
|
||||||
of "default" or just leave the value off (e.g. "##template").
|
"default" or just leave the value off (e.g. "##template").
|
||||||
|
|
||||||
ESH ESH is a template processor written in POSIX compliant shell. It
|
ESH ESH is a template processor written in POSIX compliant shell. It
|
||||||
allows executing shell commands within templates. This can be
|
allows executing shell commands within templates. This can be
|
||||||
used to reference your own configurations within templates, for
|
used to reference your own configurations within templates, for
|
||||||
example:
|
example:
|
||||||
|
|
||||||
<% yadm config mysection.myconfig %>
|
<% yadm config mysection.myconfig %>
|
||||||
|
|
||||||
To use the ESH template processor, specify the value of "esh"
|
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".
|
"j2" or "j2cli".
|
||||||
|
|
||||||
envtpl To use the envtpl Jinja template processor, specify the value of
|
envtpl To use the envtpl Jinja template processor, specify the value of
|
||||||
"j2" or "envtpl".
|
"j2" or "envtpl".
|
||||||
|
|
||||||
NOTE: Specifying "j2" as the processor will attempt to use j2cli or en‐
|
|
||||||
vtpl, whichever is available.
|
|
||||||
|
|
||||||
If the template processor specified is available, templates will be
|
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
|
||||||
processed to create or overwrite files.
|
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:
|
plate:
|
||||||
|
|
||||||
Default Jinja or ESH Description
|
Default Jinja or ESH Description
|
||||||
------------- ------------- ----------------------------
|
------------- ------------- --------------------------
|
||||||
yadm.arch YADM_ARCH uname -m
|
yadm.class YADM_CLASS Locally defined yadm class
|
||||||
yadm.class YADM_CLASS Last locally defined class
|
yadm.distro YADM_DISTRO lsb_release -si
|
||||||
yadm.classes YADM_CLASSES All classes
|
yadm.hostname YADM_HOSTNAME uname -n (without domain)
|
||||||
yadm.distro YADM_DISTRO lsb_release -si
|
yadm.os YADM_OS uname -s
|
||||||
yadm.distro_family YADM_DISTRO_FAMILY ID_LIKE from /etc/os-release
|
yadm.user YADM_USER id -u -n
|
||||||
yadm.hostname YADM_HOSTNAME uname -n (without domain)
|
yadm.source YADM_SOURCE Template filename
|
||||||
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".
|
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.
|
in /etc/os-release.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
@ -573,7 +570,7 @@
|
||||||
{% include "whatever.extra" %}
|
{% include "whatever.extra" %}
|
||||||
{% endif %}
|
{% 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":
|
user is "harvey":
|
||||||
|
|
||||||
config=work-Linux
|
config=work-Linux
|
||||||
|
@ -583,7 +580,7 @@
|
||||||
config=dev-whatever
|
config=dev-whatever
|
||||||
admin=false
|
admin=false
|
||||||
|
|
||||||
An equivalent Jinja template named whatever##template.j2 would look
|
An equivalent Jinja template named whatever##template.j2 would look
|
||||||
like:
|
like:
|
||||||
|
|
||||||
{% if YADM_USER == 'harvey' -%}
|
{% if YADM_USER == 'harvey' -%}
|
||||||
|
@ -593,7 +590,7 @@
|
||||||
{% include 'whatever.extra' %}
|
{% include 'whatever.extra' %}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
An equivalent ESH templated named whatever##template.esh would look
|
An equivalent ESH templated named whatever##template.esh would look
|
||||||
like:
|
like:
|
||||||
|
|
||||||
<% if [ "$YADM_USER" = "harvey" ]; then -%>
|
<% if [ "$YADM_USER" = "harvey" ]; then -%>
|
||||||
|
@ -605,17 +602,17 @@
|
||||||
|
|
||||||
|
|
||||||
## ENCRYPTION
|
## ENCRYPTION
|
||||||
It can be useful to manage confidential files, like SSH or GPG keys,
|
It can be useful to manage confidential files, like SSH or GPG keys,
|
||||||
across multiple systems. However, doing so would put plain text data
|
across multiple systems. However, doing so would put plain text data
|
||||||
into a Git repository, which often resides on a public system. yadm can
|
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
|
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
|
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)
|
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
|
are supported. gpg is used by default, but openssl can be configured
|
||||||
with the yadm.cipher configuration.
|
with the yadm.cypher configuration.
|
||||||
|
|
||||||
To use this feature, a list of patterns must be created and saved as
|
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
|
$HOME/.config/yadm/encrypt. This list of patterns should be relative
|
||||||
to the configured work-tree (usually $HOME). For example:
|
to the configured work-tree (usually $HOME). For example:
|
||||||
|
|
||||||
.ssh/*.key
|
.ssh/*.key
|
||||||
|
@ -624,14 +621,14 @@
|
||||||
Standard filename expansions (*, ?, [) are supported. If you have Bash
|
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
|
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
|
contents will be included, but not recursively. Paths beginning with a
|
||||||
"!" will be excluded.
|
"!" will be excluded.
|
||||||
|
|
||||||
The yadm encrypt command will find all files matching the patterns, and
|
The yadm encrypt command will find all files matching the patterns, and
|
||||||
prompt for a password. Once a password has confirmed, the matching
|
prompt for a password. Once a password has confirmed, the matching
|
||||||
files will be encrypted and saved as $HOME/.local/share/yadm/archive.
|
files will be encrypted and saved as $HOME/.local/share/yadm/archive.
|
||||||
The "encrypt" and "archive" files should be added to the yadm reposi‐
|
The "encrypt" and "archive" files should be added to the yadm reposi-
|
||||||
tory so they are available across multiple systems.
|
tory so they are available across multiple systems.
|
||||||
|
|
||||||
To decrypt these files later, or on another system run yadm decrypt and
|
To decrypt these files later, or on another system run yadm decrypt and
|
||||||
|
@ -645,26 +642,28 @@
|
||||||
confidential files, even though they are encrypted.
|
confidential files, even though they are encrypted.
|
||||||
|
|
||||||
Patterns found in $HOME/.config/yadm/encrypt are automatically added to
|
Patterns found in $HOME/.config/yadm/encrypt are automatically added to
|
||||||
the repository's info/exclude file every time yadm encrypt is run.
|
the repository's info/exclude file every time yadm encrypt is run.
|
||||||
This is to prevent accidentally committing sensitive data to the repos‐
|
This is to prevent accidentally committing sensitive data to the repos-
|
||||||
itory. This can be disabled using the yadm.auto-exclude configuration.
|
itory. This can be disabled using the yadm.auto-exclude configuration.
|
||||||
|
|
||||||
Using transcrypt or git-crypt
|
Using transcrypt or git-crypt
|
||||||
|
|
||||||
A completely separate option for encrypting data is to install and use
|
A completely separate option for encrypting data is to install and use
|
||||||
transcrypt or git-crypt. Once installed, you can use these tools by
|
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
|
parent encryption and decryption of files in a git repository. See the
|
||||||
following web sites for more information:
|
following web sites for more information:
|
||||||
|
|
||||||
- https://github.com/elasticdog/transcrypt
|
- https://github.com/elasticdog/transcrypt
|
||||||
|
|
||||||
- https://github.com/AGWA/git-crypt
|
- https://github.com/AGWA/git-crypt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## PERMISSIONS
|
## 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
|
sions are dependent upon the user's umask. Because of this, yadm will
|
||||||
automatically update the permissions of some file paths. The "group"
|
automatically update the permissions of some file paths. The "group"
|
||||||
and "others" permissions will be removed from the following files:
|
and "others" permissions will be removed from the following files:
|
||||||
|
|
||||||
- $HOME/.local/share/yadm/archive
|
- $HOME/.local/share/yadm/archive
|
||||||
|
@ -675,11 +674,11 @@
|
||||||
|
|
||||||
- The GPG directory and files, .gnupg/*
|
- The GPG directory and files, .gnupg/*
|
||||||
|
|
||||||
yadm will automatically update permissions by default. This can be dis‐
|
yadm will automatically update permissions by default. This can be dis-
|
||||||
abled using the yadm.auto-perms configuration. Even if disabled, per‐
|
abled using the yadm.auto-perms configuration. Even if disabled, per-
|
||||||
missions can be manually updated by running yadm perms. The .ssh di‐
|
missions can be manually updated by running yadm perms. The .ssh
|
||||||
rectory processing can be disabled using the yadm.ssh-perms configura‐
|
directory processing can be disabled using the yadm.ssh-perms configu-
|
||||||
tion. The .gnupg directory processing can be disabled using the
|
ration. The .gnupg directory processing can be disabled using the
|
||||||
yadm.gpg-perms configuration.
|
yadm.gpg-perms configuration.
|
||||||
|
|
||||||
When cloning a repo which includes data in a .ssh or .gnupg directory,
|
When cloning a repo which includes data in a .ssh or .gnupg directory,
|
||||||
|
@ -689,26 +688,26 @@
|
||||||
|
|
||||||
When running a Git command and .ssh or .gnupg directories do not exist,
|
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
|
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.
|
figuration.
|
||||||
|
|
||||||
|
|
||||||
## HOOKS
|
## HOOKS
|
||||||
For every command yadm supports, a program can be provided to run be‐
|
For every command yadm supports, a program can be provided to run
|
||||||
fore or after that command. These are referred to as "hooks". yadm
|
before or after that command. These are referred to as "hooks". yadm
|
||||||
looks for hooks in the directory $HOME/.config/yadm/hooks. Each hook
|
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
|
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 af‐
|
should trigger the hook. For example, to create a hook which is run
|
||||||
ter every yadm pull command, create a hook named post_pull. Hooks must
|
after every yadm pull command, create a hook named post_pull. Hooks
|
||||||
have the executable file permission set.
|
must have the executable file permission set.
|
||||||
|
|
||||||
If a pre_ hook is defined, and the hook terminates with a non-zero exit
|
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
|
status, yadm will refuse to run the yadm command. For example, if a
|
||||||
pre_commit hook is defined, but that command ends with a non-zero exit
|
pre_commit hook is defined, but that command ends with a non-zero exit
|
||||||
status, the yadm commit will never be run. This allows one to "short-
|
status, the yadm commit will never be run. This allows one to "short-
|
||||||
circuit" any operation using a pre_ hook.
|
circuit" any operation using a pre_ hook.
|
||||||
|
|
||||||
Hooks have the following environment variables available to them at
|
Hooks have the following environment variables available to them at
|
||||||
runtime:
|
runtime:
|
||||||
|
|
||||||
YADM_HOOK_COMMAND
|
YADM_HOOK_COMMAND
|
||||||
|
@ -730,37 +729,37 @@
|
||||||
|
|
||||||
|
|
||||||
## FILES
|
## FILES
|
||||||
All of yadm's configurations are relative to the "yadm directory".
|
All of yadm's configurations are relative to the "yadm directory".
|
||||||
yadm uses the "XDG Base Directory Specification" to determine this di‐
|
yadm uses the "XDG Base Directory Specification" to determine this
|
||||||
rectory. If the environment variable $XDG_CONFIG_HOME is defined as a
|
directory. If the environment variable $XDG_CONFIG_HOME is defined as
|
||||||
fully qualified path, this directory will be $XDG_CONFIG_HOME/yadm.
|
a fully qualified path, this directory will be $XDG_CONFIG_HOME/yadm.
|
||||||
Otherwise it will be $HOME/.config/yadm.
|
Otherwise it will be $HOME/.config/yadm.
|
||||||
|
|
||||||
Similarly, yadm's data files are relative to the "yadm data directory".
|
Similarly, yadm's data files are relative to the "yadm data directory".
|
||||||
yadm uses the "XDG Base Directory Specification" to determine this di‐
|
yadm uses the "XDG Base Directory Specification" to determine this
|
||||||
rectory. If the environment variable $XDG_DATA_HOME is defined as a
|
directory. If the environment variable $XDG_DATA_HOME is defined as a
|
||||||
fully qualified path, this directory will be $XDG_DATA_HOME/yadm. Oth‐
|
fully qualified path, this directory will be $XDG_DATA_HOME/yadm. Oth-
|
||||||
erwise it will be $HOME/.local/share/yadm.
|
erwise it will be $HOME/.local/share/yadm.
|
||||||
|
|
||||||
The following are the default paths yadm uses for its own data. Most
|
The following are the default paths yadm uses for its own data. Most
|
||||||
of these paths can be altered using universal options. See the OPTIONS
|
of these paths can be altered using universal options. See the OPTIONS
|
||||||
section for details.
|
section for details.
|
||||||
|
|
||||||
$HOME/.config/yadm
|
$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.
|
tive to this directory.
|
||||||
|
|
||||||
$HOME/.local/share/yadm
|
$HOME/.local/share/yadm
|
||||||
The yadm data directory. By default, all data yadm stores is
|
The yadm data directory. By default, all data yadm stores is
|
||||||
relative to this directory.
|
relative to this directory.
|
||||||
|
|
||||||
$YADM_DIR/config
|
$YADM_DIR/config
|
||||||
Configuration file for yadm.
|
Configuration file for yadm.
|
||||||
|
|
||||||
$YADM_DIR/alt
|
$YADM_DIR/alt
|
||||||
This is a directory to keep "alternate files" without having
|
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
|
plate. Alternate files placed in this directory will be created
|
||||||
relative to $HOME instead.
|
relative to $HOME instead.
|
||||||
|
|
||||||
$YADM_DATA/repo.git
|
$YADM_DATA/repo.git
|
||||||
|
@ -796,7 +795,7 @@
|
||||||
## REPORTING BUGS
|
## REPORTING BUGS
|
||||||
Report issues or create pull requests at GitHub:
|
Report issues or create pull requests at GitHub:
|
||||||
|
|
||||||
https://github.com/yadm-dev/yadm/issues
|
https://github.com/TheLocehiliosan/yadm/issues
|
||||||
|
|
||||||
|
|
||||||
## AUTHOR
|
## AUTHOR
|
||||||
|
@ -808,3 +807,5 @@
|
||||||
|
|
||||||
https://yadm.io/
|
https://yadm.io/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
%{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}-%{version}}
|
%{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}-%{version}}
|
||||||
Name: yadm
|
Name: yadm
|
||||||
Summary: Yet Another Dotfiles Manager
|
Summary: Yet Another Dotfiles Manager
|
||||||
Version: 3.3.0
|
Version: 3.0.2
|
||||||
Group: Development/Tools
|
Group: Development/Tools
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
URL: https://yadm.io
|
URL: https://yadm.io
|
||||||
|
|
Loading…
Reference in a new issue