yadm-dev / yadm

Yet Another Dotfiles Manager
https://yadm.io/
GNU General Public License v3.0
5.11k stars 175 forks source link

Impossible to run tests standalone #179

Closed kgizdov closed 4 years ago

kgizdov commented 4 years ago

Describe the bug

The current testing environment for yadm presents some issues for crossplatform testing and deployment. Specifically, it requires a custom testing environment such as:

To reproduce

Steps to reproduce the behavior:

  1. Try to create Arch Linux package with tests
  2. Run check function during build:
    check() {
    local _pwd="$(pwd)"
    local _work="${_pwd}"/work/yadm
    mkdir -p "${_work}"/.gnupg
    export HOME="${_work}"
    export GNUPGHOME="${_work}"/.gnupg
    echo "no-secmem-warning" > "${GNUPGHOME}"/gpg.conf
    export GPG_TTY="$(tty)"
    # export PINENTRY_USER_DATA="USE_CURSES=1"
    export PINENTRY_USER_DATA="USE_TTY=1"
    py.test -k "not envtpl" -m "not deprecated"
    }
  3. See error:
    
    =================================== FAILURES ===================================
    _________ test_symmetric_encrypt[clean-encrypt_exists-matching_phrase] _________

runner = <class 'conftest.Runner'> yadm_y = <function yadm_y..command_list at 0x7fea48e023a0> paths = Paths(pgm='/build/yadm/src/yadm-2.0.1/yadm', root=local('/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clea...ot/yadm/config'), encrypt=local('/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm/encrypt')) encrypt_targets = ['inc file1', 'inc dir/inc file2', 'globs file1', 'globs dir/globs file2', 'extest/inglob1'] overwrite = False, missing_encrypt = False, mismatched_phrase = False

@pytest.mark.parametrize(
    'mismatched_phrase', [False, True],
    ids=['matching_phrase', 'mismatched_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_y, paths, encrypt_targets,
        overwrite, missing_encrypt, mismatched_phrase):
    """Test symmetric encryption"""

    if missing_encrypt:
        paths.encrypt.remove()

    matched_phrase = PASSPHRASE
    if mismatched_phrase:
        matched_phrase = 'mismatched'

    if overwrite:
        paths.archive.write('existing archive')

    run = runner(yadm_y('encrypt'), expect=[
        ('passphrase:', PASSPHRASE),
        ('passphrase:', matched_phrase),
        ])

    if missing_encrypt or mismatched_phrase:
        assert run.failure
    else:
      assert run.success

E AssertionError: assert False E + where False = Runner(['expect']).success

test/test_encryption.py:181: AssertionError ---------------------------- Captured stdout setup ----------------------------- Initialized empty shared Git repository in /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm/repo.git/ ----------------------------- Captured stdout call ----------------------------- EXPECT:set timeout 2 spawn "/build/yadm/src/yadm-2.0.1/yadm" "-Y" "/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm" "encrypt" expect { "passphrase:" {send "ExamplePassword\r"} timeout {close;exit 128} } expect { "passphrase:" {send "ExamplePassword\r"} timeout {close;exit 128} } expect eof foreach {pid spawnid os_error_flag value} [wait] break exit $value Runner(['expect']) RUN: code:1 RUN: input: set timeout 2 spawn "/build/yadm/src/yadm-2.0.1/yadm" "-Y" "/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm" "encrypt" expect { "passphrase:" {send "ExamplePassword\r"} timeout {close;exit 128} } expect { "passphrase:" {send "ExamplePassword\r"} timeout {close;exit 128} } expect eof foreach {pid spawnid os_error_flag value} [wait] break exit $value RUN: stdout: spawn /build/yadm/src/yadm-2.0.1/yadm -Y /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm encrypt Encrypting the following files: extest/inglob1 globs dir globs file1 inc dir/inc file2 inc file1

gpg: WARNING: unsafe permissions on homedir '/build/yadm/src/yadm-2.0.1/work/yadm/.gnupg' gpg: keybox '/build/yadm/src/yadm-2.0.1/work/yadm/.gnupg/pubring.kbx' created gpg: problem with the agent: Permission denied gpg: error creating passphrase: Operation cancelled gpg: symmetric encryption of '[stdin]' failed: Operation cancelled ExamplePassword ERROR: Unable to write /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm/files.gpg

RUN: stderr: expect: spawn id exp4 not open while executing "expect eof" ... =========================== short test summary info ============================ SKIPPED [1] /build/yadm/src/yadm-2.0.1/test/test_syntax.py:18: Unsupported shellcheck version SKIPPED [1] /build/yadm/src/yadm-2.0.1/test/test_syntax.py:27: Unsupported pylint version SKIPPED [1] /build/yadm/src/yadm-2.0.1/test/test_syntax.py:40: Unsupported flake8 version SKIPPED [1] /build/yadm/src/yadm-2.0.1/test/test_syntax.py:49: Unsupported yamllint version FAILED test/test_encryption.py::test_symmetric_encrypt[clean-encrypt_exists-matching_phrase] FAILED test/test_encryption.py::test_symmetric_encrypt[clean-encrypt_exists-mismatched_phrase] FAILED test/test_encryption.py::test_symmetric_encrypt[overwrite-encrypt_exists-matching_phrase] FAILED test/test_encryption.py::test_symmetric_encrypt[overwrite-encrypt_exists-mismatched_phrase] FAILED test/test_encryption.py::test_symmetric_decrypt[decrypt-archive_exists-correct_phrase] FAILED test/test_encryption.py::test_symmetric_decrypt[list-archive_exists-correct_phrase] FAILED test/test_encryption.py::test_offer_to_add[tracked] - AssertionError: ... FAILED test/test_encryption.py::test_offer_to_add[untracked_answer_y] - Asser... FAILED test/test_encryption.py::test_offer_to_add[untracked_answer_n] - Asser... FAILED test/test_encryption.py::test_encrypt_added_to_exclude - AssertionErro... ========== 10 failed, 325 passed, 4 skipped, 211 deselected in 54.69s ==========



### Expected behavior

I'm trying to publish yadm in official Arch Linux repos, thus `yadm` tests should be able to run from a disto's build environment, so the package can be easily integrated. Should not be so strict with package version requirements (maybe only curb to major versions). Should have a safer way of interfacing with GPG.

### Environment

 - Operating system: Arch Linux x86_64, `gpg-2.2.17`, `shellcheck-0.7.0`, `pylint-2.4.4`, `flake8-3.7.9`, `yamllint-1.19.0`, `j2cli-0.3.12b`
 - Version yadm: 2.0.1
 - Version Git: 2.24.0

### Additional context
TheLocehiliosan commented 4 years ago

First, thank you for your packaging efforts. I appreciate it, and I'm sure Arch users will appreciate it even more.

I've gone through pains to make yadm run on a large number of systems, and gracefully downgrade when optional dependencies are not available. Indeed many aspects of it are written to the lowest common denominator. So for yadm, the systems supported make the coding very constrained. However, for the tests, I want to be as expressive as necessary, and less constrained. But that does translate to a constrained target the tests are designed to run within. This is how I settled upon Docker/Compose as the requirement for full testing. Docker is a reasonable testing dependency for most development machines.

I realize that Docker is NOT, a common dependency for automated packaging requirements. However, I also don't see the entire catalog of tests as necessary for packaging. After all, yadm is not compiled, but is a single Bash program.

I would like to help work through the testing troubles you've had. Perhaps some compromise can be reached.

The version requirements for the various linters are necessary, as the version determines what standards are enforced. I do not want any contributors surprised because their tests succeed, but my own do not (due to version differences). The tests are gracefully skipped if the supported version is missing.

Perhaps, one change could be to make gpg-based tests, gracefully skipped if the supported version is not present. Tests requiring "expect" could also be skipped if it isn't present. Based on the output above, that might allow you to run a large portion of the tests, and not have any failures.

Another possibility would be to write an appropriate test for the build environment. For example, the Homebrew formula implements a very brief test which creates a repo, and adds some content to that repo.

I'm not familiar with packaging for Arch, or what requirements might be present for the official Arch repos. Would skipping the tests that lacked dependencies solve these problems?

kgizdov commented 4 years ago

Cheers for detailed explanation. It will be probably nice to give a bit of detail myself.

Arch Linux packaging is simple, but comes with high standards for reproducibility and quality. What that means is that, we basically spawn a kind of a complete "new system" with only the required packages for each build. So we start from scratch, add only the dependencies for the package and the build itself and run the build. The tests run in that bare system. This essentially proves that the package as defined can pass its own tests in a complete Arch Linux system containing only the package and its dependent software. Simply put, we verify every Arch user can run the build script and verify the tests pass for them as well and this know the package has correct functionality on their system.

If we put Docker in there, we are not able to satisfactory prove that the package will pass its tests (work correctly) in an Arch Linux system, since the whole point of Docker is to do the opposite. Substitute any other distro/OS for Arch Linux and the same argument stands. Docker tests only run in Docker container and do not prove a package will behave correctly outside.

Thus, I run your tests by providing yadm a whole bare system with all required dependencies to run everything. Most tests do run and pass fine, but a few do fail. This issue is for those.

Basically, main issue I have is that the tests depend on gpg v1. However, I bet almost everyone caring for security actually runs gpg v2. So those tests while succeeding with gpg v1 don't actually test the correct thing.

Secondary to this, the linter versions being fixed are a bit weird as well. I understand your argument, however, on the flip side there are a couple of issues. How would anyone reasonably acquire these specific versions every time they run your tests and then move to their distribution versions after the fact? Seems like a lot of hassle. Also, newer version of linters mean better/improved standards in most cases, even better catching/parsing of weird stuff. Blocking newer versions kinda looks like blocking code improvements (not really, but you get my argument). Moreover, you can easily specify which standard you care and don't care and have any version of the linter only test for those.

Finally, I am a bit confused with the /yadm test directory. Why do you chose a directory which is both hard coded and directly under root (/)? It would be better if one could specify that directory from the environment (e.g. "${TESTPREFIX}/yadm"). Not all users will have root permissions on the machine they work with in order to provide with such a path.

I'm open to discuss these further and get something working. :)

TheLocehiliosan commented 4 years ago

I'm also sure we can reach a solution.


GnuPG

gpg-v1 implements the same interface as gpg-v2, which allows yadm to support either version.

The reason gpg-v1 is currently used in the test harness is because when I created it, I was unable to get tty-based credentials working with gpg-v2. I need to script the interaction (using expect) and the curses interface of gpg-agent (which is a requirement for gpg-v2) will not work in that way. However, I just did a bit of investigation, and I've successfully used gpg-agent + pinentry-tty within my container, so I should be able to adopt that. Assuming GnuPG2 + gpg-agent + pinentry-tty is available in Arch Linux, this should solve that dependency issue. 👍 It seems like this is the biggest issue right now.

Linting

I think it would be easy to provide an argument which removed any version checking for linters. But be aware, the tests may fail if you have different versions which enforce different constraints. In my opinion, the linting is about code quality/maintainability, and not about testing functionality. But I'm happy to create such an argument.

Test path

I don't believe a /yadm directory is required for the tests to work. pytest creates temporary directories for tests and fixtures and that is what it uses. I think you may be looking at the Makefile, which runs the tests via Docker/Compose. The compose configuration exposes the current directory as a read-only mount at /yadm. That directory could easily be anywhere else. If it isn't being run within a container, you can just run pytest directly, which looks to be what you've mostly had success with already.

Timing

I think I can work on these adjustments to testing after the next release (which hopefully isn't very far off).


Let me know if you think this will address the major packaging problems you're experiencing.

TheLocehiliosan commented 4 years ago

On other note for when I make these changes. It seems like pinentry-tty isn't necessary. It is good enough to start gpg-agent with --allow-loopback-pinentry.

kgizdov commented 4 years ago

@TheLocehiliosan ok, please let me know how it goes, so I can try it out as well. Thanks.

TheLocehiliosan commented 4 years ago

I did a bunch of experimentation with this tonight. It turns out with pinentry-tty or loopback, it still doesn't work with "expect" (it's difficult to mock). The passphrase need to come directly from the tty, it isn't part of any file handle of the gpg process.

The good news is I think I can accomplish this with a custom pinentry program which mocks the input I need for testing. I have a proof-of-concept working, but I need to flesh it out more.

rasa commented 4 years ago

Have you tried --passphrase-fd 0 per https://wiki.archlinux.org/index.php/GnuPG#Unattended_passphrase ?

TheLocehiliosan commented 4 years ago

@kgizdov I've published some changes to branch wip/test-portability-179. Can you test the GPG2 tests with that? I have not yet changed the linter version requirements, but that will be easy. I want to know if this works just with gpg2 installed. I believe it will.

TheLocehiliosan commented 4 years ago

Have you tried --passphrase-fd 0 per https://wiki.archlinux.org/index.php/GnuPG#Unattended_passphrase ?

@rasa I'm familiar with ways to get gpg to use different sources for passphrase, but I don't want to have have conditional code for testing within yadm. I just want it to use gpg-agent like it will in the wild. The new method of using a custom pinentry program works pretty well.

TheLocehiliosan commented 4 years ago

@kgizdov I've updated wip/test-portability-179 again. I've also added a command line option to pylint, --force-linters. When you provide this option, linters are run regardless of the version installed.

Please let me know how this branch works out for your package building.

kgizdov commented 4 years ago

@TheLocehiliosan cool, thanks, I'll test and come to you

kgizdov commented 4 years ago

@TheLocehiliosan so almost everything now succeeds, except for some gpg: Warning: using insecure memory! and other messages going into stderr. What I'm saying is, these are not errors per say, but do end up in the error output, which breaks the return code and tests don't pass. There should be some GPG option to suppress these warnings or at least convert them into warnings, not errors. Here's the log.

==> Starting check()...
============================= test session starts ==============================
platform linux -- Python 3.8.0, pytest-5.3.1, py-1.8.0, pluggy-0.13.1
cachedir: /tmp
rootdir: /build/yadm/src/yadm-wip-test-portability-179, inifile: pytest.ini
plugins: pylint-0.14.1, flake8-1.0.4
collected 563 items / 211 deselected / 352 selected

test/test_alt.py ...........................................             [ 12%]
test/test_alt_copy.py ..........                                         [ 15%]
test/test_assert_private_dirs.py ...                                     [ 15%]
test/test_bootstrap.py ...                                               [ 16%]
test/test_clean.py .                                                     [ 17%]
test/test_clone.py ...................                                   [ 22%]
test/test_config.py .......                                              [ 24%]
test/test_encryption.py F...F.......................F..F                 [ 33%]
test/test_enter.py .......                                               [ 35%]
test/test_git.py .                                                       [ 35%]
test/test_help.py ..                                                     [ 36%]
test/test_hooks.py ........                                              [ 38%]
test/test_init.py .....                                                  [ 40%]
test/test_introspect.py ......                                           [ 41%]
test/test_list.py ...                                                    [ 42%]
test/test_perms.py .............                                         [ 46%]
test/test_syntax.py .....                                                [ 47%]
test/test_unit_bootstrap_available.py ...                                [ 48%]
test/test_unit_choose_template_cmd.py ......                             [ 50%]
test/test_unit_configure_paths.py .......                                [ 52%]
test/test_unit_exclude_encrypted.py ............                         [ 55%]
test/test_unit_is_valid_branch_name.py ...........                       [ 58%]
test/test_unit_issue_legacy_path_warning.py ................             [ 63%]
test/test_unit_parse_encrypt.py .....                                    [ 64%]
test/test_unit_query_distro.py ...                                       [ 65%]
test/test_unit_record_score.py ......                                    [ 67%]
test/test_unit_record_template.py ..                                     [ 67%]
test/test_unit_relative_path.py ..........                               [ 70%]
test/test_unit_remove_stale_links.py ....                                [ 71%]
test/test_unit_score_file.py ........................................... [ 84%]
........................                                                 [ 90%]
test/test_unit_set_local_alt_values.py ......                            [ 92%]
test/test_unit_set_os.py ...                                             [ 93%]
test/test_unit_set_yadm_dir.py ....                                      [ 94%]
test/test_unit_template_default.py ..                                    [ 95%]
test/test_unit_template_j2.py ..                                         [ 95%]
test/test_unit_upgrade.py .......                                        [ 97%]
test/test_unit_x_program.py ......                                       [ 99%]
test/test_version.py ..                                                  [100%]

=================================== FAILURES ===================================
___________ test_symmetric_encrypt[clean-encrypt_exists-good_phrase] ___________

runner = <class 'conftest.Runner'>
yadm_y = <function yadm_y.<locals>.command_list at 0x7fba25b4f550>
paths = Paths(pgm='/build/yadm/src/yadm-wip-test-portability-179/yadm', root=local('/tmp/pytest-of-builduser/pytest-0/test_sym...ot/yadm/config'), encrypt=local('/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm/encrypt'))
encrypt_targets = ['inc file1', 'inc dir/inc file2', 'globs file1', 'globs dir/globs file2', 'extest/inglob1']
gnupg = GNUPG(home=local('/tmp/pytest-of-builduser/pytest-0/gnupghome0'), pw=<function gnupg.<locals>.register_gpg_password at 0x7fba25b35ee0>)
bad_phrase = False, overwrite = False, missing_encrypt = False

    @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_y, paths, encrypt_targets,
            gnupg, bad_phrase, overwrite, missing_encrypt):
        """Test symmetric encryption"""

        if missing_encrypt:
            paths.encrypt.remove()

        if bad_phrase:
            gnupg.pw('')
        else:
            gnupg.pw(PASSPHRASE)

        if overwrite:
            paths.archive.write('existing archive')

        env = os.environ.copy()
        env['GNUPGHOME'] = gnupg.home
        run = runner(yadm_y('encrypt'), env=env)

        if missing_encrypt or bad_phrase:
            assert run.failure
        else:
            assert run.success
