diff options
| author | Julian Andres Klode <jak@debian.org> | 2025-10-25 15:57:33 +0000 |
|---|---|---|
| committer | Julian Andres Klode <jak@debian.org> | 2025-10-25 15:57:33 +0000 |
| commit | 7e5652d0d0d9408abb8d514259b7faa46aaaa790 (patch) | |
| tree | ffa99de22e65c0a85a04cc06cf37d0c1354e99fb | |
| parent | 05fe552b0c61821e5865c5cb224b2ae25247aeab (diff) | |
| parent | 4b22a1e36787923fcf303c481f64d580607e670b (diff) | |
Merge branch 'interactive' into 'main'
CLI versioning
See merge request apt-team/apt!522
| -rw-r--r-- | apt-pkg/contrib/configuration.cc | 15 | ||||
| -rw-r--r-- | apt-pkg/contrib/configuration.h | 1 | ||||
| -rw-r--r-- | apt-private/private-cmndline.cc | 133 | ||||
| -rw-r--r-- | apt-private/private-cmndline.h | 2 | ||||
| -rw-r--r-- | apt-private/private-main.cc | 3 | ||||
| -rw-r--r-- | debian/NEWS | 17 | ||||
| -rw-r--r-- | doc/apt.ent | 29 | ||||
| -rw-r--r-- | doc/examples/configure-index | 4 | ||||
| -rw-r--r-- | test/libapt/cliversion_test.cc | 68 |
9 files changed, 243 insertions, 29 deletions
diff --git a/apt-pkg/contrib/configuration.cc b/apt-pkg/contrib/configuration.cc index cf72aa3d7..17cdb72c6 100644 --- a/apt-pkg/contrib/configuration.cc +++ b/apt-pkg/contrib/configuration.cc @@ -679,6 +679,10 @@ void Configuration::Clear(string const &Name) /*}}}*/ void Configuration::MoveSubTree(char const * const OldRootName, char const * const NewRootName)/*{{{*/ { + return MoveSubTree(OldRootName, NewRootName, true); +} +void Configuration::MoveSubTree(char const *const OldRootName, char const *const NewRootName, bool Overwrite) +{ // prevent NewRoot being a subtree of OldRoot if (OldRootName == nullptr) return; @@ -713,7 +717,10 @@ void Configuration::MoveSubTree(char const * const OldRootName, char const * con while (Top != 0 && Top->Next == 0) { - Set(NewRoot + Top->FullTag(OldRoot), Top->Value); + if (Overwrite) + Set(NewRoot + Top->FullTag(OldRoot), Top->Value); + else + CndSet((NewRoot + Top->FullTag(OldRoot)).c_str(), Top->Value); Item const * const Tmp = Top; Top = Top->Parent; delete Tmp; @@ -721,8 +728,10 @@ void Configuration::MoveSubTree(char const * const OldRootName, char const * con if (Top == Stop) return; } - - Set(NewRoot + Top->FullTag(OldRoot), Top->Value); + if (Overwrite) + Set(NewRoot + Top->FullTag(OldRoot), Top->Value); + else + CndSet((NewRoot + Top->FullTag(OldRoot)).c_str(), Top->Value); Item const * const Tmp = Top; if (Top != 0) Top = Top->Next; diff --git a/apt-pkg/contrib/configuration.h b/apt-pkg/contrib/configuration.h index 30a1cbf43..ab8181a1b 100644 --- a/apt-pkg/contrib/configuration.h +++ b/apt-pkg/contrib/configuration.h @@ -101,6 +101,7 @@ class APT_PUBLIC Configuration bool ExistsAny(const char *Name) const; void MoveSubTree(char const * const OldRoot, char const * const NewRoot); + void MoveSubTree(char const *const OldRoot, char const *const NewRoot, bool Overwrite); // clear a whole tree void Clear(const std::string &Name); diff --git a/apt-private/private-cmndline.cc b/apt-private/private-cmndline.cc index e16aa7280..2e6b47b11 100644 --- a/apt-private/private-cmndline.cc +++ b/apt-private/private-cmndline.cc @@ -3,6 +3,7 @@ #include <apt-pkg/cmndline.h> #include <apt-pkg/configuration.h> +#include <apt-pkg/debversion.h> #include <apt-pkg/error.h> #include <apt-pkg/fileutl.h> #include <apt-pkg/init.h> @@ -12,6 +13,7 @@ #include <apt-private/private-cmndline.h> #include <apt-private/private-main.h> +#include <cassert> #include <cstdarg> #include <cstdlib> #include <cstring> @@ -420,6 +422,7 @@ std::vector<CommandLine::Args> getCommandArgs(APT_CMD const Program, char const addArg('q', "silent", "quiet", CommandLine::IntLevel); addArg('c', "config-file", 0, CommandLine::ConfigFile); addArg('o', "option", 0, CommandLine::ArbItem); + addArg(0, "cli-version", "APT::Version", CommandLine::HasArg); addArg(0, NULL, NULL, 0); return Args; @@ -492,32 +495,30 @@ static void BinarySpecificConfiguration(char const * const Binary) /*{{{*/ { _config->CndSet("Binary::apt-cdrom::APT::Internal::OpProgress::EraseLines", false); } - if (binary == "apt" || binary == "apt-config") - { - if (getenv("NO_COLOR") == nullptr && getenv("APT_NO_COLOR") == nullptr) - _config->CndSet("Binary::apt::APT::Color", true); - _config->CndSet("Binary::apt::APT::Output-Version", 30); - _config->CndSet("Binary::apt::APT::Cache::Show::Version", 2); - _config->CndSet("Binary::apt::APT::Cache::AllVersions", false); - _config->CndSet("Binary::apt::APT::Cache::ShowVirtuals", true); - _config->CndSet("Binary::apt::APT::Cache::Search::Version", 2); - _config->CndSet("Binary::apt::APT::Cache::ShowDependencyType", true); - _config->CndSet("Binary::apt::APT::Cache::ShowVersion", true); - _config->CndSet("Binary::apt::APT::Get::Upgrade-Allow-New", true); - _config->CndSet("Binary::apt::APT::Cmd::Show-Update-Stats", true); - _config->CndSet("Binary::apt::DPkg::Progress-Fancy", true); - _config->CndSet("Binary::apt::APT::Keep-Downloaded-Packages", false); - _config->CndSet("Binary::apt::APT::Get::Update::InteractiveReleaseInfoChanges", true); - _config->CndSet("Binary::apt::APT::Cmd::Pattern-Only", true); - _config->CndSet("Binary::apt::Pager", true); - - if (isatty(STDIN_FILENO)) - _config->CndSet("Binary::apt::Dpkg::Lock::Timeout", -1); - else - _config->CndSet("Binary::apt::Dpkg::Lock::Timeout", 120); - } - _config->Set("Binary", binary); + + // Version specific configuration + _config->CndSet("Version::1.2::APT::Color", + getenv("NO_COLOR") == nullptr && getenv("APT_NO_COLOR") == nullptr); + _config->CndSet("Version::3.0::APT::Output-Version", 30); + _config->CndSet("Version::1.1::APT::Cache::Show::Version", 2); + _config->CndSet("Version::1.1::APT::Cache::AllVersions", false); + _config->CndSet("Version::1.1::APT::Cache::ShowVirtuals", true); + _config->CndSet("Version::1.1::APT::Cache::Search::Version", 2); + _config->CndSet("Version::1.1::APT::Cache::ShowDependencyType", true); + _config->CndSet("Version::1.1::APT::Cache::ShowVersion", true); + _config->CndSet("Version::1.1::APT::Get::Upgrade-Allow-New", true); + _config->CndSet("Version::1.1::APT::Cmd::Show-Update-Stats", true); + _config->CndSet("Version::1.2::DPkg::Progress-Fancy", true); + _config->CndSet("Version::1.2::APT::Keep-Downloaded-Packages", false); + _config->CndSet("Version::1.5::APT::Get::Update::InteractiveReleaseInfoChanges", true); + _config->CndSet("Version::2.0::APT::Cmd::Pattern-Only", true); + _config->CndSet("Version::3.0::Pager", true); + + if (isatty(STDIN_FILENO)) + _config->CndSet("Version::2.0::Dpkg::Lock::Timeout", -1); + else + _config->CndSet("Version::2.0::Dpkg::Lock::Timeout", 120); } /*}}}*/ static void BinaryCommandSpecificConfiguration(char const * const Binary, char const * const Cmd)/*{{{*/ @@ -535,6 +536,48 @@ static void BinaryCommandSpecificConfiguration(char const * const Binary, char c } #undef CmdMatches /*}}}*/ + +static std::pair<std::optional<long unsigned int>, std::optional<long unsigned int>> parseCliVersion(std::string_view version) +{ + auto level = VectorizeString(version, '.'); + long unsigned int first = 0, second = 0; + if (not StrToNum(level[0].data(), first, level[0].size(), 10)) + return {}; + if (level.size() <= 1) + return {first, {}}; + if (not StrToNum(level[1].data(), second, level[1].size(), 10)) + return {}; + + return {first, second}; +} + +bool cliVersionIsCompatible(std::string_view rawLevel, std::string_view rawPattern) +{ + auto level = parseCliVersion(rawLevel); + auto pattern = parseCliVersion(rawPattern); + + assert(level.first); + assert(level.second); + + // Requested an earlier major version + if (pattern.first && *level.first > *pattern.first) + return false; + // Requested a latter major version + if (pattern.first && *level.first < *pattern.first) + return (*level.second < 10); // Keep in mind 2.10 follows 2.8, not 2.9, but 3.0 follows 2.9 + // Same major version, requested a higher pattern version (compatible usually) + if (pattern.second && *level.second < *pattern.second) + return (*level.second != 9); // X.9 is not compatible with X.10 onward + // Special case for short pattern, X.9 does not migrate to X+1 + if (not pattern.second && level.second == 9) + return false; + // Same major version, requested a lower major version. + if (pattern.second && *level.second > *pattern.second) + return false; + + return true; +} + std::vector<CommandLine::Dispatch> ParseCommandLine(CommandLine &CmdL, APT_CMD const Binary,/*{{{*/ Configuration * const * const Cnf, pkgSystem ** const Sys, int const argc, const char *argv[], bool (*ShowHelp)(CommandLine &), std::vector<aptDispatchWithHelp> (*GetCommands)(void)) @@ -564,6 +607,7 @@ std::vector<CommandLine::Dispatch> ParseCommandLine(CommandLine &CmdL, APT_CMD c CmdCalled = CommandLine::GetCommand(Cmds.data(), argc, argv); if (CmdCalled != nullptr) BinaryCommandSpecificConfiguration(argv[0], CmdCalled); + std::string const conf = "Binary::" + _config->Find("Binary"); _config->MoveSubTree(conf.c_str(), nullptr); @@ -573,7 +617,46 @@ std::vector<CommandLine::Dispatch> ParseCommandLine(CommandLine &CmdL, APT_CMD c auto Args = getCommandArgs(Binary, CmdCalled); CmdL = CommandLine(Args.data(), _config); + auto initVersion = []() -> bool + { + auto defaultVersion = parseCliVersion(PACKAGE_VERSION); + std::string requestedVersion = _config->Find("APT::Version"); + if (requestedVersion.empty()) + { + if (_config->Find("Binary", "") == "apt") + requestedVersion = PACKAGE_VERSION; + else + strprintf(requestedVersion, "0.%lu", *defaultVersion.first * 10 + *defaultVersion.second); + } + else + { + // Check that the requested version is compatible with our version + auto parsedRequestedVersion = parseCliVersion(requestedVersion); + // This appends a 0 to the requested version if not specified. + std::string cleanRequestedVersion; + strprintf(cleanRequestedVersion, "%lu.%lu", *parsedRequestedVersion.first, parsedRequestedVersion.second ? *parsedRequestedVersion.second : 0); + // Supported version "lowers" say 3.1 to 0.31, 1.21, 2.11 depending on requested major + std::string supportedVersion; + strprintf(supportedVersion, "%lu.%lu", *parsedRequestedVersion.first, (*defaultVersion.first - *parsedRequestedVersion.first) * 10 + *defaultVersion.second); + if (not cliVersionIsCompatible(cleanRequestedVersion, supportedVersion)) + return _error->Error("Requested version %s is not supported in version %s (%s is)", requestedVersion.c_str(), PACKAGE_VERSION, supportedVersion.c_str()); + } + if (auto Versions = _config->Tree("Version")) + { + for (auto I = Versions->Child; I != NULL; I = I->Next) + { + auto sub = "Version::" + I->Tag; + if (not cliVersionIsCompatible(I->Tag, requestedVersion)) + continue; + // FIXME: 2.10 should not flow into 3.0 + _config->MoveSubTree(sub.c_str(), nullptr, false); + } + } + return true; + }; + if (CmdL.Parse(argc,argv) == false || + initVersion() == false || (Sys != NULL && pkgInitSystem(*_config, *Sys) == false)) { if (_config->FindB("version") == true) diff --git a/apt-private/private-cmndline.h b/apt-private/private-cmndline.h index 22e25d280..6e14aa836 100644 --- a/apt-private/private-cmndline.h +++ b/apt-private/private-cmndline.h @@ -4,6 +4,7 @@ #include <apt-pkg/cmndline.h> #include <apt-pkg/macros.h> +#include <string_view> #include <vector> class Configuration; @@ -39,4 +40,5 @@ APT_PUBLIC unsigned short DispatchCommandLine(CommandLine &CmdL, std::vector<Com APT_PUBLIC std::vector<CommandLine::Args> getCommandArgs(APT_CMD const Program, char const * const Cmd); +APT_PUBLIC bool cliVersionIsCompatible(std::string_view level, std::string_view pattern); #endif diff --git a/apt-private/private-main.cc b/apt-private/private-main.cc index 7b4f05a50..1c34b8cd6 100644 --- a/apt-private/private-main.cc +++ b/apt-private/private-main.cc @@ -80,7 +80,8 @@ void CheckIfCalledByScript(int argc, const char *argv[]) /*{{{*/ if (unlikely(argc < 1)) return; if (not IsStdoutAtty() && - _config->FindB("Apt::Cmd::Disable-Script-Warning", false) == false) + not _config->Exists("APT::Version") && + not _config->FindB("Apt::Cmd::Disable-Script-Warning", false)) { // NOTE: CLI interface is redundant on the I/interface, this is // intentional to make it easier to read. diff --git a/debian/NEWS b/debian/NEWS index 812e95e25..7ac58d84a 100644 --- a/debian/NEWS +++ b/debian/NEWS @@ -1,3 +1,20 @@ +apt (3.1.10) unstable; urgency=medium + + The new --cli-version optionr equests a specific CLI version. + + The default --cli-version is the current APT version for the apt(8) command + or 0.(10 * major + minor) for the other command. + + If --cli-version is specified, the output of the apt(8) command may be + recorded. This allows for replacing legacy commands like apt-get(8) in + scripts. + + The legacy commands may roll-over to the 2.X series in the future. + For further information on this mechanism, consult the apt(8) manual + page. + + -- Julian Andres Klode <jak@debian.org> Sat, 25 Oct 2025 17:27:38 +0200 + apt (2.9.24) unstable; urgency=medium /etc/apt/trusted.gpg is no longer trusted. Setting the Dir::Etc::trusted diff --git a/doc/apt.ent b/doc/apt.ent index 9034d2e66..9ac40e52a 100644 --- a/doc/apt.ent +++ b/doc/apt.ent @@ -79,6 +79,35 @@ </varlistentry> <varlistentry> + <term><option>--cli-version</option></term> + <listitem><para>Requests a specific CLI version. This is a pattern that takes + a MAJOR.MINOR version. You may omit the MINOR version, in which case the latest + supported minor level will be used. + </para> + <para> + Version tracks are presented as follows: The version 3.2 has equivalent versions + 0.32, 1.22, and 2.12, implementing new features for their specified major versions. + </para> + <para> + A special case is the X.9 version pattern: A version like 2.9 is followed by 3.0, + but 2.10 follows directly on 2.8; given that we use odd version numbers as development + series (2.9 is the 3.0 development series). + </para> + <para> + This feature has been introduced in APT 3.1.10, and replaces the binary-specific +configuration. You may use the <option>APT::Version</option> to similar effect on older +binaries, for example, use <option>-o APT::Version=2</option> to make &apt; behave like +the 2.x series. + </para> + <para> + The default value is the current APT version for &apt; and <literal>0.(10 * major + minor)</literal> + for the classic commands. The 0.X series is soft-deprecated and the other commands will + roll over to the 2.x series in the future. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><option>--no-color</option></term> <term><option>--color</option></term> <listitem><para>Turn colors on or off. Colors are on by default on supported terminals for &apt; and diff --git a/doc/examples/configure-index b/doc/examples/configure-index index 65b5032bc..1b050d0b9 100644 --- a/doc/examples/configure-index +++ b/doc/examples/configure-index @@ -237,6 +237,8 @@ APT Periodic {}; Machine-ID "<STRING>"; // Value of /etc/machine-id + + Version "<STRING>"; // Version of behavior requested }; // Options for the downloading routines @@ -913,3 +915,5 @@ update-manager::always-include-phased-updates "<BOOL>"; update-manager::never-include-phased-updates "<BOOL>"; apt::history::comment "<STRING>"; + +interactive "<BOOL>"; diff --git a/test/libapt/cliversion_test.cc b/test/libapt/cliversion_test.cc new file mode 100644 index 000000000..a51872aaa --- /dev/null +++ b/test/libapt/cliversion_test.cc @@ -0,0 +1,68 @@ +#include <config.h> + +#include <apt-private/private-cmndline.h> + +#include "common.h" + +TEST(CliVersionTest, TestShort) +{ + + EXPECT_TRUE(cliVersionIsCompatible("0.0", "0")); + EXPECT_TRUE(cliVersionIsCompatible("0.1", "0")); + EXPECT_TRUE(cliVersionIsCompatible("0.0", "1")); + EXPECT_TRUE(cliVersionIsCompatible("0.1", "1")); + EXPECT_FALSE(cliVersionIsCompatible("0.9", "0")); + EXPECT_TRUE(cliVersionIsCompatible("0.9", "1")); + EXPECT_FALSE(cliVersionIsCompatible("0.10", "1")); +} + +TEST(CliVersionTest, Test_0_10) +{ + EXPECT_TRUE(cliVersionIsCompatible("0.0", "0.10")); + EXPECT_TRUE(cliVersionIsCompatible("0.1", "0.10")); + EXPECT_TRUE(cliVersionIsCompatible("0.2", "0.10")); + EXPECT_TRUE(cliVersionIsCompatible("0.3", "0.10")); + EXPECT_TRUE(cliVersionIsCompatible("0.4", "0.10")); + EXPECT_TRUE(cliVersionIsCompatible("0.5", "0.10")); + EXPECT_TRUE(cliVersionIsCompatible("0.6", "0.10")); + EXPECT_TRUE(cliVersionIsCompatible("0.7", "0.10")); + EXPECT_TRUE(cliVersionIsCompatible("0.8", "0.10")); + EXPECT_TRUE(cliVersionIsCompatible("0.10", "0.10")); + // 0.9 does not migrate to 0.10 + EXPECT_FALSE(cliVersionIsCompatible("0.9", "0.10")); + // 0.11 is newer than 0.10 + EXPECT_FALSE(cliVersionIsCompatible("0.11", "0.10")); +} + +TEST(CliVersionTest, TestSame) +{ + EXPECT_TRUE(cliVersionIsCompatible("0.0", "0.0")); + EXPECT_TRUE(cliVersionIsCompatible("0.1", "0.1")); + EXPECT_TRUE(cliVersionIsCompatible("0.2", "0.2")); + EXPECT_TRUE(cliVersionIsCompatible("0.3", "0.3")); + EXPECT_TRUE(cliVersionIsCompatible("0.4", "0.4")); + EXPECT_TRUE(cliVersionIsCompatible("0.5", "0.5")); + EXPECT_TRUE(cliVersionIsCompatible("0.6", "0.6")); + EXPECT_TRUE(cliVersionIsCompatible("0.7", "0.7")); + EXPECT_TRUE(cliVersionIsCompatible("0.8", "0.8")); + EXPECT_TRUE(cliVersionIsCompatible("0.9", "0.9")); + EXPECT_TRUE(cliVersionIsCompatible("0.10", "0.10")); + EXPECT_TRUE(cliVersionIsCompatible("0.11", "0.11")); +} +TEST(CliVersionTest, Test_1_0) +{ + EXPECT_TRUE(cliVersionIsCompatible("0.0", "1.0")); + EXPECT_TRUE(cliVersionIsCompatible("0.1", "1.0")); + EXPECT_TRUE(cliVersionIsCompatible("0.2", "1.0")); + EXPECT_TRUE(cliVersionIsCompatible("0.3", "1.0")); + EXPECT_TRUE(cliVersionIsCompatible("0.4", "1.0")); + EXPECT_TRUE(cliVersionIsCompatible("0.5", "1.0")); + EXPECT_TRUE(cliVersionIsCompatible("0.6", "1.0")); + EXPECT_TRUE(cliVersionIsCompatible("0.7", "1.0")); + EXPECT_TRUE(cliVersionIsCompatible("0.8", "1.0")); + // 0.9 *does* migrate to 0.10 + EXPECT_TRUE(cliVersionIsCompatible("0.9", "1.0")); + // 0.11 is a continuation of the 0 branch in parallel to 1.1 + EXPECT_FALSE(cliVersionIsCompatible("0.10", "1.0")); + EXPECT_FALSE(cliVersionIsCompatible("0.11", "1.0")); +} |
