-
Notifications
You must be signed in to change notification settings - Fork 993
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
Feature/lock upgrade #17577
base: develop2
Are you sure you want to change the base?
Feature/lock upgrade #17577
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
from collections import defaultdict | ||
import os | ||
|
||
from conan.api.output import ConanOutput | ||
|
@@ -8,6 +9,9 @@ | |
from conan.cli.printers.graph import print_graph_packages, print_graph_basic | ||
from conan.internal.model.lockfile import Lockfile, LOCKFILE | ||
from conan.internal.model.recipe_ref import RecipeReference | ||
from conan.errors import ConanException | ||
from conan.internal.model.version import Version | ||
from conan.internal.model.version_range import VersionRange | ||
|
||
|
||
@conan_command(group="Consumer") | ||
|
@@ -179,3 +183,101 @@ def lock_update(conan_api, parser, subparser, *args): | |
lockfile.update(requires=args.requires, build_requires=args.build_requires, | ||
python_requires=args.python_requires, config_requires=args.config_requires) | ||
conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out) | ||
|
||
|
||
|
||
|
||
@conan_subcommand() | ||
def lock_upgrade(conan_api, parser, subparser, *args): | ||
""" | ||
(Experimental) Upgrade requires, build-requires or python-requires from an existing lockfile given a conanfile | ||
or a reference. | ||
""" | ||
|
||
""" | ||
TODOs: | ||
- [ ] args.update should always be True? | ||
- [x] manage input version ranges | ||
- [ ] Consider a new option to enable transitive dependencies on different kinds | ||
(eg. --transitive=all, --transitive=python-requires, ...) | ||
- [ ] Improve the detection of range versions | ||
""" | ||
|
||
common_graph_args(subparser) | ||
subparser.add_argument('--update-requires', action="append", help='Update requires from lockfile') | ||
subparser.add_argument('--update-build-requires', action="append", help='Update build-requires from lockfile') | ||
subparser.add_argument('--update-python-requires', action="append", help='Update python-requires from lockfile') | ||
subparser.add_argument('--update-config-requires', action="append", help='Update config-requires from lockfile') | ||
subparser.add_argument('--build-require', action='store_true', default=False, help='Whether the provided reference is a build-require') | ||
subparser.add_argument('--transitives', action='store_true', default=False, help='Upgrade also transitive dependencies') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd probably drop the |
||
args = parser.parse_args(*args) | ||
|
||
# parameter validation | ||
validate_common_graph_args(args) | ||
|
||
if not any([args.update_requires, args.update_build_requires, args.update_python_requires, args.update_config_requires]): | ||
raise ConanException("At least one of --update-requires, --update-build-requires, " | ||
"--update-python-requires or --update-config-requires should be specified") | ||
|
||
cwd = os.getcwd() | ||
path = conan_api.local.get_conanfile_path(args.path, cwd, py=None) if args.path else None | ||
remotes = conan_api.remotes.list(args.remote) if not args.no_remote else [] | ||
overrides = eval(args.lockfile_overrides) if args.lockfile_overrides else None | ||
lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile, conanfile_path=path, | ||
cwd=cwd, partial=True, overrides=overrides) | ||
profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args) | ||
|
||
def expand_graph(): | ||
if path: | ||
return conan_api.graph.load_graph_consumer(path, args.name, args.version, | ||
args.user, args.channel, | ||
profile_host, profile_build, lockfile, | ||
remotes, args.update, | ||
is_build_require=args.build_require) | ||
return conan_api.graph.load_graph_requires(args.requires, args.tool_requires, | ||
profile_host, profile_build, lockfile, | ||
remotes, args.update) | ||
requested_update = {"requires": args.update_requires or [], | ||
"build_requires": args.update_build_requires or [], | ||
"python_requires": args.update_python_requires or [], | ||
"config_requires": args.update_config_requires or []} | ||
if args.transitives: | ||
def is_version_range(ref: str): | ||
# TODO: use conan's built in | ||
return ref.find('[') != -1 or ref.find('*') != -1 | ||
|
||
def match_version(ref, node): | ||
if is_version_range(ref): | ||
return VersionRange(str(RecipeReference.loads(ref).version)).contains(Version(node.ref), resolve_prerelease=True) | ||
else: | ||
return node.ref.matches(ref, is_consumer=None) | ||
updatable_deps = defaultdict(list) | ||
for node in expand_graph().nodes: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this might be a bit overkill, I'd try to leverage the existing conan |
||
if not node.ref: | ||
continue | ||
for kind, deps in requested_update.items(): | ||
if any(match_version(ref, node) for ref in deps): | ||
updatable_deps[kind].append(node.ref.repr_notime()) | ||
for dep in node.conanfile.dependencies.values(): | ||
# TODO: dependencies.build.values() -> | ||
# dep.context # host or build require (?) | ||
updatable_deps[kind].append(dep.ref.repr_notime()) | ||
else: | ||
updatable_deps = requested_update | ||
# Remove the lockfile entries that will be updated | ||
lockfile = conan_api.lockfile.remove_lockfile(lockfile, | ||
requires=updatable_deps["requires"], | ||
python_requires=updatable_deps["python_requires"], | ||
build_requires=updatable_deps["build_requires"], | ||
config_requires=updatable_deps["config_requires"]) | ||
# Resolve new graph | ||
graph = expand_graph() | ||
print_graph_basic(graph) | ||
graph.report_graph_error() | ||
conan_api.graph.analyze_binaries(graph, args.build, remotes=remotes, update=args.update, | ||
lockfile=lockfile) | ||
print_graph_packages(graph) | ||
|
||
lockfile = conan_api.lockfile.update_lockfile(lockfile, graph, args.lockfile_packages, | ||
clean=args.lockfile_clean) | ||
conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out or "conan.lock") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have learned these are problematic, and we are moving away of the
cli
layer importing anything else besidescli
andapi
layers. So the best would be to put the required functionality directly into the api layer