>           assert run.err == ''
E           AssertionError: assert 'gpg: Warning...ure memory!\n' == ''
E             - gpg: Warning: using insecure memory!

test/test_encryption.py:212: AssertionError
---------------------------- Captured stdout setup -----------------------------
Runner(['gpg', '-k'])
  RUN: code:0
  RUN: stdout:

  RUN: stderr:
gpg: Warning: using insecure memory!
gpg: keybox '/tmp/pytest-of-builduser/pytest-0/gnupghome0/pubring.kbx' created
gpg: /tmp/pytest-of-builduser/pytest-0/gnupghome0/trustdb.gpg: trustdb created

Initialized empty shared Git repository in /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm/repo.git/
----------------------------- Captured stdout call -----------------------------
Runner(['/build/yadm/src/yadm-wip-test-portability-179/yadm', '-Y', '/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm', 'encrypt'])
  RUN: code:0
  RUN: stdout:
Encrypting the following files:
extest/inglob1
globs dir
globs file1
inc dir/inc file2
inc file1

Wrote new file: /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm/files.gpg

  RUN: stderr:
gpg: Warning: using insecure memory!

_________ test_symmetric_encrypt[overwrite-encrypt_exists-good_phrase] _________

runner = <class 'conftest.Runner'>
yadm_y = <function yadm_y.<locals>.command_list at 0x7fba25bc1ee0>
paths = Paths(pgm='/build/yadm/src/yadm-wip-test-portability-179/yadm', root=local('/tmp/pytest-of-builduser/pytest-0/test_sym...ot/yadm/config'), encrypt=local('/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_overwri0/root/yadm/encrypt'))
encrypt_targets = ['inc file1', 'inc dir/inc file2', 'globs file1', 'globs dir/globs file2', 'extest/inglob1']
gnupg = GNUPG(home=local('/tmp/pytest-of-builduser/pytest-0/gnupghome0'), pw=<function gnupg.<locals>.register_gpg_password at 0x7fba25b35ee0>)
bad_phrase = False, overwrite = True, missing_encrypt = False

    @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_y, paths, encrypt_targets,
            gnupg, bad_phrase, overwrite, missing_encrypt):
        """Test symmetric encryption"""

        if missing_encrypt:
            paths.encrypt.remove()

        if bad_phrase:
            gnupg.pw('')
        else:
            gnupg.pw(PASSPHRASE)

        if overwrite:
            paths.archive.write('existing archive')

        env = os.environ.copy()
        env['GNUPGHOME'] = gnupg.home
        run = runner(yadm_y('encrypt'), env=env)

        if missing_encrypt or bad_phrase:
            assert run.failure
        else:
            assert run.success
>           assert run.err == ''
E           AssertionError: assert 'gpg: Warning...ure memory!\n' == ''
E             - gpg: Warning: using insecure memory!

test/test_encryption.py:212: AssertionError
---------------------------- Captured stdout setup -----------------------------
Initialized empty shared Git repository in /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_overwri0/root/yadm/repo.git/
----------------------------- Captured stdout call -----------------------------
Runner(['/build/yadm/src/yadm-wip-test-portability-179/yadm', '-Y', '/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_overwri0/root/yadm', 'encrypt'])
  RUN: code:0
  RUN: stdout:
Encrypting the following files:
extest/inglob1
globs dir
globs file1
inc dir/inc file2
inc file1

Wrote new file: /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_overwri0/root/yadm/files.gpg

  RUN: stderr:
gpg: Warning: using insecure memory!

__________________________ test_offer_to_add[tracked] __________________________

runner = <class 'conftest.Runner'>
yadm_y = <function yadm_y.<locals>.command_list at 0x7fba25c12280>
paths = Paths(pgm='/build/yadm/src/yadm-wip-test-portability-179/yadm', root=local('/tmp/pytest-of-builduser/pytest-0/test_off...0/root/yadm/config'), encrypt=local('/tmp/pytest-of-builduser/pytest-0/test_offer_to_add_tracked_0/root/yadm/encrypt'))
encrypt_targets = ['inc file1', 'inc dir/inc file2', 'globs file1', 'globs dir/globs file2', 'extest/inglob1']
gnupg = GNUPG(home=local('/tmp/pytest-of-builduser/pytest-0/gnupghome0'), pw=<function gnupg.<locals>.register_gpg_password at 0x7fba25b35ee0>)
untracked = False

    @pytest.mark.parametrize(
        'untracked',
        [False, 'y', 'n'],
        ids=['tracked', 'untracked_answer_y', 'untracked_answer_n'])
    def test_offer_to_add(
            runner, yadm_y, paths, encrypt_targets, gnupg, untracked):
        """Test offer to add encrypted archive

        All the other encryption tests use an archive outside of the work tree.
        However, the archive is often inside the work tree, and if it is, there
        should be an offer to add it to the repo if it is not tracked.
        """

        worktree_archive = paths.work.join('worktree-archive.tar.gpg')

        expect = []

        gnupg.pw(PASSPHRASE)
        env = os.environ.copy()
        env['GNUPGHOME'] = gnupg.home

        if untracked:
            expect.append(('add it now', untracked))
        else:
            worktree_archive.write('exists')
            os.system(' '.join(yadm_y('add', str(worktree_archive))))

        run = runner(
            yadm_y('encrypt', '--yadm-archive', str(worktree_archive)),
            env=env,
            expect=expect
            )

        assert run.success
