bugfix: gpg2 is called 'gpg' on macOS
The gpg2 command isn't always around; it's sometimes called gpg. This is the case with the brew-installed version, and it's breaking our tests. - [x] Look for both 'gpg2' and 'gpg' when finding the command - [x] If we find 'gpg', ensure the version is 2 or higher - [x] Add tests for version detection.
This commit is contained in:
parent
910df8cb4e
commit
8011fedd9c
3 changed files with 89 additions and 13 deletions
|
@ -7,6 +7,9 @@
|
|||
|
||||
import pytest
|
||||
|
||||
import llnl.util.filesystem as fs
|
||||
|
||||
import spack.util.executable
|
||||
import spack.util.gpg
|
||||
|
||||
from spack.paths import mock_gpg_data_path, mock_gpg_keys_path
|
||||
|
@ -14,15 +17,45 @@
|
|||
from spack.util.executable import ProcessError
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def gpg():
|
||||
return SpackCommand('gpg')
|
||||
#: spack command used by tests below
|
||||
gpg = SpackCommand('gpg')
|
||||
|
||||
|
||||
# test gpg command detection
|
||||
@pytest.mark.parametrize('cmd_name,version', [
|
||||
('gpg', 'undetectable'), # undetectable version
|
||||
('gpg', 'gpg (GnuPG) 1.3.4'), # insufficient version
|
||||
('gpg', 'gpg (GnuPG) 2.2.19'), # sufficient version
|
||||
('gpg2', 'gpg (GnuPG) 2.2.19'), # gpg2 command
|
||||
])
|
||||
def test_find_gpg(cmd_name, version, tmpdir, mock_gnupghome, monkeypatch):
|
||||
with tmpdir.as_cwd():
|
||||
with open(cmd_name, 'w') as f:
|
||||
f.write("""\
|
||||
#!/bin/sh
|
||||
echo "{version}"
|
||||
""".format(version=version))
|
||||
fs.set_executable(cmd_name)
|
||||
|
||||
monkeypatch.setitem(os.environ, "PATH", str(tmpdir))
|
||||
if version == 'undetectable' or version.endswith('1.3.4'):
|
||||
with pytest.raises(spack.util.gpg.SpackGPGError):
|
||||
exe = spack.util.gpg.Gpg.gpg()
|
||||
else:
|
||||
exe = spack.util.gpg.Gpg.gpg()
|
||||
assert isinstance(exe, spack.util.executable.Executable)
|
||||
|
||||
|
||||
def test_no_gpg_in_path(tmpdir, mock_gnupghome, monkeypatch):
|
||||
monkeypatch.setitem(os.environ, "PATH", str(tmpdir))
|
||||
with pytest.raises(spack.util.gpg.SpackGPGError):
|
||||
spack.util.gpg.Gpg.gpg()
|
||||
|
||||
|
||||
@pytest.mark.maybeslow
|
||||
@pytest.mark.skipif(not spack.util.gpg.Gpg.gpg(),
|
||||
reason='These tests require gnupg2')
|
||||
def test_gpg(gpg, tmpdir, mock_gnupghome):
|
||||
def test_gpg(tmpdir, mock_gnupghome):
|
||||
# Verify a file with an empty keyring.
|
||||
with pytest.raises(ProcessError):
|
||||
gpg('verify', os.path.join(mock_gpg_data_path, 'content.txt'))
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
import tempfile
|
||||
import xml.etree.ElementTree
|
||||
|
||||
import ordereddict_backport
|
||||
|
@ -674,9 +675,19 @@ def writer_key_function():
|
|||
|
||||
|
||||
@pytest.fixture()
|
||||
def mock_gnupghome(tmpdir, monkeypatch):
|
||||
monkeypatch.setattr(spack.util.gpg, 'GNUPGHOME', str(tmpdir.join('gpg')))
|
||||
def mock_gnupghome(monkeypatch):
|
||||
# GNU PGP can't handle paths longer than 108 characters (wtf!@#$) so we
|
||||
# have to make our own tmpdir with a shorter name than pytest's.
|
||||
# This comes up because tmp paths on macOS are already long-ish, and
|
||||
# pytest makes them longer.
|
||||
short_name_tmpdir = tempfile.mkdtemp()
|
||||
monkeypatch.setattr(spack.util.gpg, 'GNUPGHOME', short_name_tmpdir)
|
||||
monkeypatch.setattr(spack.util.gpg.Gpg, '_gpg', None)
|
||||
|
||||
yield
|
||||
|
||||
# clean up, since we are doing this manually
|
||||
shutil.rmtree(short_name_tmpdir)
|
||||
|
||||
##########
|
||||
# Fake archives and repositories
|
||||
|
|
|
@ -4,10 +4,14 @@
|
|||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
import spack.error
|
||||
import spack.paths
|
||||
from spack.util.executable import Executable
|
||||
import spack.version
|
||||
from spack.util.executable import which
|
||||
|
||||
_gnupg_version_re = r"^gpg \(GnuPG\) (.*)$"
|
||||
|
||||
GNUPGHOME = spack.paths.gpg_path
|
||||
|
||||
|
@ -28,15 +32,39 @@ def parse_keys_output(output):
|
|||
|
||||
|
||||
class Gpg(object):
|
||||
_gpg = None
|
||||
|
||||
@staticmethod
|
||||
def gpg():
|
||||
# TODO: Support loading up a GPG environment from a built gpg.
|
||||
gpg = Executable('gpg2')
|
||||
if not os.path.exists(GNUPGHOME):
|
||||
os.makedirs(GNUPGHOME)
|
||||
os.chmod(GNUPGHOME, 0o700)
|
||||
gpg.add_default_env('GNUPGHOME', GNUPGHOME)
|
||||
return gpg
|
||||
if Gpg._gpg is None:
|
||||
gpg = which('gpg2', 'gpg')
|
||||
|
||||
if not gpg:
|
||||
raise SpackGPGError("Spack requires gpg version 2 or higher.")
|
||||
|
||||
# ensure that the version is actually >= 2 if we find 'gpg'
|
||||
if gpg.name == 'gpg':
|
||||
output = gpg('--version', output=str)
|
||||
match = re.search(_gnupg_version_re, output, re.M)
|
||||
|
||||
if not match:
|
||||
raise SpackGPGError("Couldn't determine version of gpg")
|
||||
|
||||
v = spack.version.Version(match.group(1))
|
||||
if v < spack.version.Version('2'):
|
||||
raise SpackGPGError("Spack requires GPG version >= 2")
|
||||
|
||||
# make the GNU PG path if we need to
|
||||
# TODO: does this need to be in the spack directory?
|
||||
# we should probably just use GPG's regular conventions
|
||||
if not os.path.exists(GNUPGHOME):
|
||||
os.makedirs(GNUPGHOME)
|
||||
os.chmod(GNUPGHOME, 0o700)
|
||||
gpg.add_default_env('GNUPGHOME', GNUPGHOME)
|
||||
|
||||
Gpg._gpg = gpg
|
||||
return Gpg._gpg
|
||||
|
||||
@classmethod
|
||||
def create(cls, **kwargs):
|
||||
|
@ -112,3 +140,7 @@ def list(cls, trusted, signing):
|
|||
cls.gpg()('--list-public-keys')
|
||||
if signing:
|
||||
cls.gpg()('--list-secret-keys')
|
||||
|
||||
|
||||
class SpackGPGError(spack.error.SpackError):
|
||||
"""Class raised when GPG errors are detected."""
|
||||
|
|
Loading…
Reference in a new issue