Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Required Conan version #7183

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions conans/client/conan_api.py
Expand Up @@ -20,6 +20,7 @@
from conans.client.cmd.uploader import CmdUpload
from conans.client.cmd.user import user_set, users_clean, users_list, token_present
from conans.client.conanfile.package import run_package_method
from conans.client.conf.required_version import check_required_conan_version
from conans.client.graph.graph import RECIPE_EDITABLE
from conans.client.graph.graph_binaries import GraphBinariesAnalyzer
from conans.client.graph.graph_manager import GraphManager
Expand Down Expand Up @@ -233,6 +234,7 @@ def __init__(self, cache_folder=None, output=None, user_io=None, http_requester=
# Migration system
migrator = ClientMigrator(self.cache_folder, Version(client_version), self.out)
migrator.migrate()
check_required_conan_version(self.cache_folder, self.out)
if not get_env(CONAN_V2_MODE_ENVVAR, False):
# FIXME Remove in Conan 2.0
sys.path.append(os.path.join(self.cache_folder, "python"))
Expand Down
8 changes: 8 additions & 0 deletions conans/client/conf/__init__.py
Expand Up @@ -177,6 +177,7 @@ def get_default_settings_yml(force_v1=False):
{% endif %}

# config_install_interval = 1h
# required_conan_version = >=1.26

[storage]
# This is the default path, but you can write your own. It must be an absolute path or a
Expand Down Expand Up @@ -709,3 +710,10 @@ def config_install_interval(self):
except Exception as e:
raise ConanException("Incorrect definition of general.config_install_interval: %s"
% interval)

@property
def required_conan_version(self):
try:
return self.get_item("general.required_conan_version")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't add support env var, and I don't think that's a good idea. For instance, the company wants a specific version, but the developer overrides that version by env vars

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to stop the spread of env-vars as a config mechanism along the conan codebase, because it leads to bad practices, like setting env-vars in recipes, in profiles, that affect how Conan behaves as an application.

The whole purpose of this feature is that developers cannot use a Conan version that they company configuration don't want them to use. Still possible to conan config set general.required_conan_version.

I am ok with using env-vars that are loaded at app startup time, popped from the environment, and used to initialize the Conan config object. But that is another feature (that will be breaking for other variables), in the meantime I don't want pull requests to keep adding these env-vars.

except ConanException:
return None
30 changes: 30 additions & 0 deletions conans/client/conf/required_version.py
@@ -0,0 +1,30 @@
from conans.client.cache.cache import ClientCache
from semver import satisfies, Range
from conans import __version__ as client_version
from conans.errors import ConanException


def check_required_conan_version(cache_folder, out):
""" Check if the required Conan version in config file matches to the current Conan version

When required_conan_version is not configured, it's skipped
When required_conan_version is configured, Conan's version must matches the required
version
When it doesn't match, an ConanException is raised

:param cache_folder: Conan cache folder
:param out: Output stream
:return: None
"""
cache = ClientCache(cache_folder, out)
required_version = cache.config.required_conan_version
if required_version:
try:
Range(required_version, False)
except ValueError:
raise ConanException("The required version expression '{}' is not valid."
.format(required_version))
result = satisfies(client_version, required_version)
if not result:
raise ConanException("The current Conan version ({}) does not match to the required"
" version ({}).".format(client_version, required_version))
48 changes: 48 additions & 0 deletions conans/test/functional/configuration/required_version_test.py
@@ -0,0 +1,48 @@
import unittest
import mock
from conans.test.utils.tools import TestClient
from conans.errors import ConanException


class RequiredVersionTest(unittest.TestCase):

@mock.patch("conans.client.conf.required_version.client_version", "1.26.0")
def test_wrong_version(self):
required_version = "1.23.0"
client = TestClient()
client.run("config set general.required_conan_version={}".format(required_version))
with self.assertRaises(ConanException) as error:
client.run("help")
self.assertIn("The current Conan version ({}) "
"does not match to the required version ({})."
.format("1.26.0", required_version), str(error.exception))

@mock.patch("conans.client.conf.required_version.client_version", "1.22.0")
def test_exact_version(self):
client = TestClient()
client.run("config set general.required_conan_version=1.22.0")
client.run("help")
self.assertNotIn("ERROR", client.out)

@mock.patch("conans.client.conf.required_version.client_version", "2.1.0")
def test_lesser_version(self):
client = TestClient()
client.run("config set general.required_conan_version=<3")
client.run("help")
self.assertNotIn("ERROR", client.out)

@mock.patch("conans.client.conf.required_version.client_version", "1.0.0")
def test_greater_version(self):
client = TestClient()
client.run("config set general.required_conan_version=>0.1.0")
client.run("help")
self.assertNotIn("ERROR", client.out)

def test_bad_format(self):
client = TestClient()
required_version = "1.0.0.0-foobar"
client.run("config set general.required_conan_version={}".format(required_version))
jgsogo marked this conversation as resolved.
Show resolved Hide resolved
with self.assertRaises(ConanException) as error:
client.run("help", assert_error=True)
self.assertIn("The required version expression '{}' is not valid.".format(required_version),
str(error.exception))