>       assert run.err == ''
E       AssertionError: assert 'gpg: Warning...ure memory!\n' == ''
E         - gpg: Warning: using insecure memory!

test/test_encryption.py:402: AssertionError
---------------------------- Captured stdout setup -----------------------------
Initialized empty shared Git repository in /tmp/pytest-of-builduser/pytest-0/test_offer_to_add_tracked_0/root/yadm/repo.git/
----------------------------- Captured stdout call -----------------------------
Runner(['/build/yadm/src/yadm-wip-test-portability-179/yadm', '-Y', '/tmp/pytest-of-builduser/pytest-0/test_offer_to_add_tracked_0/root/yadm', 'encrypt', '--yadm-archive', '/tmp/pytest-of-builduser/pytest-0/test_offer_to_add_tracked_0/root/work/worktree-archive.tar.gpg'])
  RUN: code:0
  RUN: stdout:
Encrypting the following files:
extest/inglob1
globs dir
globs file1
inc dir/inc file2
inc file1

Wrote new file: /tmp/pytest-of-builduser/pytest-0/test_offer_to_add_tracked_0/root/work/worktree-archive.tar.gpg

  RUN: stderr:
gpg: Warning: using insecure memory!

________________________ test_encrypt_added_to_exclude _________________________

runner = <class 'conftest.Runner'>
yadm_y = <function yadm_y.<locals>.command_list at 0x7fba25adc790>
paths = Paths(pgm='/build/yadm/src/yadm-wip-test-portability-179/yadm', root=local('/tmp/pytest-of-builduser/pytest-0/test_enc...oot/yadm/config'), encrypt=local('/tmp/pytest-of-builduser/pytest-0/test_encrypt_added_to_exclude0/root/yadm/encrypt'))
gnupg = GNUPG(home=local('/tmp/pytest-of-builduser/pytest-0/gnupghome0'), pw=<function gnupg.<locals>.register_gpg_password at 0x7fba25b35ee0>)

    @pytest.mark.usefixtures('ds1_copy')
    def test_encrypt_added_to_exclude(runner, yadm_y, paths, gnupg):
        """Confirm that .config/yadm/encrypt is added to exclude"""

        gnupg.pw(PASSPHRASE)
        env = os.environ.copy()
        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)

        run = runner(yadm_y('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 run.success
>       assert run.err == ''
E       AssertionError: assert 'gpg: Warning...ure memory!\n' == ''
E         - gpg: Warning: using insecure memory!

test/test_encryption.py:440: AssertionError
----------------------------- Captured stdout call -----------------------------
Runner(['/build/yadm/src/yadm-wip-test-portability-179/yadm', '-Y', '/tmp/pytest-of-builduser/pytest-0/test_encrypt_added_to_exclude0/root/yadm', 'encrypt'])
  RUN: code:0
  RUN: stdout:
Encrypting the following files:
test-encrypt-data

Wrote new file: /tmp/pytest-of-builduser/pytest-0/test_encrypt_added_to_exclude0/root/yadm/files.gpg

  RUN: stderr:
gpg: Warning: using insecure memory!

=========================== short test summary info ============================
FAILED test/test_encryption.py::test_symmetric_encrypt[clean-encrypt_exists-good_phrase]
FAILED test/test_encryption.py::test_symmetric_encrypt[overwrite-encrypt_exists-good_phrase]
FAILED test/test_encryption.py::test_offer_to_add[tracked] - AssertionError: ...
FAILED test/test_encryption.py::test_encrypt_added_to_exclude - AssertionErro...
=========== 4 failed, 348 passed, 211 deselected in 60.03s (0:01:00) ===========
==> ERROR: A failure occurred in check().
TheLocehiliosan commented 4 years ago

@kgizdov OK, we're close... I'm curious, what version of libgcrypt is installed on the build host?

kgizdov commented 4 years ago

I assume you mean libgcrypt 1.8.5

TheLocehiliosan commented 4 years ago

@kgizdov OK, just checking as I read about a bug with an older version. I'm not sure what the situation is with the build environment, but that's a warning that should be suppressible during tests. I just pushed another update. Can you try that?

TheLocehiliosan commented 4 years ago

@kgizdov Any luck with my changes that suppress the insecure memory warnings?

kgizdov commented 4 years ago

@TheLocehiliosan just tested and everything passed fine. Good work!

TheLocehiliosan commented 4 years ago

@kgizdov Fantastic! I'll merge these changes into develop, and likely release 2.2.0 tomorrow. Thanks for your patience. I'm much happier with mocking the gpg-agent credentials this way also.