Verified Commit 6a0bb155 authored by Karel Koci's avatar Karel Koci 🤘
Browse files

Refine support for Install and Uninstall version requests

This changes how user can specify version limitation for Install request
and adds the same feature for Uninstall as well.

The original method might have been in some ways confusing. Extra field
affects potentially multiple packages but version is commonly specified
only for one specific package request. There is also fully supported way
to specify version limitation in dependency description. This includes
that and reuses the same code.

Also note that code that splits package name from version is improved
here. It should be more reliable now.

This change makes extra option version of Install request obsolete so it
is removed and no longer supported.

Important thing to point out is also that documentation stated that
version extra option can be also table but code never supported that. It
could always have been just single string. That means that we do not
loose any feature set.
parent e7f1fdac
Pipeline #72551 passed with stages
in 6 minutes and 47 seconds
......@@ -7,12 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- extra argument `pkg_hash_required` for `Repository` command
- support for version limitation of `Install` and `Uninstall` requests in package
name (such as `foo (>= 1.0.0)`).
### Changed
- Execution is now terminated when there is no hash for package being installed in
repository index unless `pkg_hash_required` is set to `false` for that
repository.
### Removed
- Extra option `version` for `Install` request. You should append version
specifier to package name the same way as it is done for dependencies.
## [68.0.0] - 2020-11-06
### Added
- Queue messages for 'upgrade' and 'downgrade' now also prints current version in
......
......@@ -291,6 +291,9 @@ Install
This command takes multiple package names. It requires that given packages are to
be present in the system.
Package name can be extended by version specification the same way as descibed for
dependency description (_check for `requests_version` feature_).
The resolving of extra options acts the same as with `Uninstall`.
Available extra options:
......@@ -301,19 +304,6 @@ priority::
requirement with the higher priority wins. In case of a draw, an
error is reported. The priority defaults to 50 and must be between 0
and 100.
version::
Limits the considered versions. This may be a single string or a
table with multiple strings. If there are multiple, each is
considered a condition and all must pass for a version to be
accepted. Using of operators `<`, `<=`, `>`, `>=` is possible. Also,
if the version is prefixed with `~`, it acts as a lua string match
pattern against the version. So this would accept versions between 3
and 7 and ignore the `.0` ones: `{ ">=3.0", "<=7.0", "~^%d+%.[1-9]%d*" }`.
The default is no condition, therefore all versions available pass.
From the versions that match and satisfy all dependency
requirements, the one with highest version is chosen. In case when
no available version matches, the currently installed version is
also considered as a fallback.
repository::
Usually, all repositories are searched according to their
priorities. If you specify this option as a lua table, only the
......@@ -349,6 +339,10 @@ NOTE: There is also obsoleted but still working option `ignore` which if set to
any boolean true value it is considered as if `optional` extra option would be
set to `true`.
NOTE: In the past there was also `version` extra option. This was a way to request
specific version before version could have been specified as an extension to
package name.
Uninstall
~~~~~~~~~
......@@ -357,7 +351,10 @@ Uninstall
This command is the opposite of `Install`. This command takes multiple package
names. It ensures none of the packages is installed.
TIP: This is not needed most cases, since unneeded packages are removed
Package name can be extended by version specification the same way as descibed for
dependency description.
TIP: This is not needed in most cases, since unneeded packages are removed
automatically.
Extra options modify the packages preceding them, but only up to the
......@@ -603,6 +600,9 @@ request_condition::
fatal_missing_pkg_hash::
Missing or no supported hash for package in repository index is considered as
fatal error now.
requests_version::
Updater supports versioned `Install` and `Uninstall` requests. Extra argument
`version` of `Install` no longer works.
installed
~~~~~~~~~
......
......@@ -63,7 +63,7 @@ module "backend"
-- luacheck: globals cmd_timeout cmd_kill_timeout
-- Functions that we want to access from outside (ex. for testing purposes)
-- luacheck: globals block_parse block_split block_dump_ordered pkg_status_dump package_postprocess status_parse get_parent config_modified
-- luacheck: globals repo_parse status_dump pkg_unpack pkg_examine collision_check installed_confs steal_configs pkg_merge_files pkg_merge_control pkg_config_info pkg_cleanup_files pkg_update_alternatives pkg_remove_alternatives script_run control_cleanup version_cmp version_match run_state user_path_move get_nonconf_files get_changed_files
-- luacheck: globals repo_parse status_dump pkg_unpack pkg_examine collision_check installed_confs steal_configs pkg_merge_files pkg_merge_control pkg_config_info pkg_cleanup_files pkg_update_alternatives pkg_remove_alternatives script_run control_cleanup parse_pkg_specifier version_cmp version_match run_state user_path_move get_nonconf_files get_changed_files
--[[
Configuration of the module. It is supported (yet unlikely to be needed) to modify
......@@ -1095,6 +1095,23 @@ function config_modified(file, hash)
end
end
--[[
Parse/split package name and version string.
Returns name and version. Name can be returned as nil and in that case package
specified is invalid. Version can be nil when name is not and in that case version
just wasn't specified.
]]
function parse_pkg_specifier(spec)
local name, version
name, version = spec:match("^%s*(%S+)%s*%(([^)]*)%)%s*$")
name = name or spec:match("^%s*(%S+)%s*$")
if version then
version = version:gsub("^%s*([<>=~]*)%s*(%S+)%s*$", "%1%2")
end
if version and version:match("^%s*$") then version = nil end
return name, version
end
--[[
Compare two version strings. Return -1, 0, 1 if the first version
is smaller, equal or larger respectively.
......
......@@ -138,15 +138,12 @@ function deps_canon(old_deps)
sub = sub
})
elseif old_deps:match('%s') then
-- When there is space in parsed name, then there might be version specified
local dep = old_deps:gsub('^%s', ''):gsub('%s$', '')
local name = dep:match('^%S+')
local version = dep:match('%(.+%)$')
version = version and version:sub(2,-2):gsub('^%s', ''):gsub('%s$', '')
if not version or version == "" then
return name -- No version detected, use just name.
else
local name, version = backend.parse_pkg_specifier(old_deps)
if version then
return { tp = "dep-package", name = name, version = version }
else
-- TODO possibly report error if name is nil?
return name -- No version detected, use just name.
end
elseif old_deps == '' then
return nil
......
......@@ -35,6 +35,7 @@ local assert = assert
local table = table
local utils = require "utils"
local uri = require "uri"
local backend = require "backend"
local opmode = opmode
local DBG = DBG
local WARN = WARN
......@@ -326,11 +327,17 @@ local function content_request(cmd, allowed, ...)
if extras.condition then
extra_check_deps(cmd, "condition", extras.condition)
end
extra_annul_ignore(extras, 'Install extra option "version" is obsolete and should not be used. Specify version directly to package name instead.', true) -- Note: this is applicable only to Install
extra_annul_ignore(extras, 'Install extra option "ignore" is obsolete and should not be used. Use "optional" instead.', true) -- Note: this is applicable only to Install
for _, pkg_name in ipairs(batch) do
DBG("Request " .. cmd .. " of " .. pkg_name)
for _, pkg_spec in ipairs(batch) do
local pkg_name, pkg_version = backend.parse_pkg_specifier(pkg_spec)
if not pkg_name then
error(utils.exception("bad value", "Invalid package specifier for request " .. cmd .. ": " .. tostring(pkg_spec)))
end
DBG("Request " .. cmd .. " of " .. pkg_spec)
local request = {
package = new_package(pkg_name, {}),
version = pkg_version,
tp = cmd
}
utils.table_merge(request, extras)
......@@ -351,12 +358,12 @@ end
local allowed_install_extras = {
["priority"] = utils.arr2set({"number"}),
["version"] = utils.arr2set({"string"}),
["repository"] = utils.arr2set({"string", "table"}),
["reinstall"] = utils.arr2set({"boolean"}),
["critical"] = utils.arr2set({"boolean"}),
["optional"] = utils.arr2set({"boolean"}),
["condition"] = utils.arr2set({"string", "table"}),
["version"] = utils.arr2set({"string"}), -- obsolete
["ignore"] = utils.arr2set({"table"}), -- obsolete
}
......
......@@ -57,6 +57,7 @@ local updater_features = utils.arr2set({
'no_error_virtual',
'request_condition',
'fatal_missing_pkg_hash',
'requests_version',
})
-- Available functions and "constants" from global environment
......
......@@ -958,6 +958,23 @@ Depends: libc, ubox, libubox, libuci
]]))
end
function test_parse_pkg_specifier()
for _, v in pairs({"foo", " foo "}) do
assert_equal("foo", B.parse_pkg_specifier(v))
end
for _, v in pairs({"foo (> 1.2.3)", "foo(> 1.2.3)", " foo (> 1.2.3) ", " foo (> 1.2.3) "}) do
local pkg, version = B.parse_pkg_specifier(v)
print("here: " .. v .. " vs " .. tostring(pkg))
assert_equal("foo", pkg)
assert_equal(">1.2.3", version)
end
assert_nil(B.parse_pkg_specifier(""))
assert_equal("foo", B.parse_pkg_specifier("foo ( )"))
assert_equal("foo", B.parse_pkg_specifier("foo ()"))
assert_nil(B.parse_pkg_specifier("fee foo"))
assert_nil(B.parse_pkg_specifier("fee foo (> 1.2.3)"))
end
function test_version_cmp()
assert_equal(0, B.version_cmp("1.2.3", "1.2.3"))
assert_equal(-1, B.version_cmp("1.2.3", "1.2.4"))
......@@ -990,4 +1007,4 @@ function teardown()
syscnf.set_root_dir()
utils.cleanup_dirs(tmp_dirs)
tmp_dirs = {}
end
end
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