Repository navigation
feat(ansible,docker,webauto-ci): use Python venv for rosdep pip keys - #7393
hakuturu583 wants to merge 4 commits into
Conversation
On Ubuntu 24.04 (ROS 2 Jazzy) the system Python is externally managed (PEP 668), so rosdep cannot install pip keys. Following the discussion in autowarefoundation#7376 / autowarefoundation#7388, add an optional role that creates a venv per Autoware workspace at <workspace>/.venv: - create the venv with --system-site-packages so apt ROS 2 Python packages and colcon extensions stay visible - install rosdep and colcon-core into the venv so they run on the venv interpreter (required for rosdep's PEP 668 check and for ament_python shebangs) - add COLCON_IGNORE to the venv and ignore .venv/ in git Run with: ansible-playbook autoware.dev_env.install_dev_env --tags python_venv then: source .venv/bin/activate && rosdep install ... --as-root pip:false Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017krcQAZb8E7hVL1jGo7euG Signed-off-by: Claude <noreply@anthropic.com>
|
Thank you for contributing to the Autoware project! 🚧 If your pull request is in progress, switch it to draft mode. Please ensure:
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b44891830a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- pass paths to venv/pip as argv so paths with spaces stay single arguments - follow symlinks when checking that the workspace directory exists Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017krcQAZb8E7hVL1jGo7euG Signed-off-by: Claude <noreply@anthropic.com>
- docker: create /opt/autoware-venv in base image with --system-site-packages, install rosdep and colcon-core into it and put it first on PATH, so rosdep pip keys install into the venv on Jazzy. The venv is outside /opt/autoware so runtime images do not get build-time pip packages from devel images. - webauto-ci: create $AUTOWARE_PATH/.venv with the python_venv role in autoware-setup, activate it in autoware-build and run rosdep with --as-root pip:false. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017krcQAZb8E7hVL1jGo7euG Signed-off-by: Claude <noreply@anthropic.com>
xmfcx
left a comment
There was a problem hiding this comment.
Thank you for this. The design follows the discussion and the meeting, and the Docker layout is right: one venv in base, outside /opt/autoware, so the runtime images keep the same shebang path.
One blocker: the second pip install rosdep colcon-core pulls PyPI copies of pytest, catkin_pkg, rosdistro and rospkg into the venv, and these hide the apt versions in every image. The tools do not need a PyPI copy at all. The venv already sees the apt rosdep2 and colcon_core modules, so a small launcher that runs them on the venv interpreter is enough. The inline suggestions do this for Docker and for the role. I tested them on Humble and Jazzy.
We also want this on Humble, and on Humble we do not want any pip install into the system Python either. Python 3.10 has no PEP 668 block, so the suggestions add a require-virtualenv guard: always in the images, and as an opt-in variable in the role for hosts.
For Web.Auto, please show a real Web.Auto run on the final commit. The scripts are checked with shellcheck only.
Review notes
Requested changes
| # | Where | Change | Why |
|---|---|---|---|
| 1 | docker/base.Dockerfile |
Launchers instead of the two pip install lines |
The second install replaces apt pytest 7.4.4 with 9.2.0 and adds 9 more unpinned PyPI packages |
| 2 | docker/base.Dockerfile |
Install python3-rosdep in base |
ros:*-ros-base ships rosdep 0.26.0, which has no venv check. The runtime stages never upgrade it. |
| 3 | docker/base.Dockerfile |
/etc/pip.conf with require-virtualenv = true |
Humble has no PEP 668 block, so nothing stops a system pip install |
| 4 | ansible/roles/python_venv |
Launchers instead of the two pip tasks, and new python_venv__tools fields |
Same as 1 |
| 5 | ansible/roles/python_venv/tasks/main.yaml |
Fail if the venv path has a space | Linux cannot run a shebang path with a space, so every built ament_python executable fails |
| 6 | .github/workflows/health-check-ansible.yaml |
Make sure that the tools and pytest come from apt | The current step passes with the PyPI copies |
| 7 | .webauto-ci/main/autoware-build/run.sh |
Install xmlschema into the venv, not with sudo pip3 |
Web.Auto is Humble, so this is a system pip install today |
| 8 | READMEs | Match the new setup, and say that only root can install pip keys in the images | The venv belongs to root, so the aw user cannot write to it |
| 9 | ansible/roles/python_venv |
Opt-in python_venv__require_virtualenv that sets require-virtualenv = true in /etc/pip.conf |
On a Humble host without the venv, rosdep runs sudo -H pip3 and installs into the system Python with no error |
Please also add a pip key install and one ament_python build to the health check, if the job has the ROS sources for it. The PR body describes these tests, but CI does not run them.
What ran
Containers ros:humble-ros-base and ros:jazzy-ros-base, as root. A venv at /opt/autoware-venv with the two launchers, and one ament_python package with <exec_depend>python3-typesafe-carla-pip</exec_depend>.
| Check | Humble (Python 3.10) | Jazzy (Python 3.12) |
|---|---|---|
rosdep install ... --as-root pip:false, rosdep 0.27.0 |
Successfully installed typesafe-carla-0.8.5 |
Successfully installed typesafe-carla-0.8.5 |
| Same, with the image rosdep 0.26.0 | Successfully installed typesafe-carla-0.8.5 |
PEP 668 error, typesafe_carla not installed |
typesafe_carla from /usr/bin/python3 |
ModuleNotFoundError |
ModuleNotFoundError |
| Shebang of the built executable | #!/opt/autoware-venv/bin/python3 |
#!/opt/autoware-venv/bin/python3 |
| pytest after the install | 6.2.5, the apt version | 7.4.4, the apt version |
| Venv site-packages | pip, setuptools 59.6.0, typesafe-carla |
pip, typesafe-carla |
- The exact
RUNof suggestion 1, on both distros:rosdep --versionprinted0.27.0,colcon --helpprinted its usage, andpipx install cowsayas a normal user worked with thepip.confguard. - With the guard,
/usr/bin/python3 -m pip install sixas root printedERROR: Could not find an activated virtualenv (required). - With the guard on Humble, the system
rosdep install(no venv) failed atpip3 install -U typesafe-carlaand installed nothing. - The role tasks of suggestions 4 and 5, on ansible-core 2.21.5: first run
changed, second runchanged=0, and a path with a space failed with the new message. - The opt-in guard tasks of suggestion 5, in both containers with an existing
/etc/pip.confthat heldtimeout = 60: off by default (skipped=2), first run onchanged=1, second runchanged=0. The file kepttimeout = 60and gotrequire-virtualenv = true, andsudo -H pip3 install sixthen printedERROR: Could not find an activated virtualenv (required). - A copy of the role with suggestions 3, 4 and 5 applied: the first
ansible-lint26.10.1 run failed withno-changed-whenon thepip config settask. Afterchanged_when: trueit printedPassed: 0 failure(s), 0 warning(s)on theproductionprofile, andyamllint -sexited 0. - The health check line of suggestion 6 passed in a fresh venv, and failed with
AssertionError: ['pytest']afterpip install pytest==9.0.0into it. - A workspace path with a space, with the PR as it is:
colcon buildfinished, but the built executable failed withcannot execute: required file not found. Its shebang was#!"<path with space>/.venv/bin/python3". - Locked versions: the Jazzy snapshot
2026-10-05servespython3-rosdep0.27.0-1. The Humble snapshot2026-05-14serves0.26.0-1, which works on Humble. - Not run: a full image build with these changes,
colcon testof any Autoware package under the guard, theawuser, Web.Auto, and the role on a real host.
These review notes are AI generated.
| gosu \ | ||
| python3-venv | ||
|
|
||
| # Python venv for rosdep pip keys (PEP 668 on Ubuntu 24.04 blocks pip into the system Python). | ||
| # - --system-site-packages keeps apt-installed ROS 2 Python packages visible. | ||
| # - rosdep and colcon are installed into the venv so that they run on the venv interpreter: | ||
| # rosdep's pip installer requires running inside a venv, and ament_python executables | ||
| # get the shebang of the interpreter that built them. | ||
| # - The venv lives outside /opt/autoware so that `COPY --from=*-devel /opt/autoware` does not | ||
| # bring build-time pip dependencies into runtime images. | ||
| # See ansible/roles/python_venv/README.md for the same setup on a host. | ||
| ENV VIRTUAL_ENV=/opt/autoware-venv | ||
| RUN python3 -m venv --system-site-packages "${VIRTUAL_ENV}" && \ | ||
| "${VIRTUAL_ENV}/bin/pip" install --no-cache-dir --ignore-installed --no-deps rosdep colcon-core && \ | ||
| "${VIRTUAL_ENV}/bin/pip" install --no-cache-dir rosdep colcon-core | ||
| ENV PATH="${VIRTUAL_ENV}/bin:${PATH}" |
There was a problem hiding this comment.
The second pip install resolves the PyPI requirements of colcon-core, and it replaces apt packages in every image. On ros:jazzy-ros-base it printed:
Successfully installed catkin-pkg-1.1.1 coverage-7.17.2 iniconfig-2.3.1 pluggy-1.7.0 pytest-9.2.0 pytest-cov-7.1.0 pytest-repeat-0.9.4 pytest-rerunfailures-16.7 rosdistro-1.1.0 rospkg-1.6.3
After that, colcon test in the devel images runs pytest 9.2.0 instead of the apt 7.4.4, and none of these versions is locked.
The venv already sees the apt rosdep2 and colcon_core modules through --system-site-packages. Only the launchers /usr/bin/rosdep and /usr/bin/colcon start /usr/bin/python3. So a launcher in the venv that runs the apt module on the venv interpreter is enough, and nothing comes from PyPI.
Two more changes in this suggestion:
python3-rosdep: the venv check of rosdep came in 0.27.0.ros:*-ros-baseships 0.26.0, and the runtime stages do not runros2_dev_tools. With 0.26.0 on Jazzy, a pip key fails with the PEP 668 error./etc/pip.confwithrequire-virtualenv = true: on Humble nothing blocks a pip install into the system Python. With this line, pip refuses every install outside a venv, on both distros.pipxstill works, because it runs pip inside its own venvs.
| gosu \ | |
| python3-venv | |
| # Python venv for rosdep pip keys (PEP 668 on Ubuntu 24.04 blocks pip into the system Python). | |
| # - --system-site-packages keeps apt-installed ROS 2 Python packages visible. | |
| # - rosdep and colcon are installed into the venv so that they run on the venv interpreter: | |
| # rosdep's pip installer requires running inside a venv, and ament_python executables | |
| # get the shebang of the interpreter that built them. | |
| # - The venv lives outside /opt/autoware so that `COPY --from=*-devel /opt/autoware` does not | |
| # bring build-time pip dependencies into runtime images. | |
| # See ansible/roles/python_venv/README.md for the same setup on a host. | |
| ENV VIRTUAL_ENV=/opt/autoware-venv | |
| RUN python3 -m venv --system-site-packages "${VIRTUAL_ENV}" && \ | |
| "${VIRTUAL_ENV}/bin/pip" install --no-cache-dir --ignore-installed --no-deps rosdep colcon-core && \ | |
| "${VIRTUAL_ENV}/bin/pip" install --no-cache-dir rosdep colcon-core | |
| ENV PATH="${VIRTUAL_ENV}/bin:${PATH}" | |
| gosu \ | |
| python3-rosdep \ | |
| python3-venv | |
| # One venv for all images, outside /opt/autoware so that runtime images do not get the pip packages of the devel images. | |
| # The launchers run the apt rosdep and colcon on the venv interpreter. pip.conf blocks pip installs outside a venv. | |
| ENV VIRTUAL_ENV=/opt/autoware-venv | |
| RUN python3 -m venv --system-site-packages "${VIRTUAL_ENV}" && \ | |
| printf '#!%s/bin/python3\nimport sys\nfrom rosdep2.main import rosdep_main\nsys.exit(rosdep_main())\n' \ | |
| "${VIRTUAL_ENV}" >"${VIRTUAL_ENV}/bin/rosdep" && \ | |
| printf '#!%s/bin/python3\nimport sys\nfrom colcon_core.command import main\nsys.exit(main())\n' \ | |
| "${VIRTUAL_ENV}" >"${VIRTUAL_ENV}/bin/colcon" && \ | |
| chmod 755 "${VIRTUAL_ENV}/bin/rosdep" "${VIRTUAL_ENV}/bin/colcon" && \ | |
| printf '[global]\nrequire-virtualenv = true\n' >/etc/pip.conf | |
| ENV PATH="${VIRTUAL_ENV}/bin:${PATH}" |
| All images have a Python venv at `/opt/autoware-venv`, created in `base` with `--system-site-packages`. | ||
| `VIRTUAL_ENV` is set and `/opt/autoware-venv/bin` is first on `PATH`, so `python3`, `pip`, `rosdep` and `colcon` all use the venv. | ||
| Python packages from rosdep pip keys (`*-pip`) are installed into the venv instead of the system Python, which is externally managed on Ubuntu 24.04 ([PEP 668](https://peps.python.org/pep-0668/)). | ||
| The venv is kept outside `/opt/autoware` so that runtime images do not get the build-time pip packages of the devel images. |
There was a problem hiding this comment.
The venv belongs to root. The image stages run rosdep install as root, so they work. The aw user cannot write to the venv, so pip keys do not install from a devcontainer shell.
| All images have a Python venv at `/opt/autoware-venv`, created in `base` with `--system-site-packages`. | |
| `VIRTUAL_ENV` is set and `/opt/autoware-venv/bin` is first on `PATH`, so `python3`, `pip`, `rosdep` and `colcon` all use the venv. | |
| Python packages from rosdep pip keys (`*-pip`) are installed into the venv instead of the system Python, which is externally managed on Ubuntu 24.04 ([PEP 668](https://peps.python.org/pep-0668/)). | |
| The venv is kept outside `/opt/autoware` so that runtime images do not get the build-time pip packages of the devel images. | |
| All images have a Python venv at `/opt/autoware-venv`, created in `base` with `--system-site-packages`. | |
| `VIRTUAL_ENV` is set and `/opt/autoware-venv/bin` is first on `PATH`, so `python3`, `pip`, `rosdep` and `colcon` all use the venv. | |
| `rosdep` and `colcon` in the venv are small launchers that run the apt-installed tools on the venv interpreter. | |
| Python packages from rosdep pip keys (`*-pip`) are installed into the venv instead of the system Python. `/etc/pip.conf` sets `require-virtualenv = true`, so pip refuses to install outside a venv. | |
| The venv belongs to root, so only root can install pip keys into it. | |
| The venv is kept outside `/opt/autoware` so that runtime images do not get the build-time pip packages of the devel images. |
| # Python tools whose entry points must run on the venv interpreter. | ||
| # - rosdep: its pip installer only works under PEP 668 when it runs inside a venv. | ||
| # - colcon-core: ament_python executables get the shebang of the interpreter that built them. | ||
| python_venv__tools: | ||
| - package: rosdep | ||
| executable: rosdep | ||
| - package: colcon-core | ||
| executable: colcon |
There was a problem hiding this comment.
The same change as in base.Dockerfile. Each tool gets the module and the function that its launcher calls. The new python_venv__require_virtualenv is the opt-in guard of inline comment 5.
| # Python tools whose entry points must run on the venv interpreter. | |
| # - rosdep: its pip installer only works under PEP 668 when it runs inside a venv. | |
| # - colcon-core: ament_python executables get the shebang of the interpreter that built them. | |
| python_venv__tools: | |
| - package: rosdep | |
| executable: rosdep | |
| - package: colcon-core | |
| executable: colcon | |
| # Set to true to make pip refuse installs outside a virtual environment, for the whole host (/etc/pip.conf). | |
| python_venv__require_virtualenv: false | |
| # apt-installed Python tools that must run on the venv interpreter. The role writes a launcher for each one. | |
| # - rosdep: its pip installer only works under PEP 668 when it runs inside a venv. | |
| # - colcon: ament_python executables get the shebang of the interpreter that built them. | |
| python_venv__tools: | |
| - executable: rosdep | |
| module: rosdep2.main | |
| function: rosdep_main | |
| - executable: colcon | |
| module: colcon_core.command | |
| function: main |
| msg: >- | ||
| Autoware workspace '{{ python_venv__workspace_dir }}' does not exist. | ||
| Run the playbook from the workspace root or set python_venv__workspace_dir. | ||
| when: not (python_venv__workspace_stat.stat.isdir | default(false)) |
There was a problem hiding this comment.
The PR body says that a workspace path with a space works. The venv works, but every ament_python executable that colcon builds does not start. I tried it with the PR as it is: the shebang became #!"<path with space>/.venv/bin/python3", and the executable failed with cannot execute: required file not found. Linux cannot run a shebang path with a space, so I think that the role must stop early.
| when: not (python_venv__workspace_stat.stat.isdir | default(false)) | |
| when: not (python_venv__workspace_stat.stat.isdir | default(false)) | |
| - name: Fail if the virtual environment path has a space | |
| ansible.builtin.fail: | |
| msg: >- | |
| The venv path '{{ python_venv__venv_dir }}' has a space. Linux cannot run a shebang line with a space, | |
| so colcon-built Python executables cannot start. Use a workspace path without spaces. | |
| when: "' ' in python_venv__venv_dir" |
| # The tools are usually already installed from apt, so pip would consider them satisfied by the system site-packages | ||
| # and would not create entry points in the venv. --ignore-installed --no-deps installs only the tools themselves. | ||
| - name: Install Python tools into the virtual environment | ||
| ansible.builtin.command: | ||
| argv: | ||
| - "{{ python_venv__venv_dir }}/bin/pip" | ||
| - install | ||
| - --ignore-installed | ||
| - --no-deps | ||
| - "{{ item.package }}" | ||
| creates: "{{ python_venv__venv_dir }}/bin/{{ item.executable }}" | ||
| loop: "{{ python_venv__tools }}" | ||
| loop_control: | ||
| label: "{{ item.package }}" | ||
|
|
||
| # Installs only the dependencies that are not provided by the system site-packages. | ||
| - name: Install missing dependencies of the Python tools | ||
| ansible.builtin.command: | ||
| argv: "{{ [python_venv__venv_dir ~ '/bin/pip', 'install'] + (python_venv__tools | map(attribute='package') | list) }}" | ||
| register: python_venv__pip_deps | ||
| changed_when: "'Successfully installed' in python_venv__pip_deps.stdout" | ||
|
|
There was a problem hiding this comment.
The same change as in base.Dockerfile: one launcher for each tool, and no pip install. A second run reports changed=0, and an apt upgrade of rosdep or colcon applies to the venv at once, because the launcher runs the apt code.
Please also add an opt-in guard for hosts. On Humble, if someone runs rosdep install without the venv, rosdep runs sudo -H pip3 and installs into the system Python with no error. With python_venv__require_virtualenv: true, the role sets require-virtualenv = true in /etc/pip.conf, and pip refuses every install outside a venv. It changes pip for the whole host, so it stays off by default. pip config --global set adds the line and keeps the other settings in an existing /etc/pip.conf, and community.general.ini_file is not available in this collection.
| # The tools are usually already installed from apt, so pip would consider them satisfied by the system site-packages | |
| # and would not create entry points in the venv. --ignore-installed --no-deps installs only the tools themselves. | |
| - name: Install Python tools into the virtual environment | |
| ansible.builtin.command: | |
| argv: | |
| - "{{ python_venv__venv_dir }}/bin/pip" | |
| - install | |
| - --ignore-installed | |
| - --no-deps | |
| - "{{ item.package }}" | |
| creates: "{{ python_venv__venv_dir }}/bin/{{ item.executable }}" | |
| loop: "{{ python_venv__tools }}" | |
| loop_control: | |
| label: "{{ item.package }}" | |
| # Installs only the dependencies that are not provided by the system site-packages. | |
| - name: Install missing dependencies of the Python tools | |
| ansible.builtin.command: | |
| argv: "{{ [python_venv__venv_dir ~ '/bin/pip', 'install'] + (python_venv__tools | map(attribute='package') | list) }}" | |
| register: python_venv__pip_deps | |
| changed_when: "'Successfully installed' in python_venv__pip_deps.stdout" | |
| # rosdep and colcon come from apt. Each launcher runs the apt module on the venv interpreter, so nothing comes from PyPI. | |
| - name: Run the apt-installed Python tools on the venv interpreter | |
| ansible.builtin.copy: | |
| dest: "{{ python_venv__venv_dir }}/bin/{{ item.executable }}" | |
| mode: "0755" | |
| content: | | |
| #!{{ python_venv__venv_dir }}/bin/python3 | |
| import sys | |
| from {{ item.module }} import {{ item.function }} | |
| sys.exit({{ item.function }}()) | |
| loop: "{{ python_venv__tools }}" | |
| loop_control: | |
| label: "{{ item.executable }}" | |
| - name: Read the pip setting that refuses installs outside a virtual environment | |
| ansible.builtin.command: | |
| argv: | |
| - python3 | |
| - -m | |
| - pip | |
| - config | |
| - --global | |
| - get | |
| - global.require-virtualenv | |
| register: python_venv__require_virtualenv_get | |
| changed_when: false | |
| failed_when: false | |
| when: python_venv__require_virtualenv | |
| - name: Make pip refuse installs outside a virtual environment | |
| become: true | |
| ansible.builtin.command: | |
| argv: | |
| - python3 | |
| - -m | |
| - pip | |
| - config | |
| - --global | |
| - set | |
| - global.require-virtualenv | |
| - "true" | |
| changed_when: true | |
| when: | |
| - python_venv__require_virtualenv | |
| - python_venv__require_virtualenv_get.stdout != "true" | |
| - installs `rosdep` and `colcon-core` into the venv so that they run on the venv interpreter. | ||
| - rosdep must run on the venv interpreter, otherwise its PEP 668 check fails. | ||
| - colcon must run on the venv interpreter, because `ament_python` executables get the shebang of the interpreter that built them. |
There was a problem hiding this comment.
| - installs `rosdep` and `colcon-core` into the venv so that they run on the venv interpreter. | |
| - rosdep must run on the venv interpreter, otherwise its PEP 668 check fails. | |
| - colcon must run on the venv interpreter, because `ament_python` executables get the shebang of the interpreter that built them. | |
| - writes small `rosdep` and `colcon` launchers into the venv. They run the apt-installed tools on the venv interpreter, so nothing comes from PyPI. | |
| - rosdep must run on the venv interpreter, otherwise its PEP 668 check fails. | |
| - colcon must run on the venv interpreter, because `ament_python` executables get the shebang of the interpreter that built them. |
| | ---------------------------- | -------- | ----------------------------------------------------------------------------------------- | | ||
| | `python_venv__workspace_dir` | false | Root of the Autoware workspace. Defaults to the directory `ansible-playbook` is run from. | | ||
| | `python_venv__venv_dir` | false | Path of the venv. Defaults to `<python_venv__workspace_dir>/.venv`. | | ||
| | `python_venv__tools` | false | Python tools installed into the venv. Defaults to `rosdep` and `colcon-core`. | |
There was a problem hiding this comment.
| | `python_venv__tools` | false | Python tools installed into the venv. Defaults to `rosdep` and `colcon-core`. | | |
| | `python_venv__tools` | false | apt-installed Python tools that get a launcher in the venv. Defaults to `rosdep` and `colcon`. | | |
| | `python_venv__require_virtualenv` | false | If `true`, sets `require-virtualenv = true` in `/etc/pip.conf`, so pip refuses every install outside a venv on this host. Defaults to `false`. | |
| # rosdep and colcon are usually already installed from apt, so `--ignore-installed --no-deps` is needed | ||
| # to create their entry points in the venv. The second command installs only missing dependencies. | ||
| .venv/bin/pip install --ignore-installed --no-deps rosdep colcon-core | ||
| .venv/bin/pip install rosdep colcon-core |
There was a problem hiding this comment.
| # rosdep and colcon are usually already installed from apt, so `--ignore-installed --no-deps` is needed | |
| # to create their entry points in the venv. The second command installs only missing dependencies. | |
| .venv/bin/pip install --ignore-installed --no-deps rosdep colcon-core | |
| .venv/bin/pip install rosdep colcon-core | |
| # rosdep and colcon come from apt. These launchers run them on the venv interpreter. | |
| printf '#!%s/bin/python3\nimport sys\nfrom rosdep2.main import rosdep_main\nsys.exit(rosdep_main())\n' "$PWD/.venv" >.venv/bin/rosdep | |
| printf '#!%s/bin/python3\nimport sys\nfrom colcon_core.command import main\nsys.exit(main())\n' "$PWD/.venv" >.venv/bin/colcon | |
| chmod 755 .venv/bin/rosdep .venv/bin/colcon |
| rosdep --version | ||
| # colcon extensions installed from apt must be visible from the venv | ||
| colcon --help | grep -q mixin |
There was a problem hiding this comment.
The current checks pass with the PyPI copies too. This line fails if the venv hides an apt package. It passed in a fresh venv here, and it failed with AssertionError: ['pytest'] after a PyPI pytest went into the venv.
| rosdep --version | |
| # colcon extensions installed from apt must be visible from the venv | |
| colcon --help | grep -q mixin | |
| rosdep --version | |
| # colcon extensions installed from apt must be visible from the venv | |
| colcon --help | grep -q mixin | |
| # the tools and their dependencies must come from apt, not from PyPI | |
| .venv/bin/python3 -c 'import catkin_pkg, colcon_core, pytest, rosdep2; bad = [m.__name__ for m in (catkin_pkg, colcon_core, pytest, rosdep2) if not m.__file__.startswith("/usr/lib/python3/")]; assert not bad, bad' |
| # Use rosdep and colcon from the workspace venv created in autoware-setup, so that rosdep pip keys | ||
| # are installed into the venv. `--as-root pip:false` keeps pip from running via sudo (system pip). | ||
| # shellcheck disable=SC1091 | ||
| source "$AUTOWARE_PATH/.venv/bin/activate" | ||
| rosdep update | ||
| rosdep install -y --from-paths src --ignore-src --rosdistro "$ROS_DISTRO" | ||
| rosdep install -y --from-paths src --ignore-src --rosdistro "$ROS_DISTRO" --as-root pip:false |
There was a problem hiding this comment.
Web.Auto runs on Humble (.webauto-ci.yaml:71 uses ubuntu:22.04), so sudo pip3 install xmlschema==3.4.5 on line 27 installs into the system Python. With the venv in place, it can go into the venv instead. Please also delete lines 26 and 27. They are outside the diff, so this suggestion cannot remove them. I did not run this on Web.Auto.
The built ament_python executables get #!$AUTOWARE_PATH/.venv/bin/python3. Does the Web.Auto runtime keep $AUTOWARE_PATH/.venv and put it on PATH?
| # Use rosdep and colcon from the workspace venv created in autoware-setup, so that rosdep pip keys | |
| # are installed into the venv. `--as-root pip:false` keeps pip from running via sudo (system pip). | |
| # shellcheck disable=SC1091 | |
| source "$AUTOWARE_PATH/.venv/bin/activate" | |
| rosdep update | |
| rosdep install -y --from-paths src --ignore-src --rosdistro "$ROS_DISTRO" | |
| rosdep install -y --from-paths src --ignore-src --rosdistro "$ROS_DISTRO" --as-root pip:false | |
| # Use rosdep and colcon from the workspace venv created in autoware-setup, so that rosdep pip keys | |
| # are installed into the venv. `--as-root pip:false` keeps pip from running via sudo (system pip). | |
| # shellcheck disable=SC1091 | |
| source "$AUTOWARE_PATH/.venv/bin/activate" | |
| # install xmlschema<4.0.0 before rosdep install as workaround for scenario_simulator_v2 | |
| pip install xmlschema==3.4.5 | |
| rosdep update | |
| rosdep install -y --from-paths src --ignore-src --rosdistro "$ROS_DISTRO" --as-root pip:false |
Description
On Ubuntu 24.04 (ROS 2 Jazzy), the system Python is marked as externally managed (PEP 668). Because of this,
rosdep installfails for any pip key (*-pip). This PR sets up a Python venv so that pip keys are installed into the venv instead of the system Python. It covers host setups (Ansible), Docker images and Web.Auto CI.This follows the design discussion in https://github.com/orgs/autowarefoundation/discussions/7376 and the meeting notes in https://github.com/orgs/autowarefoundation/discussions/7388, which agreed on these points:
<workspace>/.venv.Common approach
--system-site-packages. Python packages installed with apt (rclpy, colcon extensions and so on) stay visible.rosdepandcolcon-coreare installed into the venv so that both run on the venv interpreter.ament_pythonexecutables get the shebang of the interpreter that built them, so colcon has to run from the venv too.pip installtreats them as satisfied and creates no entry points in the venv. So they are installed with--ignore-installed --no-deps, then a secondpip installadds any missing dependencies.Ansible: new optional role
python_venvpython3-venvand creates<workspace>/.venv. The workspace defaults to the directoryansible-playbookis run from, or setpython_venv__workspace_dir..venv/COLCON_IGNORE, and adds.venv/to.gitignore.install_dev_env.yamlwithtags: [never, python_venv], so it runs only when that tag is requested.argv, so a path with spaces works. The workspace check follows symlinks.--as-root pip:falseis required when rosdep is not run as root. Without it, rosdep runssudo -H pip3, which resetsPATHand uses the system pip. apt keys are still installed with sudo.Docker
docker/base.Dockerfilecreates the venv at/opt/autoware-venv, setsVIRTUAL_ENV, and puts/opt/autoware-venv/binfirst onPATH. Every image inherits it, and all existingrosdep installandcolcon buildsteps use the venv with no further changes./opt/autowareon purpose. Runtime images runCOPY --from=*-devel /opt/autoware /opt/autoware. If the venv were inside/opt/autoware, that copy would overwrite the runtime venv with the devel one, which includes build- and test-only pip packages. Because every image has the venv at the same path,ament_pythonshebangs built in devel images are valid in runtime images.docker/README.md.Web.Auto CI
autoware-setupcreates$AUTOWARE_PATH/.venvwith thepython_venvrole.autoware-buildactivates it and runsrosdep install ... --as-root pip:false, so colcon also runs from the venv.How was this PR tested?
ros:jazzy-ros-base, which has/usr/lib/python3.12/EXTERNALLY-MANAGED. I ran the same commands asbase.Dockerfilein a container:rosdep,colconandpython3resolve to/opt/autoware-venv. The venv's colcon still sees colcon-ros installed from apt, and rclpy imports.rosdep installinstalled a test pip key into/opt/autoware-venv. The rosdep source was local.colcon buildof anament_pythonpackage produced#!/opt/autoware-venv/bin/python3, andros2 runimported the pip dependency from the venv.baseimage locally reached the venv step. pip then failed only because my sandbox's HTTPS proxy uses its own CA. CI will show the real result..venv, and a second run reportedchanged=0.ansible-lint ansible/, yamllint, prettier, markdownlint, shellcheck and shfmt pass. I could not run hadolint locally, so CI covers it.health-check-ansiblenow also runs the role on Ubuntu 22.04 and 24.04. It checks thatrosdepandcolconresolve to the venv and that colcon extensions installed with apt are visible.🤖 Generated with Claude Code
https://claude.ai/code/session_017krcQAZb8E7hVL1jGo7euG