Commit b29acb69 authored by Cyril Roelandt's avatar Cyril Roelandt
Browse files

Allow users to specify a specific version of the package on the CLI.

parent 97c4730d
......@@ -6,6 +6,8 @@ Based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Added
- New option: --color, to show colored logs
- Allow users to specify a specific version of the package on the CLI
using "package@version"
## [0.10.3] - 2019-07-31
### Fixed
......
......@@ -69,6 +69,10 @@ You may also want to recursively package all of its requirements:
$ upt package -f pypi -b guix -r requests
You may also want to package a version other than the latest one:
$ upt package -f pypi -b guix requests@2.16.0
The package definition will be written to the standard output.
......
......@@ -14,7 +14,7 @@ upt list-backends
upt list-frontends
upt package [-f FRONTEND] [-b BACKEND] [-o OUTPUT] [\--debug] [\--quiet] PACKAGE
upt package [-f FRONTEND] [-b BACKEND] [-o OUTPUT] [\--debug] [\--quiet] PACKAGE[@VERSION]
# DESCRIPTION
Create a package for a distribution (such as OpenBSD, Fedora, etc.) from a
......@@ -36,8 +36,9 @@ The following subcommands are available:
**list-frontends**
: List all installed frontends.
**package [options...] \<package>**
**package [options...] \<package>[@version]**
: Package the given package. This usually requires options, described below.
If no version is specified, the latest one is used.
# OPTIONS
......@@ -127,6 +128,11 @@ on the backend: it should do the most natural thing.
: upt package requests
**Same, but package a given version**
: upt package requests@2.16.0
# BUGS
Bugs can be reported to upt@framalistes.org. A web interface is also available
......
......@@ -10,6 +10,7 @@ from .upt import Frontend
from .upt import Package
from .upt import PackageRequirement
from .exceptions import InvalidPackageNameError
from .exceptions import InvalidPackageVersionError
from .exceptions import UnhandledFrontendError
from .checksum import HashUnknown
from .checksum import HashUnavailable
......@@ -14,6 +14,18 @@ class InvalidPackageNameError(Exception):
f'{self.backend}')
class InvalidPackageVersionError(Exception):
"""Invalid or non-existing version of a valid package"""
def __init__(self, backend, pkg_name, version):
self.backend = backend
self.pkg_name = pkg_name
self.version = version
def __str__(self):
return (f'Version {self.version} of package {self.pkg_name} '
f'could not be found by backend {self.backend}')
class UnhandledFrontendError(Exception):
"""Frontend not supported by backend."""
def __init__(self, backend, frontend):
......
......@@ -117,6 +117,19 @@ class TestUtils(unittest.TestCase):
self.assertDictEqual(upt.upt._get_installed_backends(),
fake_backends)
def test_extract_name_version_from_package(self):
test_data = [
('foobar', ('foobar', None)),
('foobar@1.2', ('foobar', '1.2')),
('foobar@', ('foobar', None)),
]
for pkgname, expected_result in test_data:
self.assertEqual(
upt.upt._extract_name_version_from_package(pkgname),
expected_result
)
class TestParser(unittest.TestCase):
def test_package_single_frontend(self):
......@@ -134,6 +147,11 @@ class TestParser(unittest.TestCase):
args = 'package -r requests'.split()
parser.parse_args(args)
def test_package_specific_version(self):
parser = upt.upt.create_parser(['pypi'], ['guix'])
args = 'package requests@2.16.0'.split()
parser.parse_args(args)
def test_package_missing_frontend(self):
parser = upt.upt.create_parser(['cpan', 'pypi'], ['guix', 'nix'])
args = 'package -b guix requests'.split()
......@@ -291,6 +309,27 @@ class TestCommandLine(unittest.TestCase):
upt.upt.main()
self.assertEqual(exit.exception.code, 1)
@mock.patch('upt.upt._get_installed_frontends',
return_value={'valid-frontend': mock.Mock()})
@mock.patch('upt.upt._get_installed_backends',
return_value={'valid-backend': mock.Mock()})
def test_package_invalid_package_version(self, m_backends, m_frontends):
cmdline = 'package -f valid-frontend -b valid-backend pkgname@13.37'
sys.argv.extend(cmdline.split())
def frontend_fail(*args, **kwargs):
raise upt.exceptions.InvalidPackageVersionError('valid-backend',
'pkgname',
'13.37')
fake_frontend = mock.Mock()
fake_frontend().parse.side_effect = frontend_fail
m_frontends.return_value = {
'valid-frontend': fake_frontend
}
with self.assertRaises(SystemExit) as exit:
upt.upt.main()
self.assertEqual(exit.exception.code, 1)
class TestArchive(unittest.TestCase):
def setUp(self):
......@@ -350,7 +389,8 @@ class TestRecursion(unittest.TestCase):
self.backend.package_versions = mock.Mock()
def test_no_dependencies(self):
upt.upt.package('foo', self.frontend, self.backend, None, True, [])
upt.upt.package('foo', None, self.frontend, self.backend,
None, True, [])
self.backend.create_package.assert_called()
self.backend.needs_requirement.assert_not_called()
......@@ -358,7 +398,8 @@ class TestRecursion(unittest.TestCase):
self.package.requirements = {
'run': [upt.PackageRequirement('bar')]
}
upt.upt.package('foo', self.frontend, self.backend, None, True, [])
upt.upt.package('foo', None, self.frontend, self.backend,
None, True, [])
self.backend.create_package.assert_called()
self.backend.needs_requirement.assert_called()
......@@ -366,7 +407,8 @@ class TestRecursion(unittest.TestCase):
self.package.requirements = {
'run': [upt.PackageRequirement('foo')]
}
upt.upt.package('foo', self.frontend, self.backend, None, True, [])
upt.upt.package('foo', None, self.frontend, self.backend,
None, True, [])
self.backend.create_package.assert_called_once()
self.backend.needs_requirement.assert_not_called()
......@@ -374,7 +416,8 @@ class TestRecursion(unittest.TestCase):
self.package.requirements = {
'run': [upt.PackageRequirement('bar')]
}
upt.upt.package('foo', self.frontend, self.backend, None, False, [])
upt.upt.package('foo', None, self.frontend, self.backend,
None, False, [])
self.backend.create_package.assert_called_once()
self.backend.needs_requirement.assert_not_called()
......
......@@ -281,7 +281,9 @@ def create_parser(frontends, backends):
help='Recursively package requirements'),
parser_package.add_argument('-c', '--color', action='store_true',
help='Show colored logging output'),
parser_package.add_argument('package', help='Name of the package')
parser_package.add_argument('package', help='Name of the package. '
'Use <package>@<version> to '
'package a specific version.')
return parser
......@@ -303,13 +305,14 @@ def _get_installed_backends():
return _get_installed_plugins('upt.backends')
def package(pkg_name, frontend, backend, output, recursive, packaged=[]):
def package(pkg_name, version, frontend, backend, output, recursive,
packaged=[]):
logger = logging.getLogger('upt')
try:
upt_pkg = None
upt.log.logger_set_formatter(logger, 'Frontend')
upt_pkg = frontend.parse(pkg_name)
upt_pkg = frontend.parse(pkg_name, version)
upt_pkg.frontend = frontend.name
upt.log.logger_set_formatter(logger, 'Backend')
backend.create_package(upt_pkg, output=output)
......@@ -322,12 +325,13 @@ def package(pkg_name, frontend, backend, output, recursive, packaged=[]):
continue
if backend.needs_requirement(requirement, phase):
package(requirement.name, frontend, backend,
package(requirement.name, None, frontend, backend,
output, recursive, packaged)
except upt.exceptions.UnhandledFrontendError as e:
print(e, file=sys.stderr)
sys.exit(1)
except upt.exceptions.InvalidPackageNameError as e:
except (upt.exceptions.InvalidPackageNameError,
upt.exceptions.InvalidPackageVersionError) as e:
print(e, file=sys.stderr)
sys.exit(1)
finally:
......@@ -336,6 +340,14 @@ def package(pkg_name, frontend, backend, output, recursive, packaged=[]):
upt_pkg._clean()
def _extract_name_version_from_package(package):
try:
name, version = package.split('@')
except ValueError:
name, version = package, None
return name, version or None
def main():
frontends = _get_installed_frontends()
backends = _get_installed_backends()
......@@ -375,5 +387,6 @@ def main():
# values for us.
frontend = frontends[args.frontend]()
backend = backends[args.backend]()
name, version = _extract_name_version_from_package(args.package)
upt.log.create_logger(args.log_level, args.color)
package(args.package, frontend, backend, args.output, args.recursive)
package(name, version, frontend, backend, args.output, args.recursive)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment