-
Notifications
You must be signed in to change notification settings - Fork 986
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
conan create remote WIP #15856
conan create remote WIP #15856
Conversation
69b9720
to
9a2a05a
Compare
conans/requirements_dev.txt
Outdated
@@ -4,3 +4,4 @@ parameterized>=0.6.3 | |||
mock>=1.3.0, <1.4.0 | |||
WebTest>=2.0.18, <2.1.0 | |||
bottle | |||
docker |
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.
the docker
pypi package is pure python, but it's worth checking whether all the dependencies are also pure python and to estimate the likelihood of dependency conflicts in the future.
otherwise I wonder if we may want to consider making it an optional dependency?
pip install conan[docker]
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.
a few comments :D but this is amazing
@@ -0,0 +1,7 @@ | |||
FROM ubuntu |
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.
if it's forst tests I would do FROM ubuntu:jammy
or a specific LTS
RUN apt update && apt upgrade -y | ||
RUN apt install -y build-essential | ||
RUN apt install -y python3-pip cmake git build-essential | ||
RUN pip install docker |
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.
each RUN
line is cached independently, this Dockerfile is only guaranteed to build properly from a clean cache (or a --no-cache
flag when building)
Remember that apt install
does the dependency resolution against the cached outcome of apt update
) - we want to avoid running an apt install
against an outdated cache (it will fail, since the apt remotes delete versions quite quickly)
Recommended way is something like:
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential \
cmake \
ninja-build \
python3 \
python3-venv
# && rm -rf /var/lib/apt/lists/*
The &&
guarantees that apt-get update
and apt-get install
are part of the same RUN
command, and the last line ensures that the apt cache is removed as part of the same command: it is outdated almost by definition.
RUN apt install -y python3-pip cmake git build-essential | ||
RUN pip install docker | ||
COPY . /root/conan-io | ||
RUN cd /root/conan-io && pip install -e . |
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.
IIRC newer versions of Python in ubuntu will complain when pip is invoked at the system level (for the better) - I'd install it inside a virtual environment
type=docker | ||
dockerfile={dockerfile_path("Dockerfile_test")} | ||
docker_build_path={conan_base_path()} | ||
image=conan-runner-default-test |
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.
My intuition here... but having both dockerfile
and image
strike me as contradictory? I would expect to have one or the other:
My expectation:
- if we have an
image
, assume it exists and launch a container from that image - if we are given a
dockerfile
- build the dockerfile and launch a container from the resulting image, I would not expect to pass an image - I would expect this to be internal toconan
to decide what the image is called
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.
If you only define the dockerfile, it will generate the image with the default name. If you have several profiles with dockerfiles, you can have a mechanism that doesn't overwrite the images and not need to rebuild them when changing profiles.
In addition, it assumes that the image exists if you only define the name of the image.
docker_build_path={conan_base_path()} | ||
image=conan-runner-default-test | ||
cache=copy | ||
remove=True |
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.
what's the meaning of remove
?
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.
remove the container at the end of the conan command execution
[runner] | ||
type=docker | ||
dockerfile={dockerfile_path("Dockerfile_test")} | ||
docker_build_path={conan_base_path()} |
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.
I would suggest to rename this to docker_build_context
or similar - and document:
- what the default behaviour is when this is not provided
- what it is relative to, if a relative path is provided (if it's even possible)
- (assuming that it will work as expected if a full path is provided, but that tends to be tricky when this is expressed in distributable config files)
return os.path.dirname(os.path.dirname(conans.__file__)) | ||
|
||
|
||
def dockerfile_path(name=None): |
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.
One minor comment regarding the test style. I think it would be better to declare the file as a string here (or import the string from a common place once many tests in different places use it) and create the file using client.save()
, then you can access the path with client.current_folder...
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.
this is a static path inside the conan test folder.
For example: conans/test/integration/command/dockerfiles/Dockerfile
conan/internal/runner/docker.py
Outdated
cwd = os.getcwd() | ||
if profiles: | ||
for profile in profiles: | ||
profile_path = ProfileLoader.get_profile_path(os.path.join(ConfigAPI(self.conan_api).home(), 'profiles'), profile, cwd) |
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.
I could be wrong here - if we are using the profile name, rather than the profile already "composed" by the create
command - this may result in an incomplete thing being copied if the profile has a include()
referring to other files - we are only going to copy the referenced file, rather than anything else it might need.
if we propagate the resolved profile from the conan create
command - we can just do a profile.dumps()
and save it in the container - that should resolve includes()
, but I suspect it will also render jinja templates and the likes
a reason this is important is that we may have a profile like this:
include(windows-msvc193)
[runner]
type=ssh
host=my-remote-host
and then the file windows-msvc193
is a different profile file that doesn't have any [runner]
related things. I expect devs to operate like this to avoid repetition
@memsharded may be able to provide more context here : D
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.
This is correct, I initially discussed with @davidsanfal that we might want to transfer the files, because of the same reason, there would be files that would evaluate differently in different machines due to jinja rendering.
But it is totally true that the include()
would be a limitation and fail unexpectedly. We need to think about it.
2c50c14
to
e31be33
Compare
db89115
to
84ef97d
Compare
a81379f
to
07232cd
Compare
Just noting down here that we had excellent used feeback on the addition of this feature on the Using cpp::std conference after we showed it in our talk! 🥳 |
conan/internal/runner/docker.py
Outdated
except: | ||
raise ConanException("Docker Client failed to initialize." | ||
"\n - Check if docker is installed and running" | ||
"\n - Run 'pip install docker>=5.0.0, <=5.0.3'") |
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.
Better pip isntall ... [runners]
?
conan/cli/commands/create.py
Outdated
'docker': DockerRunner, | ||
'ssh': SSHRunner, | ||
'wsl': WSLRunner, | ||
}[profile_host.runner.get('type')](conan_api, 'create', profile_host, profile_build, args, raw_args).run() |
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.
A bit too much, I wouldn't mind something in 3 separate lines, and also something that raise a controlled ConanException if the runner.type
is not defined or incorrect.
import os | ||
from conan import ConanFile | ||
from conan.tools.cmake import CMake, CMakeToolchain | ||
|
||
class Library(ConanFile): | ||
name = "hello" | ||
version = "1.0" |
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.
Is there any reason not to use a standard conan new cmake_lib
template? That would be better.
The Ninja
generator can be defined in the profile if necessary.
FROM ubuntu:22.04 | ||
RUN apt-get update \ | ||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ | ||
build-essential \ | ||
cmake \ | ||
ninja-build \ | ||
python3 \ | ||
python3-pip \ | ||
python3-venv \ |
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.
If the 3 dockerfiles are very similar, wouldn't it make sense to define a common base one? that would make tests that use these dockerfiles faster to build locally, isn't it? test speed is important to keep as fast as possible.
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.
It could be done via multi stage feature: https://docs.docker.com/build/building/multi-stage/
@@ -24,6 +24,7 @@ def __init__(self): | |||
self.conf = ConfDefinition() | |||
self.buildenv = ProfileEnvironment() | |||
self.runenv = ProfileEnvironment() | |||
self.runner = {} |
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.
It seems the Profile
might need to add serialize()
and dumps()
for full management of the runners
information
self.args.profile_host[0]: self.host_profile.dumps(), | ||
self.args.profile_build[0]: self.build_profile.dumps() |
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.
Profile dumps()
doesn't include the runners information now, but it might.
3803ac5
to
6a44fbf
Compare
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.
Looks great, I have some comments about errors I found while testing this locally
Co-authored-by: Rubén Rincón Blanco <[email protected]>
Co-authored-by: Rubén Rincón Blanco <[email protected]>
Co-authored-by: Rubén Rincón Blanco <[email protected]>
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.
🥳
self.image = host_profile.runner.get('image') or self.configfile.image | ||
if not (self.dockerfile or self.image): | ||
raise ConanException("'dockerfile' or docker image name is needed") | ||
self.image = self.image or 'conan-runner-default' |
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.
Maybe not for this PR, but suggesting here that we make the default image name configurable with something like core.runners.docker.default_image_name
self.dockerfile = host_profile.runner.get('dockerfile') or self.configfile.build.dockerfile | ||
self.docker_build_context = host_profile.runner.get('build_context') or self.configfile.build.build_context | ||
self.image = host_profile.runner.get('image') or self.configfile.image |
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.
I find the preference order in selecting from the profile or the config file a bit unnatural, because you can set in the profile contradictory things, like for example using a configfile that points to a dockerfile and be pointing in the profile to another one, shouldn't the configfile and the profile settings that overlap be incompatible in some way?
Changelog: Feature: Added transparent support for running Conan within a Docker container.
Docs: conan-io/docs#3699
develop
branch, documenting this one.