From 87ff97bbd624eedad0f24d513fe45034162391cf Mon Sep 17 00:00:00 2001 From: Ross Smith II Date: Fri, 8 Jul 2022 17:06:30 -0700 Subject: [PATCH 01/28] docs: Fix cypher typo --- yadm.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yadm.md b/yadm.md index 73890d8..292cddc 100644 --- a/yadm.md +++ b/yadm.md @@ -616,7 +616,7 @@ version can be maintained in the Git repository. This feature will only work if a supported tool is available. Both gpg(1) and openssl(1) are supported. gpg is used by default, but openssl can be configured - with the yadm.cypher configuration. + with the yadm.cipher configuration. To use this feature, a list of patterns must be created and saved as $HOME/.config/yadm/encrypt. This list of patterns should be relative From 24ee8413724ba8194bd2867e83af723e178a649d Mon Sep 17 00:00:00 2001 From: Ross Smith II Date: Fri, 8 Jul 2022 17:08:51 -0700 Subject: [PATCH 02/28] docs: Fix cypher typo --- yadm.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yadm.1 b/yadm.1 index 8048f0a..ebadb9e 100644 --- a/yadm.1 +++ b/yadm.1 @@ -746,7 +746,7 @@ and .BR openssl (1) are supported. gpg is used by default, but openssl can be configured with the -.I yadm.cypher +.I yadm.cipher configuration. To use this feature, a list of patterns must be created and saved as From 19b7a3066822b8a68caa00f37eb584ec03ca3589 Mon Sep 17 00:00:00 2001 From: Nicolas signed-log FORMICHELLA Date: Sat, 27 Aug 2022 11:39:22 +0200 Subject: [PATCH 03/28] [Makefile] Support docker-like OCI engines Support setting a different OCI engine than docker - Create OCI variable to override the docker engine - Refactor the test-docker error message to expose this possibility Signed-off-by: Nicolas signed-log FORMICHELLA --- Makefile | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index b8f5cca..f9deaa1 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ PYTESTS = $(wildcard test/test_*.py) -IMAGE = yadm/testbed:2022-01-07 +IMAGE = docker.io/yadm/testbed:2022-01-07 +OCI = docker .PHONY: all all: @@ -94,7 +95,7 @@ test: py.test -v $(testargs); \ else \ $(MAKE) -s require-docker && \ - docker run \ + $(OCI) run \ --rm -t$(shell test -t 0 && echo i) \ -v "$(CURDIR):/yadm:ro" \ $(IMAGE) \ @@ -117,7 +118,7 @@ test: .PHONY: testhost testhost: require-docker .testyadm @echo "Starting testhost" - @docker run \ + @$(OCI) run \ -w /root \ --hostname testhost \ --rm -it \ @@ -129,7 +130,7 @@ testhost: require-docker .testyadm scripthost: require-docker .testyadm @echo "Starting scripthost \(recording script\)" @printf '' > script.gz - @docker run \ + @$(OCI) run \ -w /root \ --hostname scripthost \ --rm -it \ @@ -159,7 +160,7 @@ testenv: .PHONY: image image: - @docker build -f test/Dockerfile . -t "$(IMAGE)" + @$(OCI) build -f test/Dockerfile . -t "$(IMAGE)" .PHONY: man @@ -204,11 +205,11 @@ install: .PHONY: sync-clock sync-clock: - docker run --rm --privileged alpine hwclock -s + $(OCI) run --rm --privileged alpine hwclock -s .PHONY: require-docker require-docker: - @if ! command -v "docker" > /dev/null 2>&1; then \ - echo "Sorry, this make target requires docker to be installed."; \ + @if ! command -v $(OCI) > /dev/null 2>&1; then \ + echo "Sorry, this make target requires docker to be installed, to use another docker-compatible engine, like podman, re-run the make command adding OCI=podman"; \ false; \ fi From 075cd1b06b8ee5ff23479dbefa42f86b527b6806 Mon Sep 17 00:00:00 2001 From: Nicolas signed-log FORMICHELLA Date: Sat, 27 Aug 2022 11:54:24 +0200 Subject: [PATCH 04/28] [Documentation] Document OCI switch Document the OCI override switch Signed-off-by: Nicolas signed-log FORMICHELLA --- .github/CONTRIBUTING.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 392a24d..3afd731 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -212,6 +212,11 @@ these principles when making changes. ```text $ make test ``` + If you don't use `docker` but an OCI engine akin to `podman`, you can set it through the `OCI` switch for every target + + ```text + $ make test OCI=podman + ``` 5. Create a feature branch, based off the `develop` branch. From 36fda72bec071b3516af28431441043cc0428e55 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sat, 27 Aug 2022 13:55:24 -0500 Subject: [PATCH 05/28] Remove superfluous space --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3afd731..66ea8b2 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -213,7 +213,7 @@ these principles when making changes. $ 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 ``` From 8f390cf085938e5230774ef68a3f60a8ccdf9a82 Mon Sep 17 00:00:00 2001 From: Nicolas signed-log FORMICHELLA Date: Sun, 28 Aug 2022 18:24:51 +0200 Subject: [PATCH 06/28] Accept _ as space substitutes Replace all spaces in the ID/ID_LIKE scoring to allow for space replacement Signed-off-by: Nicolas signed-log FORMICHELLA --- yadm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yadm b/yadm index 97a56c1..a9330b0 100755 --- a/yadm +++ b/yadm @@ -204,14 +204,14 @@ function score_file() { return fi elif [[ "$label" =~ ^(d|distro)$ ]]; then - if [ "$value" = "$local_distro" ]; then + if [ "${value/\ /_}" = "${local_distro/\ /_}" ]; then score=$((score + 4)) else score=0 return fi elif [[ "$label" =~ ^(f|distro_family)$ ]]; then - if [ "$value" = "$local_distro_family" ]; then + if [ "${value/\ /_}" = "${local_distro_family/\ /_}" ]; then score=$((score + 8)) else score=0 From 0f8538d3e312b3fe31db89a817f44d089300058d Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Tue, 13 Sep 2022 12:38:18 -0500 Subject: [PATCH 07/28] Create tests for underscores in distro/family --- test/test_unit_score_file.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/test_unit_score_file.py b/test/test_unit_score_file.py index a63246a..e59c128 100644 --- a/test/test_unit_score_file.py +++ b/test/test_unit_score_file.py @@ -315,3 +315,37 @@ def test_template_recording(runner, yadm, cmd_generated): assert run.success assert run.err == '' assert run.out.rstrip() == expected + + +def test_underscores_in_distro_and_family(runner, yadm): + """Test replacing spaces in distro / distro_family with underscores""" + local_distro = 'test distro' + local_distro_family = 'test family' + filenames = { + 'filename##distro.test distro': 1004, + 'filename##distro.test-distro': 0, + 'filename##distro.test_distro': 1004, + 'filename##distro_family.test family': 1008, + 'filename##distro_family.test-family': 0, + 'filename##distro_family.test_family': 1008, + } + + script = f""" + YADM_TEST=1 source {yadm} + score=0 + local_distro="{local_distro}" + local_distro_family="{local_distro_family}" + """ + expected = '' + for filename in filenames: + script += f""" + score_file "{filename}" + echo "{filename}" + echo "$score" + """ + expected += filename + '\n' + expected += str(filenames[filename]) + '\n' + run = runner(command=['bash'], inp=script) + assert run.success + assert run.err == '' + assert run.out == expected From 5981f6329e85c902f545bf861befdc5e614553f9 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Mon, 23 Jan 2023 13:16:53 -0600 Subject: [PATCH 08/28] Adjust markdown creation for new version of groff --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f9deaa1..69fa003 100644 --- a/Makefile +++ b/Makefile @@ -176,7 +176,7 @@ man-ps: @groff -man -Tps ./yadm.1 > yadm.ps yadm.md: yadm.1 - @groff -man -Tascii ./yadm.1 | col -bx | sed 's/^[A-Z]/## &/g' | sed '/yadm(1)/d' > yadm.md + @groff -man -Tutf8 -Z ./yadm.1 | grotty -c | col -bx | sed 's/^[A-Z]/## &/g' | sed '/yadm(1)/d' > yadm.md .PHONY: contrib contrib: SHELL = /bin/bash From 2d4dcd05ef78b7f4776a89809774fda9d3b713d5 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Mon, 23 Jan 2023 13:25:54 -0600 Subject: [PATCH 09/28] Update version number and update documentation * 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) --- CHANGES | 7 ++ CONTRIBUTORS | 6 +- README.md | 2 +- yadm | 4 +- yadm.1 | 2 +- yadm.md | 284 +++++++++++++++++++++++++-------------------------- yadm.spec | 2 +- 7 files changed, 157 insertions(+), 150 deletions(-) diff --git a/CHANGES b/CHANGES index baa12e5..9acb665 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +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) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 2fc77ea..c1176e6 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -12,8 +12,12 @@ Espen Henriksen Cameron Eagans Klas Mellbourn James Clark +Glenn Waters +Nicolas signed-log FORMICHELLA Tomas Cernaj +Joshua Cold jonasc +Nicolas stig124 FORMICHELLA Chad Wade Day, Jr Sébastien Gross David Mandelberg @@ -26,7 +30,6 @@ Siôn Le Roux Mateusz Piotrowski Uroš Golja Satoshi Ohki -Nicolas stig124 FORMICHELLA Jonas Franciszek Madej Daniel Wagenknecht @@ -46,3 +49,4 @@ Thomas Luzat Russ Allbery Brayden Banks Alexandre GV +Felipe S. S. Schneider diff --git a/README.md b/README.md index 0c78766..7adb0e5 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ The star count helps others discover yadm. [master-badge]: https://img.shields.io/github/workflow/status/TheLocehiliosan/yadm/Tests/master?label=master [master-commits]: https://github.com/TheLocehiliosan/yadm/commits/master [master-date]: https://img.shields.io/github/last-commit/TheLocehiliosan/yadm/master.svg?label=master -[obs-badge]: https://img.shields.io/badge/OBS-v3.2.1-blue +[obs-badge]: https://img.shields.io/badge/OBS-v3.2.2-blue [obs-link]: https://software.opensuse.org//download.html?project=home%3ATheLocehiliosan%3Ayadm&package=yadm [releases-badge]: https://img.shields.io/github/tag/TheLocehiliosan/yadm.svg?label=latest+release [releases-link]: https://github.com/TheLocehiliosan/yadm/releases diff --git a/yadm b/yadm index a9330b0..003d954 100755 --- a/yadm +++ b/yadm @@ -1,6 +1,6 @@ #!/bin/sh # yadm - Yet Another Dotfiles Manager -# Copyright (C) 2015-2022 Tim Byrne +# Copyright (C) 2015-2023 Tim Byrne # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ if [ -z "$BASH_VERSION" ]; then [ "$YADM_TEST" != 1 ] && exec bash "$0" "$@" fi -VERSION=3.2.1 +VERSION=3.2.2 YADM_WORK="$HOME" YADM_DIR= diff --git a/yadm.1 b/yadm.1 index ebadb9e..c689eb6 100644 --- a/yadm.1 +++ b/yadm.1 @@ -1,5 +1,5 @@ .\" vim: set spell so=8: -.TH yadm 1 "17 March 2022" "3.2.1" +.TH yadm 1 "23 January 2023" "3.2.2" .SH NAME diff --git a/yadm.md b/yadm.md index 292cddc..328e384 100644 --- a/yadm.md +++ b/yadm.md @@ -42,68 +42,68 @@ ## DESCRIPTION - yadm is a tool for managing a collection of files across multiple com- - puters, using a shared Git repository. In addition, yadm provides a - feature to select alternate versions of files for particular systems. - Lastly, yadm supplies the ability to manage a subset of secure files, + yadm is a tool for managing a collection of files across multiple com‐ + puters, using a shared Git repository. In addition, yadm provides a + feature to select alternate versions of files for particular systems. + Lastly, yadm supplies the ability to manage a subset of secure files, which are encrypted before they are included in the repository. ## COMMANDS git-command or git-alias - Any command not internally handled by yadm is passed through to - git(1). Git commands or aliases are invoked with the yadm man- + Any command not internally handled by yadm is passed through to + git(1). Git commands or aliases are invoked with the yadm man‐ aged repository. The working directory for Git commands will be the configured work-tree (usually $HOME). - Dotfiles are managed by using standard git commands; add, com- + Dotfiles are managed by using standard git commands; add, com‐ mit, push, pull, etc. - The config command is not passed directly through. Instead use + The config command is not passed directly through. Instead use the gitconfig command (see below). - alt Create symbolic links and process templates for any managed - files matching the naming rules described in the ALTERNATES and - TEMPLATES sections. It is usually unnecessary to run this com- - mand, as yadm automatically processes alternates by default. - This automatic behavior can be disabled by setting the configu- + alt Create symbolic links and process templates for any managed + files matching the naming rules described in the ALTERNATES and + TEMPLATES sections. It is usually unnecessary to run this com‐ + mand, as yadm automatically processes alternates by default. + This automatic behavior can be disabled by setting the configu‐ ration yadm.auto-alt to "false". bootstrap Execute $HOME/.config/yadm/bootstrap if it exists. clone url - Clone a remote repository for tracking dotfiles. After the con- - tents of the remote repository have been fetched, a "check out" - of the remote HEAD branch is attempted. If there are conflict- - ing files already present in the work-tree, the local version - will be left unmodified and you'll have to review and resolve + Clone a remote repository for tracking dotfiles. After the con‐ + tents of the remote repository have been fetched, a "check out" + of the remote HEAD branch is attempted. If there are conflict‐ + ing files already present in the work-tree, the local version + will be left unmodified and you'll have to review and resolve the difference. - The repository is stored in $HOME/.local/share/yadm/repo.git. + The repository is stored in $HOME/.local/share/yadm/repo.git. By default, $HOME will be used as the work-tree, but this can be - overridden with the -w option. yadm can be forced to overwrite - an existing repository by providing the -f option. If you want - to use a branch other than the remote HEAD branch you can spec- - ify it using the -b option. By default yadm will ask the user - if the bootstrap program should be run (if it exists). The - options --bootstrap or --no-bootstrap will either force the - bootstrap to be run, or prevent it from being run, without - prompting the user. + overridden with the -w option. yadm can be forced to overwrite + an existing repository by providing the -f option. If you want + to use a branch other than the remote HEAD branch you can spec‐ + ify it using the -b option. By default yadm will ask the user + if the bootstrap program should be run (if it exists). The op‐ + tions --bootstrap or --no-bootstrap will either force the boot‐ + strap to be run, or prevent it from being run, without prompting + the user. - config This command manages configurations for yadm. This command + config This command manages configurations for yadm. This command works exactly the way git-config(1) does. See the CONFIGURATION section for more details. decrypt - Decrypt all files stored in $HOME/.local/share/yadm/archive. - Files decrypted will be relative to the configured work-tree + Decrypt all files stored in $HOME/.local/share/yadm/archive. + Files decrypted will be relative to the configured work-tree (usually $HOME). Using the -l option will list the files stored without extracting them. encrypt - Encrypt all files matching the patterns found in $HOME/.con- - fig/yadm/encrypt. See the ENCRYPTION section for more details. + Encrypt all files matching the patterns found in $HOME/.con‐ + fig/yadm/encrypt. See the ENCRYPTION section for more details. enter Run a sub-shell with all Git variables set. Exit the sub-shell the same way you leave your normal shell (usually with the @@ -116,7 +116,7 @@ of invoking your shell, that command will be run with all of the Git variables exposed to the command's environment. - Emacs Tramp and Magit can manage files by using this configura- + Emacs Tramp and Magit can manage files by using this configura‐ tion: (add-to-list 'tramp-methods @@ -130,9 +130,9 @@ With this config, use (magit-status "/yadm::"). git-crypt options - If git-crypt is installed, this command allows you to pass - options directly to git-crypt, with the environment configured - to use the yadm repository. + If git-crypt is installed, this command allows you to pass op‐ + tions directly to git-crypt, with the environment configured to + use the yadm repository. git-crypt enables transparent encryption and decryption of files in a git repository. You can read https://github.com/AGWA/git- @@ -140,13 +140,13 @@ gitconfig Pass options to the git config command. Since yadm already uses - the config command to manage its own configurations, this com- - mand is provided as a way to change configurations of the repos- + the config command to manage its own configurations, this com‐ + mand is provided as a way to change configurations of the repos‐ itory managed by yadm. One useful case might be to configure the repository so untracked files are shown in status commands. yadm initially configures its repository so that untracked files are not shown. If you wish use the default Git behavior (to - show untracked files and directories), you can remove this con- + show untracked files and directories), you can remove this con‐ figuration. yadm gitconfig --unset status.showUntrackedFiles @@ -169,15 +169,15 @@ support command line completion. perms Update permissions as described in the PERMISSIONS section. It - is usually unnecessary to run this command, as yadm automati- + is usually unnecessary to run this command, as yadm automati‐ cally processes permissions by default. This automatic behavior can be disabled by setting the configuration yadm.auto-perms to "false". transcrypt options - If transcrypt is installed, this command allows you to pass - options directly to transcrypt, with the environment configured - to use the yadm repository. + If transcrypt is installed, this command allows you to pass op‐ + tions directly to transcrypt, with the environment configured to + use the yadm repository. transcrypt enables transparent encryption and decryption of files in a git repository. You can read @@ -186,7 +186,7 @@ upgrade Version 3 of yadm uses a different directory for storing data. When you start to use version 3 for the first time, you may see - warnings about moving your data to this new directory. The eas- + warnings about moving your data to this new directory. The eas‐ iest way to accomplish this is by running "yadm upgrade". This command will start by moving your yadm repo to the new path. Next it will move any archive data. If the archive is tracked @@ -194,18 +194,18 @@ that file in the repo's index. Upgrading will attempt to de-initialize and re-initialize your - submodules. If your submodules cannot be de-initialized, the - upgrade will fail. The most common reason submodules will fail - to de-initialize is because they have local modifications. If - you are willing to lose the local modifications to those submod- - ules, you can use the -f option with the "upgrade" command to - force the de-initialization. + submodules. If your submodules cannot be de-initialized, the up‐ + grade will fail. The most common reason submodules will fail to + de-initialize is because they have local modifications. If you + are willing to lose the local modifications to those submodules, + you can use the -f option with the "upgrade" command to force + the de-initialization. After running "yadm upgrade", you should run "yadm status" to review changes which have been staged, and commit them to your repository. - You can read https://yadm.io/docs/upgrade_from_2 for more infor- + You can read https://yadm.io/docs/upgrade_from_2 for more infor‐ mation. version @@ -214,7 +214,7 @@ ## OPTIONS yadm supports a set of universal options that alter the paths it uses. - The default paths are documented in the FILES section. Any path speci- + The default paths are documented in the FILES section. Any path speci‐ fied by these options must be fully qualified. If you always want to override one or more of these paths, it may be useful to create an alias for the yadm command. For example, the following alias could be @@ -261,24 +261,24 @@ The following is the full list of supported configurations: yadm.alt-copy - If set to "true", alternate files will be copies instead of sym- + If set to "true", alternate files will be copies instead of sym‐ bolic links. This might be desirable, because some systems may not properly support symlinks. yadm.auto-alt - Disable the automatic linking described in the section ALTER- + Disable the automatic linking described in the section ALTER‐ NATES. If disabled, you may still run "yadm alt" manually to - create the alternate links. This feature is enabled by default. + create the alternate links. This feature is enabled by default. yadm.auto-exclude - Disable the automatic exclusion of patterns defined in + Disable the automatic exclusion of patterns defined in $HOME/.config/yadm/encrypt. This feature is enabled by default. yadm.auto-perms - Disable the automatic permission changes described in the sec- + Disable the automatic permission changes described in the sec‐ tion PERMISSIONS. If disabled, you may still run yadm perms - manually to update permissions. This feature is enabled by - default. + manually to update permissions. This feature is enabled by de‐ + fault. yadm.auto-private-dirs Disable the automatic creating of private directories described @@ -287,20 +287,20 @@ yadm.cipher Configure which encryption system is used by the encrypt/decrypt commands. Valid options are "gpg" and "openssl". The default is - "gpg". Detailed information can be found in the section ENCRYP- + "gpg". Detailed information can be found in the section ENCRYP‐ TION. yadm.git-program - Specify an alternate program to use instead of "git". By - default, the first "git" found in $PATH is used. + Specify an alternate program to use instead of "git". By de‐ + fault, the first "git" found in $PATH is used. yadm.gpg-perms Disable the permission changes to $HOME/.gnupg/*. This feature is enabled by default. yadm.gpg-program - Specify an alternate program to use instead of "gpg". By - default, the first "gpg" found in $PATH is used. + Specify an alternate program to use instead of "gpg". By de‐ + fault, the first "gpg" found in $PATH is used. yadm.gpg-recipient Asymmetrically encrypt files with a gpg public/private key pair. @@ -308,9 +308,9 @@ The key must exist in your public keyrings. Multiple recipients can be specified (separated by space). If left blank or not provided, symmetric encryption is used instead. If set to - "ASK", gpg will interactively ask for recipients. See the - ENCRYPTION section for more details. This feature is disabled - by default. + "ASK", gpg will interactively ask for recipients. See the EN‐ + CRYPTION section for more details. This feature is disabled by + default. yadm.openssl-ciphername Specify which cipher should be used by openssl. "aes-256-cbc" @@ -337,13 +337,13 @@ local.class Specify a class for the purpose of symlinking alternate files. - By default, no class will be matched. The local host can be - assigned multiple classes using command: + By default, no class will be matched. The local host can be as‐ + signed multiple classes using command: yadm config --add local.class local.arch - Override the architecture for the purpose of symlinking alter- + Override the architecture for the purpose of symlinking alter‐ nate files. local.hostname @@ -362,53 +362,53 @@ to have an automated way of choosing an alternate version of a file for a different operating system, host, user, etc. - yadm will automatically create a symbolic link to the appropriate ver- - sion of a file, when a valid suffix is appended to the filename. The - suffix contains the conditions that must be met for that file to be + yadm will automatically create a symbolic link to the appropriate ver‐ + sion of a file, when a valid suffix is appended to the filename. The + suffix contains the conditions that must be met for that file to be used. - The suffix begins with "##", followed by any number of conditions sepa- + The suffix begins with "##", followed by any number of conditions sepa‐ rated by commas. ##[,,...] - Each condition is an attribute/value pair, separated by a period. Some - conditions do not require a "value", and in that case, the period and - value can be omitted. Most attributes can be abbreviated as a single + Each condition is an attribute/value pair, separated by a period. Some + conditions do not require a "value", and in that case, the period and + value can be omitted. Most attributes can be abbreviated as a single letter. [.] - These are the supported attributes, in the order of the weighted prece- + These are the supported attributes, in the order of the weighted prece‐ dence: template, t - Valid when the value matches a supported template processor. + Valid when the value matches a supported template processor. See the TEMPLATES section for more details. user, u - Valid if the value matches the current user. Current user is + Valid if the value matches the current user. Current user is calculated by running id -u -n. hostname, h - Valid if the value matches the short hostname. Hostname is cal- + Valid if the value matches the short hostname. Hostname is cal‐ culated by running uname -n, and trimming off any domain. class, c Valid if the value matches the local.class configuration. Class must be manually set using yadm config local.class . See - the CONFIGURATION section for more details about setting - local.class. + the CONFIGURATION section for more details about setting lo‐ + cal.class. distro, d - Valid if the value matches the distro. Distro is calculated by - running lsb_release -si or by inspecting the ID from /etc/os- - release. + Valid if the value matches the distro. Distro is calculated by + running lsb_release -si or by inspecting the ID from /etc/os-re‐ + lease. distro_family, f - Valid if the value matches the distro family. Distro family is - calculated by inspecting the ID_LIKE line from /etc/os-release. + 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. @@ -426,7 +426,6 @@ certain extension to e.g. make editors highlight the content properly. - NOTE: The OS for "Windows Subsystem for Linux" is reported as "WSL", even though uname identifies as "Linux". @@ -439,15 +438,15 @@ The "most appropriate" version is determined by calculating a score for each version of a file. A template is always scored higher than any symlink condition. The number of conditions is the next largest factor - in scoring. Files with more conditions will always be favored. Any - invalid condition will disqualify that file completely. + in scoring. Files with more conditions will always be favored. Any in‐ + valid condition will disqualify that file completely. If you don't care to have all versions of alternates stored in the same directory as the generated symlink, you can place them in the $HOME/.config/yadm/alt directory. The generated symlink or processed template will be created using the same relative path. - Alternate linking may best be demonstrated by example. Assume the fol- + Alternate linking may best be demonstrated by example. Assume the fol‐ lowing files are managed by yadm's repository: - $HOME/path/example.txt##default @@ -462,7 +461,7 @@ If running on a Macbook named "host2", yadm will create a symbolic link which looks like this: - $HOME/path/example.txt -> $HOME/path/example.txt##os.Darwin,host- + $HOME/path/example.txt -> $HOME/path/example.txt##os.Darwin,host‐ name.host2 However, on another Mackbook named "host3", yadm will create a symbolic @@ -491,7 +490,7 @@ Links are also created for directories named this way, as long as they have at least one yadm managed file within them (at the top level). - yadm will automatically create these links by default. This can be dis- + yadm will automatically create these links by default. This can be dis‐ abled using the yadm.auto-alt configuration. Even if disabled, links can be manually created by running yadm alt. @@ -504,8 +503,8 @@ yadm config local.class Work Similarly, the values of architecture, os, hostname, and user can be - manually overridden using the configuration options local.arch, - local.os, local.hostname, and local.user. + manually overridden using the configuration options local.arch, lo‐ + cal.os, local.hostname, and local.user. ## TEMPLATES @@ -517,7 +516,7 @@ default This is yadm's built-in template processor. This processor is - very basic, with a Jinja-like syntax. The advantage of this pro- + very basic, with a Jinja-like syntax. The advantage of this pro‐ cessor is that it only depends upon awk, which is available on most *nix systems. To use this processor, specify the value of "default" or just leave the value off (e.g. "##template"). @@ -537,14 +536,13 @@ envtpl To use the envtpl Jinja template processor, specify the value of "j2" or "envtpl". - - NOTE: Specifying "j2" as the processor will attempt to use j2cli or - envtpl, whichever is available. + 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 processed to create or overwrite files. - During processing, the following variables are available in the tem- + During processing, the following variables are available in the tem‐ plate: Default Jinja or ESH Description @@ -635,7 +633,7 @@ The yadm encrypt command will find all files matching the patterns, and prompt for a password. Once a password has confirmed, the matching files will be encrypted and saved as $HOME/.local/share/yadm/archive. - The "encrypt" and "archive" files should be added to the yadm reposi- + The "encrypt" and "archive" files should be added to the yadm reposi‐ tory so they are available across multiple systems. To decrypt these files later, or on another system run yadm decrypt and @@ -650,27 +648,25 @@ Patterns found in $HOME/.config/yadm/encrypt are automatically added to the repository's info/exclude file every time yadm encrypt is run. - This is to prevent accidentally committing sensitive data to the repos- + This is to prevent accidentally committing sensitive data to the repos‐ itory. This can be disabled using the yadm.auto-exclude configuration. Using transcrypt or git-crypt - A completely separate option for encrypting data is to install and use - transcrypt or git-crypt. Once installed, you can use these tools by - running yadm transcrypt or yadm git-crypt. These tools enables trans- - parent encryption and decryption of files in a git repository. See the + A completely separate option for encrypting data is to install and use + transcrypt or git-crypt. Once installed, you can use these tools by + running yadm transcrypt or yadm git-crypt. These tools enables trans‐ + parent encryption and decryption of files in a git repository. See the following web sites for more information: - https://github.com/elasticdog/transcrypt - https://github.com/AGWA/git-crypt - - ## PERMISSIONS - When files are checked out of a Git repository, their initial permis- - sions are dependent upon the user's umask. Because of this, yadm will - automatically update the permissions of some file paths. The "group" + When files are checked out of a Git repository, their initial permis‐ + sions are dependent upon the user's umask. Because of this, yadm will + automatically update the permissions of some file paths. The "group" and "others" permissions will be removed from the following files: - $HOME/.local/share/yadm/archive @@ -681,40 +677,40 @@ - The GPG directory and files, .gnupg/* - yadm will automatically update permissions by default. This can be dis- - abled using the yadm.auto-perms configuration. Even if disabled, per- - missions can be manually updated by running yadm perms. The .ssh - directory processing can be disabled using the yadm.ssh-perms configu- - ration. The .gnupg directory processing can be disabled using the + yadm will automatically update permissions by default. This can be dis‐ + abled using the yadm.auto-perms configuration. Even if disabled, per‐ + missions can be manually updated by running yadm perms. The .ssh di‐ + rectory processing can be disabled using the yadm.ssh-perms configura‐ + tion. The .gnupg directory processing can be disabled using the yadm.gpg-perms configuration. - When cloning a repo which includes data in a .ssh or .gnupg directory, - if those directories do not exist at the time of cloning, yadm will + When cloning a repo which includes data in a .ssh or .gnupg directory, + if those directories do not exist at the time of cloning, yadm will create the directories with mask 0700 prior to merging the fetched data into the work-tree. When running a Git command and .ssh or .gnupg directories do not exist, - yadm will create those directories with mask 0700 prior to running the - Git command. This can be disabled using the yadm.auto-private-dirs con- + 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‐ figuration. ## HOOKS - For every command yadm supports, a program can be provided to run - before or after that command. These are referred to as "hooks". yadm - looks for hooks in the directory $HOME/.config/yadm/hooks. Each hook + For every command yadm supports, a program can be provided to run be‐ + fore or after that command. These are referred to as "hooks". yadm + looks for hooks in the directory $HOME/.config/yadm/hooks. Each hook is named using a prefix of pre_ or post_, followed by the command which - should trigger the hook. For example, to create a hook which is run - after every yadm pull command, create a hook named post_pull. Hooks - must have the executable file permission set. + should trigger the hook. For example, to create a hook which is run af‐ + ter every yadm pull command, create a hook named post_pull. Hooks must + have the executable file permission set. If a pre_ hook is defined, and the hook terminates with a non-zero exit - status, yadm will refuse to run the yadm command. For example, if a - 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, 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 + status, the yadm commit will never be run. This allows one to "short- 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: YADM_HOOK_COMMAND @@ -736,37 +732,37 @@ ## FILES - All of yadm's configurations are relative to the "yadm directory". - yadm uses the "XDG Base Directory Specification" to determine this - directory. If the environment variable $XDG_CONFIG_HOME is defined as - a fully qualified path, this directory will be $XDG_CONFIG_HOME/yadm. + All of yadm's configurations are relative to the "yadm directory". + yadm uses the "XDG Base Directory Specification" to determine this di‐ + rectory. If the environment variable $XDG_CONFIG_HOME is defined as a + fully qualified path, this directory will be $XDG_CONFIG_HOME/yadm. Otherwise it will be $HOME/.config/yadm. Similarly, yadm's data files are relative to the "yadm data directory". - yadm uses the "XDG Base Directory Specification" to determine this - directory. If the environment variable $XDG_DATA_HOME is defined as a - fully qualified path, this directory will be $XDG_DATA_HOME/yadm. Oth- + yadm uses the "XDG Base Directory Specification" to determine this di‐ + rectory. If the environment variable $XDG_DATA_HOME is defined as a + fully qualified path, this directory will be $XDG_DATA_HOME/yadm. Oth‐ erwise it will be $HOME/.local/share/yadm. - The following are the default paths yadm uses for its own data. Most + 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 section for details. $HOME/.config/yadm - The yadm directory. By default, all configs yadm stores is rela- + The yadm directory. By default, all configs yadm stores is rela‐ tive to this directory. $HOME/.local/share/yadm - 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. $YADM_DIR/config Configuration file for yadm. $YADM_DIR/alt - This is a directory to keep "alternate files" without having - them side-by-side with the resulting symlink or processed tem- - plate. Alternate files placed in this directory will be created + This is a directory to keep "alternate files" without having + them side-by-side with the resulting symlink or processed tem‐ + plate. Alternate files placed in this directory will be created relative to $HOME instead. $YADM_DATA/repo.git diff --git a/yadm.spec b/yadm.spec index 90954db..f01dea5 100644 --- a/yadm.spec +++ b/yadm.spec @@ -1,7 +1,7 @@ %{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}-%{version}} Name: yadm Summary: Yet Another Dotfiles Manager -Version: 3.2.1 +Version: 3.2.2 Group: Development/Tools Release: 1%{?dist} URL: https://yadm.io From e4bb8a79a4b67f447d6cdf5fda16034498a5fb27 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Mon, 23 Jan 2023 13:51:06 -0600 Subject: [PATCH 10/28] Update to shields.io new API --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7adb0e5..503db26 100644 --- a/README.md +++ b/README.md @@ -58,18 +58,18 @@ The star count helps others discover yadm. [OpenSSL]: https://www.openssl.org/ [arch-badge]: https://img.shields.io/archlinux/v/community/any/yadm [arch-link]: https://archlinux.org/packages/community/any/yadm/ -[dev-pages-badge]: https://img.shields.io/github/workflow/status/TheLocehiliosan/yadm/Test%20Site/dev-pages?label=dev-pages -[develop-badge]: https://img.shields.io/github/workflow/status/TheLocehiliosan/yadm/Tests/develop?label=develop +[dev-pages-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=dev-pages +[develop-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=develop [develop-commits]: https://github.com/TheLocehiliosan/yadm/commits/develop [develop-date]: https://img.shields.io/github/last-commit/TheLocehiliosan/yadm/develop.svg?label=develop [dotfiles]: https://en.wikipedia.org/wiki/Hidden_file_and_hidden_directory -[gh-pages-badge]: https://img.shields.io/github/workflow/status/TheLocehiliosan/yadm/Test%20Site/gh-pages?label=gh-pages +[gh-pages-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=gh-pages [git-crypt]: https://github.com/AGWA/git-crypt [homebrew-badge]: https://img.shields.io/homebrew/v/yadm.svg [homebrew-link]: https://formulae.brew.sh/formula/yadm [license-badge]: https://img.shields.io/github/license/TheLocehiliosan/yadm.svg [license-link]: https://github.com/TheLocehiliosan/yadm/blob/master/LICENSE -[master-badge]: https://img.shields.io/github/workflow/status/TheLocehiliosan/yadm/Tests/master?label=master +[master-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=master [master-commits]: https://github.com/TheLocehiliosan/yadm/commits/master [master-date]: https://img.shields.io/github/last-commit/TheLocehiliosan/yadm/master.svg?label=master [obs-badge]: https://img.shields.io/badge/OBS-v3.2.2-blue From 2253e52ab7bda332f404c3109fbd36a520dbfd09 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sat, 8 Jul 2023 14:25:26 -0500 Subject: [PATCH 11/28] Update test Docker image * Update base image (which uses a newer python) * Update linters * Update other dependencies --- Makefile | 2 +- test/Dockerfile | 10 +++++----- test/conftest.py | 2 +- test/requirements.txt | 10 ++++++---- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 69fa003..5da0918 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PYTESTS = $(wildcard test/test_*.py) -IMAGE = docker.io/yadm/testbed:2022-01-07 +IMAGE = docker.io/yadm/testbed:2023-07-12 OCI = docker .PHONY: all diff --git a/test/Dockerfile b/test/Dockerfile index e6a0a97..9fb2b3a 100644 --- a/test/Dockerfile +++ b/test/Dockerfile @@ -1,9 +1,9 @@ -FROM ubuntu:18.04 +FROM ubuntu:23.04 MAINTAINER Tim Byrne # Shellcheck and esh versions -ARG SC_VER=0.8.0 -ARG ESH_VER=0.3.1 +ARG SC_VER=0.9.0 +ARG ESH_VER=0.3.2 # Install prerequisites and configure UTF-8 locale RUN \ @@ -41,8 +41,8 @@ RUN cd /opt \ # Upgrade pip3 and install requirements COPY test/requirements.txt /tmp/requirements.txt -RUN python3 -m pip install --upgrade pip setuptools \ - && python3 -m pip install --upgrade -r /tmp/requirements.txt \ +RUN python3 -m pip install --break-system-packages --upgrade pip setuptools \ + && python3 -m pip install --break-system-packages --upgrade -r /tmp/requirements.txt \ && rm -f /tmp/requirements # Install esh diff --git a/test/conftest.py b/test/conftest.py index d3bbb76..f3ce22c 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -43,7 +43,7 @@ def flake8_version(): @pytest.fixture(scope='session') def yamllint_version(): """Version of yamllint supported""" - return '1.25.0' + return '1.30.0' @pytest.fixture(scope='session') diff --git a/test/requirements.txt b/test/requirements.txt index 30da6ae..e71b349 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -1,6 +1,8 @@ +black==23.1.0 envtpl -flake8==3.8.4 +flake8==6.0.0 +isort==5.12.0 j2cli -pylint==2.6.0 -pytest==6.2.1 -yamllint==1.25.0 +pylint==2.17.0 +pytest==7.2.2 +yamllint==1.30.0 From 82ba16db34f1b8375cf3e70c490819e04e959e26 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sat, 8 Jul 2023 14:28:00 -0500 Subject: [PATCH 12/28] Support testing with newer version of Git --- test/conftest.py | 3 +++ test/test_init.py | 2 ++ test/test_upgrade.py | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/test/conftest.py b/test/conftest.py index f3ce22c..db15e4a 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -267,6 +267,9 @@ def runner(): @pytest.fixture(scope='session') def config_git(): """Configure global git configuration, if missing""" + os.system( + 'git config init.defaultBranch || ' + 'git config --global init.defaultBranch master') os.system( 'git config user.name || ' 'git config --global user.name "test"') diff --git a/test/test_init.py b/test/test_init.py index 5542332..3d149d6 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -56,6 +56,8 @@ def test_init( args.append('-f') # run init + runner(['git', 'config', '--global', 'init.defaultBranch', 'master'], + env={'HOME': home}, cwd=cwd) run = runner(yadm_cmd(*args), env={'HOME': home}, cwd=cwd) if repo_present and not force: diff --git a/test/test_upgrade.py b/test/test_upgrade.py index 2d4d697..f1a5076 100644 --- a/test/test_upgrade.py +++ b/test/test_upgrade.py @@ -22,6 +22,10 @@ def test_upgrade(tmpdir, runner, versions, submodule): # pylint: disable=too-many-statements home = tmpdir.mkdir('HOME') env = {'HOME': str(home)} + runner(['git', 'config', '--global', 'init.defaultBranch', 'master'], + env=env) + runner(['git', 'config', '--global', 'protocol.file.allow', 'always'], + env=env) if submodule: ext_repo = tmpdir.mkdir('ext_repo') From 84437894b51e4c28dac6e0e05cb2e4dbaba9b479 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sat, 8 Jul 2023 14:46:46 -0500 Subject: [PATCH 13/28] Use man.REAL --- test/test_syntax.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_syntax.py b/test/test_syntax.py index 72f4ccc..17f66ca 100644 --- a/test/test_syntax.py +++ b/test/test_syntax.py @@ -59,7 +59,7 @@ def test_yamllint(pytestconfig, runner, yamllint_version): def test_man(runner): """Check for warnings from man""" run = runner( - command=['man', '--warnings', './yadm.1']) + command=['man.REAL', '--warnings', './yadm.1']) assert run.success assert run.err == '' assert 'yadm - Yet Another Dotfiles Manager' in run.out From 9752d801ae9d4d8c0e08a4b0b98acb3ea8660e56 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sat, 8 Jul 2023 15:31:14 -0500 Subject: [PATCH 14/28] Replace deprecated "pipes" with "shlex" --- test/test_encryption.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/test_encryption.py b/test/test_encryption.py index 73f4c35..4868eb3 100644 --- a/test/test_encryption.py +++ b/test/test_encryption.py @@ -1,7 +1,7 @@ """Test encryption""" import os -import pipes +import shlex import time import pytest @@ -19,12 +19,12 @@ def add_asymmetric_key(runner, gnupg): env = os.environ.copy() env['GNUPGHOME'] = gnupg.home runner( - ['gpg', '--import', pipes.quote(KEY_FILE)], + ['gpg', '--import', shlex.quote(KEY_FILE)], env=env, shell=True, ) runner( - ['gpg', '--import-ownertrust', '<', pipes.quote(KEY_TRUST)], + ['gpg', '--import-ownertrust', '<', shlex.quote(KEY_TRUST)], env=env, shell=True, ) @@ -37,7 +37,7 @@ def remove_asymmetric_key(runner, gnupg): runner( [ 'gpg', '--batch', '--yes', - '--delete-secret-keys', pipes.quote(KEY_FINGERPRINT) + '--delete-secret-keys', shlex.quote(KEY_FINGERPRINT) ], env=env, shell=True, @@ -45,7 +45,7 @@ def remove_asymmetric_key(runner, gnupg): runner( [ 'gpg', '--batch', '--yes', - '--delete-key', pipes.quote(KEY_FINGERPRINT) + '--delete-key', shlex.quote(KEY_FINGERPRINT) ], env=env, shell=True, @@ -149,7 +149,7 @@ def decrypt_targets(tmpdir_factory, runner, gnupg): ['tar', 'cvf', '-'] + expected + ['|', 'gpg', '--batch', '--yes', '-c'] + - ['--output', pipes.quote(str(symmetric))], + ['--output', shlex.quote(str(symmetric))], cwd=tmpdir, env=env, shell=True) @@ -161,8 +161,8 @@ def decrypt_targets(tmpdir_factory, runner, gnupg): ['tar', 'cvf', '-'] + expected + ['|', 'gpg', '--batch', '--yes', '-e'] + - ['-r', pipes.quote(KEY_NAME)] + - ['--output', pipes.quote(str(asymmetric))], + ['-r', shlex.quote(KEY_NAME)] + + ['--output', shlex.quote(str(asymmetric))], cwd=tmpdir, env=env, shell=True) @@ -465,7 +465,7 @@ def encrypted_data_valid(runner, gnupg, encrypted, expected): env['GNUPGHOME'] = gnupg.home run = runner([ 'gpg', - '-d', pipes.quote(str(encrypted)), + '-d', shlex.quote(str(encrypted)), '2>/dev/null', '|', 'tar', 't'], env=env, shell=True, report=False) file_count = 0 From c8acf77e04e10437d3eb22ed7fc90f4a0ae07e00 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sat, 8 Jul 2023 15:52:41 -0500 Subject: [PATCH 15/28] Adjust pinentry mock The new test Docker image has a newer gnupg which does not behave the same way, handling invalid passwords. This type of error is simulated using an ICP error in the pinentry protocol. --- test/pinentry-mock | 18 +++++++++++++----- test/test_encryption.py | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/test/pinentry-mock b/test/pinentry-mock index d40033b..39da043 100755 --- a/test/pinentry-mock +++ b/test/pinentry-mock @@ -1,12 +1,20 @@ #!/bin/bash # This program is a custom mock pinentry program -# It always uses whatever password is found in the /tmp directory -password="$(cat /tmp/mock-password 2>/dev/null)" +# It uses whatever password is found in the /tmp directory +# If the password is empty, replies CANCEL causing an error to similate invalid +# credentials echo "OK Pleased to meet you" while read -r line; do if [[ $line =~ GETPIN ]]; then - echo -n "D " - echo "$password" + password="$(cat /tmp/mock-password 2>/dev/null)" + if [ -n "$password" ]; then + echo -n "D " + echo "$password" + echo "OK"; + else + echo "CANCEL"; + fi + else + echo "OK"; fi - echo "OK"; done diff --git a/test/test_encryption.py b/test/test_encryption.py index 4868eb3..829ca1b 100644 --- a/test/test_encryption.py +++ b/test/test_encryption.py @@ -214,7 +214,7 @@ def test_symmetric_encrypt( if missing_encrypt: assert 'does not exist' in run.err elif bad_phrase: - assert 'Invalid passphrase' in run.err + assert 'Invalid IPC' in run.err else: assert encrypted_data_valid( runner, gnupg, paths.archive, encrypt_targets) From f9e03683858a91c94271756c7f0e5973eae841a8 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sun, 9 Jul 2023 10:32:52 -0500 Subject: [PATCH 16/28] Changes for new shellcheck compliance --- test/conftest.py | 2 +- yadm | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index db15e4a..1490d55 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -25,7 +25,7 @@ def pytest_addoption(parser): @pytest.fixture(scope='session') def shellcheck_version(): """Version of shellcheck supported""" - return '0.8.0' + return '0.9.0' @pytest.fixture(scope='session') diff --git a/yadm b/yadm index 003d954..09da278 100755 --- a/yadm +++ b/yadm @@ -289,18 +289,18 @@ function record_score() { # increase the index of any existing alt_template_cmds new_cmds=() for cmd_index in "${!alt_template_cmds[@]}"; do - new_cmds[$((cmd_index+1))]="${alt_template_cmds[$cmd_index]}" + new_cmds[cmd_index+1]="${alt_template_cmds[$cmd_index]}" done alt_template_cmds=() for cmd_index in "${!new_cmds[@]}"; do - alt_template_cmds[$cmd_index]="${new_cmds[$cmd_index]}" + alt_template_cmds[cmd_index]="${new_cmds[$cmd_index]}" done else alt_targets+=("$tgt") # set index to the last index (newly created one) for index in "${!alt_targets[@]}"; do :; done # and set its initial score to zero - alt_scores[$index]=0 + alt_scores[index]=0 fi fi @@ -309,8 +309,8 @@ function record_score() { # record higher scoring sources if [ "$score" -gt "${alt_scores[$index]}" ]; then - alt_scores[$index]="$score" - alt_sources[$index]="$src" + alt_scores[index]="$score" + alt_sources[index]="$src" fi } @@ -336,8 +336,8 @@ function record_template() { fi # record the template command, last one wins - alt_template_cmds[$index]="$cmd" - alt_sources[$index]="$src" + alt_template_cmds[index]="$cmd" + alt_sources[index]="$src" } From 8a87f4a30ffaa983562cefe64c8b79cb8f8a8002 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sun, 9 Jul 2023 11:13:13 -0500 Subject: [PATCH 17/28] Changes for new pylint compliance --- test/conftest.py | 42 +++++++++++++++++++----------------- test/test_syntax.py | 2 +- test/test_unit_score_file.py | 12 +++++------ test/test_upgrade.py | 2 +- test/test_version.py | 5 ++--- test/utils.py | 2 +- 6 files changed, 33 insertions(+), 32 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 1490d55..7ccd9be 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -3,10 +3,10 @@ import collections import contextlib import copy -import distutils.dir_util # pylint: disable=no-name-in-module,import-error import os import platform import pwd +import shutil from subprocess import Popen, PIPE import py import pytest @@ -31,7 +31,7 @@ def shellcheck_version(): @pytest.fixture(scope='session') def pylint_version(): """Version of pylint supported""" - return '2.6.0' + return '2.17.0' @pytest.fixture(scope='session') @@ -204,7 +204,7 @@ class Runner(): merged_env.update(env) self.inp = inp self.wrap(expect) - process = Popen( + with Popen( self.command, stdin=PIPE, stdout=PIPE, @@ -212,14 +212,14 @@ class Runner(): shell=shell, cwd=cwd, env=merged_env, - ) - input_bytes = self.inp - if self.inp: - input_bytes = self.inp.encode() - (out_bstream, err_bstream) = process.communicate(input=input_bytes) - self.out = out_bstream.decode() - self.err = err_bstream.decode() - self.code = process.wait() + ) as process: + input_bytes = self.inp + if self.inp: + input_bytes = self.inp.encode() + (out_bstream, err_bstream) = process.communicate(input=input_bytes) + self.out = out_bstream.decode() + self.err = err_bstream.decode() + self.code = process.wait() self.success = self.code == 0 self.failure = self.code != 0 if report: @@ -365,6 +365,10 @@ def yadm_cmd(paths): return command_list +class NoRelativePath(Exception): + """Exception when finding relative paths""" + + class DataFile(): """Datafile object""" @@ -384,7 +388,7 @@ class DataFile(): """Relative path property""" if self.__parent: return self.__parent.join(self.path) - raise BaseException('Unable to provide relative path, no parent') + raise NoRelativePath('Unable to provide relative path, no parent') @property def tracked(self): @@ -405,10 +409,10 @@ class DataSet(): """Dataset object""" def __init__(self): - self.__files = list() - self.__dirs = list() - self.__tracked_dirs = list() - self.__private_dirs = list() + self.__files = [] + self.__dirs = [] + self.__tracked_dirs = [] + self.__private_dirs = [] self.__relpath = None def __repr__(self): @@ -562,15 +566,13 @@ def ds1_data(tmpdir_factory, config_git, ds1_dset, runner): @pytest.fixture() def ds1_work_copy(ds1_data, paths): """Function scoped copy of ds1_data.work""" - distutils.dir_util.copy_tree( # pylint: disable=no-member - str(ds1_data.work), str(paths.work)) + shutil.copytree(str(ds1_data.work), str(paths.work), dirs_exist_ok=True) @pytest.fixture() def ds1_repo_copy(runner, ds1_data, paths): """Function scoped copy of ds1_data.repo""" - distutils.dir_util.copy_tree( # pylint: disable=no-member - str(ds1_data.repo), str(paths.repo)) + shutil.copytree(str(ds1_data.repo), str(paths.repo), dirs_exist_ok=True) env = os.environ.copy() env['GIT_DIR'] = str(paths.repo) runner( diff --git a/test/test_syntax.py b/test/test_syntax.py index 17f66ca..1a422b3 100644 --- a/test/test_syntax.py +++ b/test/test_syntax.py @@ -26,7 +26,7 @@ def test_pylint(pytestconfig, runner, pylint_version): run = runner(command=['pylint', '--version'], report=False) if f'pylint {pylint_version}' not in run.out: pytest.skip('Unsupported pylint version') - pyfiles = list() + pyfiles = [] for tfile in os.listdir('test'): if tfile.endswith('.py'): pyfiles.append(f'test/{tfile}') diff --git a/test/test_unit_score_file.py b/test/test_unit_score_file.py index e59c128..dd2f0b7 100644 --- a/test/test_unit_score_file.py +++ b/test/test_unit_score_file.py @@ -213,14 +213,14 @@ def test_score_values( local_user={local_user} """ expected = '' - for filename in filenames: + for filename, score in filenames.items(): script += f""" score_file "{filename}" echo "{filename}" echo "$score" """ expected += filename + '\n' - expected += str(filenames[filename]) + '\n' + expected += str(score) + '\n' run = runner(command=['bash'], inp=script) assert run.success assert run.err == '' @@ -278,14 +278,14 @@ def test_score_values_templates(runner, yadm): local_user={local_user} """ expected = '' - for filename in filenames: + for filename, score in filenames.items(): script += f""" score_file "{filename}" echo "{filename}" echo "$score" """ expected += filename + '\n' - expected += str(filenames[filename]) + '\n' + expected += str(score) + '\n' run = runner(command=['bash'], inp=script) assert run.success assert run.err == '' @@ -337,14 +337,14 @@ def test_underscores_in_distro_and_family(runner, yadm): local_distro_family="{local_distro_family}" """ expected = '' - for filename in filenames: + for filename, score in filenames.items(): script += f""" score_file "{filename}" echo "{filename}" echo "$score" """ expected += filename + '\n' - expected += str(filenames[filename]) + '\n' + expected += str(score) + '\n' run = runner(command=['bash'], inp=script) assert run.success assert run.err == '' diff --git a/test/test_upgrade.py b/test/test_upgrade.py index f1a5076..77b7e4a 100644 --- a/test/test_upgrade.py +++ b/test/test_upgrade.py @@ -39,7 +39,7 @@ def test_upgrade(tmpdir, runner, versions, submodule): os.environ.pop('XDG_DATA_HOME', None) def run_version(version, *args, check_stderr=True): - yadm = 'yadm-%s' % version if version else '/yadm/yadm' + yadm = f'yadm-{version}' if version else '/yadm/yadm' run = runner([yadm, *args], shell=True, cwd=str(home), env=env) assert run.success if check_stderr: diff --git a/test/test_version.py b/test/test_version.py index d440d3b..5655e47 100644 --- a/test/test_version.py +++ b/test/test_version.py @@ -10,9 +10,8 @@ def expected_version(yadm): Expected semantic version number. This is taken directly out of yadm, searching for the VERSION= string. """ - yadm_version = re.findall( - r'VERSION=([^\n]+)', - open(yadm).read()) + with open(yadm, encoding='utf-8') as source_file: + yadm_version = re.findall(r'VERSION=([^\n]+)', source_file.read()) if yadm_version: return yadm_version[0] pytest.fail(f'version not found in {yadm}') diff --git a/test/utils.py b/test/utils.py index 67a9e53..6cbe066 100644 --- a/test/utils.py +++ b/test/utils.py @@ -81,7 +81,7 @@ def parse_alt_output(output, linked=True): regex = r'Creating (.+) from template (.+)$' if linked: regex = r'Linking (.+) to (.+)$' - parsed_list = dict() + parsed_list = {} for line in output.splitlines(): match = re.match(regex, line) if match: From bbc1ca17f09963fe5f5241834c6c3b930736125c Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sun, 9 Jul 2023 11:15:38 -0500 Subject: [PATCH 18/28] Changes for new flake8 compliance --- test/conftest.py | 2 +- test/test_config.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 7ccd9be..5e366b4 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -37,7 +37,7 @@ def pylint_version(): @pytest.fixture(scope='session') def flake8_version(): """Version of flake8 supported""" - return '3.8.4' + return '6.0.0' @pytest.fixture(scope='session') diff --git a/test/test_config.py b/test/test_config.py index d364128..19b0d02 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -140,7 +140,7 @@ def test_config_local_write(runner, yadm_cmd, paths, supported_local_configs): def test_config_without_parent_directory(runner, yadm_cmd, paths): - """Write and read attribute to/from config file with non-existent parent dir + """Write/read attribute to/from config file with non-existent parent dir Update configuration file Display value From 7af2af52aec33d7309aa5f1c70a82749d524e7fb Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Mon, 10 Jul 2023 09:14:33 -0500 Subject: [PATCH 19/28] Lint using isort --- test/conftest.py | 9 ++++++++- test/test_alt.py | 1 + test/test_alt_copy.py | 1 + test/test_assert_private_dirs.py | 1 + test/test_clone.py | 1 + test/test_config.py | 1 + test/test_encryption.py | 1 + test/test_enter.py | 1 + test/test_git.py | 1 + test/test_list.py | 1 + test/test_perms.py | 1 + test/test_syntax.py | 11 +++++++++++ test/test_unit_copy_perms.py | 1 + test/test_unit_remove_stale_links.py | 1 + test/test_unit_template_j2.py | 1 + test/test_unit_x_program.py | 1 + test/test_upgrade.py | 1 + test/test_version.py | 1 + test/utils.py | 2 +- 19 files changed, 36 insertions(+), 2 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 5e366b4..af68477 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -7,7 +7,8 @@ import os import platform import pwd import shutil -from subprocess import Popen, PIPE +from subprocess import PIPE, Popen + import py import pytest @@ -34,6 +35,12 @@ def pylint_version(): return '2.17.0' +@pytest.fixture(scope='session') +def isort_version(): + """Version of isort supported""" + return '5.12.0' + + @pytest.fixture(scope='session') def flake8_version(): """Version of flake8 supported""" diff --git a/test/test_alt.py b/test/test_alt.py index ddaf374..e57caee 100644 --- a/test/test_alt.py +++ b/test/test_alt.py @@ -1,6 +1,7 @@ """Test alt""" import os import string + import py import pytest import utils diff --git a/test/test_alt_copy.py b/test/test_alt_copy.py index fa8e09c..eb19e3d 100644 --- a/test/test_alt_copy.py +++ b/test/test_alt_copy.py @@ -1,6 +1,7 @@ """Test yadm.alt-copy""" import os + import pytest diff --git a/test/test_assert_private_dirs.py b/test/test_assert_private_dirs.py index bfd55ac..440446b 100644 --- a/test/test_assert_private_dirs.py +++ b/test/test_assert_private_dirs.py @@ -2,6 +2,7 @@ import os import re + import pytest pytestmark = pytest.mark.usefixtures('ds1_copy') diff --git a/test/test_clone.py b/test/test_clone.py index d5da7d9..9268965 100644 --- a/test/test_clone.py +++ b/test/test_clone.py @@ -2,6 +2,7 @@ import os import re + import pytest BOOTSTRAP_CODE = 123 diff --git a/test/test_config.py b/test/test_config.py index 19b0d02..d6a5e33 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -1,6 +1,7 @@ """Test config""" import os + import pytest TEST_SECTION = 'test' diff --git a/test/test_encryption.py b/test/test_encryption.py index 829ca1b..78bbf3a 100644 --- a/test/test_encryption.py +++ b/test/test_encryption.py @@ -3,6 +3,7 @@ import os import shlex import time + import pytest KEY_FILE = 'test/test_key' diff --git a/test/test_enter.py b/test/test_enter.py index f5ea2d8..5148e23 100644 --- a/test/test_enter.py +++ b/test/test_enter.py @@ -1,6 +1,7 @@ """Test enter""" import os + import pytest diff --git a/test/test_git.py b/test/test_git.py index 76eccab..41b102c 100644 --- a/test/test_git.py +++ b/test/test_git.py @@ -1,6 +1,7 @@ """Test git""" import re + import pytest diff --git a/test/test_list.py b/test/test_list.py index dcfe500..d7d09a6 100644 --- a/test/test_list.py +++ b/test/test_list.py @@ -1,6 +1,7 @@ """Test list""" import os + import pytest diff --git a/test/test_perms.py b/test/test_perms.py index 4f052bd..a49d897 100644 --- a/test/test_perms.py +++ b/test/test_perms.py @@ -1,6 +1,7 @@ """Test perms""" import os + import pytest diff --git a/test/test_syntax.py b/test/test_syntax.py index 1a422b3..8fae79e 100644 --- a/test/test_syntax.py +++ b/test/test_syntax.py @@ -1,6 +1,7 @@ """Syntax checks""" import os + import pytest @@ -34,6 +35,16 @@ def test_pylint(pytestconfig, runner, pylint_version): 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 + + def test_flake8(pytestconfig, runner, flake8_version): """Passes flake8""" if not pytestconfig.getoption("--force-linters"): diff --git a/test/test_unit_copy_perms.py b/test/test_unit_copy_perms.py index 3c79768..b043878 100644 --- a/test/test_unit_copy_perms.py +++ b/test/test_unit_copy_perms.py @@ -1,5 +1,6 @@ """Unit tests: copy_perms""" import os + import pytest OCTAL = '7654' diff --git a/test/test_unit_remove_stale_links.py b/test/test_unit_remove_stale_links.py index 0bd960b..4fcf1a1 100644 --- a/test/test_unit_remove_stale_links.py +++ b/test/test_unit_remove_stale_links.py @@ -1,5 +1,6 @@ """Unit tests: remove_stale_links""" import os + import pytest diff --git a/test/test_unit_template_j2.py b/test/test_unit_template_j2.py index 4042a2d..84afc2d 100644 --- a/test/test_unit_template_j2.py +++ b/test/test_unit_template_j2.py @@ -1,5 +1,6 @@ """Unit tests: template_j2cli & template_envtpl""" import os + import pytest FILE_MODE = 0o754 diff --git a/test/test_unit_x_program.py b/test/test_unit_x_program.py index 8302f3c..883c9af 100644 --- a/test/test_unit_x_program.py +++ b/test/test_unit_x_program.py @@ -1,6 +1,7 @@ """Unit tests: yadm.[git,gpg]-program""" import os + import pytest diff --git a/test/test_upgrade.py b/test/test_upgrade.py index 77b7e4a..1805882 100644 --- a/test/test_upgrade.py +++ b/test/test_upgrade.py @@ -1,6 +1,7 @@ """Test upgrade""" import os + import pytest diff --git a/test/test_version.py b/test/test_version.py index 5655e47..aee6f33 100644 --- a/test/test_version.py +++ b/test/test_version.py @@ -1,6 +1,7 @@ """Test version""" import re + import pytest diff --git a/test/utils.py b/test/utils.py index 6cbe066..f71a758 100644 --- a/test/utils.py +++ b/test/utils.py @@ -3,8 +3,8 @@ This module holds values/functions common to multiple tests. """ -import re import os +import re ALT_FILE1 = 'test_alt' ALT_FILE2 = 'test alt/test alt' From 1b36bf2bb64539cf0c74af94e6cfcf5c087610fd Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Mon, 10 Jul 2023 13:58:02 -0500 Subject: [PATCH 20/28] Move all test/linting configs into pyproject.toml Except for flake8 which doesn't support pyproject.toml yet. --- .flake8 | 2 ++ pylintrc | 17 ----------------- pyproject.toml | 31 +++++++++++++++++++++++++++++++ pytest.ini | 5 ----- test/pylintrc | 1 - 5 files changed, 33 insertions(+), 23 deletions(-) create mode 100644 .flake8 delete mode 100644 pylintrc create mode 100644 pyproject.toml delete mode 100644 pytest.ini delete mode 120000 test/pylintrc diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..6deafc2 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 120 diff --git a/pylintrc b/pylintrc deleted file mode 100644 index ba41b74..0000000 --- a/pylintrc +++ /dev/null @@ -1,17 +0,0 @@ -[BASIC] -good-names=pytestmark - -[DESIGN] -max-args=14 -max-locals=28 -max-attributes=8 -max-statements=65 - -[SIMILARITIES] -min-similarity-lines=8 - -[MESSAGES CONTROL] -disable=redefined-outer-name - -[TYPECHECK] -ignored-modules=py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..1e51d44 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,31 @@ +[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" diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index d032ea5..0000000 --- a/pytest.ini +++ /dev/null @@ -1,5 +0,0 @@ -[pytest] -cache_dir = /tmp -addopts = -ra -markers = - deprecated: marks tests for deprecated features (deselect with '-m "not deprecated"') diff --git a/test/pylintrc b/test/pylintrc deleted file mode 120000 index 05334af..0000000 --- a/test/pylintrc +++ /dev/null @@ -1 +0,0 @@ -../pylintrc \ No newline at end of file From e70417520124970b65bb469836180cd8be6f57d8 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Mon, 10 Jul 2023 09:18:36 -0500 Subject: [PATCH 21/28] Add black to linting --- test/conftest.py | 6 ++++++ test/test_syntax.py | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/test/conftest.py b/test/conftest.py index af68477..e05d5dc 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -47,6 +47,12 @@ def flake8_version(): return '6.0.0' +@pytest.fixture(scope='session') +def black_version(): + """Version of black supported""" + return '23.1.0' + + @pytest.fixture(scope='session') def yamllint_version(): """Version of yamllint supported""" diff --git a/test/test_syntax.py b/test/test_syntax.py index 8fae79e..aadafde 100644 --- a/test/test_syntax.py +++ b/test/test_syntax.py @@ -55,6 +55,16 @@ def test_flake8(pytestconfig, runner, flake8_version): 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 + + def test_yamllint(pytestconfig, runner, yamllint_version): """Passes yamllint""" if not pytestconfig.getoption("--force-linters"): From 76ce3defea0e69c3e8130e7cfdcd22b64322aea2 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Mon, 10 Jul 2023 14:43:17 -0500 Subject: [PATCH 22/28] Changes for black compliance --- test/conftest.py | 406 +++++++++----------- test/test_alt.py | 226 +++++------ test/test_alt_copy.py | 44 +-- test/test_assert_private_dirs.py | 46 +-- test/test_bootstrap.py | 38 +- test/test_clean.py | 6 +- test/test_clone.py | 282 +++++++------- test/test_config.py | 81 ++-- test/test_encryption.py | 373 ++++++++---------- test/test_enter.py | 140 +++---- test/test_ext_crypt.py | 36 +- test/test_git.py | 40 +- test/test_help.py | 10 +- test/test_hooks.py | 129 +++---- test/test_init.py | 66 ++-- test/test_introspect.py | 40 +- test/test_list.py | 38 +- test/test_perms.py | 86 ++--- test/test_syntax.py | 57 ++- test/test_unit_bootstrap_available.py | 10 +- test/test_unit_choose_template_cmd.py | 48 +-- test/test_unit_configure_paths.py | 94 ++--- test/test_unit_copy_perms.py | 34 +- test/test_unit_encryption.py | 80 ++-- test/test_unit_exclude_encrypted.py | 60 ++- test/test_unit_issue_legacy_path_warning.py | 33 +- test/test_unit_parse_encrypt.py | 177 +++++---- test/test_unit_private_dirs.py | 20 +- test/test_unit_query_distro.py | 28 +- test/test_unit_query_distro_family.py | 22 +- test/test_unit_record_score.py | 86 ++--- test/test_unit_record_template.py | 24 +- test/test_unit_relative_path.py | 6 +- test/test_unit_remove_stale_links.py | 28 +- test/test_unit_report_invalid_alts.py | 20 +- test/test_unit_score_file.py | 321 +++++++--------- test/test_unit_set_local_alt_values.py | 62 +-- test/test_unit_set_os.py | 38 +- test/test_unit_set_yadm_dir.py | 55 ++- test/test_unit_template_default.py | 74 ++-- test/test_unit_template_esh.py | 29 +- test/test_unit_template_j2.py | 33 +- test/test_unit_upgrade.py | 103 +++-- test/test_unit_x_program.py | 35 +- test/test_upgrade.py | 137 ++++--- test/test_version.py | 26 +- test/utils.py | 56 +-- 47 files changed, 1845 insertions(+), 2038 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index e05d5dc..b18cae4 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -23,169 +23,168 @@ def pytest_addoption(parser): ) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def shellcheck_version(): """Version of shellcheck supported""" - return '0.9.0' + return "0.9.0" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def pylint_version(): """Version of pylint supported""" - return '2.17.0' + return "2.17.0" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def isort_version(): """Version of isort supported""" - return '5.12.0' + return "5.12.0" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def flake8_version(): """Version of flake8 supported""" - return '6.0.0' + return "6.0.0" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def black_version(): """Version of black supported""" - return '23.1.0' + return "23.1.0" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def yamllint_version(): """Version of yamllint supported""" - return '1.30.0' + return "1.30.0" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def tst_user(): """Test session's user id""" return pwd.getpwuid(os.getuid()).pw_name -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def tst_host(): """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): """Test session's distro""" - distro = '' + distro = "" with contextlib.suppress(Exception): - run = runner(command=['lsb_release', '-si'], report=False) + run = runner(command=["lsb_release", "-si"], report=False) distro = run.out.strip() return distro -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def tst_distro_family(runner): """Test session's distro_family""" - family = '' + family = "" with contextlib.suppress(Exception): - run = runner(command=[ - 'grep', '-oP', r'ID_LIKE=\K.+', '/etc/os-release'], report=False) + run = runner(command=["grep", "-oP", r"ID_LIKE=\K.+", "/etc/os-release"], report=False) family = run.out.strip() return family -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def tst_sys(): """Test session's uname value""" 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') +@pytest.fixture(scope="session") def supported_commands(): """List of supported commands This list should be updated every time yadm learns a new command. """ return [ - 'alt', - 'bootstrap', - 'clean', - 'clone', - 'config', - 'decrypt', - 'encrypt', - 'enter', - 'git-crypt', - 'gitconfig', - 'help', - 'init', - 'introspect', - 'list', - 'perms', - 'transcrypt', - 'upgrade', - 'version', - ] + "alt", + "bootstrap", + "clean", + "clone", + "config", + "decrypt", + "encrypt", + "enter", + "git-crypt", + "gitconfig", + "help", + "init", + "introspect", + "list", + "perms", + "transcrypt", + "upgrade", + "version", + ] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def supported_configs(): """List of supported config options This list should be updated every time yadm learns a new config. """ return [ - 'local.arch', - 'local.class', - 'local.hostname', - 'local.os', - 'local.user', - 'yadm.alt-copy', - 'yadm.auto-alt', - 'yadm.auto-exclude', - 'yadm.auto-perms', - 'yadm.auto-private-dirs', - 'yadm.cipher', - 'yadm.git-program', - 'yadm.gpg-perms', - 'yadm.gpg-program', - 'yadm.gpg-recipient', - 'yadm.openssl-ciphername', - 'yadm.openssl-old', - 'yadm.openssl-program', - 'yadm.ssh-perms', - ] + "local.arch", + "local.class", + "local.hostname", + "local.os", + "local.user", + "yadm.alt-copy", + "yadm.auto-alt", + "yadm.auto-exclude", + "yadm.auto-perms", + "yadm.auto-private-dirs", + "yadm.cipher", + "yadm.git-program", + "yadm.gpg-perms", + "yadm.gpg-program", + "yadm.gpg-recipient", + "yadm.openssl-ciphername", + "yadm.openssl-old", + "yadm.openssl-program", + "yadm.ssh-perms", + ] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def supported_switches(): """List of supported switches This list should be updated every time yadm learns a new switch. """ return [ - '--yadm-archive', - '--yadm-bootstrap', - '--yadm-config', - '--yadm-data', - '--yadm-dir', - '--yadm-encrypt', - '--yadm-repo', - '-Y', - ] + "--yadm-archive", + "--yadm-bootstrap", + "--yadm-config", + "--yadm-data", + "--yadm-dir", + "--yadm-encrypt", + "--yadm-repo", + "-Y", + ] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def supported_local_configs(supported_configs): """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 Within yadm tests, this object should be used when running commands that @@ -198,17 +197,9 @@ class Runner(): 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: - self.command = ' '.join([str(cmd) for cmd in command]) + self.command = " ".join([str(cmd) for cmd in command]) else: self.command = command if env is None: @@ -239,56 +230,43 @@ class Runner(): self.report() def __repr__(self): - return f'Runner({self.command})' + return f"Runner({self.command})" def report(self): """Print code/stdout/stderr""" - print(f'{self}') - print(f' RUN: code:{self.code}') + print(f"{self}") + print(f" RUN: code:{self.code}") if self.inp: - print(f' RUN: input:\n{self.inp}') - print(f' RUN: stdout:\n{self.out}') - print(f' RUN: stderr:\n{self.err}') + print(f" RUN: input:\n{self.inp}") + print(f" RUN: stdout:\n{self.out}") + print(f" RUN: stderr:\n{self.err}") def wrap(self, expect): """Wrap command with expect""" if not expect: return - cmdline = ' '.join([f'"{w}"' for w in self.command]) - expect_script = f'set timeout 2\nspawn {cmdline}\n' + cmdline = " ".join([f'"{w}"' for w in self.command]) + expect_script = f"set timeout 2\nspawn {cmdline}\n" for question, answer in expect: - expect_script += ( - 'expect {\n' - f'"{question}" {{send "{answer}\\r"}}\n' - 'timeout {close;exit 128}\n' - '}\n') - expect_script += ( - 'expect eof\n' - 'foreach {pid spawnid os_error_flag value} [wait] break\n' - 'exit $value') + expect_script += "expect {\n" f'"{question}" {{send "{answer}\\r"}}\n' "timeout {close;exit 128}\n" "}\n" + expect_script += "expect eof\n" "foreach {pid spawnid os_error_flag value} [wait] break\n" "exit $value" self.inp = expect_script - print(f'EXPECT:{expect_script}') - self.command = ['expect'] + print(f"EXPECT:{expect_script}") + self.command = ["expect"] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def runner(): """Class for running commands""" return Runner -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def config_git(): """Configure global git configuration, if missing""" - os.system( - 'git config init.defaultBranch || ' - 'git config --global init.defaultBranch master') - os.system( - 'git config user.name || ' - 'git config --global user.name "test"') - os.system( - 'git config user.email || ' - 'git config --global user.email "test@test.test"') + os.system("git config init.defaultBranch || git config --global init.defaultBranch master") + os.system('git config user.name || git config --global user.name "test"') + os.system('git config user.email || git config --global user.email "test@test.test"') @pytest.fixture() @@ -298,19 +276,19 @@ def repo_config(runner, paths): def query_func(key): """Query a yadm repo configuration value""" run = runner( - command=('git', 'config', '--local', key), - env={'GIT_DIR': paths.repo}, + command=("git", "config", "--local", key), + env={"GIT_DIR": paths.repo}, report=False, - ) + ) return run.out.rstrip() return query_func -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def yadm(): """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" return full_path @@ -319,38 +297,40 @@ def yadm(): def paths(tmpdir, yadm): """Function scoped test paths""" - dir_root = tmpdir.mkdir('root') - dir_remote = dir_root.mkdir('remote') - dir_work = dir_root.mkdir('work') - dir_xdg_data = dir_root.mkdir('xdg_data') - dir_xdg_home = dir_root.mkdir('xdg_home') - dir_data = dir_xdg_data.mkdir('yadm') - dir_yadm = dir_xdg_home.mkdir('yadm') - dir_hooks = dir_yadm.mkdir('hooks') - dir_repo = dir_data.mkdir('repo.git') - file_archive = dir_data.join('archive') - file_bootstrap = dir_yadm.join('bootstrap') - file_config = dir_yadm.join('config') - file_encrypt = dir_yadm.join('encrypt') + dir_root = tmpdir.mkdir("root") + dir_remote = dir_root.mkdir("remote") + dir_work = dir_root.mkdir("work") + dir_xdg_data = dir_root.mkdir("xdg_data") + dir_xdg_home = dir_root.mkdir("xdg_home") + dir_data = dir_xdg_data.mkdir("yadm") + dir_yadm = dir_xdg_home.mkdir("yadm") + dir_hooks = dir_yadm.mkdir("hooks") + dir_repo = dir_data.mkdir("repo.git") + file_archive = dir_data.join("archive") + file_bootstrap = dir_yadm.join("bootstrap") + file_config = dir_yadm.join("config") + file_encrypt = dir_yadm.join("encrypt") paths = collections.namedtuple( - 'Paths', [ - 'pgm', - 'root', - 'remote', - 'work', - 'xdg_data', - 'xdg_home', - 'data', - 'yadm', - 'hooks', - 'repo', - 'archive', - 'bootstrap', - 'config', - 'encrypt', - ]) - os.environ['XDG_CONFIG_HOME'] = str(dir_xdg_home) - os.environ['XDG_DATA_HOME'] = str(dir_xdg_data) + "Paths", + [ + "pgm", + "root", + "remote", + "work", + "xdg_data", + "xdg_home", + "data", + "yadm", + "hooks", + "repo", + "archive", + "bootstrap", + "config", + "encrypt", + ], + ) + os.environ["XDG_CONFIG_HOME"] = str(dir_xdg_home) + os.environ["XDG_DATA_HOME"] = str(dir_xdg_data) return paths( yadm, dir_root, @@ -366,15 +346,17 @@ def paths(tmpdir, yadm): file_bootstrap, file_config, file_encrypt, - ) + ) @pytest.fixture() def yadm_cmd(paths): """Generate custom command_list function""" + def command_list(*args): """Produce params for running yadm with -Y""" return [paths.pgm] + list(args) + return command_list @@ -382,7 +364,7 @@ class NoRelativePath(Exception): """Exception when finding relative paths""" -class DataFile(): +class DataFile: """Datafile object""" def __init__(self, path, tracked=True, private=False): @@ -401,7 +383,7 @@ class DataFile(): """Relative path property""" if self.__parent: return self.__parent.join(self.path) - raise NoRelativePath('Unable to provide relative path, no parent') + raise NoRelativePath("Unable to provide relative path, no parent") @property def tracked(self): @@ -418,7 +400,7 @@ class DataFile(): self.__parent = parent -class DataSet(): +class DataSet: """Dataset object""" def __init__(self): @@ -429,11 +411,7 @@ class DataSet(): self.__relpath = None 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): return iter(self.__files) @@ -471,17 +449,17 @@ class DataSet(): @property def plain_dirs(self): """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 def hidden_dirs(self): """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 def tracked_dirs(self): """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 def private_dirs(self): @@ -511,23 +489,23 @@ class DataSet(): datafile.relative_to(self.__relpath) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def ds1_dset(tst_sys): """Meta-data for dataset one files""" dset = DataSet() - dset.add_file('t1') - dset.add_file('d1/t2') - dset.add_file(f'test_alt_copy##os.{tst_sys}') - dset.add_file('u1', tracked=False) - dset.add_file('d2/u2', tracked=False) - dset.add_file('.ssh/p1', 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/.p4', tracked=False, private=True) + dset.add_file("t1") + dset.add_file("d1/t2") + dset.add_file(f"test_alt_copy##os.{tst_sys}") + dset.add_file("u1", tracked=False) + dset.add_file("d2/u2", tracked=False) + dset.add_file(".ssh/p1", 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/.p4", tracked=False, private=True) return dset -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def ds1_data(tmpdir_factory, config_git, ds1_dset, runner): """A set of test data, worktree & repo""" # pylint: disable=unused-argument @@ -535,44 +513,24 @@ def ds1_data(tmpdir_factory, config_git, ds1_dset, runner): # @pytest.mark.usefixtures('config_git') # 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: work.join(datafile.path).write(datafile.path, ensure=True) - repo = data.mkdir('repo.git') + repo = data.mkdir("repo.git") env = os.environ.copy() - env['GIT_DIR'] = str(repo) - runner( - command=['git', 'init', '--shared=0600', '--bare', str(repo)], - report=False) - runner( - command=['git', 'config', 'core.bare', 'false'], - env=env, - 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) + env["GIT_DIR"] = str(repo) + runner(command=["git", "init", "--shared=0600", "--bare", str(repo)], report=False) + runner(command=["git", "config", "core.bare", "false"], env=env, 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) @@ -587,11 +545,8 @@ def ds1_repo_copy(runner, ds1_data, paths): """Function scoped copy of ds1_data.repo""" shutil.copytree(str(ds1_data.repo), str(paths.repo), dirs_exist_ok=True) env = os.environ.copy() - env['GIT_DIR'] = str(paths.repo) - runner( - command=['git', 'config', 'core.worktree', str(paths.work)], - env=env, - report=False) + env["GIT_DIR"] = str(paths.repo) + runner(command=["git", "config", "core.worktree", str(paths.work)], env=env, report=False) @pytest.fixture() @@ -616,30 +571,27 @@ def ds1(ds1_work_copy, paths, ds1_dset): return dscopy -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def gnupg(tmpdir_factory, runner): """Location of GNUPGHOME""" def register_gpg_password(password): """Publish a new GPG mock password""" - py.path.local('/tmp/mock-password').write(password) + py.path.local("/tmp/mock-password").write(password) - home = tmpdir_factory.mktemp('gnupghome') + home = tmpdir_factory.mktemp("gnupghome") home.chmod(0o700) - conf = home.join('gpg.conf') - conf.write('no-secmem-warning\n') + conf = home.join("gpg.conf") + conf.write("no-secmem-warning\n") conf.chmod(0o600) - agentconf = home.join('gpg-agent.conf') - agentconf.write( - f'pinentry-program {os.path.abspath("test/pinentry-mock")}\n' - 'max-cache-ttl 0\n' - ) + agentconf = home.join("gpg-agent.conf") + agentconf.write(f'pinentry-program {os.path.abspath("test/pinentry-mock")}\n' "max-cache-ttl 0\n") agentconf.chmod(0o600) - data = collections.namedtuple('GNUPG', ['home', 'pw']) + data = collections.namedtuple("GNUPG", ["home", "pw"]) env = os.environ.copy() - env['GNUPGHOME'] = home + env["GNUPGHOME"] = home # this pre-populates std files in the GNUPGHOME - runner(['gpg', '-k'], env=env) + runner(["gpg", "-k"], env=env) return data(home, register_gpg_password) diff --git a/test/test_alt.py b/test/test_alt.py index e57caee..c429ad4 100644 --- a/test/test_alt.py +++ b/test/test_alt.py @@ -9,34 +9,34 @@ import utils TEST_PATHS = [utils.ALT_FILE1, utils.ALT_FILE2, utils.ALT_DIR] -@pytest.mark.usefixtures('ds1_copy') -@pytest.mark.parametrize('yadm_alt', [True, False], ids=['alt', 'worktree']) +@pytest.mark.usefixtures("ds1_copy") +@pytest.mark.parametrize("yadm_alt", [True, False], ids=["alt", "worktree"]) @pytest.mark.parametrize( - 'tracked,encrypt,exclude', [ + "tracked,encrypt,exclude", + [ (False, False, False), (True, False, False), (False, True, False), (False, True, True), - ], ids=['untracked', 'tracked', 'encrypted', 'excluded']) -def test_alt_source( - runner, paths, - tracked, encrypt, exclude, - yadm_alt): + ], + ids=["untracked", "tracked", "encrypted", "excluded"], +) +def test_alt_source(runner, paths, tracked, encrypt, exclude, yadm_alt): """Test yadm alt operates on all expected sources of alternates""" yadm_dir, yadm_data = setup_standard_yadm_dir(paths) utils.create_alt_files( - 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']) + 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"]) assert run.success - assert run.err == '' + assert run.err == "" 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: - source_file_content = link_path + '##default' + source_file_content = link_path + "##default" source_file = basepath.join(source_file_content) link_file = paths.work.join(link_path) if tracked or (encrypt and not exclude): @@ -46,63 +46,69 @@ def test_alt_source( assert link_file.read() == source_file_content assert str(source_file) in linked 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 else: assert not link_file.exists() assert str(source_file) not in linked -@pytest.mark.usefixtures('ds1_copy') -@pytest.mark.parametrize('yadm_alt', [True, False], ids=['alt', 'worktree']) +@pytest.mark.usefixtures("ds1_copy") +@pytest.mark.parametrize("yadm_alt", [True, False], ids=["alt", "worktree"]) def test_relative_link(runner, paths, yadm_alt): """Confirm links created are relative""" yadm_dir, yadm_data = setup_standard_yadm_dir(paths) utils.create_alt_files( - 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']) + 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"]) 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: - source_file_content = link_path + '##default' + source_file_content = link_path + "##default" source_file = basepath.join(source_file_content) link_file = paths.work.join(link_path) 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 -@pytest.mark.usefixtures('ds1_copy') -@pytest.mark.parametrize('suffix', [ - '##default', - '##default,e.txt', '##default,extension.txt', - '##a.$tst_arch', '##arch.$tst_arch', - '##o.$tst_sys', '##os.$tst_sys', - '##d.$tst_distro', '##distro.$tst_distro', - '##f.$tst_distro_family', '##distro_family.$tst_distro_family', - '##c.$tst_class', '##class.$tst_class', - '##h.$tst_host', '##hostname.$tst_host', - '##u.$tst_user', '##user.$tst_user', - ]) -def test_alt_conditions( - runner, paths, - tst_arch, tst_sys, tst_distro, tst_distro_family, tst_host, tst_user, - suffix): +@pytest.mark.usefixtures("ds1_copy") +@pytest.mark.parametrize( + "suffix", + [ + "##default", + "##default,e.txt", + "##default,extension.txt", + "##a.$tst_arch", + "##arch.$tst_arch", + "##o.$tst_sys", + "##os.$tst_sys", + "##d.$tst_distro", + "##distro.$tst_distro", + "##f.$tst_distro_family", + "##distro_family.$tst_distro_family", + "##c.$tst_class", + "##class.$tst_class", + "##h.$tst_host", + "##hostname.$tst_host", + "##u.$tst_user", + "##user.$tst_user", + ], +) +def test_alt_conditions(runner, paths, tst_arch, tst_sys, tst_distro, tst_distro_family, tst_host, tst_user, suffix): """Test conditions supported by yadm alt""" yadm_dir, yadm_data = setup_standard_yadm_dir(paths) # set the class - tst_class = 'testclass' - utils.set_local(paths, 'class', tst_class + ".before") - utils.set_local(paths, 'class', tst_class, add=True) - utils.set_local(paths, 'class', tst_class + ".after", add=True) + tst_class = "testclass" + utils.set_local(paths, "class", tst_class + ".before") + utils.set_local(paths, "class", tst_class, add=True) + utils.set_local(paths, "class", tst_class + ".after", add=True) suffix = string.Template(suffix).substitute( tst_arch=tst_arch, @@ -115,9 +121,9 @@ def test_alt_conditions( ) 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.err == '' + assert run.err == "" linked = utils.parse_alt_output(run.out) for link_path in TEST_PATHS: @@ -128,27 +134,31 @@ def test_alt_conditions( assert paths.work.join(link_path).read() == source_file assert str(paths.work.join(source_file)) in linked 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 -@pytest.mark.usefixtures('ds1_copy') +@pytest.mark.usefixtures("ds1_copy") +@pytest.mark.parametrize("kind", ["default", "", None, "envtpl", "j2cli", "j2", "esh"]) @pytest.mark.parametrize( - 'kind', ['default', '', None, 'envtpl', 'j2cli', 'j2', 'esh']) -@pytest.mark.parametrize('label', ['t', 'template', 'yadm', ]) -def test_alt_templates( - runner, paths, kind, label): + "label", + [ + "t", + "template", + "yadm", + ], +) +def test_alt_templates(runner, paths, kind, label): """Test templates supported by yadm alt""" yadm_dir, yadm_data = setup_standard_yadm_dir(paths) - suffix = f'##{label}.{kind}' + suffix = f"##{label}.{kind}" if kind is None: - suffix = f'##{label}' + suffix = f"##{label}" 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.err == '' + assert run.err == "" created = utils.parse_alt_output(run.out, linked=False) for created_path in TEST_PATHS: @@ -159,41 +169,39 @@ def test_alt_templates( assert str(paths.work.join(source_file)) in created -@pytest.mark.usefixtures('ds1_copy') -@pytest.mark.parametrize('autoalt', [None, 'true', 'false']) +@pytest.mark.usefixtures("ds1_copy") +@pytest.mark.parametrize("autoalt", [None, "true", "false"]) def test_auto_alt(runner, yadm_cmd, paths, autoalt): """Test auto alt""" # set the value of auto-alt 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') - run = runner(yadm_cmd('status')) + utils.create_alt_files(paths, "##default") + run = runner(yadm_cmd("status")) assert run.success - assert run.err == '' + assert run.err == "" linked = utils.parse_alt_output(run.out) for link_path in TEST_PATHS: - source_file = link_path + '##default' - if autoalt == 'false': + source_file = link_path + "##default" + if autoalt == "false": assert not paths.work.join(link_path).exists() else: 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(): assert paths.work.join(link_path).read() == source_file # no linking output when run via auto-alt assert str(paths.work.join(source_file)) not in linked 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 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): """Stale links to alternative files are removed @@ -202,48 +210,47 @@ def test_stale_link_removal(runner, yadm_cmd, paths): """ # set the class - tst_class = 'testclass' - utils.set_local(paths, 'class', tst_class) + tst_class = "testclass" + utils.set_local(paths, "class", tst_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 = runner(yadm_cmd('alt')) + run = runner(yadm_cmd("alt")) assert run.success - assert run.err == '' + assert run.err == "" linked = utils.parse_alt_output(run.out) # assert the proper linking has occurred 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() target = py.path.local(os.path.realpath(paths.work.join(stale_path))) if target.isfile(): assert paths.work.join(stale_path).read() == source_file assert str(paths.work.join(source_file)) in linked 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 # 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 = runner(yadm_cmd('alt')) + run = runner(yadm_cmd("alt")) assert run.success - assert run.err == '' + assert run.err == "" linked = utils.parse_alt_output(run.out) # assert the linking is removed 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 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): """Remove symlinks before processing a template @@ -252,45 +259,44 @@ def test_template_overwrite_symlink(runner, yadm_cmd, paths, tst_sys): be removed just before processing a template. """ - target = paths.work.join(f'test_link##os.{tst_sys}') - target.write('target') + target = paths.work.join(f"test_link##os.{tst_sys}") + target.write("target") - link = paths.work.join('test_link') + link = paths.work.join("test_link") link.mksymlinkto(target, absolute=1) - template = paths.work.join('test_link##template.default') - template.write('test-data') + template = paths.work.join("test_link##template.default") + template.write("test-data") - run = runner(yadm_cmd('add', target, template)) + run = runner(yadm_cmd("add", target, template)) assert run.success - assert run.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" assert not link.islink() - assert target.read().strip() == 'target' - assert link.read().strip() == 'test-data' + assert target.read().strip() == "target" + assert link.read().strip() == "test-data" -@pytest.mark.usefixtures('ds1_copy') -@pytest.mark.parametrize('style', ['symlink', 'template']) +@pytest.mark.usefixtures("ds1_copy") +@pytest.mark.parametrize("style", ["symlink", "template"]) def test_ensure_alt_path(runner, paths, style): """Test that directories are created before making alternates""" yadm_dir, yadm_data = setup_standard_yadm_dir(paths) - suffix = 'default' if style == 'symlink' else 'template' - filename = 'a/b/c/file' - source = yadm_dir.join(f'alt/{filename}##{suffix}') - source.write('test-data', ensure=True) - run = runner([ - paths.pgm, '-Y', yadm_dir, '--yadm-data', yadm_data, 'add', source]) + suffix = "default" if style == "symlink" else "template" + filename = "a/b/c/file" + source = yadm_dir.join(f"alt/{filename}##{suffix}") + source.write("test-data", ensure=True) + run = runner([paths.pgm, "-Y", yadm_dir, "--yadm-data", yadm_data, "add", source]) assert run.success - assert run.err == '' - assert run.out == '' - assert paths.work.join(filename).read().strip() == 'test-data' + assert run.err == "" + assert run.out == "" + assert paths.work.join(filename).read().strip() == "test-data" def setup_standard_yadm_dir(paths): """Configure a yadm home within the work tree""" - std_yadm_dir = paths.work.mkdir('.config').mkdir('yadm') - std_yadm_data = paths.work.mkdir('.local').mkdir('share').mkdir('yadm') - std_yadm_data.join('repo.git').mksymlinkto(paths.repo, absolute=1) - std_yadm_dir.join('encrypt').mksymlinkto(paths.encrypt, absolute=1) + std_yadm_dir = paths.work.mkdir(".config").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_dir.join("encrypt").mksymlinkto(paths.encrypt, absolute=1) return std_yadm_dir, std_yadm_data diff --git a/test/test_alt_copy.py b/test/test_alt_copy.py index eb19e3d..e1beece 100644 --- a/test/test_alt_copy.py +++ b/test/test_alt_copy.py @@ -6,41 +6,41 @@ import pytest @pytest.mark.parametrize( - 'setting, expect_link, pre_existing', [ + "setting, expect_link, pre_existing", + [ (None, True, None), (True, False, None), (False, True, None), - (True, False, 'link'), - (True, False, 'file'), + (True, False, "link"), + (True, False, "file"), ], ids=[ - 'unset', - 'true', - 'false', - 'pre-existing symlink', - 'pre-existing file', - ]) -@pytest.mark.usefixtures('ds1_copy') -def test_alt_copy( - runner, yadm_cmd, paths, tst_sys, - setting, expect_link, pre_existing): + "unset", + "true", + "false", + "pre-existing symlink", + "pre-existing file", + ], +) +@pytest.mark.usefixtures("ds1_copy") +def test_alt_copy(runner, yadm_cmd, paths, tst_sys, setting, expect_link, pre_existing): """Test yadm.alt-copy""" 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') - if pre_existing == 'symlink': + alt_path = paths.work.join("test_alt_copy") + if pre_existing == "symlink": alt_path.mklinkto(expected_content) - elif pre_existing == 'file': - alt_path.write('wrong content') + elif pre_existing == "file": + alt_path.write("wrong content") - run = runner(yadm_cmd('alt')) + run = runner(yadm_cmd("alt")) assert run.success - assert run.err == '' - assert 'Linking' in run.out + assert run.err == "" + assert "Linking" in run.out assert alt_path.read() == expected_content assert alt_path.islink() == expect_link diff --git a/test/test_assert_private_dirs.py b/test/test_assert_private_dirs.py index 440446b..0cba1f6 100644 --- a/test/test_assert_private_dirs.py +++ b/test/test_assert_private_dirs.py @@ -5,11 +5,11 @@ import re import pytest -pytestmark = pytest.mark.usefixtures('ds1_copy') -PRIVATE_DIRS = ['.gnupg', '.ssh'] +pytestmark = pytest.mark.usefixtures("ds1_copy") +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): """Private dirs (private dirs missing) @@ -25,15 +25,15 @@ def test_pdirs_missing(runner, yadm_cmd, paths, home): path.remove() assert not path.exists() - env = {'DEBUG': 'yes'} + env = {"DEBUG": "yes"} if home: - env['HOME'] = paths.work + env["HOME"] = paths.work # run status - run = runner(command=yadm_cmd('status'), env=env) + run = runner(command=yadm_cmd("status"), env=env) assert run.success - assert run.err == '' - assert 'On branch master' in run.out + assert run.err == "" + assert "On branch master" in run.out # confirm directories are created # and are protected @@ -41,17 +41,15 @@ def test_pdirs_missing(runner, yadm_cmd, paths, home): path = paths.work.join(pdir) if home: 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: assert not path.exists() # confirm directories are created before command is run: if home: assert re.search( - (r'Creating.+\.(gnupg|ssh).+Creating.+\.(gnupg|ssh).+' - r'Running git command git status'), - run.out, re.DOTALL), 'directories created before command is run' + r"Creating.+\.(gnupg|ssh).+Creating.+\.(gnupg|ssh).+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): @@ -71,14 +69,13 @@ def test_pdirs_missing_apd_false(runner, yadm_cmd, paths): assert not path.exists() # 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 = runner(command=yadm_cmd('status')) + run = runner(command=yadm_cmd("status")) assert run.success - assert run.err == '' - assert 'On branch master' in run.out + assert run.err == "" + assert "On branch master" in run.out # confirm directories are STILL missing for pdir in PRIVATE_DIRS: @@ -100,19 +97,18 @@ def test_pdirs_exist_apd_false(runner, yadm_cmd, paths): if not path.isdir(): path.mkdir() 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 - 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 = runner(command=yadm_cmd('status')) + run = runner(command=yadm_cmd("status")) assert run.success - assert run.err == '' - assert 'On branch master' in run.out + assert run.err == "" + assert "On branch master" in run.out # created directories are STILL permissive for pdir in PRIVATE_DIRS: 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" diff --git a/test/test_bootstrap.py b/test/test_bootstrap.py index 4865ece..099dde8 100644 --- a/test/test_bootstrap.py +++ b/test/test_bootstrap.py @@ -4,32 +4,30 @@ import pytest @pytest.mark.parametrize( - 'exists, executable, code, expect', [ - (False, False, 1, 'Cannot execute bootstrap'), - (True, False, 1, 'is not an executable program'), - (True, True, 123, 'Bootstrap successful'), - ], ids=[ - 'missing', - 'not executable', - 'executable', - ]) -def test_bootstrap( - runner, yadm_cmd, paths, exists, executable, code, expect): + "exists, executable, code, expect", + [ + (False, False, 1, "Cannot execute bootstrap"), + (True, False, 1, "is not an executable program"), + (True, True, 123, "Bootstrap successful"), + ], + ids=[ + "missing", + "not executable", + "executable", + ], +) +def test_bootstrap(runner, yadm_cmd, paths, exists, executable, code, expect): """Test bootstrap command""" if exists: - paths.bootstrap.write('') + paths.bootstrap.write("") 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) - run = runner(command=yadm_cmd('bootstrap')) + run = runner(command=yadm_cmd("bootstrap")) assert run.code == code if exists and executable: - assert run.err == '' + assert run.err == "" assert expect in run.out else: assert expect in run.err - assert run.out == '' + assert run.out == "" diff --git a/test/test_clean.py b/test/test_clean.py index 39e7e54..4eaf1e4 100644 --- a/test/test_clean.py +++ b/test/test_clean.py @@ -3,9 +3,9 @@ def test_clean_command(runner, yadm_cmd): """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 # report the command as disabled and exit as a failure assert run.failure - assert run.out == '' - assert 'disabled' in run.err + assert run.out == "" + assert "disabled" in run.err diff --git a/test/test_clone.py b/test/test_clone.py index 9268965..1cae929 100644 --- a/test/test_clone.py +++ b/test/test_clone.py @@ -6,27 +6,28 @@ import re import pytest BOOTSTRAP_CODE = 123 -BOOTSTRAP_MSG = 'Bootstrap successful' +BOOTSTRAP_MSG = "Bootstrap successful" -@pytest.mark.usefixtures('remote') +@pytest.mark.usefixtures("remote") @pytest.mark.parametrize( - 'good_remote, repo_exists, force, conflicts', [ + "good_remote, repo_exists, force, conflicts", + [ (False, False, False, False), (True, False, False, False), (True, True, False, False), (True, True, True, False), (True, False, False, True), - ], ids=[ - 'bad remote', - 'simple', - 'existing repo', - '-f', - 'conflicts', - ]) -def test_clone( - runner, paths, yadm_cmd, repo_config, ds1, - good_remote, repo_exists, force, conflicts): + ], + ids=[ + "bad remote", + "simple", + "existing repo", + "-f", + "conflicts", + ], +) +def test_clone(runner, paths, yadm_cmd, repo_config, ds1, good_remote, repo_exists, force, conflicts): """Test basic clone operation""" # clear out the work path @@ -34,74 +35,70 @@ def test_clone( paths.work.mkdir() # determine remote url - remote_url = f'file://{paths.remote}' + remote_url = f"file://{paths.remote}" if not good_remote: - remote_url = 'file://bad_remote' + remote_url = "file://bad_remote" old_repo = None if repo_exists: # put a repo in the way paths.repo.mkdir() - old_repo = paths.repo.join('old_repo') - old_repo.write('old_repo') + old_repo = paths.repo.join("old_repo") + old_repo.write("old_repo") if conflicts: - ds1.tracked[0].relative.write('conflict') + ds1.tracked[0].relative.write("conflict") assert ds1.tracked[0].relative.exists() # run the clone command - args = ['clone', '-w', paths.work] + args = ["clone", "-w", paths.work] if force: - args += ['-f'] + args += ["-f"] args += [remote_url] run = runner(command=yadm_cmd(*args)) if not good_remote: # clone should fail assert run.failure - assert run.out == '' - assert 'Unable to clone the repository' in run.err + assert run.out == "" + assert "Unable to clone the repository" in run.err assert not paths.repo.exists() elif repo_exists and not force: # can't overwrite data assert run.failure - assert run.out == '' - assert 'Git repo already exists' in run.err + assert run.out == "" + assert "Git repo already exists" in run.err else: # clone should succeed, and repo should be configured properly assert successful_clone(run, paths, repo_config) # these clones should have master as HEAD - verify_head(paths, 'master') + verify_head(paths, "master") # ensure conflicts are handled properly if conflicts: - assert 'NOTE' in run.out - assert 'Local files with content that differs' in run.out + assert "NOTE" in run.out + assert "Local files with content that differs" in run.out # 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.err == '' - assert f'origin\t{remote_url}' in run.out + assert run.err == "" + assert f"origin\t{remote_url}" in run.out # ensure conflicts are really preserved if conflicts: # test that the conflicts are preserved in the work tree - 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.err == '' + assert run.err == "" assert str(ds1.tracked[0].path) in run.out # verify content of the conflicts - run = runner(command=yadm_cmd('diff'), cwd=paths.work) + run = runner(command=yadm_cmd("diff"), cwd=paths.work) assert run.success - assert run.err == '' - assert '\n+conflict' in run.out, 'conflict overwritten' + assert run.err == "" + assert "\n+conflict" in run.out, "conflict overwritten" # another force-related assertion if old_repo: @@ -111,54 +108,56 @@ def test_clone( assert old_repo.exists() -@pytest.mark.usefixtures('remote') +@pytest.mark.usefixtures("remote") @pytest.mark.parametrize( - 'bs_exists, bs_param, answer', [ - (False, '--bootstrap', None), - (True, '--bootstrap', None), - (True, '--no-bootstrap', None), - (True, None, 'n'), - (True, None, 'y'), - ], ids=[ - 'force, missing', - 'force, existing', - 'prevent', - 'existing, answer n', - 'existing, answer y', - ]) -def test_clone_bootstrap( - runner, paths, yadm_cmd, repo_config, bs_exists, bs_param, answer): + "bs_exists, bs_param, answer", + [ + (False, "--bootstrap", None), + (True, "--bootstrap", None), + (True, "--no-bootstrap", None), + (True, None, "n"), + (True, None, "y"), + ], + ids=[ + "force, missing", + "force, existing", + "prevent", + "existing, answer n", + "existing, answer y", + ], +) +def test_clone_bootstrap(runner, paths, yadm_cmd, repo_config, bs_exists, bs_param, answer): """Test bootstrap clone features""" # establish a bootstrap create_bootstrap(paths, bs_exists) # run the clone command - args = ['clone', '-w', paths.work] + args = ["clone", "-w", paths.work] if bs_param: args += [bs_param] - args += [f'file://{paths.remote}'] + args += [f"file://{paths.remote}"] expect = [] 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) 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 - if bs_exists and bs_param != '--no-bootstrap': + if bs_exists and bs_param != "--no-bootstrap": expected_code = BOOTSTRAP_CODE - if answer == 'y': + if answer == "y": expected_code = BOOTSTRAP_CODE assert BOOTSTRAP_MSG in run.out - elif answer == 'n': + elif answer == "n": expected_code = 0 assert BOOTSTRAP_MSG not in run.out assert successful_clone(run, paths, repo_config, expected_code) - verify_head(paths, 'master') + verify_head(paths, "master") if not bs_exists: assert BOOTSTRAP_MSG not in run.out @@ -167,108 +166,90 @@ def test_clone_bootstrap( def create_bootstrap(paths, exists): """Create bootstrap file for test""" 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) assert paths.bootstrap.exists() else: assert not paths.bootstrap.exists() -@pytest.mark.usefixtures('remote') +@pytest.mark.usefixtures("remote") @pytest.mark.parametrize( - 'private_type, in_repo, in_work', [ - ('ssh', False, True), - ('gnupg', False, True), - ('ssh', True, True), - ('gnupg', True, True), - ('ssh', True, False), - ('gnupg', True, False), - ], ids=[ - 'open ssh, not tracked', - 'open gnupg, not tracked', - 'open ssh, tracked', - 'open gnupg, tracked', - 'missing ssh, tracked', - 'missing gnupg, tracked', - ]) -def test_clone_perms( - runner, yadm_cmd, paths, repo_config, - private_type, in_repo, in_work): + "private_type, in_repo, in_work", + [ + ("ssh", False, True), + ("gnupg", False, True), + ("ssh", True, True), + ("gnupg", True, True), + ("ssh", True, False), + ("gnupg", True, False), + ], + ids=[ + "open ssh, not tracked", + "open gnupg, not tracked", + "open ssh, tracked", + "open gnupg, tracked", + "missing ssh, tracked", + "missing gnupg, tracked", + ], +) +def test_clone_perms(runner, yadm_cmd, paths, repo_config, private_type, in_repo, in_work): """Test clone permission-related functions""" # update remote repo to include private data if in_repo: - rpath = paths.work.mkdir(f'.{private_type}').join('related') - rpath.write('related') + rpath = paths.work.mkdir(f".{private_type}").join("related") + rpath.write("related") os.system(f'GIT_DIR="{paths.remote}" git add {rpath}') os.system(f'GIT_DIR="{paths.remote}" git commit -m "{rpath}"') rpath.remove() # ensure local private data is insecure at the start if in_work: - pdir = paths.work.join(f'.{private_type}') + pdir = paths.work.join(f".{private_type}") if not pdir.exists(): pdir.mkdir() - pfile = pdir.join('existing') - pfile.write('existing') + pfile = pdir.join("existing") + pfile.write("existing") pdir.chmod(0o777) pfile.chmod(0o777) else: paths.work.remove() paths.work.mkdir() - env = {'HOME': paths.work} - run = runner( - yadm_cmd('clone', '-d', '-w', paths.work, f'file://{paths.remote}'), - env=env - ) + env = {"HOME": paths.work} + run = runner(yadm_cmd("clone", "-d", "-w", paths.work, f"file://{paths.remote}"), env=env) assert successful_clone(run, paths, repo_config) - verify_head(paths, 'master') + verify_head(paths, "master") if in_work: # private directories which already exist, should be left as they are, # which in this test is "insecure". - assert re.search( - f'initial private dir perms drwxrwxrwx.+.{private_type}', - run.out) - assert re.search( - f'pre-checkout private dir perms drwxrwxrwx.+.{private_type}', - run.out) - assert re.search( - f'post-checkout private dir perms drwxrwxrwx.+.{private_type}', - run.out) + assert re.search(f"initial private dir perms drwxrwxrwx.+.{private_type}", run.out) + assert re.search(f"pre-checkout private dir perms drwxrwxrwx.+.{private_type}", run.out) + assert re.search(f"post-checkout private dir perms drwxrwxrwx.+.{private_type}", run.out) else: # private directories which are created, should be done prior to # checkout, and with secure permissions. - 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( - f'post-checkout private dir perms drwx------.+.{private_type}', - 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(f"post-checkout private dir perms drwx------.+.{private_type}", run.out) # standard perms still apply afterwards unless disabled with auto.perms - assert oct( - paths.work.join(f'.{private_type}').stat().mode).endswith('00'), ( - f'.{private_type} has not been secured by auto.perms') + assert oct(paths.work.join(f".{private_type}").stat().mode).endswith( + "00" + ), f".{private_type} has not been secured by auto.perms" -@pytest.mark.usefixtures('remote') -@pytest.mark.parametrize( - 'branch', ['master', 'default', 'valid', 'invalid']) +@pytest.mark.usefixtures("remote") +@pytest.mark.parametrize("branch", ["master", "default", "valid", "invalid"]) def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch): """Test cloning a branch other than master""" # 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 commit ' - f'--allow-empty -m "This branch is valid"') - if branch != 'default': + os.system(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 # will remain "valid" to validate identification the correct default # branch by inspecting the repo. Otherwise it will be set back to @@ -279,45 +260,43 @@ def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch): paths.work.remove() paths.work.mkdir() - remote_url = f'file://{paths.remote}' + remote_url = f"file://{paths.remote}" # run the clone command - args = ['clone', '-w', paths.work] - if branch not in ['master', 'default']: - args += ['-b', branch] + args = ["clone", "-w", paths.work] + if branch not in ["master", "default"]: + args += ["-b", branch] args += [remote_url] run = runner(command=yadm_cmd(*args)) - if branch == 'invalid': + if branch == "invalid": assert run.failure - assert 'ERROR: Unable to clone the repository' in run.err + assert "ERROR: Unable to clone the repository" in run.err assert f"Remote branch {branch} not found in upstream" in run.err else: assert successful_clone(run, paths, repo_config) # 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.err == '' - assert f'origin\t{remote_url}' in run.out - run = runner(command=yadm_cmd('show')) - if branch == 'master': - assert 'Initial commit' in run.out - verify_head(paths, 'master') + assert run.err == "" + assert f"origin\t{remote_url}" in run.out + run = runner(command=yadm_cmd("show")) + if branch == "master": + assert "Initial commit" in run.out + verify_head(paths, "master") else: - assert 'This branch is valid' in run.out - verify_head(paths, 'valid') + assert "This branch is valid" in run.out + verify_head(paths, "valid") def successful_clone(run, paths, repo_config, expected_code=0): """Assert clone is successful""" assert run.code == expected_code - assert oct(paths.repo.stat().mode).endswith('00'), 'Repo is not secured' - assert repo_config('core.bare') == 'false' - assert repo_config('status.showUntrackedFiles') == 'no' - assert repo_config('yadm.managed') == 'true' + assert oct(paths.repo.stat().mode).endswith("00"), "Repo is not secured" + assert repo_config("core.bare") == "false" + assert repo_config("status.showUntrackedFiles") == "no" + assert repo_config("yadm.managed") == "true" return True @@ -332,15 +311,18 @@ def remote(paths, ds1_repo_copy): paths.repo.move(paths.remote) -def test_no_repo(runner, yadm_cmd, ): +def test_no_repo( + runner, + yadm_cmd, +): """Test cloning without specifying a repo""" - run = runner(command=yadm_cmd('clone', '-f')) + run = runner(command=yadm_cmd("clone", "-f")) assert run.failure - assert run.out == '' - assert 'ERROR: Unable to clone the repository' in run.err - assert 'repository \'repo.git\' does not exist' in run.err + assert run.out == "" + assert "ERROR: Unable to clone the repository" in run.err + assert "repository 'repo.git' does not exist" in run.err def verify_head(paths, branch): """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" diff --git a/test/test_config.py b/test/test_config.py index d6a5e33..c43d81c 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -4,11 +4,11 @@ import os import pytest -TEST_SECTION = 'test' -TEST_ATTRIBUTE = 'attribute' -TEST_KEY = f'{TEST_SECTION}.{TEST_ATTRIBUTE}' -TEST_VALUE = 'testvalue' -TEST_FILE = f'[{TEST_SECTION}]\n\t{TEST_ATTRIBUTE} = {TEST_VALUE}' +TEST_SECTION = "test" +TEST_ATTRIBUTE = "attribute" +TEST_KEY = f"{TEST_SECTION}.{TEST_ATTRIBUTE}" +TEST_VALUE = "testvalue" +TEST_FILE = f"[{TEST_SECTION}]\n\t{TEST_ATTRIBUTE} = {TEST_VALUE}" def test_config_no_params(runner, yadm_cmd, supported_configs): @@ -19,11 +19,11 @@ def test_config_no_params(runner, yadm_cmd, supported_configs): Exit with 0 """ - run = runner(yadm_cmd('config')) + run = runner(yadm_cmd("config")) assert run.success - assert run.err == '' - assert 'Please read the CONFIGURATION section' in run.out + assert run.err == "" + assert "Please read the CONFIGURATION section" in run.out for config in supported_configs: assert config in run.out @@ -35,11 +35,11 @@ def test_config_read_missing(runner, yadm_cmd): Exit with 0 """ - run = runner(yadm_cmd('config', TEST_KEY)) + run = runner(yadm_cmd("config", TEST_KEY)) assert run.success - assert run.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" def test_config_write(runner, yadm_cmd, paths): @@ -50,11 +50,11 @@ def test_config_write(runner, yadm_cmd, paths): 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.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" assert paths.config.read().strip() == TEST_FILE @@ -66,10 +66,10 @@ def test_config_read(runner, yadm_cmd, paths): """ paths.config.write(TEST_FILE) - run = runner(yadm_cmd('config', TEST_KEY)) + run = runner(yadm_cmd("config", TEST_KEY)) assert run.success - assert run.err == '' + assert run.err == "" assert run.out.strip() == TEST_VALUE @@ -83,16 +83,16 @@ def test_config_update(runner, yadm_cmd, paths): 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.err == '' - assert run.out == '' + assert run.err == "" + 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): """Read local attribute @@ -102,19 +102,17 @@ def test_config_local_read(runner, yadm_cmd, paths, supported_local_configs): # populate test values 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 for config in supported_local_configs: - run = runner(yadm_cmd('config', config)) + run = runner(yadm_cmd("config", config)) assert run.success - assert run.err == '' - assert run.out.strip() == f'value_of_{config}' + assert run.err == "" + 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): """Write local attribute @@ -125,19 +123,17 @@ def test_config_local_write(runner, yadm_cmd, paths, supported_local_configs): # run yadm config 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.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" # verify test values 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.err == '' - assert run.out.strip() == f'value_of_{config}' + assert run.err == "" + assert run.out.strip() == f"value_of_{config}" def test_config_without_parent_directory(runner, yadm_cmd, paths): @@ -148,17 +144,16 @@ def test_config_without_parent_directory(runner, yadm_cmd, paths): 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.err == '' - assert run.out == '' + assert run.err == "" + 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.err == '' + assert run.err == "" assert run.out.strip() == TEST_VALUE diff --git a/test/test_encryption.py b/test/test_encryption.py index 78bbf3a..8c64222 100644 --- a/test/test_encryption.py +++ b/test/test_encryption.py @@ -6,26 +6,26 @@ import time import pytest -KEY_FILE = 'test/test_key' -KEY_FINGERPRINT = 'F8BBFC746C58945442349BCEBA54FFD04C599B1A' -KEY_NAME = 'yadm-test1' -KEY_TRUST = 'test/ownertrust.txt' -PASSPHRASE = 'ExamplePassword' +KEY_FILE = "test/test_key" +KEY_FINGERPRINT = "F8BBFC746C58945442349BCEBA54FFD04C599B1A" +KEY_NAME = "yadm-test1" +KEY_TRUST = "test/ownertrust.txt" +PASSPHRASE = "ExamplePassword" -pytestmark = pytest.mark.usefixtures('config_git') +pytestmark = pytest.mark.usefixtures("config_git") def add_asymmetric_key(runner, gnupg): """Add asymmetric key""" env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home runner( - ['gpg', '--import', shlex.quote(KEY_FILE)], + ["gpg", "--import", shlex.quote(KEY_FILE)], env=env, shell=True, ) runner( - ['gpg', '--import-ownertrust', '<', shlex.quote(KEY_TRUST)], + ["gpg", "--import-ownertrust", "<", shlex.quote(KEY_TRUST)], env=env, shell=True, ) @@ -34,20 +34,14 @@ def add_asymmetric_key(runner, gnupg): def remove_asymmetric_key(runner, gnupg): """Remove asymmetric key""" env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home runner( - [ - 'gpg', '--batch', '--yes', - '--delete-secret-keys', shlex.quote(KEY_FINGERPRINT) - ], + ["gpg", "--batch", "--yes", "--delete-secret-keys", shlex.quote(KEY_FINGERPRINT)], env=env, shell=True, ) runner( - [ - 'gpg', '--batch', '--yes', - '--delete-key', shlex.quote(KEY_FINGERPRINT) - ], + ["gpg", "--batch", "--yes", "--delete-key", shlex.quote(KEY_FINGERPRINT)], env=env, shell=True, ) @@ -79,48 +73,48 @@ def encrypt_targets(yadm_cmd, paths): """ # 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 = [] # standard files w/ dirs & spaces - paths.work.join('inc file1').write('inc file1') - expected.append('inc file1') - paths.encrypt.write('inc file1\n') - paths.work.join('inc dir').mkdir() - paths.work.join('inc dir/inc file2').write('inc file2') - expected.append('inc dir/inc file2') - paths.encrypt.write('inc dir/inc file2\n', mode='a') + paths.work.join("inc file1").write("inc file1") + expected.append("inc file1") + paths.encrypt.write("inc file1\n") + paths.work.join("inc dir").mkdir() + paths.work.join("inc dir/inc file2").write("inc file2") + expected.append("inc dir/inc file2") + paths.encrypt.write("inc dir/inc file2\n", mode="a") # standard globs w/ dirs & spaces - paths.work.join('globs file1').write('globs file1') - expected.append('globs file1') - paths.work.join('globs dir').mkdir() - paths.work.join('globs dir/globs file2').write('globs file2') - expected.append('globs dir/globs file2') - paths.encrypt.write('globs*\n', mode='a') + paths.work.join("globs file1").write("globs file1") + expected.append("globs file1") + paths.work.join("globs dir").mkdir() + paths.work.join("globs dir/globs file2").write("globs file2") + expected.append("globs dir/globs file2") + paths.encrypt.write("globs*\n", mode="a") # blank lines - paths.encrypt.write('\n \n\t\n', mode='a') + paths.encrypt.write("\n \n\t\n", mode="a") # comments - paths.work.join('commentfile1').write('commentfile1') - paths.encrypt.write('#commentfile1\n', mode='a') - paths.encrypt.write(' #commentfile1\n', mode='a') + paths.work.join("commentfile1").write("commentfile1") + paths.encrypt.write("#commentfile1\n", mode="a") + paths.encrypt.write(" #commentfile1\n", mode="a") # exclusions - paths.work.join('extest').mkdir() - paths.encrypt.write('extest/*\n', mode='a') # include within extest - paths.work.join('extest/inglob1').write('inglob1') - paths.work.join('extest/exglob1').write('exglob1') - paths.work.join('extest/exglob2').write('exglob2') - paths.encrypt.write('!extest/ex*\n', mode='a') # exclude the ex* - expected.append('extest/inglob1') # should be left with only in* + paths.work.join("extest").mkdir() + paths.encrypt.write("extest/*\n", mode="a") # include within extest + paths.work.join("extest/inglob1").write("inglob1") + paths.work.join("extest/exglob1").write("exglob1") + paths.work.join("extest/exglob2").write("exglob2") + paths.encrypt.write("!extest/ex*\n", mode="a") # exclude the ex* + expected.append("extest/inglob1") # should be left with only in* return expected -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def decrypt_targets(tmpdir_factory, runner, gnupg): """Fixture for setting data to decrypt @@ -129,234 +123,203 @@ def decrypt_targets(tmpdir_factory, runner, gnupg): * creates a list of expected decrypted files """ - tmpdir = tmpdir_factory.mktemp('decrypt_targets') - symmetric = tmpdir.join('symmetric.tar.gz.gpg') - asymmetric = tmpdir.join('asymmetric.tar.gz.gpg') + tmpdir = tmpdir_factory.mktemp("decrypt_targets") + symmetric = tmpdir.join("symmetric.tar.gz.gpg") + asymmetric = tmpdir.join("asymmetric.tar.gz.gpg") expected = [] - tmpdir.join('decrypt1').write('decrypt1') - expected.append('decrypt1') - tmpdir.join('decrypt2').write('decrypt2') - expected.append('decrypt2') - tmpdir.join('subdir').mkdir() - tmpdir.join('subdir/decrypt3').write('subdir/decrypt3') - expected.append('subdir/decrypt3') + tmpdir.join("decrypt1").write("decrypt1") + expected.append("decrypt1") + tmpdir.join("decrypt2").write("decrypt2") + expected.append("decrypt2") + tmpdir.join("subdir").mkdir() + tmpdir.join("subdir/decrypt3").write("subdir/decrypt3") + expected.append("subdir/decrypt3") gnupg.pw(PASSPHRASE) env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home run = runner( - ['tar', 'cvf', '-'] + - expected + - ['|', 'gpg', '--batch', '--yes', '-c'] + - ['--output', shlex.quote(str(symmetric))], + ["tar", "cvf", "-"] + + expected + + ["|", "gpg", "--batch", "--yes", "-c"] + + ["--output", shlex.quote(str(symmetric))], cwd=tmpdir, env=env, - shell=True) + shell=True, + ) assert run.success - gnupg.pw('') + gnupg.pw("") add_asymmetric_key(runner, gnupg) run = runner( - ['tar', 'cvf', '-'] + - expected + - ['|', 'gpg', '--batch', '--yes', '-e'] + - ['-r', shlex.quote(KEY_NAME)] + - ['--output', shlex.quote(str(asymmetric))], + ["tar", "cvf", "-"] + + expected + + ["|", "gpg", "--batch", "--yes", "-e"] + + ["-r", shlex.quote(KEY_NAME)] + + ["--output", shlex.quote(str(asymmetric))], cwd=tmpdir, env=env, - shell=True) + shell=True, + ) assert run.success remove_asymmetric_key(runner, gnupg) return { - 'asymmetric': asymmetric, - 'expected': expected, - 'symmetric': symmetric, + "asymmetric": asymmetric, + "expected": expected, + "symmetric": symmetric, } -@pytest.mark.parametrize( - 'bad_phrase', [False, True], - ids=['good_phrase', 'bad_phrase']) -@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): +@pytest.mark.parametrize("bad_phrase", [False, True], ids=["good_phrase", "bad_phrase"]) +@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""" if missing_encrypt: paths.encrypt.remove() if bad_phrase: - gnupg.pw('') + gnupg.pw("") else: gnupg.pw(PASSPHRASE) if overwrite: - paths.archive.write('existing archive') + paths.archive.write("existing archive") env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home - run = runner(yadm_cmd('encrypt'), env=env) + env["GNUPGHOME"] = gnupg.home + run = runner(yadm_cmd("encrypt"), env=env) if missing_encrypt or bad_phrase: assert run.failure else: assert run.success - assert run.err == '' + assert run.err == "" if missing_encrypt: - assert 'does not exist' in run.err + assert "does not exist" in run.err elif bad_phrase: - assert 'Invalid IPC' in run.err + assert "Invalid IPC" in run.err 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( - '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): +@pytest.mark.parametrize("bad_phrase", [False, True], ids=["good_phrase", "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""" # 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: - gnupg.pw('') + gnupg.pw("") time.sleep(1) # allow gpg-agent cache to expire else: gnupg.pw(PASSPHRASE) if archive_exists: - decrypt_targets['symmetric'].copy(paths.archive) + decrypt_targets["symmetric"].copy(paths.archive) # to test overwriting - paths.work.join('decrypt1').write('pre-existing file') + paths.work.join("decrypt1").write("pre-existing file") env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home args = [] if dolist: - args.append('-l') - run = runner(yadm_cmd('decrypt') + args, env=env) + args.append("-l") + run = runner(yadm_cmd("decrypt") + args, env=env) if archive_exists and not bad_phrase: assert run.success - assert 'encrypted with 1 passphrase' in run.err + assert "encrypted with 1 passphrase" in run.err if dolist: - for filename in decrypt_targets['expected']: - if filename != 'decrypt1': # this one should exist + for filename in decrypt_targets["expected"]: + if filename != "decrypt1": # this one should exist assert not paths.work.join(filename).exists() assert filename in run.out else: - for filename in decrypt_targets['expected']: + for filename in decrypt_targets["expected"]: assert paths.work.join(filename).read() == filename else: assert run.failure -@pytest.mark.usefixtures('asymmetric_key') -@pytest.mark.parametrize( - 'ask', [False, True], - ids=['no_ask', '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): +@pytest.mark.usefixtures("asymmetric_key") +@pytest.mark.parametrize("ask", [False, True], ids=["no_ask", "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""" # specify encryption recipient if ask: - os.system(' '.join(yadm_cmd('config', 'yadm.gpg-recipient', 'ASK'))) - expect = [('Enter the user ID', KEY_NAME), ('Enter the user ID', '')] + os.system(" ".join(yadm_cmd("config", "yadm.gpg-recipient", "ASK"))) + expect = [("Enter the user ID", KEY_NAME), ("Enter the user ID", "")] else: - os.system(' '.join(yadm_cmd('config', 'yadm.gpg-recipient', KEY_NAME))) + os.system(" ".join(yadm_cmd("config", "yadm.gpg-recipient", KEY_NAME))) expect = [] if overwrite: - paths.archive.write('existing archive') + paths.archive.write("existing archive") if not key_exists: remove_asymmetric_key(runner, gnupg) 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: assert run.success - assert encrypted_data_valid( - runner, gnupg, paths.archive, encrypt_targets) + assert encrypted_data_valid(runner, gnupg, paths.archive, encrypt_targets) else: 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: - assert 'Enter the user ID' in run.out + assert "Enter the user ID" in run.out -@pytest.mark.usefixtures('asymmetric_key') -@pytest.mark.usefixtures('encrypt_targets') +@pytest.mark.usefixtures("asymmetric_key") +@pytest.mark.usefixtures("encrypt_targets") def test_multi_key(runner, yadm_cmd, gnupg): """Test multiple recipients""" # 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'"second-key {KEY_NAME}"'))) 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 '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.parametrize( - 'key_exists', [True, False], - 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): +@pytest.mark.usefixtures("asymmetric_key") +@pytest.mark.parametrize("key_exists", [True, False], 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""" # 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 - paths.work.join('decrypt1').write('pre-existing file') + paths.work.join("decrypt1").write("pre-existing file") if not key_exists: remove_asymmetric_key(runner, gnupg) @@ -364,32 +327,28 @@ def test_asymmetric_decrypt( args = [] if dolist: - args.append('-l') + args.append("-l") env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home - run = runner(yadm_cmd('decrypt') + args, env=env) + env["GNUPGHOME"] = gnupg.home + run = runner(yadm_cmd("decrypt") + args, env=env) if key_exists: assert run.success if dolist: - for filename in decrypt_targets['expected']: - if filename != 'decrypt1': # this one should exist + for filename in decrypt_targets["expected"]: + if filename != "decrypt1": # this one should exist assert not paths.work.join(filename).exists() assert filename in run.out else: - for filename in decrypt_targets['expected']: + for filename in decrypt_targets["expected"]: assert paths.work.join(filename).read() == filename else: 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']) -def test_offer_to_add( - runner, yadm_cmd, paths, encrypt_targets, gnupg, untracked): +@pytest.mark.parametrize("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 All the other encryption tests use an archive outside of the work tree. @@ -397,85 +356,75 @@ def test_offer_to_add( 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 = [] gnupg.pw(PASSPHRASE) env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home if untracked: - expect.append(('add it now', untracked)) + expect.append(("add it now", untracked)) else: - worktree_archive.write('exists') - os.system(' '.join(yadm_cmd('add', str(worktree_archive)))) + worktree_archive.write("exists") + 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.err == '' - assert encrypted_data_valid( - runner, gnupg, worktree_archive, encrypt_targets) + assert run.err == "" + 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.err == '' + assert run.err == "" - if untracked == 'y': + if untracked == "y": # should be added to the index - assert f'A {worktree_archive.basename}' in run.out - elif untracked == 'n': + assert f"A {worktree_archive.basename}" in run.out + elif untracked == "n": # should NOT be added to the index - assert f'?? {worktree_archive.basename}' in run.out + assert f"?? {worktree_archive.basename}" in run.out else: # 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): """Confirm that .config/yadm/encrypt is added to exclude""" gnupg.pw(PASSPHRASE) env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home - exclude_file = paths.repo.join('info/exclude') - paths.encrypt.write('test-encrypt-data\n') - paths.work.join('test-encrypt-data').write('') - exclude_file.write('original-data', ensure=True) + exclude_file = paths.repo.join("info/exclude") + paths.encrypt.write("test-encrypt-data\n") + paths.work.join("test-encrypt-data").write("") + 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 'original-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 run.success - assert run.err == '' + assert run.err == "" def encrypted_data_valid(runner, gnupg, encrypted, expected): """Verify encrypted data matches expectations""" gnupg.pw(PASSPHRASE) env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home - run = runner([ - 'gpg', - '-d', shlex.quote(str(encrypted)), - '2>/dev/null', - '|', 'tar', 't'], env=env, shell=True, report=False) + env["GNUPGHOME"] = gnupg.home + run = runner( + ["gpg", "-d", shlex.quote(str(encrypted)), "2>/dev/null", "|", "tar", "t"], env=env, shell=True, report=False + ) file_count = 0 for filename in run.out.splitlines(): - if filename.endswith('/'): + if filename.endswith("/"): continue file_count += 1 - assert filename in expected, ( - f'Unexpected file in archive: {filename}') - assert file_count == len(expected), ( - 'Number of files in archive does not match expected') + assert filename in expected, f"Unexpected file in archive: {filename}" + assert file_count == len(expected), "Number of files in archive does not match expected" return True diff --git a/test/test_enter.py b/test/test_enter.py index 5148e23..b0626ae 100644 --- a/test/test_enter.py +++ b/test/test_enter.py @@ -6,106 +6,112 @@ import pytest @pytest.mark.parametrize( - 'shell, success', [ - ('delete', True), # if there is no shell variable, bash creates it - ('', False), - ('/usr/bin/env', True), - ('noexec', False), - ], ids=[ - 'shell-missing', - 'shell-empty', - 'shell-env', - 'shell-noexec', - ]) -@pytest.mark.usefixtures('ds1_copy') + "shell, success", + [ + ("delete", True), # if there is no shell variable, bash creates it + ("", False), + ("/usr/bin/env", True), + ("noexec", False), + ], + ids=[ + "shell-missing", + "shell-empty", + "shell-env", + "shell-noexec", + ], +) +@pytest.mark.usefixtures("ds1_copy") def test_enter(runner, yadm_cmd, paths, shell, success): """Enter tests""" env = os.environ.copy() - if shell == 'delete': + if shell == "delete": # remove shell - if 'SHELL' in env: - del env['SHELL'] - elif shell == 'noexec': + if "SHELL" in env: + del env["SHELL"] + elif shell == "noexec": # specify a non-executable path - noexec = paths.root.join('noexec') - noexec.write('') + noexec = paths.root.join("noexec") + noexec.write("") noexec.chmod(0o664) - env['SHELL'] = str(noexec) + env["SHELL"] = str(noexec) 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 - prompt = f'yadm shell ({paths.repo})' + prompt = f"yadm shell ({paths.repo})" if success: - assert run.out.startswith('Entering yadm repo') - assert run.out.rstrip().endswith('Leaving yadm repo') - assert run.err == '' + assert run.out.startswith("Entering yadm repo") + assert run.out.rstrip().endswith("Leaving yadm repo") + assert run.err == "" else: - assert 'does not refer to an executable' in run.err - if 'env' in shell: - assert f'GIT_DIR={paths.repo}' in run.out - assert f'GIT_WORK_TREE={paths.work}' in run.out - assert f'PROMPT={prompt}' in run.out - assert f'PS1={prompt}' in run.out + assert "does not refer to an executable" in run.err + if "env" in shell: + assert f"GIT_DIR={paths.repo}" in run.out + assert f"GIT_WORK_TREE={paths.work}" in run.out + assert f"PROMPT={prompt}" in run.out + assert f"PS1={prompt}" in run.out @pytest.mark.parametrize( - 'shell, opts, path', [ - ('bash', '--norc', '\\w'), - ('csh', '-f', '%~'), - ('zsh', '-f', '%~'), - ], ids=[ - 'bash', - 'csh', - 'zsh', - ]) + "shell, opts, path", + [ + ("bash", "--norc", "\\w"), + ("csh", "-f", "%~"), + ("zsh", "-f", "%~"), + ], + ids=[ + "bash", + "csh", + "zsh", + ], +) @pytest.mark.parametrize( - 'cmd', - [False, 'cmd', 'cmd-bad-exit'], - ids=['no-cmd', 'cmd', 'cmd-bad-exit']) + "cmd", + [False, "cmd", "cmd-bad-exit"], + ids=["no-cmd", "cmd", "cmd-bad-exit"], +) @pytest.mark.parametrize( - 'term', ['', 'dumb'], - ids=['term-empty', 'term-dumb']) -@pytest.mark.usefixtures('ds1_copy') -def test_enter_shell_ops(runner, yadm_cmd, paths, shell, - opts, path, cmd, term): + "term", + ["", "dumb"], + ids=["term-empty", "term-dumb"], +) +@pytest.mark.usefixtures("ds1_copy") +def test_enter_shell_ops(runner, yadm_cmd, paths, shell, opts, path, cmd, term): """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 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) - test_cmd = ['test1', 'test2', 'test3'] + test_cmd = ["test1", "test2", "test3"] - enter_cmd = ['enter'] + enter_cmd = ["enter"] if cmd: enter_cmd += test_cmd env = os.environ.copy() - env['TERM'] = term - env['SHELL'] = custom_shell + env["TERM"] = term + env["SHELL"] = custom_shell - if shell == 'zsh' and term == 'dumb': - opts += ' --no-zle' + if shell == "zsh" and term == "dumb": + opts += " --no-zle" run = runner(command=yadm_cmd(*enter_cmd), env=env) - if cmd == 'cmd-bad-exit': + if cmd == "cmd-bad-exit": assert run.failure else: assert run.success - assert run.err == '' - assert f'OPTS={opts}' in run.out - assert f'PROMPT=yadm shell ({paths.repo}) {path} >' in run.out + assert run.err == "" + assert f"OPTS={opts}" in run.out + assert f"PROMPT=yadm shell ({paths.repo}) {path} >" in run.out if cmd: - assert '-c ' + ' '.join(test_cmd) in run.out - assert 'Entering yadm repo' not in run.out - assert 'Leaving yadm repo' not in run.out + assert "-c " + " ".join(test_cmd) in run.out + assert "Entering yadm repo" not in run.out + assert "Leaving yadm repo" not in run.out else: - assert 'Entering yadm repo' in run.out - assert 'Leaving yadm repo' in run.out + assert "Entering yadm repo" in run.out + assert "Leaving yadm repo" in run.out diff --git a/test/test_ext_crypt.py b/test/test_ext_crypt.py index cb74afc..92f703f 100644 --- a/test/test_ext_crypt.py +++ b/test/test_ext_crypt.py @@ -4,28 +4,30 @@ import pytest @pytest.mark.parametrize( - 'crypt', - [False, 'installed', 'installed-but-failed'], - ids=['not-installed', 'installed', 'installed-but-failed'] + "crypt", + [False, "installed", "installed-but-failed"], + ids=["not-installed", "installed", "installed-but-failed"], ) @pytest.mark.parametrize( - 'cmd,var', [ - ['git_crypt', 'GIT_CRYPT_PROGRAM'], - ['transcrypt', 'TRANSCRYPT_PROGRAM'], - ], - ids=['git-crypt', 'transcrypt']) + "cmd,var", + [ + ["git_crypt", "GIT_CRYPT_PROGRAM"], + ["transcrypt", "TRANSCRYPT_PROGRAM"], + ], + ids=["git-crypt", "transcrypt"], +) def test_ext_encryption(runner, yadm, paths, tmpdir, crypt, cmd, var): """External encryption tests""" paths.repo.ensure(dir=True) - bindir = tmpdir.mkdir('bin') - pgm = bindir.join('test-ext-crypt') + bindir = tmpdir.mkdir("bin") + pgm = bindir.join("test-ext-crypt") if crypt: - pgm.write('#!/bin/sh\necho ext-crypt ran\n') + pgm.write("#!/bin/sh\necho ext-crypt ran\n") pgm.chmod(0o775) - if crypt == 'installed-but-failed': - pgm.write('false\n', mode='a') + if crypt == "installed-but-failed": + pgm.write("false\n", mode="a") script = f""" YADM_TEST=1 source {yadm} @@ -34,15 +36,15 @@ def test_ext_encryption(runner, yadm, paths, tmpdir, crypt, cmd, var): {cmd} "param1" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) if crypt: - if crypt == 'installed-but-failed': + if crypt == "installed-but-failed": assert run.failure else: assert run.success - assert run.out.strip() == 'ext-crypt ran' - assert run.err == '' + assert run.out.strip() == "ext-crypt ran" + assert run.err == "" else: assert run.failure assert f"command '{pgm}' cannot be located" in run.err diff --git a/test/test_git.py b/test/test_git.py index 41b102c..dea538d 100644 --- a/test/test_git.py +++ b/test/test_git.py @@ -5,7 +5,7 @@ import re import pytest -@pytest.mark.usefixtures('ds1_copy') +@pytest.mark.usefixtures("ds1_copy") def test_git(runner, yadm_cmd, paths): """Test series of passthrough git commands @@ -18,42 +18,42 @@ def test_git(runner, yadm_cmd, paths): """ # passthru unknown commands to Git - run = runner(command=yadm_cmd('bogus')) + run = runner(command=yadm_cmd("bogus")) assert run.failure assert "git: 'bogus' is not a git command." in run.err assert "See 'git --help'" in run.err - assert run.out == '' + assert run.out == "" # 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 "pathspec 'does_not_exist' did not match any files" in run.err - assert run.out == '' + assert run.out == "" # git command 'add' - newfile = paths.work.join('test_git') - newfile.write('test_git') - run = runner(command=yadm_cmd('add', '-v', str(newfile))) + newfile = paths.work.join("test_git") + newfile.write("test_git") + run = runner(command=yadm_cmd("add", "-v", str(newfile))) assert run.success - assert run.err == '' + assert run.err == "" assert "add 'test_git'" in run.out # git command 'status' - run = runner(command=yadm_cmd('status')) + run = runner(command=yadm_cmd("status")) assert run.success - assert run.err == '' - assert re.search(r'new file:\s+test_git', run.out) + assert run.err == "" + assert re.search(r"new file:\s+test_git", run.out) # 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.err == '' - assert '1 file changed' in run.out - assert '1 insertion' in run.out - assert re.search(r'create mode .+ test_git', run.out) + assert run.err == "" + assert "1 file changed" in run.out + assert "1 insertion" in run.out + assert re.search(r"create mode .+ test_git", run.out) # git command 'log' - run = runner(command=yadm_cmd('log', '--oneline')) + run = runner(command=yadm_cmd("log", "--oneline")) assert run.success - assert run.err == '' - assert 'Add test_git' in run.out + assert run.err == "" + assert "Add test_git" in run.out diff --git a/test/test_help.py b/test/test_help.py index 0d8f2c3..cbfabcc 100644 --- a/test/test_help.py +++ b/test/test_help.py @@ -6,14 +6,14 @@ def test_missing_command(runner, yadm_cmd): """Run without any command""" run = runner(command=yadm_cmd()) assert run.failure - assert run.err == '' - assert run.out.startswith('Usage: yadm') + assert run.err == "" + 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): """Run with help command""" run = runner(command=yadm_cmd(cmd)) assert run.failure - assert run.err == '' - assert run.out.startswith('Usage: yadm') + assert run.err == "" + assert run.out.startswith("Usage: yadm") diff --git a/test/test_hooks.py b/test/test_hooks.py index 704636a..aff3daf 100644 --- a/test/test_hooks.py +++ b/test/test_hooks.py @@ -4,7 +4,8 @@ import pytest @pytest.mark.parametrize( - 'pre, pre_code, post, post_code', [ + "pre, pre_code, post, post_code", + [ (False, 0, False, 0), (True, 0, False, 0), (True, 5, False, 0), @@ -12,83 +13,83 @@ import pytest (False, 0, True, 5), (True, 0, True, 0), (True, 5, True, 5), - ], ids=[ - 'no-hooks', - 'pre-success', - 'pre-fail', - 'post-success', - 'post-fail', - 'pre-post-success', - 'pre-post-fail', - ]) -@pytest.mark.parametrize('cmd', ['--version', 'version']) -def test_hooks( - runner, yadm_cmd, paths, cmd, - pre, pre_code, post, post_code): + ], + ids=[ + "no-hooks", + "pre-success", + "pre-fail", + "post-success", + "post-fail", + "pre-post-success", + "pre-post-fail", + ], +) +@pytest.mark.parametrize("cmd", ["--version", "version"]) +def test_hooks(runner, yadm_cmd, paths, cmd, pre, pre_code, post, post_code): """Test pre/post hook""" # generate hooks if pre: - create_hook(paths, 'pre_version', pre_code) + create_hook(paths, "pre_version", pre_code) if post: - create_hook(paths, 'post_version', post_code) + create_hook(paths, "post_version", post_code) # run yadm run = runner(yadm_cmd(cmd)) # when a pre hook fails, yadm should exit with the hook's code assert run.code == pre_code - assert run.err == '' + assert run.err == "" 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 run.success: if post: - assert 'HOOK:post_version' in run.out + assert "HOOK:post_version" in run.out else: # 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 - 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 -@pytest.mark.usefixtures('ds1_repo_copy') +@pytest.mark.usefixtures("ds1_repo_copy") def test_hook_env(runner, yadm_cmd, paths): """Test hook environment""" # test will be done with a non existent "git" passthru command # which should exit with a failing code - cmd = 'passthrucmd' + cmd = "passthrucmd" # write the hook - hook = paths.hooks.join(f'post_{cmd}') - hook.write('#!/bin/bash\nenv\ndeclare\n') + hook = paths.hooks.join(f"post_{cmd}") + hook.write("#!/bin/bash\nenv\ndeclare\n") hook.chmod(0o755) - run = runner(yadm_cmd(cmd, 'extra_args')) + run = runner(yadm_cmd(cmd, "extra_args")) # expect passthru to fail assert run.failure assert f"'{cmd}' is not a git command" in run.err # verify hook environment - assert 'YADM_HOOK_EXIT=1\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_FULL_COMMAND={cmd} extra_args\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 'YADM_ENCRYPT_INCLUDE_FILES=\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_DIR={paths.yadm}\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_WORK={paths.work}\n" in run.out + assert "YADM_ENCRYPT_INCLUDE_FILES=\n" in run.out # verify the hook environment contains certain exported functions for func in [ - 'builtin_dirname', - 'relative_path', - 'unix_path', - 'mixed_path', + "builtin_dirname", + "relative_path", + "unix_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 script = f""" @@ -98,10 +99,10 @@ def test_hook_env(runner, yadm_cmd, paths): ENCRYPT_INCLUDE_FILES=(a b c) invoke_hook "post" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'YADM_ENCRYPT_INCLUDE_FILES=a\nb\nc\n' in run.out + assert run.err == "" + assert "YADM_ENCRYPT_INCLUDE_FILES=a\nb\nc\n" in run.out def test_escaped(runner, yadm_cmd, paths): @@ -109,35 +110,33 @@ def test_escaped(runner, yadm_cmd, paths): # test will be done with a non existent "git" passthru command # which should exit with a failing code - cmd = 'passthrucmd' + cmd = "passthrucmd" # write the hook - hook = paths.hooks.join(f'post_{cmd}') - hook.write('#!/bin/bash\nenv\n') + hook = paths.hooks.join(f"post_{cmd}") + hook.write("#!/bin/bash\nenv\n") 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 assert run.failure # 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): """Verify hook must be exectuable""" - cmd = 'version' - hook = paths.hooks.join(f'pre_{cmd}') - hook.write('#!/bin/sh\necho HOOK\n') + cmd = "version" + hook = paths.hooks.join(f"pre_{cmd}") + hook.write("#!/bin/sh\necho HOOK\n") hook.chmod(0o644) - if condition == 'exec': + if condition == "exec": hook.chmod(0o755) - mingw = 'OPERATING_SYSTEM="MINGWx"' if condition == 'mingw' else '' + mingw = 'OPERATING_SYSTEM="MINGWx"' if condition == "mingw" else "" script = f""" YADM_TEST=1 source {paths.pgm} YADM_HOOKS="{paths.hooks}" @@ -145,27 +144,23 @@ def test_executable(runner, paths, condition): {mingw} 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.err == '' + assert run.err == "" else: assert run.failure - assert 'Permission denied' in run.err + assert "Permission denied" in run.err - if condition == 'exec': - assert 'HOOK' in run.out - elif condition == 'no-exec': - assert 'HOOK' not in run.out + if condition == "exec": + assert "HOOK" in run.out + elif condition == "no-exec": + assert "HOOK" not in run.out def create_hook(paths, name, code): """Create hook""" 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) diff --git a/test/test_init.py b/test/test_init.py index 3d149d6..a8ba493 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -4,22 +4,24 @@ import pytest @pytest.mark.parametrize( - 'alt_work, repo_present, force', [ + "alt_work, repo_present, force", + [ (False, False, False), (True, False, False), (False, True, False), (False, True, True), (True, True, True), - ], ids=[ - 'simple', - '-w', - 'existing repo', - '-f', - '-w & -f', - ]) -@pytest.mark.usefixtures('ds1_work_copy') -def test_init( - runner, yadm_cmd, paths, repo_config, alt_work, repo_present, force): + ], + ids=[ + "simple", + "-w", + "existing repo", + "-f", + "-w & -f", + ], +) +@pytest.mark.usefixtures("ds1_work_copy") +def test_init(runner, yadm_cmd, paths, repo_config, alt_work, repo_present, force): """Test init Repos should have attribs: @@ -31,56 +33,54 @@ def test_init( """ # 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. - old_repo = paths.repo.join('old_repo') + old_repo = paths.repo.join("old_repo") if repo_present: # Let's put some data in it, so we can confirm that data is gone when # forced to be overwritten. - old_repo.write('old repo data') + old_repo.write("old repo data") assert old_repo.isfile() else: paths.repo.remove() # command args - args = ['init'] + args = ["init"] cwd = None if alt_work: if force: cwd = paths.work.dirname - args.extend(['-w', paths.work.basename]) + args.extend(["-w", paths.work.basename]) else: - args.extend(['-w', paths.work]) + args.extend(["-w", paths.work]) if force: - args.append('-f') + args.append("-f") # run init - runner(['git', 'config', '--global', 'init.defaultBranch', 'master'], - env={'HOME': home}, cwd=cwd) - run = runner(yadm_cmd(*args), env={'HOME': home}, cwd=cwd) + runner(["git", "config", "--global", "init.defaultBranch", "master"], env={"HOME": home}, cwd=cwd) + run = runner(yadm_cmd(*args), env={"HOME": home}, cwd=cwd) if repo_present and not force: assert run.failure - assert 'repo already exists' in run.err - assert old_repo.isfile(), 'Missing original repo' + assert "repo already exists" in run.err + assert old_repo.isfile(), "Missing original repo" else: assert run.success - assert 'Initialized empty shared Git repository' in run.out + assert "Initialized empty shared Git repository" in run.out 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 == '' + assert run.err == "" if alt_work: - assert repo_config('core.worktree') == paths.work + assert repo_config("core.worktree") == paths.work else: - assert repo_config('core.worktree') == home + assert repo_config("core.worktree") == home # uniform repo assertions - assert oct(paths.repo.stat().mode).endswith('00'), ( - 'Repo is not secure') - assert repo_config('core.bare') == 'false' - assert repo_config('status.showUntrackedFiles') == 'no' - assert repo_config('yadm.managed') == 'true' + assert oct(paths.repo.stat().mode).endswith("00"), "Repo is not secure" + assert repo_config("core.bare") == "false" + assert repo_config("status.showUntrackedFiles") == "no" + assert repo_config("yadm.managed") == "true" diff --git a/test/test_introspect.py b/test/test_introspect.py index b292bd4..3a4fb46 100644 --- a/test/test_introspect.py +++ b/test/test_introspect.py @@ -4,38 +4,38 @@ import pytest @pytest.mark.parametrize( - 'name', [ - '', - 'invalid', - 'commands', - 'configs', - 'repo', - 'switches', - ]) -def test_introspect_category( - runner, yadm_cmd, paths, name, - supported_commands, supported_configs, supported_switches): + "name", + [ + "", + "invalid", + "commands", + "configs", + "repo", + "switches", + ], +) +def test_introspect_category(runner, yadm_cmd, paths, name, supported_commands, supported_configs, supported_switches): """Validate introspection category""" if name: - run = runner(command=yadm_cmd('introspect', name)) + run = runner(command=yadm_cmd("introspect", name)) else: - run = runner(command=yadm_cmd('introspect')) + run = runner(command=yadm_cmd("introspect")) assert run.success - assert run.err == '' + assert run.err == "" expected = [] - if name == 'commands': + if name == "commands": expected = supported_commands - elif name == 'configs': + elif name == "configs": expected = supported_configs - elif name == 'switches': + elif name == "switches": expected = supported_switches # assert values - if name in ('', 'invalid'): - assert run.out == '' - if name == 'repo': + if name in ("", "invalid"): + assert run.out == "" + if name == "repo": assert run.out.rstrip() == paths.repo # make sure every expected value is present diff --git a/test/test_list.py b/test/test_list.py index d7d09a6..afcea6f 100644 --- a/test/test_list.py +++ b/test/test_list.py @@ -6,27 +6,29 @@ import pytest @pytest.mark.parametrize( - 'location', [ - 'work', - 'outside', - 'subdir', - ]) -@pytest.mark.usefixtures('ds1_copy') + "location", + [ + "work", + "outside", + "subdir", + ], +) +@pytest.mark.usefixtures("ds1_copy") def test_list(runner, yadm_cmd, paths, ds1, location): """List tests""" - if location == 'work': + if location == "work": run_dir = paths.work - elif location == 'outside': - run_dir = paths.work.join('..') - elif location == 'subdir': + elif location == "outside": + run_dir = paths.work.join("..") + elif location == "subdir": # first directory with tracked data run_dir = paths.work.join(ds1.tracked_dirs[0]) with run_dir.as_cwd(): # test with '-a' # 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.err == '' + assert run.err == "" returned_files = set(run.out.splitlines()) expected_files = {e.path for e in ds1 if e.tracked} assert returned_files == expected_files @@ -34,16 +36,14 @@ def test_list(runner, yadm_cmd, paths, ds1, location): # 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 - run = runner(command=yadm_cmd('list')) + run = runner(command=yadm_cmd("list")) assert run.success - assert run.err == '' + assert run.err == "" returned_files = set(run.out.splitlines()) - if location == 'subdir': + if location == "subdir": basepath = os.path.basename(os.getcwd()) # only expect files within the subdir # names should be relative to subdir - expected_files = { - e.path[len(basepath)+1:] - for e in ds1 if e.tracked and e.path.startswith(basepath) - } + index = len(basepath) + 1 + expected_files = {e.path[index:] for e in ds1 if e.tracked and e.path.startswith(basepath)} assert returned_files == expected_files diff --git a/test/test_perms.py b/test/test_perms.py index a49d897..1491c16 100644 --- a/test/test_perms.py +++ b/test/test_perms.py @@ -5,18 +5,17 @@ import os import pytest -@pytest.mark.parametrize('autoperms', ['notest', 'unset', 'true', 'false']) -@pytest.mark.usefixtures('ds1_copy') +@pytest.mark.parametrize("autoperms", ["notest", "unset", "true", "false"]) +@pytest.mark.usefixtures("ds1_copy") def test_perms(runner, yadm_cmd, paths, ds1, autoperms): """Test perms""" # set the value of auto-perms - if autoperms != 'notest': - if autoperms != 'unset': - os.system(' '.join( - yadm_cmd('config', 'yadm.auto-perms', autoperms))) + if autoperms != "notest": + if autoperms != "unset": + os.system(" ".join(yadm_cmd("config", "yadm.auto-perms", autoperms))) # 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] # create an archive file @@ -24,82 +23,71 @@ def test_perms(runner, yadm_cmd, paths, ds1, autoperms): privatepaths.append(paths.archive) # create encrypted file test data - efile1 = paths.work.join('efile1') - efile1.write('efile1') - efile2 = paths.work.join('efile2') - efile2.write('efile2') - paths.encrypt.write('efile1\nefile2\n!efile1\n') + efile1 = paths.work.join("efile1") + efile1.write("efile1") + efile2 = paths.work.join("efile2") + efile2.write("efile2") + paths.encrypt.write("efile1\nefile2\n!efile1\n") insecurepaths = [efile1] privatepaths.append(efile2) # assert these paths begin unsecured 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' - if autoperms != 'notest': - cmd = 'status' - run = runner(yadm_cmd(cmd), env={'HOME': paths.work}) + cmd = "perms" + if autoperms != "notest": + cmd = "status" + run = runner(yadm_cmd(cmd), env={"HOME": paths.work}) assert run.success - assert run.err == '' - if cmd == 'perms': - assert run.out == '' + assert run.err == "" + if cmd == "perms": + assert run.out == "" # these paths should be secured if processing perms for private in privatepaths: - if autoperms == 'false': - assert not oct(private.stat().mode).endswith('00'), ( - 'Path should not be secured') + if autoperms == "false": + assert not oct(private.stat().mode).endswith("00"), "Path should not be secured" 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 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('gpgperms', [None, 'true', 'false']) -@pytest.mark.usefixtures('ds1_copy') +@pytest.mark.parametrize("sshperms", [None, "true", "false"]) +@pytest.mark.parametrize("gpgperms", [None, "true", "false"]) +@pytest.mark.usefixtures("ds1_copy") def test_perms_control(runner, yadm_cmd, paths, ds1, sshperms, gpgperms): """Test fine control of perms""" # set the value of ssh-perms 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 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 = [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] # assert these paths begin unsecured 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.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" # these paths should be secured if processing perms for private in privatepaths: - if ( - (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') + if (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: - 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 - assert oct(paths.work.stat().mode).endswith('0755') + assert oct(paths.work.stat().mode).endswith("0755") diff --git a/test/test_syntax.py b/test/test_syntax.py index aadafde..6549822 100644 --- a/test/test_syntax.py +++ b/test/test_syntax.py @@ -7,80 +7,77 @@ import pytest def test_yadm_syntax(runner, yadm): """Is syntactically valid""" - run = runner(command=['bash', '-n', yadm]) + run = runner(command=["bash", "-n", yadm]) assert run.success def test_shellcheck(pytestconfig, runner, yadm, shellcheck_version): """Passes shellcheck""" if not pytestconfig.getoption("--force-linters"): - run = runner(command=['shellcheck', '-V'], report=False) - if f'version: {shellcheck_version}' not in run.out: - pytest.skip('Unsupported shellcheck version') - run = runner(command=['shellcheck', '-s', 'bash', yadm]) + run = runner(command=["shellcheck", "-V"], report=False) + if f"version: {shellcheck_version}" not in run.out: + pytest.skip("Unsupported shellcheck version") + run = runner(command=["shellcheck", "-s", "bash", yadm]) assert run.success def test_pylint(pytestconfig, runner, pylint_version): """Passes pylint""" if not pytestconfig.getoption("--force-linters"): - run = runner(command=['pylint', '--version'], report=False) - if f'pylint {pylint_version}' not in run.out: - pytest.skip('Unsupported pylint version') + run = runner(command=["pylint", "--version"], report=False) + if f"pylint {pylint_version}" not in run.out: + pytest.skip("Unsupported pylint version") pyfiles = [] - for tfile in os.listdir('test'): - if tfile.endswith('.py'): - pyfiles.append(f'test/{tfile}') - run = runner(command=['pylint'] + pyfiles) + for tfile in os.listdir("test"): + if tfile.endswith(".py"): + pyfiles.append(f"test/{tfile}") + 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) + 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']) + pytest.skip("Unsupported isort version") + run = runner(command=["isort", "-c", "test"]) assert run.success def test_flake8(pytestconfig, runner, flake8_version): """Passes flake8""" 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): - pytest.skip('Unsupported flake8 version') - run = runner(command=['flake8', 'test']) + pytest.skip("Unsupported flake8 version") + 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) + 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']) + pytest.skip("Unsupported black version") + run = runner(command=["black", "--check", "test"]) assert run.success def test_yamllint(pytestconfig, runner, yamllint_version): """Passes yamllint""" 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): - pytest.skip('Unsupported yamllint version') - run = runner( - command=['yamllint', '-s', '$(find . -name \\*.yml)'], - shell=True) + pytest.skip("Unsupported yamllint version") + run = runner(command=["yamllint", "-s", "$(find . -name \\*.yml)"], shell=True) assert run.success def test_man(runner): """Check for warnings from man""" - run = runner( - command=['man.REAL', '--warnings', './yadm.1']) + run = runner(command=["man.REAL", "--warnings", "./yadm.1"]) assert run.success - assert run.err == '' - assert 'yadm - Yet Another Dotfiles Manager' in run.out + assert run.err == "" + assert "yadm - Yet Another Dotfiles Manager" in run.out diff --git a/test/test_unit_bootstrap_available.py b/test/test_unit_bootstrap_available.py index f37ac08..6ab8df8 100644 --- a/test/test_unit_bootstrap_available.py +++ b/test/test_unit_bootstrap_available.py @@ -8,14 +8,14 @@ def test_bootstrap_missing(runner, paths): def test_bootstrap_no_exec(runner, paths): """Test result of bootstrap_available, when bootstrap not executable""" - paths.bootstrap.write('') + paths.bootstrap.write("") paths.bootstrap.chmod(0o644) run_test(runner, paths, False) def test_bootstrap_exec(runner, paths): """Test result of bootstrap_available, when bootstrap executable""" - paths.bootstrap.write('') + paths.bootstrap.write("") paths.bootstrap.chmod(0o775) run_test(runner, paths, True) @@ -27,7 +27,7 @@ def run_test(runner, paths, success): YADM_BOOTSTRAP='{paths.bootstrap}' bootstrap_available """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success == success - assert run.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" diff --git a/test/test_unit_choose_template_cmd.py b/test/test_unit_choose_template_cmd.py index 536735d..cf7600b 100644 --- a/test/test_unit_choose_template_cmd.py +++ b/test/test_unit_choose_template_cmd.py @@ -2,20 +2,20 @@ import pytest -@pytest.mark.parametrize('label', ['', 'default', 'other']) -@pytest.mark.parametrize('awk', [True, False], ids=['awk', 'no-awk']) +@pytest.mark.parametrize("label", ["", "default", "other"]) +@pytest.mark.parametrize("awk", [True, False], ids=["awk", "no-awk"]) def test_kind_default(runner, yadm, awk, label): """Test kind: default""" - expected = 'template_default' - awk_avail = 'true' + expected = "template_default" + awk_avail = "true" if not awk: - awk_avail = 'false' - expected = '' + awk_avail = "false" + expected = "" - if label == 'other': - expected = '' + if label == "other": + expected = "" script = f""" YADM_TEST=1 source {yadm} @@ -23,30 +23,30 @@ def test_kind_default(runner, yadm, awk, label): template="$(choose_template_cmd "{label}")" echo "TEMPLATE:$template" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert f'TEMPLATE:{expected}\n' in run.out + assert run.err == "" + assert f"TEMPLATE:{expected}\n" in run.out -@pytest.mark.parametrize('label', ['envtpl', 'j2cli', 'j2', 'other']) -@pytest.mark.parametrize('envtpl', [True, False], ids=['envtpl', 'no-envtpl']) -@pytest.mark.parametrize('j2cli', [True, False], ids=['j2cli', 'no-j2cli']) +@pytest.mark.parametrize("label", ["envtpl", "j2cli", "j2", "other"]) +@pytest.mark.parametrize("envtpl", [True, False], ids=["envtpl", "no-envtpl"]) +@pytest.mark.parametrize("j2cli", [True, False], ids=["j2cli", "no-j2cli"]) def test_kind_j2cli_envtpl(runner, yadm, envtpl, j2cli, label): """Test kind: j2 (both j2cli & envtpl) j2cli is preferred over envtpl if available. """ - envtpl_avail = 'true' if envtpl else 'false' - j2cli_avail = 'true' if j2cli else 'false' + envtpl_avail = "true" if envtpl else "false" + j2cli_avail = "true" if j2cli else "false" - if label in ('j2cli', 'j2') and j2cli: - expected = 'template_j2cli' - elif label in ('envtpl', 'j2') and envtpl: - expected = 'template_envtpl' + if label in ("j2cli", "j2") and j2cli: + expected = "template_j2cli" + elif label in ("envtpl", "j2") and envtpl: + expected = "template_envtpl" else: - expected = '' + expected = "" script = f""" YADM_TEST=1 source {yadm} @@ -55,7 +55,7 @@ def test_kind_j2cli_envtpl(runner, yadm, envtpl, j2cli, label): template="$(choose_template_cmd "{label}")" echo "TEMPLATE:$template" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert f'TEMPLATE:{expected}\n' in run.out + assert run.err == "" + assert f"TEMPLATE:{expected}\n" in run.out diff --git a/test/test_unit_configure_paths.py b/test/test_unit_configure_paths.py index 8ecd0ea..d2a680e 100644 --- a/test/test_unit_configure_paths.py +++ b/test/test_unit_configure_paths.py @@ -2,52 +2,56 @@ import pytest -ARCHIVE = 'archive' -BOOTSTRAP = 'bootstrap' -CONFIG = 'config' -ENCRYPT = 'encrypt' -HOME = '/testhome' -REPO = 'repo.git' -YDIR = '.config/yadm' -YDATA = '.local/share/yadm' +ARCHIVE = "archive" +BOOTSTRAP = "bootstrap" +CONFIG = "config" +ENCRYPT = "encrypt" +HOME = "/testhome" +REPO = "repo.git" +YDIR = ".config/yadm" +YDATA = ".local/share/yadm" @pytest.mark.parametrize( - 'override, expect', [ + "override, expect", + [ (None, {}), - ('-Y', {'yadm': 'YADM_DIR'}), - ('--yadm-data', {'data': 'YADM_DATA'}), - ('--yadm-repo', {'repo': 'YADM_REPO', 'git': 'GIT_DIR'}), - ('--yadm-config', {'config': 'YADM_CONFIG'}), - ('--yadm-encrypt', {'encrypt': 'YADM_ENCRYPT'}), - ('--yadm-archive', {'archive': 'YADM_ARCHIVE'}), - ('--yadm-bootstrap', {'bootstrap': 'YADM_BOOTSTRAP'}), - ], ids=[ - 'default', - 'override yadm dir', - 'override yadm data', - 'override repo', - 'override config', - 'override encrypt', - 'override archive', - 'override bootstrap', - ]) + ("-Y", {"yadm": "YADM_DIR"}), + ("--yadm-data", {"data": "YADM_DATA"}), + ("--yadm-repo", {"repo": "YADM_REPO", "git": "GIT_DIR"}), + ("--yadm-config", {"config": "YADM_CONFIG"}), + ("--yadm-encrypt", {"encrypt": "YADM_ENCRYPT"}), + ("--yadm-archive", {"archive": "YADM_ARCHIVE"}), + ("--yadm-bootstrap", {"bootstrap": "YADM_BOOTSTRAP"}), + ], + ids=[ + "default", + "override yadm dir", + "override yadm data", + "override repo", + "override config", + "override encrypt", + "override archive", + "override bootstrap", + ], +) @pytest.mark.parametrize( - 'path', ['.', './override', 'override', '.override', '/override'], ids=[ - 'cwd', './relative', 'relative', 'hidden relative', 'absolute' - ]) + "path", + [".", "./override", "override", ".override", "/override"], + ids=["cwd", "./relative", "relative", "hidden relative", "absolute"], +) def test_config(runner, paths, override, expect, path): """Test configure_paths""" - if path.startswith('/'): + if path.startswith("/"): expected_path = path else: expected_path = str(paths.root.join(path)) args = [override, path] if override else [] - if override == '-Y': + if override == "-Y": matches = match_map(expected_path) - elif override == '--yadm-data': + elif override == "--yadm-data": matches = match_map(None, expected_path) else: matches = match_map() @@ -61,23 +65,23 @@ def test_config(runner, paths, override, expect, path): def match_map(yadm_dir=None, yadm_data=None): """Create a dictionary of matches, relative to yadm_dir""" if not yadm_dir: - yadm_dir = '/'.join([HOME, YDIR]) + yadm_dir = "/".join([HOME, YDIR]) if not yadm_data: - yadm_data = '/'.join([HOME, YDATA]) + yadm_data = "/".join([HOME, YDATA]) return { - 'yadm': f'YADM_DIR="{yadm_dir}"', - 'repo': f'YADM_REPO="{yadm_data}/{REPO}"', - 'config': f'YADM_CONFIG="{yadm_dir}/{CONFIG}"', - 'encrypt': f'YADM_ENCRYPT="{yadm_dir}/{ENCRYPT}"', - 'archive': f'YADM_ARCHIVE="{yadm_data}/{ARCHIVE}"', - 'bootstrap': f'YADM_BOOTSTRAP="{yadm_dir}/{BOOTSTRAP}"', - 'git': f'GIT_DIR="{yadm_data}/{REPO}"', - } + "yadm": f'YADM_DIR="{yadm_dir}"', + "repo": f'YADM_REPO="{yadm_data}/{REPO}"', + "config": f'YADM_CONFIG="{yadm_dir}/{CONFIG}"', + "encrypt": f'YADM_ENCRYPT="{yadm_dir}/{ENCRYPT}"', + "archive": f'YADM_ARCHIVE="{yadm_data}/{ARCHIVE}"', + "bootstrap": f'YADM_BOOTSTRAP="{yadm_dir}/{BOOTSTRAP}"', + "git": f'GIT_DIR="{yadm_data}/{REPO}"', + } def run_test(runner, paths, args, expected_matches, cwd=None): """Run proces global args, and run configure_paths""" - argstring = ' '.join(['"'+a+'"' for a in args]) + argstring = " ".join(['"' + a + '"' for a in args]) script = f""" YADM_TEST=1 HOME="{HOME}" source {paths.pgm} process_global_args {argstring} @@ -87,8 +91,8 @@ def run_test(runner, paths, args, expected_matches, cwd=None): configure_paths declare -p | grep -E '(YADM|GIT)_' """ - run = runner(command=['bash'], inp=script, cwd=cwd) + run = runner(command=["bash"], inp=script, cwd=cwd) assert run.success - assert run.err == '' + assert run.err == "" for match in expected_matches: assert match in run.out diff --git a/test/test_unit_copy_perms.py b/test/test_unit_copy_perms.py index b043878..c0ea04f 100644 --- a/test/test_unit_copy_perms.py +++ b/test/test_unit_copy_perms.py @@ -3,42 +3,40 @@ import os import pytest -OCTAL = '7654' -NON_OCTAL = '9876' +OCTAL = "7654" +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): """Test function copy_perms""" src_mode = 0o754 dst_mode = 0o644 - source = tmpdir.join('source') - source.write('test', ensure=True) + source = tmpdir.join("source") + source.write("test", ensure=True) source.chmod(src_mode) - dest = tmpdir.join('dest') - dest.write('test', ensure=True) + dest = tmpdir.join("dest") + dest.write("test", ensure=True) dest.chmod(dst_mode) - override_stat = '' + override_stat = "" if stat_broken: - override_stat = 'function stat() { echo broken; }' + override_stat = "function stat() { echo broken; }" script = f""" YADM_TEST=1 source {yadm} {override_stat} copy_perms "{source}" "{dest}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" expected = dst_mode if stat_broken else src_mode 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): """Test function get_mode""" script = f""" @@ -47,8 +45,8 @@ def test_get_mode(runner, yadm, stat_output): mode=$(get_mode abc) echo "MODE:$mode" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" expected = OCTAL if stat_output == OCTAL else "" - assert f'MODE:{expected}\n' in run.out + assert f"MODE:{expected}\n" in run.out diff --git a/test/test_unit_encryption.py b/test/test_unit_encryption.py index ab03c62..098d5f6 100644 --- a/test/test_unit_encryption.py +++ b/test/test_unit_encryption.py @@ -3,12 +3,12 @@ import pytest -@pytest.mark.parametrize('condition', ['default', 'override']) +@pytest.mark.parametrize("condition", ["default", "override"]) def test_get_cipher(runner, paths, condition): """Test _get_cipher()""" - if condition == 'override': - paths.config.write('[yadm]\n\tcipher = override-cipher') + if condition == "override": + paths.config.write("[yadm]\n\tcipher = override-cipher") script = f""" YADM_TEST=1 source {paths.pgm} @@ -19,18 +19,18 @@ def test_get_cipher(runner, paths, condition): echo "output_archive:$output_archive" echo "yadm_cipher:$yadm_cipher" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'output_archive:test-archive' in run.out - if condition == 'override': - assert 'yadm_cipher:override-cipher' in run.out + assert run.err == "" + assert "output_archive:test-archive" in run.out + if condition == "override": + assert "yadm_cipher:override-cipher" in run.out 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('mode', ['_encrypt_to', '_decrypt_from']) +@pytest.mark.parametrize("cipher", ["gpg", "openssl", "bad"]) +@pytest.mark.parametrize("mode", ["_encrypt_to", "_decrypt_from"]) def test_encrypt_decrypt(runner, paths, cipher, mode): """Test _encrypt_to() & _decrypt_from""" @@ -49,24 +49,24 @@ def test_encrypt_decrypt(runner, paths, cipher, mode): GPG_PROGRAM=mock_gpg {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.out.startswith(cipher) assert str(paths.archive) in run.out - assert run.err == '' + assert run.err == "" else: 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): """Test _get_openssl_ciphername()""" - if condition == 'override': - paths.config.write('[yadm]\n\topenssl-ciphername = override-cipher') + if condition == "override": + paths.config.write("[yadm]\n\topenssl-ciphername = override-cipher") script = f""" YADM_TEST=1 source {paths.pgm} @@ -76,21 +76,21 @@ def test_get_openssl_ciphername(runner, paths, condition): result=$(_get_openssl_ciphername) echo "result:$result" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if condition == 'override': - assert run.out.strip() == 'result:override-cipher' + assert run.err == "" + if condition == "override": + assert run.out.strip() == "result:override-cipher" 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): """Test _set_openssl_options()""" - if condition == 'old': - paths.config.write('[yadm]\n\topenssl-old = true') + if condition == "old": + paths.config.write("[yadm]\n\topenssl-old = true") script = f""" YADM_TEST=1 source {paths.pgm} @@ -101,20 +101,20 @@ def test_set_openssl_options(runner, paths, condition): _set_openssl_options echo "result:${{OPENSSL_OPTS[@]}}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if condition == 'old': - assert '-testcipher -salt -md md5' in run.out + assert run.err == "" + if condition == "old": + assert "-testcipher -salt -md md5" in run.out 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): """Test _set_gpg_options()""" - paths.config.write(f'[yadm]\n\tgpg-recipient = {recipient}') + paths.config.write(f"[yadm]\n\tgpg-recipient = {recipient}") script = f""" YADM_TEST=1 source {paths.pgm} @@ -124,12 +124,12 @@ def test_set_gpg_options(runner, paths, recipient): _set_gpg_options echo "result:${{GPG_OPTS[@]}}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if recipient == 'ASK': - assert run.out.strip() == 'result:--no-default-recipient -e' - elif recipient != '': - assert run.out.strip() == f'result:-e -r {recipient}' + assert run.err == "" + if recipient == "ASK": + assert run.out.strip() == "result:--no-default-recipient -e" + elif recipient != "": + assert run.out.strip() == f"result:-e -r {recipient}" else: - assert run.out.strip() == 'result:-c' + assert run.out.strip() == "result:-c" diff --git a/test/test_unit_exclude_encrypted.py b/test/test_unit_exclude_encrypted.py index 9d9a074..8937a8b 100644 --- a/test/test_unit_exclude_encrypted.py +++ b/test/test_unit_exclude_encrypted.py @@ -2,38 +2,28 @@ import pytest -@pytest.mark.parametrize( - 'exclude', ['missing', 'outdated', 'up-to-date']) -@pytest.mark.parametrize( - '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): +@pytest.mark.parametrize("exclude", ["missing", "outdated", "up-to-date"]) +@pytest.mark.parametrize("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()""" - 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";}' if auto_exclude: - config_function = 'function config() { return; }' + config_function = "function config() { return; }" - encrypt_file = tmpdir.join('encrypt_file') - repo_dir = tmpdir.join('repodir') - exclude_file = repo_dir.join('info/exclude') + encrypt_file = tmpdir.join("encrypt_file") + repo_dir = tmpdir.join("repodir") + exclude_file = repo_dir.join("info/exclude") if encrypt_exists: - encrypt_file.write('test-encrypt-data\n', ensure=True) - if exclude == 'outdated': - exclude_file.write( - f'original-exclude\n{header}outdated\n', ensure=True) - elif exclude == 'up-to-date': - exclude_file.write( - f'original-exclude\n{header}test-encrypt-data\n', ensure=True) + encrypt_file.write("test-encrypt-data\n", ensure=True) + if exclude == "outdated": + exclude_file.write(f"original-exclude\n{header}outdated\n", ensure=True) + elif exclude == "up-to-date": + exclude_file.write(f"original-exclude\n{header}test-encrypt-data\n", ensure=True) script = f""" YADM_TEST=1 source {yadm} @@ -43,24 +33,22 @@ def test_exclude_encrypted( YADM_REPO="{repo_dir}" exclude_encrypted """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" if auto_exclude: if encrypt_exists: assert exclude_file.exists() - if exclude == 'missing': - assert exclude_file.read() == f'{header}test-encrypt-data\n' + if exclude == "missing": + assert exclude_file.read() == f"{header}test-encrypt-data\n" else: - assert exclude_file.read() == ( - 'original-exclude\n' - f'{header}test-encrypt-data\n') - if exclude != 'up-to-date': - assert f'Updating {exclude_file}' in run.out + assert exclude_file.read() == ("original-exclude\n" f"{header}test-encrypt-data\n") + if exclude != "up-to-date": + assert f"Updating {exclude_file}" in run.out else: - assert run.out == '' + assert run.out == "" else: - assert run.out == '' + assert run.out == "" else: - assert run.out == '' + assert run.out == "" diff --git a/test/test_unit_issue_legacy_path_warning.py b/test/test_unit_issue_legacy_path_warning.py index e43228b..faae7fa 100644 --- a/test/test_unit_issue_legacy_path_warning.py +++ b/test/test_unit_issue_legacy_path_warning.py @@ -3,25 +3,24 @@ import pytest @pytest.mark.parametrize( - 'legacy_path', [ + "legacy_path", + [ None, - 'repo.git', - 'files.gpg', - ], - ) -@pytest.mark.parametrize( - 'override', [True, False], ids=['override', 'no-override']) -@pytest.mark.parametrize( - 'upgrade', [True, False], ids=['upgrade', 'no-upgrade']) + "repo.git", + "files.gpg", + ], +) +@pytest.mark.parametrize("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): """Use issue_legacy_path_warning""" - home = tmpdir.mkdir('home') + home = tmpdir.mkdir("home") 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 '' - main_args = 'MAIN_ARGS=("upgrade")' if upgrade else '' + override = "YADM_OVERRIDE_REPO=override" if override else "" + main_args = 'MAIN_ARGS=("upgrade")' if upgrade else "" script = f""" XDG_CONFIG_HOME= XDG_DATA_HOME= @@ -32,10 +31,10 @@ def test_legacy_warning(tmpdir, runner, yadm, upgrade, override, legacy_path): set_yadm_dirs issue_legacy_path_warning """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.out == '' + assert run.out == "" 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: - assert 'Legacy paths have been detected' not in run.err + assert "Legacy paths have been detected" not in run.err diff --git a/test/test_unit_parse_encrypt.py b/test/test_unit_parse_encrypt.py index ec3a6ee..6a5c23b 100644 --- a/test/test_unit_parse_encrypt.py +++ b/test/test_unit_parse_encrypt.py @@ -7,136 +7,137 @@ def test_not_called(runner, paths): """Test parse_encrypt (not called)""" run = run_parse_encrypt(runner, paths, skip_parse=True) assert run.success - assert run.err == '' - 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 run.err == "" + assert "EIF:unparsed" in run.out, "EIF should be unparsed" + assert "EIF_COUNT:1" in run.out, "Only value of EIF should be unparsed" def test_short_circuit(runner, paths): """Test parse_encrypt (short-circuit)""" run = run_parse_encrypt(runner, paths, twice=True) assert run.success - assert run.err == '' - assert 'PARSE_ENCRYPT_SHORT=parse_encrypt() not reprocessed' in run.out, ( - 'parse_encrypt() should short-circuit') + assert run.err == "" + assert "PARSE_ENCRYPT_SHORT=parse_encrypt() not reprocessed" in run.out, "parse_encrypt() should short-circuit" @pytest.mark.parametrize( - 'encrypt', [ - ('missing'), - ('empty'), - ]) + "encrypt", + [ + ("missing"), + ("empty"), + ], +) def test_empty(runner, paths, encrypt): """Test parse_encrypt (file missing/empty)""" # write encrypt file - if encrypt == 'missing': - assert not paths.encrypt.exists(), 'Encrypt should be missing' + if encrypt == "missing": + assert not paths.encrypt.exists(), "Encrypt should be missing" else: - paths.encrypt.write('') - assert paths.encrypt.exists(), 'Encrypt should exist' - assert paths.encrypt.size() == 0, 'Encrypt should be empty' + paths.encrypt.write("") + assert paths.encrypt.exists(), "Encrypt should exist" + assert paths.encrypt.size() == 0, "Encrypt should be empty" # run parse_encrypt run = run_parse_encrypt(runner, paths) assert run.success - assert run.err == '' + assert run.err == "" # 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): """Generate test data for testing encrypt""" - edata = '' + edata = "" expected = set() # empty line - edata += '\n' + edata += "\n" # simple comments - edata += '# a simple comment\n' - edata += ' # a comment with leading space\n' + edata += "# a simple comment\n" + edata += " # a comment with leading space\n" # unreferenced directory - paths.work.join('unreferenced').mkdir() + paths.work.join("unreferenced").mkdir() # simple files - edata += 'simple_file\n' - edata += 'simple.file\n' - paths.work.join('simple_file').write('') - paths.work.join('simple.file').write('') - paths.work.join('simple_file2').write('') - paths.work.join('simple.file2').write('') - expected.add('simple_file') - expected.add('simple.file') + edata += "simple_file\n" + edata += "simple.file\n" + paths.work.join("simple_file").write("") + paths.work.join("simple.file").write("") + paths.work.join("simple_file2").write("") + paths.work.join("simple.file2").write("") + expected.add("simple_file") + expected.add("simple.file") # simple files in directories - edata += 'simple_dir/simple_file\n' - paths.work.join('simple_dir/simple_file').write('', ensure=True) - paths.work.join('simple_dir/simple_file2').write('', ensure=True) - expected.add('simple_dir/simple_file') + edata += "simple_dir/simple_file\n" + paths.work.join("simple_dir/simple_file").write("", ensure=True) + paths.work.join("simple_dir/simple_file2").write("", ensure=True) + expected.add("simple_dir/simple_file") # paths with spaces - edata += 'with space/with space\n' - paths.work.join('with space/with space').write('', ensure=True) - paths.work.join('with space/with space2').write('', ensure=True) - expected.add('with space/with space') + edata += "with space/with space\n" + paths.work.join("with space/with space").write("", ensure=True) + paths.work.join("with space/with space2").write("", ensure=True) + expected.add("with space/with space") # hidden files - edata += '.hidden\n' - paths.work.join('.hidden').write('') - expected.add('.hidden') + edata += ".hidden\n" + paths.work.join(".hidden").write("") + expected.add(".hidden") # hidden files in directories - edata += '.hidden_dir/.hidden_file\n' - paths.work.join('.hidden_dir/.hidden_file').write('', ensure=True) - expected.add('.hidden_dir/.hidden_file') + edata += ".hidden_dir/.hidden_file\n" + paths.work.join(".hidden_dir/.hidden_file").write("", ensure=True) + expected.add(".hidden_dir/.hidden_file") # wildcards - edata += 'wild*\n' - paths.work.join('wildcard1').write('', ensure=True) - paths.work.join('wildcard2').write('', ensure=True) - expected.add('wildcard1') - expected.add('wildcard2') + edata += "wild*\n" + paths.work.join("wildcard1").write("", ensure=True) + paths.work.join("wildcard2").write("", ensure=True) + expected.add("wildcard1") + expected.add("wildcard2") - edata += 'dirwild*\n' - paths.work.join('dirwildcard/file1').write('', ensure=True) - paths.work.join('dirwildcard/file2').write('', ensure=True) - expected.add('dirwildcard') + edata += "dirwild*\n" + paths.work.join("dirwildcard/file1").write("", ensure=True) + paths.work.join("dirwildcard/file2").write("", ensure=True) + expected.add("dirwildcard") # excludes - edata += 'exclude*\n' - edata += 'ex ex/*\n' - paths.work.join('exclude_file1').write('') - paths.work.join('exclude_file2.ex').write('') - paths.work.join('exclude_file3.ex3').write('') - expected.add('exclude_file1') - expected.add('exclude_file3.ex3') - edata += '!*.ex\n' - edata += '!ex ex/*.txt\n' - paths.work.join('ex ex/file4').write('', ensure=True) - paths.work.join('ex ex/file5.txt').write('', ensure=True) - paths.work.join('ex ex/file6.text').write('', ensure=True) - expected.add('ex ex/file4') - expected.add('ex ex/file6.text') + edata += "exclude*\n" + edata += "ex ex/*\n" + paths.work.join("exclude_file1").write("") + paths.work.join("exclude_file2.ex").write("") + paths.work.join("exclude_file3.ex3").write("") + expected.add("exclude_file1") + expected.add("exclude_file3.ex3") + edata += "!*.ex\n" + edata += "!ex ex/*.txt\n" + paths.work.join("ex ex/file4").write("", ensure=True) + paths.work.join("ex ex/file5.txt").write("", ensure=True) + paths.work.join("ex ex/file6.text").write("", ensure=True) + expected.add("ex ex/file4") + expected.add("ex ex/file6.text") # double star - edata += 'doublestar/**/file*\n' - edata += '!**/file3\n' - paths.work.join('doublestar/a/b/file1').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/g/h/nomatch').write('', ensure=True) - expected.add('doublestar/a/b/file1') - expected.add('doublestar/c/d/file2') + edata += "doublestar/**/file*\n" + edata += "!**/file3\n" + paths.work.join("doublestar/a/b/file1").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/g/h/nomatch").write("", ensure=True) + expected.add("doublestar/a/b/file1") + expected.add("doublestar/c/d/file2") # doublestar/e/f/file3 is excluded return edata, expected -@pytest.mark.usefixtures('ds1_repo_copy') +@pytest.mark.usefixtures("ds1_repo_copy") def test_file_parse_encrypt(runner, paths): """Test parse_encrypt @@ -147,39 +148,35 @@ def test_file_parse_encrypt(runner, paths): edata, expected = create_test_encrypt_data(paths) # write encrypt file - print(f'ENCRYPT:\n---\n{edata}---\n') + print(f"ENCRYPT:\n---\n{edata}---\n") paths.encrypt.write(edata) assert paths.encrypt.isfile() # run parse_encrypt run = run_parse_encrypt(runner, paths) 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: - 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 -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 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 lines. """ - parse_cmd = 'parse_encrypt' + parse_cmd = "parse_encrypt" if skip_parse: - parse_cmd = '' + parse_cmd = "" if twice: - parse_cmd = 'parse_encrypt; parse_encrypt' + parse_cmd = "parse_encrypt; parse_encrypt" script = f""" YADM_TEST=1 source {paths.pgm} YADM_ENCRYPT={paths.encrypt} @@ -197,5 +194,5 @@ def run_parse_encrypt( echo "EIF:$value" done """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) return run diff --git a/test/test_unit_private_dirs.py b/test/test_unit_private_dirs.py index 4f182da..58bd39c 100644 --- a/test/test_unit_private_dirs.py +++ b/test/test_unit_private_dirs.py @@ -3,15 +3,15 @@ import pytest @pytest.mark.parametrize( - 'gnupghome', + "gnupghome", [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): """Test translate_to_relative""" - alt_gnupghome = 'alt/gnupghome' + alt_gnupghome = "alt/gnupghome" env_gnupghome = paths.work.join(alt_gnupghome) script = f""" @@ -22,13 +22,13 @@ def test_relative_path(runner, paths, gnupghome, param): env = {} if gnupghome: - env['GNUPGHOME'] = env_gnupghome + env["GNUPGHOME"] = env_gnupghome - expected = alt_gnupghome if gnupghome else '.gnupg' - if param == 'all': - expected = f'.ssh {expected}' + expected = alt_gnupghome if gnupghome else ".gnupg" + if param == "all": + 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.err == '' + assert run.err == "" assert run.out.strip() == expected diff --git a/test/test_unit_query_distro.py b/test/test_unit_query_distro.py index 4f46501..c32760b 100644 --- a/test/test_unit_query_distro.py +++ b/test/test_unit_query_distro.py @@ -2,18 +2,16 @@ 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): """Match lsb_release -si when present""" - test_release = 'testrelease' - lsb_release = '' - 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={quotes}{test_release}{quotes}\nrelease") - if condition != 'lsb_release': + test_release = "testrelease" + lsb_release = "" + 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={quotes}{test_release}{quotes}\nrelease") + if condition != "lsb_release": lsb_release = 'LSB_RELEASE_PROGRAM="missing_lsb_release"' script = f""" YADM_TEST=1 source {yadm} @@ -21,12 +19,12 @@ def test_query_distro(runner, yadm, tst_distro, tmp_path, condition): OS_RELEASE="{os_release}" query_distro """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if condition == 'lsb_release': + assert run.err == "" + if condition == "lsb_release": assert run.out.rstrip() == tst_distro - elif 'os-release' in condition: + elif "os-release" in condition: assert run.out.rstrip() == test_release else: - assert run.out.rstrip() == '' + assert run.out.rstrip() == "" diff --git a/test/test_unit_query_distro_family.py b/test/test_unit_query_distro_family.py index bf68319..1935bf6 100644 --- a/test/test_unit_query_distro_family.py +++ b/test/test_unit_query_distro_family.py @@ -2,25 +2,23 @@ import pytest -@pytest.mark.parametrize( - 'condition', ['os-release', 'os-release-quotes', 'missing']) +@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") + 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) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if 'os-release' in condition: + assert run.err == "" + if "os-release" in condition: assert run.out.rstrip() == test_family else: - assert run.out.rstrip() == '' + assert run.out.rstrip() == "" diff --git a/test/test_unit_record_score.py b/test/test_unit_record_score.py index 78596e1..a82046c 100644 --- a/test/test_unit_record_score.py +++ b/test/test_unit_record_score.py @@ -30,13 +30,13 @@ def test_dont_record_zeros(runner, yadm): record_score "0" "testtgt" "testsrc" {REPORT_RESULTS} """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'SIZE:0\n' in run.out - assert 'SCORES:\n' in run.out - assert 'TARGETS:\n' in run.out - assert 'SOURCES:\n' in run.out + assert run.err == "" + assert "SIZE:0\n" in run.out + assert "SCORES:\n" in run.out + assert "TARGETS:\n" in run.out + assert "SOURCES:\n" in run.out def test_new_scores(runner, yadm): @@ -50,29 +50,29 @@ def test_new_scores(runner, yadm): record_score "4" "tgt_three" "src_three" {REPORT_RESULTS} """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'SIZE:3\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 'SOURCES:src_one src_two src_three\n' in run.out + assert run.err == "" + assert "SIZE:3\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 "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): """Test existing scores""" - expected_score = '2' - expected_src = 'existing_src' - if difference == 'lower': - score = '1' - elif difference == 'equal': - score = '2' + expected_score = "2" + expected_src = "existing_src" + if difference == "lower": + score = "1" + elif difference == "equal": + score = "2" else: - score = '4' - expected_score = '4' - expected_src = 'new_src' + score = "4" + expected_score = "4" + expected_src = "new_src" script = f""" YADM_TEST=1 source {yadm} @@ -83,13 +83,13 @@ def test_existing_scores(runner, yadm, difference): record_score "{score}" "testtgt" "new_src" {REPORT_RESULTS} """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'SIZE:1\n' in run.out - assert f'SCORES:{expected_score}\n' in run.out - assert 'TARGETS:testtgt\n' in run.out - assert f'SOURCES:{expected_src}\n' in run.out + assert run.err == "" + assert "SIZE:1\n" in run.out + assert f"SCORES:{expected_score}\n" in run.out + assert "TARGETS:testtgt\n" in run.out + assert f"SOURCES:{expected_src}\n" in run.out def test_existing_template(runner, yadm): @@ -105,19 +105,19 @@ def test_existing_template(runner, yadm): record_score "2" "testtgt" "new_src" {REPORT_RESULTS} """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'SIZE:1\n' in run.out - assert 'SCORES:1\n' in run.out - assert 'TARGETS:testtgt\n' in run.out - assert 'SOURCES:\n' in run.out + assert run.err == "" + assert "SIZE:1\n" in run.out + assert "SCORES:1\n" in run.out + assert "TARGETS:testtgt\n" in run.out + assert "SOURCES:\n" in run.out def test_config_first(runner, yadm): """Verify YADM_CONFIG is always processed first""" - config = 'yadm_config_file' + config = "yadm_config_file" script = f""" YADM_TEST=1 source {yadm} {INIT_VARS} @@ -130,12 +130,12 @@ def test_config_first(runner, yadm): echo "CMD_VALUE:${{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.err == '' - assert 'SIZE:3\n' in run.out - assert 'SCORES:2 1 3\n' in run.out - assert f'TARGETS:{config} tgt_before tgt_tmp tgt_after\n' in run.out - assert 'SOURCES:src_config src_before src_tmp src_after\n' in run.out - assert 'CMD_VALUE:cmd_tmp\n' in run.out - assert 'CMD_INDEX:2\n' in run.out + assert run.err == "" + assert "SIZE:3\n" in run.out + assert "SCORES:2 1 3\n" in run.out + assert f"TARGETS:{config} tgt_before tgt_tmp tgt_after\n" in run.out + assert "SOURCES:src_config src_before src_tmp src_after\n" in run.out + assert "CMD_VALUE:cmd_tmp\n" in run.out + assert "CMD_INDEX:2\n" in run.out diff --git a/test/test_unit_record_template.py b/test/test_unit_record_template.py index 6bfd012..4f3c3e8 100644 --- a/test/test_unit_record_template.py +++ b/test/test_unit_record_template.py @@ -25,13 +25,13 @@ def test_new_template(runner, yadm): record_template "tgt_three" "cmd_three" "src_three" {REPORT_RESULTS} """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'SIZE:3\n' in run.out - assert 'TARGETS:tgt_one tgt_two tgt_three\n' in run.out - assert 'CMDS:cmd_one cmd_two cmd_three\n' in run.out - assert 'SOURCES:src_one src_two src_three\n' in run.out + assert run.err == "" + assert "SIZE:3\n" in run.out + assert "TARGETS:tgt_one tgt_two tgt_three\n" in run.out + assert "CMDS:cmd_one cmd_two cmd_three\n" in run.out + assert "SOURCES:src_one src_two src_three\n" in run.out def test_existing_template(runner, yadm): @@ -46,10 +46,10 @@ def test_existing_template(runner, yadm): record_template "testtgt" "new_cmd" "new_src" {REPORT_RESULTS} """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'SIZE:1\n' in run.out - assert 'TARGETS:testtgt\n' in run.out - assert 'CMDS:new_cmd\n' in run.out - assert 'SOURCES:new_src\n' in run.out + assert run.err == "" + assert "SIZE:1\n" in run.out + assert "TARGETS:testtgt\n" in run.out + assert "CMDS:new_cmd\n" in run.out + assert "SOURCES:new_src\n" in run.out diff --git a/test/test_unit_relative_path.py b/test/test_unit_relative_path.py index f723c84..e0b32f5 100644 --- a/test/test_unit_relative_path.py +++ b/test/test_unit_relative_path.py @@ -3,7 +3,7 @@ import pytest @pytest.mark.parametrize( - 'base,full_path,expected', + "base,full_path,expected", [ ("/A/B/C", "/A", "../.."), ("/A/B/C", "/A/B", ".."), @@ -25,7 +25,7 @@ def test_relative_path(runner, paths, base, full_path, expected): relative_path "{base}" "{full_path}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert run.out.strip() == expected diff --git a/test/test_unit_remove_stale_links.py b/test/test_unit_remove_stale_links.py index 4fcf1a1..f389ed8 100644 --- a/test/test_unit_remove_stale_links.py +++ b/test/test_unit_remove_stale_links.py @@ -4,21 +4,21 @@ import os import pytest -@pytest.mark.parametrize('linked', [True, False]) -@pytest.mark.parametrize('kind', ['file', 'symlink']) +@pytest.mark.parametrize("linked", [True, False]) +@pytest.mark.parametrize("kind", ["file", "symlink"]) def test_remove_stale_links(runner, yadm, tmpdir, kind, linked): """Test remove_stale_links()""" - source_file = tmpdir.join('source_file') - source_file.write('source file', ensure=True) - link = tmpdir.join('link') + source_file = tmpdir.join("source_file") + source_file.write("source file", ensure=True) + link = tmpdir.join("link") - if kind == 'file': - link.write('link file', ensure=True) + if kind == "file": + link.write("link file", ensure=True) else: - os.system(f'ln -s {source_file} {link}') + os.system(f"ln -s {source_file} {link}") - alt_linked = '' + alt_linked = "" if linked: alt_linked = source_file @@ -30,9 +30,9 @@ def test_remove_stale_links(runner, yadm, tmpdir, kind, linked): remove_stale_links """ - run = runner(command=['bash'], inp=script) - assert run.err == '' - if kind == 'symlink' and not linked: - assert f'rm -f {link}' in run.out + run = runner(command=["bash"], inp=script) + assert run.err == "" + if kind == "symlink" and not linked: + assert f"rm -f {link}" in run.out else: - assert run.out == '' + assert run.out == "" diff --git a/test/test_unit_report_invalid_alts.py b/test/test_unit_report_invalid_alts.py index 8730d61..996b1ef 100644 --- a/test/test_unit_report_invalid_alts.py +++ b/test/test_unit_report_invalid_alts.py @@ -2,15 +2,15 @@ import pytest -@pytest.mark.parametrize('valid', [True, False], ids=['valid', 'no_valid']) -@pytest.mark.parametrize('previous', [True, False], ids=['prev', 'no_prev']) +@pytest.mark.parametrize("valid", [True, False], ids=["valid", "no_valid"]) +@pytest.mark.parametrize("previous", [True, False], ids=["prev", "no_prev"]) def test_report_invalid_alts(runner, yadm, valid, previous): """Use report_invalid_alts""" - lwi = '' - alts = 'INVALID_ALT=()' + lwi = "" + alts = "INVALID_ALT=()" if previous: - lwi = 'LEGACY_WARNING_ISSUED=1' + lwi = "LEGACY_WARNING_ISSUED=1" if not valid: alts = 'INVALID_ALT=("file##invalid")' @@ -20,11 +20,11 @@ def test_report_invalid_alts(runner, yadm, valid, previous): {alts} report_invalid_alts """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.out == '' + assert run.out == "" if not valid and not previous: - assert 'WARNING' in run.err - assert 'file##invalid' in run.err + assert "WARNING" in run.err + assert "file##invalid" in run.err else: - assert run.err == '' + assert run.err == "" diff --git a/test/test_unit_score_file.py b/test/test_unit_score_file.py index dd2f0b7..c84fb1e 100644 --- a/test/test_unit_score_file.py +++ b/test/test_unit_score_file.py @@ -2,40 +2,40 @@ import pytest CONDITION = { - 'default': { - 'labels': ['default'], - 'modifier': 0, - }, - 'arch': { - 'labels': ['a', 'arch'], - 'modifier': 1, - }, - 'system': { - 'labels': ['o', 'os'], - 'modifier': 2, - }, - 'distro': { - 'labels': ['d', 'distro'], - 'modifier': 4, - }, - 'distro_family': { - 'labels': ['f', 'distro_family'], - 'modifier': 8, - }, - 'class': { - 'labels': ['c', 'class'], - 'modifier': 16, - }, - 'hostname': { - 'labels': ['h', 'hostname'], - 'modifier': 32, - }, - 'user': { - 'labels': ['u', 'user'], - 'modifier': 64, - }, - } -TEMPLATE_LABELS = ['t', 'template', 'yadm'] + "default": { + "labels": ["default"], + "modifier": 0, + }, + "arch": { + "labels": ["a", "arch"], + "modifier": 1, + }, + "system": { + "labels": ["o", "os"], + "modifier": 2, + }, + "distro": { + "labels": ["d", "distro"], + "modifier": 4, + }, + "distro_family": { + "labels": ["f", "distro_family"], + "modifier": 8, + }, + "class": { + "labels": ["c", "class"], + "modifier": 16, + }, + "hostname": { + "labels": ["h", "hostname"], + "modifier": 32, + }, + "user": { + "labels": ["u", "user"], + "modifier": 64, + }, +} +TEMPLATE_LABELS = ["t", "template", "yadm"] def calculate_score(filename): @@ -43,48 +43,48 @@ def calculate_score(filename): # pylint: disable=too-many-branches score = 0 - _, conditions = filename.split('##', 1) + _, conditions = filename.split("##", 1) - for condition in conditions.split(','): + for condition in conditions.split(","): label = condition value = None - if '.' in condition: - label, value = condition.split('.', 1) - if label in CONDITION['default']['labels']: + if "." in condition: + label, value = condition.split(".", 1) + if label in CONDITION["default"]["labels"]: score += 1000 - elif label in CONDITION['arch']['labels']: - if value == 'testarch': - score += 1000 + CONDITION['arch']['modifier'] + elif label in CONDITION["arch"]["labels"]: + if value == "testarch": + score += 1000 + CONDITION["arch"]["modifier"] else: score = 0 break - elif label in CONDITION['system']['labels']: - if value == 'testsystem': - score += 1000 + CONDITION['system']['modifier'] + elif label in CONDITION["system"]["labels"]: + if value == "testsystem": + score += 1000 + CONDITION["system"]["modifier"] else: score = 0 break - elif label in CONDITION['distro']['labels']: - if value == 'testdistro': - score += 1000 + CONDITION['distro']['modifier'] + elif label in CONDITION["distro"]["labels"]: + if value == "testdistro": + score += 1000 + CONDITION["distro"]["modifier"] else: score = 0 break - elif label in CONDITION['class']['labels']: - if value == 'testclass': - score += 1000 + CONDITION['class']['modifier'] + elif label in CONDITION["class"]["labels"]: + if value == "testclass": + score += 1000 + CONDITION["class"]["modifier"] else: score = 0 break - elif label in CONDITION['hostname']['labels']: - if value == 'testhost': - score += 1000 + CONDITION['hostname']['modifier'] + elif label in CONDITION["hostname"]["labels"]: + if value == "testhost": + score += 1000 + CONDITION["hostname"]["modifier"] else: score = 0 break - elif label in CONDITION['user']['labels']: - if value == 'testuser': - score += 1000 + CONDITION['user']['modifier'] + elif label in CONDITION["user"]["labels"]: + if value == "testuser": + score += 1000 + CONDITION["user"]["modifier"] else: score = 0 break @@ -94,111 +94,85 @@ def calculate_score(filename): return score -@pytest.mark.parametrize( - 'default', ['default', None], ids=['default', 'no-default']) -@pytest.mark.parametrize( - 'arch', ['arch', None], ids=['arch', 'no-arch']) -@pytest.mark.parametrize( - 'system', ['system', None], ids=['system', 'no-system']) -@pytest.mark.parametrize( - 'distro', ['distro', None], ids=['distro', 'no-distro']) -@pytest.mark.parametrize( - '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, arch, system, distro, cla, host, user): +@pytest.mark.parametrize("default", ["default", None], ids=["default", "no-default"]) +@pytest.mark.parametrize("arch", ["arch", None], ids=["arch", "no-arch"]) +@pytest.mark.parametrize("system", ["system", None], ids=["system", "no-system"]) +@pytest.mark.parametrize("distro", ["distro", None], ids=["distro", "no-distro"]) +@pytest.mark.parametrize("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, arch, system, distro, cla, host, user): """Test score results""" # pylint: disable=too-many-branches - local_class = 'testclass' - local_arch = 'testarch' - local_system = 'testsystem' - local_distro = 'testdistro' - local_host = 'testhost' - local_user = 'testuser' - filenames = {'filename##': 0} + local_class = "testclass" + local_arch = "testarch" + local_system = "testsystem" + local_distro = "testdistro" + local_host = "testhost" + local_user = "testuser" + filenames = {"filename##": 0} if default: for filename in list(filenames): - for label in CONDITION[default]['labels']: + for label in CONDITION[default]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' + if not newfile.endswith("##"): + newfile += "," newfile += label filenames[newfile] = calculate_score(newfile) if arch: for filename in list(filenames): for match in [True, False]: - for label in CONDITION[arch]['labels']: + for label in CONDITION[arch]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([ - label, - local_arch if match else 'badarch' - ]) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, local_arch if match else "badarch"]) filenames[newfile] = calculate_score(newfile) if system: for filename in list(filenames): for match in [True, False]: - for label in CONDITION[system]['labels']: + for label in CONDITION[system]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([ - label, - local_system if match else 'badsys' - ]) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, local_system if match else "badsys"]) filenames[newfile] = calculate_score(newfile) if distro: for filename in list(filenames): for match in [True, False]: - for label in CONDITION[distro]['labels']: + for label in CONDITION[distro]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([ - label, - local_distro if match else 'baddistro' - ]) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, local_distro if match else "baddistro"]) filenames[newfile] = calculate_score(newfile) if cla: for filename in list(filenames): for match in [True, False]: - for label in CONDITION[cla]['labels']: + for label in CONDITION[cla]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([ - label, - local_class if match else 'badclass' - ]) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, local_class if match else "badclass"]) filenames[newfile] = calculate_score(newfile) if host: for filename in list(filenames): for match in [True, False]: - for label in CONDITION[host]['labels']: + for label in CONDITION[host]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([ - label, - local_host if match else 'badhost' - ]) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, local_host if match else "badhost"]) filenames[newfile] = calculate_score(newfile) if user: for filename in list(filenames): for match in [True, False]: - for label in CONDITION[user]['labels']: + for label in CONDITION[user]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([ - label, - local_user if match else 'baduser' - ]) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, local_user if match else "baduser"]) filenames[newfile] = calculate_score(newfile) script = f""" @@ -212,29 +186,29 @@ def test_score_values( local_host={local_host} local_user={local_user} """ - expected = '' + 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) + expected += filename + "\n" + expected += str(score) + "\n" + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert run.out == expected -@pytest.mark.parametrize('ext', [None, 'e', 'extension']) +@pytest.mark.parametrize("ext", [None, "e", "extension"]) def test_extensions(runner, yadm, ext): """Verify extensions do not effect scores""" - local_user = 'testuser' - filename = f'filename##u.{local_user}' + local_user = "testuser" + filename = f"filename##u.{local_user}" if ext: - filename += f',{ext}.xyz' - expected = '' + filename += f",{ext}.xyz" + expected = "" script = f""" YADM_TEST=1 source {yadm} score=0 @@ -243,28 +217,28 @@ def test_extensions(runner, yadm, ext): echo "$score" """ expected = f'{1000 + CONDITION["user"]["modifier"]}\n' - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert run.out == expected def test_score_values_templates(runner, yadm): """Test score results""" - local_class = 'testclass' - local_arch = 'arch' - local_system = 'testsystem' - local_distro = 'testdistro' - local_host = 'testhost' - local_user = 'testuser' - filenames = {'filename##': 0} + local_class = "testclass" + local_arch = "arch" + local_system = "testsystem" + local_distro = "testdistro" + local_host = "testhost" + local_user = "testuser" + filenames = {"filename##": 0} for filename in list(filenames): for label in TEMPLATE_LABELS: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([label, 'testtemplate']) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, "testtemplate"]) filenames[newfile] = calculate_score(newfile) script = f""" @@ -277,33 +251,30 @@ def test_score_values_templates(runner, yadm): local_host={local_host} local_user={local_user} """ - expected = '' + 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) + expected += filename + "\n" + expected += str(score) + "\n" + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" 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): """Template should be recorded if choose_template_cmd outputs a command""" - mock = 'function choose_template_cmd() { return; }' - expected = '' + mock = "function choose_template_cmd() { return; }" + expected = "" if cmd_generated: mock = 'function choose_template_cmd() { echo "test_cmd"; }' - expected = 'template recorded' + expected = "template recorded" script = f""" YADM_TEST=1 source {yadm} @@ -311,24 +282,24 @@ def test_template_recording(runner, yadm, cmd_generated): {mock} score_file "testfile##template.kind" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert run.out.rstrip() == expected def test_underscores_in_distro_and_family(runner, yadm): """Test replacing spaces in distro / distro_family with underscores""" - local_distro = 'test distro' - local_distro_family = 'test family' + 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, - } + "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} @@ -336,16 +307,16 @@ def test_underscores_in_distro_and_family(runner, yadm): local_distro="{local_distro}" local_distro_family="{local_distro_family}" """ - expected = '' + 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) + expected += filename + "\n" + expected += str(score) + "\n" + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert run.out == expected diff --git a/test/test_unit_set_local_alt_values.py b/test/test_unit_set_local_alt_values.py index 449850c..fa5749d 100644 --- a/test/test_unit_set_local_alt_values.py +++ b/test/test_unit_set_local_alt_values.py @@ -4,26 +4,26 @@ import utils @pytest.mark.parametrize( - 'override', [ + "override", + [ False, - 'class', - 'arch', - 'os', - 'hostname', - 'user', - ], + "class", + "arch", + "os", + "hostname", + "user", + ], ids=[ - 'no-override', - 'override-class', - 'override-arch', - 'override-os', - 'override-hostname', - 'override-user', - ] - ) -@pytest.mark.usefixtures('ds1_copy') -def test_set_local_alt_values( - runner, yadm, paths, tst_arch, tst_sys, tst_host, tst_user, override): + "no-override", + "override-class", + "override-arch", + "override-os", + "override-hostname", + "override-user", + ], +) +@pytest.mark.usefixtures("ds1_copy") +def test_set_local_alt_values(runner, yadm, paths, tst_arch, tst_sys, tst_host, tst_user, override): """Use issue_legacy_path_warning""" script = f""" YADM_TEST=1 source {yadm} && @@ -37,37 +37,37 @@ def test_set_local_alt_values( echo "user='$local_user'" """ - if override == 'class': - utils.set_local(paths, override, 'first') - utils.set_local(paths, override, 'override', add=True) + if override == "class": + utils.set_local(paths, override, "first") + utils.set_local(paths, override, "override", add=True) elif override: - utils.set_local(paths, override, 'override') + utils.set_local(paths, override, "override") - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" - if override == 'class': + if override == "class": assert "class='override'" in run.out else: assert "class=''" in run.out - if override == 'arch': + if override == "arch": assert "arch='override'" in run.out else: assert f"arch='{tst_arch}'" in run.out - if override == 'os': + if override == "os": assert "os='override'" in run.out else: assert f"os='{tst_sys}'" in run.out - if override == 'hostname': + if override == "hostname": assert "host='override'" in run.out else: assert f"host='{tst_host}'" in run.out - if override == 'user': + if override == "user": assert "user='override'" in run.out else: assert f"user='{tst_user}'" in run.out @@ -85,8 +85,8 @@ def test_distro_and_family(runner, yadm): 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.err == '' + assert run.err == "" assert "distro='testdistro'" in run.out assert "distro_family='testfamily'" in run.out diff --git a/test/test_unit_set_os.py b/test/test_unit_set_os.py index 936986f..ac61de2 100644 --- a/test/test_unit_set_os.py +++ b/test/test_unit_set_os.py @@ -4,25 +4,27 @@ import pytest @pytest.mark.parametrize( - 'proc_value, expected_os', [ - ('missing', 'uname'), - ('has microsoft inside', 'WSL'), # case insensitive - ('has Microsoft inside', 'WSL'), # case insensitive - ('another value', 'uname'), - ], ids=[ - '/proc/version missing', - '/proc/version includes ms', - '/proc/version excludes Ms', - 'another value', - ]) -def test_set_operating_system( - runner, paths, tst_sys, proc_value, expected_os): + "proc_value, expected_os", + [ + ("missing", "uname"), + ("has microsoft inside", "WSL"), # case insensitive + ("has Microsoft inside", "WSL"), # case insensitive + ("another value", "uname"), + ], + ids=[ + "/proc/version missing", + "/proc/version includes ms", + "/proc/version excludes Ms", + "another value", + ], +) +def test_set_operating_system(runner, paths, tst_sys, proc_value, expected_os): """Run set_operating_system and test result""" # Normally /proc/version (set in PROC_VERSION) is inspected to identify # WSL. During testing, we will override that value. - proc_version = paths.root.join('proc_version') - if proc_value != 'missing': + proc_version = paths.root.join("proc_version") + if proc_value != "missing": proc_version.write(proc_value) script = f""" YADM_TEST=1 source {paths.pgm} @@ -30,9 +32,9 @@ def test_set_operating_system( set_operating_system echo $OPERATING_SYSTEM """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if expected_os == 'uname': + assert run.err == "" + if expected_os == "uname": expected_os = tst_sys assert run.out.rstrip() == expected_os diff --git a/test/test_unit_set_yadm_dir.py b/test/test_unit_set_yadm_dir.py index 32af8bf..b56c98d 100644 --- a/test/test_unit_set_yadm_dir.py +++ b/test/test_unit_set_yadm_dir.py @@ -3,25 +3,20 @@ import pytest @pytest.mark.parametrize( - 'condition', [ - 'basic', - 'override', - 'override_data', - 'xdg_config_home', - 'xdg_data_home' - ], - ) + "condition", + ["basic", "override", "override_data", "xdg_config_home", "xdg_data_home"], +) def test_set_yadm_dirs(runner, yadm, condition): """Test set_yadm_dirs""" - setup = '' - if condition == 'override': - setup = 'YADM_DIR=/override' - elif condition == 'override_data': - setup = 'YADM_DATA=/override' - elif condition == 'xdg_config_home': - setup = 'XDG_CONFIG_HOME=/xdg' - elif condition == 'xdg_data_home': - setup = 'XDG_DATA_HOME=/xdg' + setup = "" + if condition == "override": + setup = "YADM_DIR=/override" + elif condition == "override_data": + setup = "YADM_DATA=/override" + elif condition == "xdg_config_home": + setup = "XDG_CONFIG_HOME=/xdg" + elif condition == "xdg_data_home": + setup = "XDG_DATA_HOME=/xdg" script = f""" HOME=/testhome YADM_TEST=1 source {yadm} @@ -32,17 +27,17 @@ def test_set_yadm_dirs(runner, yadm, condition): echo "YADM_DIR=$YADM_DIR" echo "YADM_DATA=$YADM_DATA" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if condition == 'basic': - assert 'YADM_DIR=/testhome/.config/yadm' in run.out - assert 'YADM_DATA=/testhome/.local/share/yadm' in run.out - elif condition == 'override': - assert 'YADM_DIR=/override' in run.out - elif condition == 'override_data': - assert 'YADM_DATA=/override' in run.out - elif condition == 'xdg_config_home': - assert 'YADM_DIR=/xdg/yadm' in run.out - elif condition == 'xdg_data_home': - assert 'YADM_DATA=/xdg/yadm' in run.out + assert run.err == "" + if condition == "basic": + assert "YADM_DIR=/testhome/.config/yadm" in run.out + assert "YADM_DATA=/testhome/.local/share/yadm" in run.out + elif condition == "override": + assert "YADM_DIR=/override" in run.out + elif condition == "override_data": + assert "YADM_DATA=/override" in run.out + elif condition == "xdg_config_home": + assert "YADM_DIR=/xdg/yadm" in run.out + elif condition == "xdg_data_home": + assert "YADM_DATA=/xdg/yadm" in run.out diff --git a/test/test_unit_template_default.py b/test/test_unit_template_default.py index 729784e..7243877 100644 --- a/test/test_unit_template_default.py +++ b/test/test_unit_template_default.py @@ -12,7 +12,7 @@ LOCAL_HOST = "default_Test+@-!^Host" LOCAL_USER = "default_Test+@-!^User" LOCAL_DISTRO = "default_Test+@-!^Distro" LOCAL_DISTRO_FAMILY = "default_Test+@-!^Family" -TEMPLATE = f''' +TEMPLATE = f""" start of template default class = >{{{{yadm.class}}}}< default arch = >{{{{yadm.arch}}}}< @@ -98,8 +98,8 @@ Included section for distro_family = \ wrong family 2 {{% endif %}} end of template -''' -EXPECTED = f''' +""" +EXPECTED = f""" start of template default class = >{LOCAL_CLASS}< default arch = >{LOCAL_ARCH}< @@ -122,17 +122,17 @@ Included section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again) Included section for distro_family = \ {LOCAL_DISTRO_FAMILY} ({LOCAL_DISTRO_FAMILY} again) end of template -''' +""" -INCLUDE_BASIC = 'basic\n' -INCLUDE_VARIABLES = '''\ +INCLUDE_BASIC = "basic\n" +INCLUDE_VARIABLES = """\ included <{{ yadm.class }}> file empty line above -''' -INCLUDE_NESTED = 'no newline at the end' +""" +INCLUDE_NESTED = "no newline at the end" -TEMPLATE_INCLUDE = '''\ +TEMPLATE_INCLUDE = """\ The first line {% include empty %} An empty file removes the line above @@ -141,8 +141,8 @@ An empty file removes the line above {% include dir/nested %} Include basic again: {% include basic %} -''' -EXPECTED_INCLUDE = f'''\ +""" +EXPECTED_INCLUDE = f"""\ The first line An empty file removes the line above basic @@ -152,21 +152,21 @@ empty line above no newline at the end Include basic again: basic -''' +""" def test_template_default(runner, yadm, tmpdir): """Test template_default""" - input_file = tmpdir.join('input') + input_file = tmpdir.join("input") input_file.write(TEMPLATE, ensure=True) 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.write("existing") output_file.chmod(0o400) script = f""" @@ -182,9 +182,9 @@ def test_template_default(runner, yadm, tmpdir): local_distro_family="{LOCAL_DISTRO_FAMILY}" template_default "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read() == EXPECTED assert os.stat(output_file).st_mode == os.stat(input_file).st_mode @@ -192,19 +192,19 @@ def test_template_default(runner, yadm, tmpdir): def test_source(runner, yadm, tmpdir): """Test yadm.source""" - input_file = tmpdir.join('input') - input_file.write('{{yadm.source}}', ensure=True) + input_file = tmpdir.join("input") + input_file.write("{{yadm.source}}", ensure=True) input_file.chmod(FILE_MODE) - output_file = tmpdir.join('output') + 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) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read().strip() == str(input_file) assert os.stat(output_file).st_mode == os.stat(input_file).st_mode @@ -212,22 +212,22 @@ def test_source(runner, yadm, tmpdir): def test_include(runner, yadm, tmpdir): """Test include""" - empty_file = tmpdir.join('empty') - empty_file.write('', ensure=True) + empty_file = tmpdir.join("empty") + empty_file.write("", ensure=True) - basic_file = tmpdir.join('basic') + basic_file = tmpdir.join("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) - nested_file = tmpdir.join('dir').join('nested') + nested_file = tmpdir.join("dir").join("nested") nested_file.write(INCLUDE_NESTED, ensure=True) - input_file = tmpdir.join('input') + input_file = tmpdir.join("input") input_file.write(TEMPLATE_INCLUDE) input_file.chmod(FILE_MODE) - output_file = tmpdir.join('output') + output_file = tmpdir.join("output") script = f""" YADM_TEST=1 source {yadm} @@ -236,9 +236,9 @@ def test_include(runner, yadm, tmpdir): local_system="{LOCAL_SYSTEM}" template_default "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read() == EXPECTED_INCLUDE assert os.stat(output_file).st_mode == os.stat(input_file).st_mode @@ -246,17 +246,17 @@ def test_include(runner, yadm, tmpdir): def test_env(runner, yadm, tmpdir): """Test env""" - input_file = tmpdir.join('input') - input_file.write('{{env.PWD}}', ensure=True) + input_file = tmpdir.join("input") + input_file.write("{{env.PWD}}", ensure=True) input_file.chmod(FILE_MODE) - output_file = tmpdir.join('output') + 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) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert output_file.read().strip() == os.environ['PWD'] + assert run.err == "" + assert output_file.read().strip() == os.environ["PWD"] diff --git a/test/test_unit_template_esh.py b/test/test_unit_template_esh.py index 7f2f2b9..2c91c20 100644 --- a/test/test_unit_template_esh.py +++ b/test/test_unit_template_esh.py @@ -11,7 +11,7 @@ LOCAL_HOST = "esh_Test+@-!^Host" LOCAL_USER = "esh_Test+@-!^User" LOCAL_DISTRO = "esh_Test+@-!^Distro" LOCAL_DISTRO_FAMILY = "esh_Test+@-!^Family" -TEMPLATE = f''' +TEMPLATE = f""" start of template esh class = ><%=$YADM_CLASS%>< esh arch = ><%=$YADM_ARCH%>< @@ -90,8 +90,8 @@ Included esh section for distro_family = \ wrong family 2 <% fi -%> end of template -''' -EXPECTED = f''' +""" +EXPECTED = f""" start of template esh class = >{LOCAL_CLASS}< esh arch = >{LOCAL_ARCH}< @@ -111,21 +111,22 @@ 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 -''' +""" def test_template_esh(runner, yadm, tmpdir): """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.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.write("existing") output_file.chmod(0o400) script = f""" @@ -140,9 +141,9 @@ def test_template_esh(runner, yadm, tmpdir): local_distro_family="{LOCAL_DISTRO_FAMILY}" template_esh "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read().strip() == str(EXPECTED).strip() assert os.stat(output_file).st_mode == os.stat(input_file).st_mode @@ -150,17 +151,17 @@ def test_template_esh(runner, yadm, tmpdir): def test_source(runner, yadm, tmpdir): """Test YADM_SOURCE""" - input_file = tmpdir.join('input') - input_file.write('<%= $YADM_SOURCE %>', ensure=True) + input_file = tmpdir.join("input") + input_file.write("<%= $YADM_SOURCE %>", ensure=True) input_file.chmod(FILE_MODE) - output_file = tmpdir.join('output') + output_file = tmpdir.join("output") script = f""" YADM_TEST=1 source {yadm} template_esh "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read().strip() == str(input_file) assert os.stat(output_file).st_mode == os.stat(input_file).st_mode diff --git a/test/test_unit_template_j2.py b/test/test_unit_template_j2.py index 84afc2d..750ee8c 100644 --- a/test/test_unit_template_j2.py +++ b/test/test_unit_template_j2.py @@ -13,7 +13,7 @@ LOCAL_HOST = "j2_Test+@-!^Host" LOCAL_USER = "j2_Test+@-!^User" LOCAL_DISTRO = "j2_Test+@-!^Distro" LOCAL_DISTRO_FAMILY = "j2_Test+@-!^Family" -TEMPLATE = f''' +TEMPLATE = f""" start of template j2 class = >{{{{YADM_CLASS}}}}< j2 arch = >{{{{YADM_ARCH}}}}< @@ -94,8 +94,8 @@ Included j2 section for distro_family = \ wrong family 2 {{%- endif %}} end of template -''' -EXPECTED = f''' +""" +EXPECTED = f""" start of template j2 class = >{LOCAL_CLASS}< j2 arch = >{LOCAL_ARCH}< @@ -116,22 +116,23 @@ 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 -''' +""" -@pytest.mark.parametrize('processor', ('j2cli', 'envtpl')) +@pytest.mark.parametrize("processor", ("j2cli", "envtpl")) def test_template_j2(runner, yadm, tmpdir, processor): """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.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.write("existing") output_file.chmod(0o400) script = f""" @@ -146,28 +147,28 @@ def test_template_j2(runner, yadm, tmpdir, processor): local_distro_family="{LOCAL_DISTRO_FAMILY}" template_{processor} "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read() == EXPECTED 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): """Test YADM_SOURCE""" - input_file = tmpdir.join('input') - input_file.write('{{YADM_SOURCE}}', ensure=True) + input_file = tmpdir.join("input") + input_file.write("{{YADM_SOURCE}}", ensure=True) input_file.chmod(FILE_MODE) - output_file = tmpdir.join('output') + output_file = tmpdir.join("output") script = f""" YADM_TEST=1 source {yadm} template_{processor} "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read().strip() == str(input_file) assert os.stat(output_file).st_mode == os.stat(input_file).st_mode diff --git a/test/test_unit_upgrade.py b/test/test_unit_upgrade.py index 3463740..cf4f6f4 100644 --- a/test/test_unit_upgrade.py +++ b/test/test_unit_upgrade.py @@ -2,21 +2,21 @@ 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): """Test upgrade() error conditions""" - home = tmpdir.mkdir('home') - yadm_dir = home.join('.config/yadm') - yadm_data = home.join('.local/share/yadm') - override = '' - if condition == 'override': - override = 'override' - if condition == 'equal': + home = tmpdir.mkdir("home") + yadm_dir = home.join(".config/yadm") + yadm_data = home.join(".local/share/yadm") + override = "" + if condition == "override": + override = "override" + if condition == "equal": yadm_data = yadm_dir - if condition == 'existing_repo': - yadm_dir.ensure_dir('repo.git') - yadm_data.ensure_dir('repo.git') + if condition == "existing_repo": + yadm_dir.ensure_dir("repo.git") + yadm_data.ensure_dir("repo.git") script = f""" YADM_TEST=1 source {yadm} @@ -27,17 +27,16 @@ def test_upgrade_errors(tmpdir, runner, yadm, condition): YADM_OVERRIDE_REPO="{override}" upgrade """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.failure - assert 'Unable to upgrade' in run.err - if condition in ['override', 'equal']: - assert 'Paths have been overridden' in run.err - elif condition == 'existing_repo': - assert 'already exists' in run.err + assert "Unable to upgrade" in run.err + if condition in ["override", "equal"]: + assert "Paths have been overridden" in run.err + elif condition == "existing_repo": + 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): """Test upgrade() @@ -45,21 +44,21 @@ def test_upgrade(tmpdir, runner, yadm, condition): mock for git. echo will return true, simulating a positive result from "git ls-files". Also echo will report the parameters for "git mv". """ - legacy_paths = ('config', 'encrypt', 'bootstrap', 'hooks/pre_cmd') - home = tmpdir.mkdir('home') - yadm_dir = home.join('.config/yadm') - yadm_data = home.join('.local/share/yadm') - yadm_legacy = home.join('.yadm') + legacy_paths = ("config", "encrypt", "bootstrap", "hooks/pre_cmd") + home = tmpdir.mkdir("home") + yadm_dir = home.join(".config/yadm") + yadm_data = home.join(".local/share/yadm") + yadm_legacy = home.join(".yadm") - if condition != 'no-paths': - yadm_dir.join('repo.git/config').write('test-repo', ensure=True) - yadm_dir.join('files.gpg').write('files.gpg', ensure=True) + if condition != "no-paths": + yadm_dir.join("repo.git/config").write("test-repo", ensure=True) + yadm_dir.join("files.gpg").write("files.gpg", ensure=True) for path in legacy_paths: yadm_legacy.join(path).write(path, ensure=True) mock_git = "" - if condition != 'no-paths': - mock_git = f''' + if condition != "no-paths": + mock_git = f""" function git() {{ echo "$@" if [[ "$*" = *"submodule status" ]]; then @@ -71,7 +70,7 @@ def test_upgrade(tmpdir, runner, yadm, condition): fi return 0 }} - ''' + """ script = f""" YADM_TEST=1 source {yadm} @@ -85,38 +84,30 @@ def test_upgrade(tmpdir, runner, yadm, condition): function cd {{ echo "$@";}} upgrade """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if condition == 'no-paths': - assert 'Upgrade is not necessary' in run.out + assert run.err == "" + if condition == "no-paths": + assert "Upgrade is not necessary" in run.out else: - for (lpath, npath) in [ - ('repo.git', 'repo.git'), ('files.gpg', 'archive')]: - expected = ( - f'Moving {yadm_dir.join(lpath)} ' - f'to {yadm_data.join(npath)}') + for lpath, npath in [("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 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 - if condition == 'untracked': - assert 'test-repo' in yadm_data.join('repo.git/config').read() - assert 'files.gpg' in yadm_data.join('archive').read() + if condition == "untracked": + assert "test-repo" in yadm_data.join("repo.git/config").read() + assert "files.gpg" in yadm_data.join("archive").read() for path in legacy_paths: assert path in yadm_dir.join(path).read() - elif condition in ['tracked', 'submodules']: - expected = ( - f'mv {yadm_dir.join("files.gpg")} ' - f'{yadm_data.join("archive")}') + elif condition in ["tracked", "submodules"]: + expected = f'mv {yadm_dir.join("files.gpg")} ' f'{yadm_data.join("archive")}' assert expected in run.out - assert 'files tracked by yadm have been renamed' in run.out - if condition == 'submodules': - assert 'submodule deinit -- mymodule' in run.out - assert 'submodule update --init --recursive -- mymodule' \ - in run.out + assert "files tracked by yadm have been renamed" in run.out + if condition == "submodules": + assert "submodule deinit -- mymodule" in run.out + assert "submodule update --init --recursive -- mymodule" in run.out else: - assert 'submodule deinit' not in run.out - assert 'submodule update --init --recursive' not in run.out + assert "submodule deinit" not in run.out + assert "submodule update --init --recursive" not in run.out diff --git a/test/test_unit_x_program.py b/test/test_unit_x_program.py index 883c9af..33b8473 100644 --- a/test/test_unit_x_program.py +++ b/test/test_unit_x_program.py @@ -6,24 +6,25 @@ import pytest @pytest.mark.parametrize( - 'executable, success, value, match', [ - (None, True, 'program', None), - ('cat', True, 'cat', None), - ('badprogram', False, None, 'badprogram'), - ], ids=[ - 'executable missing', - 'valid alternative', - 'invalid alternative', - ]) -@pytest.mark.parametrize('program', ['git', 'gpg']) -def test_x_program( - runner, yadm_cmd, paths, program, executable, success, value, match): + "executable, success, value, match", + [ + (None, True, "program", None), + ("cat", True, "cat", None), + ("badprogram", False, None, "badprogram"), + ], + ids=[ + "executable missing", + "valid alternative", + "invalid alternative", + ], +) +@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 configuration 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] script = f""" @@ -33,11 +34,11 @@ def test_x_program( require_{program} echo ${program.upper()}_PROGRAM """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success == success # [GIT,GPG]_PROGRAM set correctly - if value == 'program': + if value == "program": assert run.out.rstrip() == program elif value: assert run.out.rstrip() == value @@ -46,4 +47,4 @@ def test_x_program( if match: assert match in run.err else: - assert run.err == '' + assert run.err == "" diff --git a/test/test_upgrade.py b/test/test_upgrade.py index 1805882..8ab1e94 100644 --- a/test/test_upgrade.py +++ b/test/test_upgrade.py @@ -6,129 +6,126 @@ import pytest @pytest.mark.parametrize( - 'versions', [ - ('1.12.0', '2.5.0'), - ('1.12.0',), - ('2.5.0',), - ], ids=[ - '1.12.0 -> 2.5.0 -> latest', - '1.12.0 -> latest', - '2.5.0 -> latest', - ]) -@pytest.mark.parametrize( - 'submodule', [False, True], - ids=['no submodule', 'with submodules']) + "versions", + [ + ("1.12.0", "2.5.0"), + ("1.12.0",), + ("2.5.0",), + ], + ids=[ + "1.12.0 -> 2.5.0 -> latest", + "1.12.0 -> latest", + "2.5.0 -> latest", + ], +) +@pytest.mark.parametrize("submodule", [False, True], ids=["no submodule", "with submodules"]) def test_upgrade(tmpdir, runner, versions, submodule): """Upgrade tests""" # pylint: disable=too-many-statements - home = tmpdir.mkdir('HOME') - env = {'HOME': str(home)} - runner(['git', 'config', '--global', 'init.defaultBranch', 'master'], - env=env) - runner(['git', 'config', '--global', 'protocol.file.allow', 'always'], - env=env) + home = tmpdir.mkdir("HOME") + env = {"HOME": str(home)} + runner(["git", "config", "--global", "init.defaultBranch", "master"], env=env) + runner(["git", "config", "--global", "protocol.file.allow", "always"], env=env) if submodule: - ext_repo = tmpdir.mkdir('ext_repo') - ext_repo.join('afile').write('some data') + ext_repo = tmpdir.mkdir("ext_repo") + ext_repo.join("afile").write("some data") - for cmd in (('init',), ('add', 'afile'), ('commit', '-m', 'test')): - run = runner(['git', '-C', str(ext_repo), *cmd]) + for cmd in (("init",), ("add", "afile"), ("commit", "-m", "test")): + run = runner(["git", "-C", str(ext_repo), *cmd]) assert run.success - os.environ.pop('XDG_CONFIG_HOME', None) - os.environ.pop('XDG_DATA_HOME', None) + os.environ.pop("XDG_CONFIG_HOME", None) + os.environ.pop("XDG_DATA_HOME", None) def run_version(version, *args, check_stderr=True): - yadm = f'yadm-{version}' if version else '/yadm/yadm' + yadm = f"yadm-{version}" if version else "/yadm/yadm" run = runner([yadm, *args], shell=True, cwd=str(home), env=env) assert run.success if check_stderr: - assert run.err == '' + assert run.err == "" return run # Initialize the repo with the first version first = versions[0] - run_version(first, 'init') + run_version(first, "init") - home.join('file').write('some data') - run_version(first, 'add', 'file') - run_version(first, 'commit', '-m', '"First commit"') + home.join("file").write("some data") + run_version(first, "add", "file") + run_version(first, "commit", "-m", '"First commit"') if submodule: # When upgrading via 2.5.0 we can't have a submodule that's been added # after being cloned as 2.5.0 fails the upgrade in that case. - can_upgrade_cloned_submodule = '2.5.0' not in versions[1:] + can_upgrade_cloned_submodule = "2.5.0" not in versions[1:] if can_upgrade_cloned_submodule: # Check out a repo and then add it as a submodule - run = runner(['git', '-C', str(home), 'clone', str(ext_repo), 'b']) + run = runner(["git", "-C", str(home), "clone", str(ext_repo), "b"]) 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 - run_version(first, 'submodule', 'add', str(ext_repo), 'a', - check_stderr=False) - run_version(first, 'submodule', 'add', str(ext_repo), 'c', - check_stderr=False) + run_version(first, "submodule", "add", str(ext_repo), "a", 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) if yadm_dir.exists(): break - yadm_dir.join('bootstrap').write('init stuff') - run_version(first, 'add', yadm_dir.join('bootstrap')) - run_version(first, 'commit', '-m', 'bootstrap') + yadm_dir.join("bootstrap").write("init stuff") + run_version(first, "add", yadm_dir.join("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.join('pre_status').write('status') - hooks_dir.join('post_commit').write('commit') + hooks_dir = yadm_dir.mkdir("hooks") + hooks_dir.join("pre_status").write("status") + hooks_dir.join("post_commit").write("commit") - run_version(first, 'config', 'local.class', 'test') - run_version(first, 'config', 'foo.bar', 'true') + run_version(first, "config", "local.class", "test") + run_version(first, "config", "foo.bar", "true") # Run upgrade with intermediate versions and latest latest = None 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: lines = run.err.splitlines() if can_upgrade_cloned_submodule: - assert 'Migrating git directory of' in lines[0] - assert str(home.join('b/.git')) in lines[1] - assert str(yadm_dir.join('repo.git/modules/b')) in lines[2] + assert "Migrating git directory of" in lines[0] + assert str(home.join("b/.git")) in lines[1] + assert str(yadm_dir.join("repo.git/modules/b")) in lines[2] del lines[:3] for line in lines: - assert line.startswith('Submodule') - assert 'registered for path' in line + assert line.startswith("Submodule") + assert "registered for path" in line # Verify result for the final upgrade - run_version(latest, 'status') + run_version(latest, "status") - run = run_version(latest, 'show', 'HEAD:file') - assert run.out == 'some data' + run = run_version(latest, "show", "HEAD:file") + assert run.out == "some data" if submodule: if can_upgrade_cloned_submodule: - assert home.join('b/afile').read() == 'some data' - assert home.join('a/afile').read() == 'some data' - assert home.join('c/afile').read() == 'some data' + assert home.join("b/afile").read() == "some data" + assert home.join("a/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('encrypt').read() == 'secret' + assert yadm_dir.join("bootstrap").read() == "init stuff" + assert yadm_dir.join("encrypt").read() == "secret" - hooks_dir = yadm_dir.join('hooks') - assert hooks_dir.join('pre_status').read() == 'status' - assert hooks_dir.join('post_commit').read() == 'commit' + hooks_dir = yadm_dir.join("hooks") + assert hooks_dir.join("pre_status").read() == "status" + assert hooks_dir.join("post_commit").read() == "commit" - run = run_version(latest, 'config', 'local.class') - assert run.out.rstrip() == 'test' + run = run_version(latest, "config", "local.class") + assert run.out.rstrip() == "test" - run = run_version(latest, 'config', 'foo.bar') - assert run.out.rstrip() == 'true' + run = run_version(latest, "config", "foo.bar") + assert run.out.rstrip() == "true" diff --git a/test/test_version.py b/test/test_version.py index aee6f33..52b18ab 100644 --- a/test/test_version.py +++ b/test/test_version.py @@ -5,34 +5,32 @@ import re import pytest -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def expected_version(yadm): """ Expected semantic version number. This is taken directly out of yadm, searching for the VERSION= string. """ - with open(yadm, encoding='utf-8') as source_file: - yadm_version = re.findall(r'VERSION=([^\n]+)', source_file.read()) + with open(yadm, encoding="utf-8") as source_file: + yadm_version = re.findall(r"VERSION=([^\n]+)", source_file.read()) if yadm_version: return yadm_version[0] - pytest.fail(f'version not found in {yadm}') - return 'not found' + pytest.fail(f"version not found in {yadm}") + return "not found" def test_semantic_version(expected_version): """Version is semantic""" # 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']) -def test_reported_version( - runner, yadm_cmd, cmd, expected_version): +@pytest.mark.parametrize("cmd", ["--version", "version"]) +def test_reported_version(runner, yadm_cmd, cmd, expected_version): """Report correct version and bash/git versions""" run = runner(command=yadm_cmd(cmd)) assert run.success - assert run.err == '' - assert 'bash version' in run.out - assert 'git version' in run.out - assert run.out.endswith(f'\nyadm version {expected_version}\n') + assert run.err == "" + assert "bash version" in run.out + assert "git version" in run.out + assert run.out.endswith(f"\nyadm version {expected_version}\n") diff --git a/test/utils.py b/test/utils.py index f71a758..c36ecac 100644 --- a/test/utils.py +++ b/test/utils.py @@ -6,35 +6,39 @@ This module holds values/functions common to multiple tests. import os import re -ALT_FILE1 = 'test_alt' -ALT_FILE2 = 'test alt/test alt' -ALT_DIR = 'test alt/test alt dir' +ALT_FILE1 = "test_alt" +ALT_FILE2 = "test alt/test alt" +ALT_DIR = "test alt/test alt dir" # Directory based alternates must have a tracked contained file. # This will be the test contained file name -CONTAINED = 'contained_file' +CONTAINED = "contained_file" # These variables are used for making include files which will be processed # within jinja templates -INCLUDE_FILE = 'inc_file' -INCLUDE_DIRS = ['', 'test alt'] -INCLUDE_CONTENT = '8780846c02e34c930d0afd127906668f' +INCLUDE_FILE = "inc_file" +INCLUDE_DIRS = ["", "test alt"] +INCLUDE_CONTENT = "8780846c02e34c930d0afd127906668f" def set_local(paths, variable, value, add=False): """Set local override""" add = "--add" if add else "" - os.system( - f'GIT_DIR={str(paths.repo)} ' - f'git config --local {add} "local.{variable}" "{value}"' - ) + os.system(f"GIT_DIR={str(paths.repo)} " f'git config --local {add} "local.{variable}" "{value}"') -def create_alt_files(paths, suffix, - preserve=False, tracked=True, - encrypt=False, exclude=False, - content=None, includefile=False, - yadm_alt=False, yadm_dir=None): +def create_alt_files( + paths, + suffix, + preserve=False, + tracked=True, + encrypt=False, + exclude=False, + content=None, + includefile=False, + yadm_alt=False, + yadm_dir=None, +): """Create new files, and add to the repo This is used for testing alternate files. In each case, a suffix is @@ -42,7 +46,7 @@ def create_alt_files(paths, suffix, repo handling are dependent upon the function arguments. """ - basepath = yadm_dir.join('alt') if yadm_alt else paths.work + basepath = yadm_dir.join("alt") if yadm_alt else paths.work if not preserve: for remove_path in (ALT_FILE1, ALT_FILE2, ALT_DIR): @@ -60,27 +64,27 @@ def create_alt_files(paths, suffix, # Do not test directory support for jinja alternates test_paths = [new_file1, new_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_names += [ALT_DIR] for test_path in test_paths: if content: - test_path.write('\n' + content, mode='a', ensure=True) + test_path.write("\n" + content, mode="a", ensure=True) assert test_path.exists() _create_includefiles(includefile, test_paths, basepath) _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) def parse_alt_output(output, linked=True): """Parse output of 'alt', and return list of linked files""" - regex = r'Creating (.+) from template (.+)$' + regex = r"Creating (.+) from template (.+)$" if linked: - regex = r'Linking (.+) to (.+)$' + regex = r"Linking (.+) to (.+)$" parsed_list = {} for line in output.splitlines(): match = re.match(regex, line) @@ -95,7 +99,7 @@ def parse_alt_output(output, linked=True): def _create_includefiles(includefile, test_paths, basepath): if includefile: for dpath in INCLUDE_DIRS: - incfile = basepath.join(dpath + '/' + INCLUDE_FILE) + incfile = basepath.join(dpath + "/" + INCLUDE_FILE) incfile.write(INCLUDE_CONTENT, ensure=True) test_paths += [incfile] @@ -110,8 +114,6 @@ def _create_tracked(tracked, test_paths, paths): def _create_encrypt(encrypt, test_names, suffix, paths, exclude, prefix): if encrypt: 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: - paths.encrypt.write( - f'!{prefix + encrypt_name + suffix}\n', mode='a') + paths.encrypt.write(f"!{prefix + encrypt_name + suffix}\n", mode="a") From 040dd461bddb0503359db4c83fda84768dab782c Mon Sep 17 00:00:00 2001 From: LFdev <146497073+LFd3v@users.noreply.github.com> Date: Wed, 8 Nov 2023 23:02:14 +0000 Subject: [PATCH 23/28] Update Arch Linux badge in README.md community repo was merged with extra, please refer to: https://archlinux.org/news/git-migration-announcement/ https://archlinux.org/news/git-migration-completed/ --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 503db26..437144c 100644 --- a/README.md +++ b/README.md @@ -56,8 +56,8 @@ The star count helps others discover yadm. [Git]: https://git-scm.com/ [GnuPG]: https://gnupg.org/ [OpenSSL]: https://www.openssl.org/ -[arch-badge]: https://img.shields.io/archlinux/v/community/any/yadm -[arch-link]: https://archlinux.org/packages/community/any/yadm/ +[arch-badge]: https://img.shields.io/archlinux/v/extra/any/yadm +[arch-link]: https://archlinux.org/packages/extra/any/yadm/ [dev-pages-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=dev-pages [develop-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=develop [develop-commits]: https://github.com/TheLocehiliosan/yadm/commits/develop From 38880abc68cb820759dd96bde7844a844d042f46 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Wed, 30 Oct 2024 17:18:49 -0500 Subject: [PATCH 24/28] Update repo location --- README.md | 37 ++++++++++++++++++------------------- bootstrap | 2 +- completion/README.md | 2 +- contrib/hooks/README.md | 2 +- test/Dockerfile | 4 ++-- yadm.1 | 2 +- yadm.md | 2 +- 7 files changed, 25 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 437144c..21e10aa 100644 --- a/README.md +++ b/README.md @@ -58,30 +58,29 @@ The star count helps others discover yadm. [OpenSSL]: https://www.openssl.org/ [arch-badge]: https://img.shields.io/archlinux/v/extra/any/yadm [arch-link]: https://archlinux.org/packages/extra/any/yadm/ -[dev-pages-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=dev-pages -[develop-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=develop -[develop-commits]: https://github.com/TheLocehiliosan/yadm/commits/develop -[develop-date]: https://img.shields.io/github/last-commit/TheLocehiliosan/yadm/develop.svg?label=develop +[dev-pages-badge]: https://img.shields.io/github/actions/workflow/status/yadm-dev/yadm/test.yml?branch=dev-pages +[develop-badge]: https://img.shields.io/github/actions/workflow/status/yadm-dev/yadm/test.yml?branch=develop +[develop-commits]: https://github.com/yadm-dev/yadm/commits/develop +[develop-date]: https://img.shields.io/github/last-commit/yadm-dev/yadm/develop.svg?label=develop [dotfiles]: https://en.wikipedia.org/wiki/Hidden_file_and_hidden_directory -[gh-pages-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=gh-pages +[gh-pages-badge]: https://img.shields.io/github/actions/workflow/status/yadm-dev/yadm/test.yml?branch=gh-pages [git-crypt]: https://github.com/AGWA/git-crypt [homebrew-badge]: https://img.shields.io/homebrew/v/yadm.svg [homebrew-link]: https://formulae.brew.sh/formula/yadm -[license-badge]: https://img.shields.io/github/license/TheLocehiliosan/yadm.svg -[license-link]: https://github.com/TheLocehiliosan/yadm/blob/master/LICENSE -[master-badge]: https://img.shields.io/github/actions/workflow/status/TheLocehiliosan/yadm/test.yml?branch=master -[master-commits]: https://github.com/TheLocehiliosan/yadm/commits/master -[master-date]: https://img.shields.io/github/last-commit/TheLocehiliosan/yadm/master.svg?label=master +[license-badge]: https://img.shields.io/github/license/yadm-dev/yadm.svg +[license-link]: https://github.com/yadm-dev/yadm/blob/master/LICENSE +[master-badge]: https://img.shields.io/github/actions/workflow/status/yadm-dev/yadm/test.yml?branch=master +[master-commits]: https://github.com/yadm-dev/yadm/commits/master +[master-date]: https://img.shields.io/github/last-commit/yadm-dev/yadm/master.svg?label=master [obs-badge]: https://img.shields.io/badge/OBS-v3.2.2-blue [obs-link]: https://software.opensuse.org//download.html?project=home%3ATheLocehiliosan%3Ayadm&package=yadm -[releases-badge]: https://img.shields.io/github/tag/TheLocehiliosan/yadm.svg?label=latest+release -[releases-link]: https://github.com/TheLocehiliosan/yadm/releases +[releases-badge]: https://img.shields.io/github/tag/yadm-dev/yadm.svg?label=latest+release +[releases-link]: https://github.com/yadm-dev/yadm/releases [transcrypt]: https://github.com/elasticdog/transcrypt -[travis-ci]: https://travis-ci.com/TheLocehiliosan/yadm/branches -[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-commits]: https://github.com/yadm-dev/yadm/commits/gh-pages +[website-date]: https://img.shields.io/github/last-commit/yadm-dev/yadm/gh-pages.svg?label=website [website-link]: https://yadm.io/ -[workflow-dev-pages]: https://github.com/thelocehiliosan/yadm/actions?query=workflow%3a%22test+site%22+branch%3adev-pages -[workflow-develop]: https://github.com/TheLocehiliosan/yadm/actions?query=workflow%3ATests+branch%3Adevelop -[workflow-gh-pages]: https://github.com/thelocehiliosan/yadm/actions?query=workflow%3a%22test+site%22+branch%3agh-pages -[workflow-master]: https://github.com/TheLocehiliosan/yadm/actions?query=workflow%3ATests+branch%3Amaster +[workflow-dev-pages]: https://github.com/yadm-dev/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-gh-pages]: https://github.com/yadm-dev/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 diff --git a/bootstrap b/bootstrap index ab62aa3..70a9687 100755 --- a/bootstrap +++ b/bootstrap @@ -29,7 +29,7 @@ # source <(curl -L bootstrap.yadm.io) # -YADM_REPO="https://github.com/TheLocehiliosan/yadm" +YADM_REPO="https://github.com/yadm-dev/yadm" YADM_RELEASE=${release:-master} REPO_URL="" diff --git a/completion/README.md b/completion/README.md index 1edd861..d84253a 100644 --- a/completion/README.md +++ b/completion/README.md @@ -39,7 +39,7 @@ Load `_yadm` as a plugin in your `.zshrc`: ```zsh fpath=("$ZPLUG_HOME/bin" $fpath) -zplug "TheLocehiliosan/yadm", use:"completion/zsh/_yadm", as:command, defer:2 +zplug "yadm-dev/yadm", use:"completion/zsh/_yadm", as:command, defer:2 ``` ## Fish (manual installation) diff --git a/contrib/hooks/README.md b/contrib/hooks/README.md index 551f6f0..c475ae7 100644 --- a/contrib/hooks/README.md +++ b/contrib/hooks/README.md @@ -11,4 +11,4 @@ this is a place to share it. I recommend *careful review* of any code from here before using it. No guarantees of code quality is assumed. -[hooks-help]: https://github.com/TheLocehiliosan/yadm/blob/master/yadm.md#hooks +[hooks-help]: https://github.com/yadm-dev/yadm/blob/master/yadm.md#hooks diff --git a/test/Dockerfile b/test/Dockerfile index e6a0a97..5485561 100644 --- a/test/Dockerfile +++ b/test/Dockerfile @@ -57,8 +57,8 @@ RUN mkdir /yadm \ && echo "\t@false" >> /yadm/Makefile # Include released versions of yadm to test upgrades -ADD https://raw.githubusercontent.com/TheLocehiliosan/yadm/1.12.0/yadm /usr/local/bin/yadm-1.12.0 -ADD https://raw.githubusercontent.com/TheLocehiliosan/yadm/2.5.0/yadm /usr/local/bin/yadm-2.5.0 +ADD https://raw.githubusercontent.com/yadm-dev/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 RUN chmod +x /usr/local/bin/yadm-* # Configure git to make it easier to test yadm manually diff --git a/yadm.1 b/yadm.1 index c689eb6..6a639be 100644 --- a/yadm.1 +++ b/yadm.1 @@ -989,7 +989,7 @@ Commit a new set of encrypted files Report issues or create pull requests at GitHub: -https://github.com/TheLocehiliosan/yadm/issues +https://github.com/yadm-dev/yadm/issues .SH AUTHOR diff --git a/yadm.md b/yadm.md index 328e384..db607c2 100644 --- a/yadm.md +++ b/yadm.md @@ -798,7 +798,7 @@ ## REPORTING BUGS Report issues or create pull requests at GitHub: - https://github.com/TheLocehiliosan/yadm/issues + https://github.com/yadm-dev/yadm/issues ## AUTHOR From e097d16d8838d558da55ade490f8b3564510c478 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Thu, 31 Oct 2024 15:37:40 -0500 Subject: [PATCH 25/28] Update docker image used by scheduled test --- .github/workflows/schedule.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/schedule.yml b/.github/workflows/schedule.yml index 6f1e267..29befa5 100644 --- a/.github/workflows/schedule.yml +++ b/.github/workflows/schedule.yml @@ -14,7 +14,7 @@ jobs: docker create -t --name yadm-website --entrypoint test/validate - yadm/jekyll:2019-10-17; + yadm/jekyll:2024-10-31; docker cp ./ yadm-website:/srv/jekyll - name: Test Site run: docker start yadm-website -a From 8ba9823407d4c177497f765379bd76f3d83f74ae Mon Sep 17 00:00:00 2001 From: Erik Flodin Date: Sun, 27 Oct 2024 13:38:12 +0100 Subject: [PATCH 26/28] Rewrite default template to handle nested ifs, != and env vars in if The awk script now performs all processing in the BEGIN block using an implementation that is capable of handling if statements which contain nested if statments (fixes #436). To make nested ifs look better, if, else and endif lines can now have optional whitespace before {%. Includes are now handled in the same way as the main file which means that included files can both include other files and have if statements in addition to variables (fixes #406). Include lines can now also have optional whitespace before {%. All variables are handled in the same way now so it's now possible to use env variables in if statements (fixes #488). Also add support for != in addition to == (fixes #358). Thus it's now e.g. possible to check if a variable is set (#477) by doing: {% if yadm.class != ""%} Class is set to {{ yadm.class }} {% endif %} A non-existing yadm or env variable is now replaced with the empty string. --- test/test_unit_template_default.py | 75 ++++++++++++- yadm | 169 ++++++++++++++++------------- 2 files changed, 168 insertions(+), 76 deletions(-) diff --git a/test/test_unit_template_default.py b/test/test_unit_template_default.py index 7243877..aaae0fe 100644 --- a/test/test_unit_template_default.py +++ b/test/test_unit_template_default.py @@ -1,4 +1,5 @@ """Unit tests: template_default""" + import os FILE_MODE = 0o754 @@ -12,6 +13,7 @@ LOCAL_HOST = "default_Test+@-!^Host" LOCAL_USER = "default_Test+@-!^User" LOCAL_DISTRO = "default_Test+@-!^Distro" LOCAL_DISTRO_FAMILY = "default_Test+@-!^Family" +ENV_VAR = "default_Test+@-!^Env" TEMPLATE = f""" start of template default class = >{{{{yadm.class}}}}< @@ -30,6 +32,9 @@ Included section from else {{% if yadm.class == "wrongclass1" %}} wrong class 1 {{% endif %}} +{{% if yadm.class != "wronglcass" %}} +Included section from != +{{% endif\t\t %}} {{% if yadm.class == "{LOCAL_CLASS}" %}} Included section for class = {{{{yadm.class}}}} ({{{{yadm.class}}}} repeated) Multiple lines @@ -97,6 +102,13 @@ Included section for distro_family = \ {{% 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 """ EXPECTED = f""" @@ -111,6 +123,7 @@ default distro_family = >{LOCAL_DISTRO_FAMILY}< classes = >{LOCAL_CLASS2} {LOCAL_CLASS}< Included section from else +Included section from != Included section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated) Multiple lines Included section for second class @@ -121,6 +134,8 @@ Included section for user = {LOCAL_USER} ({LOCAL_USER} repeated) Included section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again) Included section for distro_family = \ {LOCAL_DISTRO_FAMILY} ({LOCAL_DISTRO_FAMILY} again) +Included section for env.VAR = {ENV_VAR} ({ENV_VAR} again) +yadm.no_such_var="" and env.NO_SUCH_VAR="" end of template """ @@ -138,7 +153,7 @@ The first line An empty file removes the line above {%include basic%} {% include "./variables.{{ yadm.os }}" %} -{% include dir/nested %} + {% include dir/nested %} Include basic again: {% include basic %} """ @@ -154,6 +169,42 @@ Include basic again: 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): """Test template_default""" @@ -182,7 +233,7 @@ def test_template_default(runner, yadm, tmpdir): local_distro_family="{LOCAL_DISTRO_FAMILY}" template_default "{input_file}" "{output_file}" """ - run = runner(command=["bash"], inp=script) + run = runner(command=["bash"], inp=script, env={"VAR": ENV_VAR}) assert run.success assert run.err == "" assert output_file.read() == EXPECTED @@ -243,12 +294,30 @@ def test_include(runner, yadm, tmpdir): 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) - input_file.chmod(FILE_MODE) output_file = tmpdir.join("output") script = f""" diff --git a/yadm b/yadm index 09da278..9dc9240 100755 --- a/yadm +++ b/yadm @@ -368,87 +368,110 @@ function template_default() { # the explicit "space + tab" character class used below is used because not # all versions of awk seem to support the POSIX character classes [[:blank:]] read -r -d '' awk_pgm << "EOF" -# built-in default template processor BEGIN { - blank = "[ ]" - c["class"] = class - c["classes"] = classes - c["arch"] = arch - c["os"] = os - c["hostname"] = host - c["user"] = user - c["distro"] = distro - c["distro_family"] = distro_family - c["source"] = source - ifs = "^{%" blank "*if" - els = "^{%" blank "*else" blank "*%}$" - end = "^{%" blank "*endif" blank "*%}$" - skp = "^{%" blank "*(if|else|endif)" - vld = conditions() - inc_start = "^{%" blank "*include" blank "+\"?" - inc_end = "\"?" blank "*%}$" - inc = inc_start ".+" inc_end - prt = 1 - err = 0 -} -END { exit err } -{ replace_vars() } # variable replacements -$0 ~ vld, $0 ~ end { - if ($0 ~ vld || $0 ~ end) prt=1; - if ($0 ~ els) prt=0; - if ($0 ~ skp) next; -} -($0 ~ ifs && $0 !~ vld), $0 ~ end { - if ($0 ~ ifs && $0 !~ vld) prt=0; - if ($0 ~ els || $0 ~ end) prt=1; - if ($0 ~ skp) next; -} -{ if (!prt) next } -$0 ~ inc { - file = $0 - sub(inc_start, "", file) - sub(inc_end, "", file) - sub(/^[^\/].*$/, source_dir "/&", file) + yadm["class"] = class + yadm["classes"] = classes + yadm["arch"] = arch + yadm["os"] = os + yadm["hostname"] = host + yadm["user"] = user + yadm["distro"] = distro + yadm["distro_family"] = distro_family + yadm["source"] = source - while ((res = getline 0) { - replace_vars() - print + VARIABLE = "(env|yadm)\\.[a-zA-Z0-9_]+" + + current = 0 + filename[current] = ARGV[1] + line[current] = 0 + + level = 0 + skip[level] = 0 + + for (; current >= 0; --current) { + while ((res = getline 0) { + ++line[current] + if ($0 ~ "^[ \t]*\\{%[ \t]*if[ \t]+" VARIABLE "[ \t]*[!=]=[ \t]*\".*\"[ \t]*%\\}$") { + if (skip[level]) { skip[++level] = 1; continue } + + match($0, VARIABLE) + lhs = substr($0, RSTART, RLENGTH) + match($0, /[!=]=/) + op = substr($0, RSTART, RLENGTH) + match($0, /".*"/) + rhs = replace_vars(substr($0, RSTART + 1, RLENGTH - 2)) + + if (lhs == "yadm.class") { + lhs = "not" rhs + split(classes, cls_array, "\n") + for (idx in cls_array) { + if (rhs == cls_array[idx]) { lhs = rhs; break } + } + } + else { + lhs = replace_vars("{{" lhs "}}") + } + + if (op == "==") { skip[++level] = lhs != rhs } + else { skip[++level] = lhs == rhs } + } + else if (/^[ \t]*\{%[ \t]*else[ \t]*%\}$/) { + if (level == 0 || skip[level] < 0) { error("else without matching if") } + skip[level] = skip[level] ? skip[level - 1] : -1 + } + else if (/^[ \t]*\{%[ \t]*endif[ \t]*%\}$/) { + if (--level < 0) { error("endif without matching if") } + } + else if (!skip[level]) { + $0 = replace_vars($0) + if (match($0, /^[ \t]*\{%[ \t]*include[ \t]+("[^"]+"|[^"]+)[ \t]*%\}$/)) { + include = $0 + sub(/^[ \t]*\{%[ \t]*include[ \t]+"?/, "", include) + sub(/"?[ \t]*%\}$/, "", include) + if (index(include, "/") != 1) { + include = source_dir "/" include + } + filename[++current] = include + line[current] = 0 + } + else { print } + } + } + if (res >= 0) { close(filename[current]) } + else if (current == 0) { error("could not read input file") } + else { --current; error("could not read include file '" filename[current + 1] "'") } } - if (res < 0) { - printf "%s:%d: error: could not read '%s'\n", FILENAME, NR, file | "cat 1>&2" - err = 1 + if (level > 0) { + current = 0 + error("unterminated if") } - close(file) - next + exit 0 } -{ print } -function replace_vars() { - for (label in c) { - gsub(("{{" blank "*yadm\\." label blank "*}}"), c[label]) - } - for (label in ENVIRON) { - gsub(("{{" blank "*env\\." label blank "*}}"), ENVIRON[label]) - } +function error(text) { + printf "%s:%d: error: %s\n", + filename[current], line[current], text > "/dev/stderr" + exit 1 } -function condition_helper(label, value) { - gsub(/[\\.^$(){}\[\]|*+?]/, "\\\\&", value) - return sprintf("yadm\\.%s" blank "*==" blank "*\"%s\"", label, value) -} -function conditions() { - pattern = ifs blank "+(" - for (label in c) { - if (label != "class") { - value = c[label] - pattern = sprintf("%s%s|", pattern, condition_helper(label, value)); +function replace_vars(input) { + output = "" + while (match(input, "\\{\\{[ \t]*" VARIABLE "[ \t]*\\}\\}")) { + if (RSTART > 1) { + output = output substr(input, 0, RSTART - 1) + } + data = substr(input, RSTART + 2, RLENGTH - 4) + input = substr(input, RSTART + RLENGTH) + + gsub(/[ \t]+/, "", data) + split(data, fields, /\./) + + if (fields[1] == "env") { + output = output ENVIRON[fields[2]] + } + else { + output = output yadm[fields[2]] } } - split(classes, cls_array, "\n") - for (idx in cls_array) { - value = cls_array[idx] - pattern = sprintf("%s%s|", pattern, condition_helper("class", value)); - } - sub(/\|$/, ")" blank "*%}$", pattern) - return pattern + return output input } EOF From 144f35b82d9b1943f456103de870b23b0d8aa84f Mon Sep 17 00:00:00 2001 From: Erik Flodin Date: Mon, 4 Nov 2024 23:12:28 +0100 Subject: [PATCH 27/28] Update links under .github/ to point to new github org --- .github/CONTRIBUTING.md | 30 +++++++++++------------ .github/ISSUE_TEMPLATE.md | 2 +- .github/ISSUE_TEMPLATE/BUG_REPORT.md | 6 ++--- .github/ISSUE_TEMPLATE/FEATURE_REQUEST.md | 2 +- .github/ISSUE_TEMPLATE/OTHER.md | 2 +- .github/ISSUE_TEMPLATE/SUPPORT.md | 4 +-- .github/PULL_REQUEST_TEMPLATE.md | 4 +-- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 66ea8b2..b98bc2c 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,7 +1,7 @@ # Introduction -Thank you for considering contributing to **yadm**. I develop this project in my -limited spare time, so help is very appreciated. +Thank you for considering contributing to **yadm**. We develop this project in +our limited spare time, so help is very appreciated. All contributors must follow our [Code of Conduct][conduct]. Please make sure you are welcoming and friendly during your interactions, and report any @@ -17,7 +17,8 @@ To contribute, you can: * Star the yadm repo, the star count helps others discover yadm. * Report [bugs](#reporting-a-bug) * Request [features/enhancements](#suggesting-a-feature-or-enhancement) -* Contribute changes to [code, tests](#contributing-code), and [documentation](#improving-documentation) +* Contribute changes to [code, tests](#contributing-code), and + [documentation](#improving-documentation) * Maintain installation [packages](#maintaining-packages) * Help other users by [answering support questions](#answering-support-questions) @@ -70,14 +71,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 the problem being fixed. -The easiest way to start this container, is to clone the [TheLocehiliosan/yadm +The easiest way to start this container, is to clone the [yadm repo][yadm-repo], and use the `scripthost` make target. _(You will need `make` and `docker` installed.)_ For example: ```text -$ git clone https://github.com/TheLocehiliosan/yadm.git +$ git clone https://github.com/yadm-dev/yadm.git $ cd yadm $ make scripthost version=1.12.0 Starting scripthost version="1.12.0" (recording script) @@ -203,7 +204,7 @@ these principles when making changes. 3. Add the official repository (`upstream`) as a remote repository. ```text - $ git remote add upstream https://github.com/TheLocehiliosan/yadm.git + $ git remote add upstream https://github.com/yadm-dev/yadm.git ``` 4. Verify you can run the test harness. _(This will require dependencies: @@ -361,25 +362,24 @@ see if you can help. [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 [conduct]: CODE_OF_CONDUCT.md -[contrib-hooks]: https://github.com/TheLocehiliosan/yadm/tree/master/contrib/hooks +[contrib-hooks]: https://github.com/yadm-dev/yadm/tree/master/contrib/hooks [flake8]: https://pypi.org/project/flake8/ [groff-man]: https://www.gnu.org/software/groff/manual/html_node/man.html -[hooks-help]: https://github.com/TheLocehiliosan/yadm/blob/master/yadm.md#hooks +[hooks-help]: https://github.com/yadm-dev/yadm/blob/master/yadm.md#hooks [html-proofer]: https://github.com/gjtorikian/html-proofer [jekyll]: https://jekyllrb.com -[new-bug]: https://github.com/TheLocehiliosan/yadm/issues/new?template=BUG_REPORT.md -[new-feature]: https://github.com/TheLocehiliosan/yadm/issues/new?template=FEATURE_REQUEST.md -[open-issues]: https://github.com/TheLocehiliosan/yadm/issues +[new-bug]: https://github.com/yadm-dev/yadm/issues/new?template=BUG_REPORT.md +[new-feature]: https://github.com/yadm-dev/yadm/issues/new?template=FEATURE_REQUEST.md +[open-issues]: https://github.com/yadm-dev/yadm/issues [pr-help]: https://help.github.com/en/articles/creating-a-pull-request-from-a-fork [pylint]: https://pylint.org/ [pytest]: https://pytest.org/ -[questions]: https://github.com/TheLocehiliosan/yadm/labels/question -[refactor]: https://github.com/TheLocehiliosan/yadm/issues/146 +[questions]: https://github.com/yadm-dev/yadm/labels/question [shellcheck]: https://www.shellcheck.net [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 -[yadm-man]: https://github.com/TheLocehiliosan/yadm/blob/master/yadm.md -[yadm-repo]: https://github.com/TheLocehiliosan/yadm +[yadm-man]: https://github.com/yadm-dev/yadm/blob/master/yadm.md +[yadm-repo]: https://github.com/yadm-dev/yadm [yadm/jekyll]: https://hub.docker.com/r/yadm/jekyll [yadm/testbed]: https://hub.docker.com/r/yadm/testbed [yamllint]: https://github.com/adrienverge/yamllint diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 29dc730..ecfa5a2 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,6 +1,6 @@ ### Is your feature request related to a problem? Please describe. diff --git a/.github/ISSUE_TEMPLATE/OTHER.md b/.github/ISSUE_TEMPLATE/OTHER.md index 936a4a4..43fa999 100644 --- a/.github/ISSUE_TEMPLATE/OTHER.md +++ b/.github/ISSUE_TEMPLATE/OTHER.md @@ -8,7 +8,7 @@ assignees: '' --- ### This issue is about diff --git a/.github/ISSUE_TEMPLATE/SUPPORT.md b/.github/ISSUE_TEMPLATE/SUPPORT.md index 22bd849..92dc298 100644 --- a/.github/ISSUE_TEMPLATE/SUPPORT.md +++ b/.github/ISSUE_TEMPLATE/SUPPORT.md @@ -8,11 +8,11 @@ assignees: '' --- ### This question is about diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d2f12b9..80061e4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -29,6 +29,6 @@ Be sure to preface the issue/PR numbers with a "#". Please review [yadm's Contributing Guide][3] for best practices. -[1]: https://github.com/TheLocehiliosan/yadm/blob/master/.github/CONTRIBUTING.md#test-conventions +[1]: https://github.com/yadm-dev/yadm/blob/master/.github/CONTRIBUTING.md#test-conventions [2]: https://help.github.com/en/articles/signing-commits -[3]: https://github.com/TheLocehiliosan/yadm/blob/master/.github/CONTRIBUTING.md +[3]: https://github.com/yadm-dev/yadm/blob/master/.github/CONTRIBUTING.md From 8e5d4b1578d4a93d45972575a10daa49d4f07eea Mon Sep 17 00:00:00 2001 From: Erik Flodin Date: Fri, 8 Nov 2024 19:51:25 +0100 Subject: [PATCH 28/28] Pass classes as separate arguments to template_default To work around problem with passing newlines in variable with awk on darwin. This fixes #437. --- yadm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/yadm b/yadm index 9dc9240..2afbe65 100755 --- a/yadm +++ b/yadm @@ -369,6 +369,10 @@ function template_default() { # all versions of awk seem to support the POSIX character classes [[:blank:]] read -r -d '' awk_pgm << "EOF" BEGIN { + classes = ARGV[2] + for (i = 3; i < ARGC; ++i) { + classes = classes "\n" ARGV[i] + } yadm["class"] = class yadm["classes"] = classes yadm["arch"] = arch @@ -485,9 +489,8 @@ EOF -v distro_family="$local_distro_family" \ -v source="$input" \ -v source_dir="$(dirname "$input")" \ - -v classes="$(join_string $'\n' "${local_classes[@]}")" \ "$awk_pgm" \ - "$input" > "$temp_file" || rm -f "$temp_file" + "$input" "${local_classes[@]}" > "$temp_file" || rm -f "$temp_file" move_file "$input" "$output" "$temp_file" }