Skip to content

feat(ansible,docker,webauto-ci): use Python venv for rosdep pip keys - #7393

Open
hakuturu583 wants to merge 4 commits into
autowarefoundation:mainfrom
hakuturu583:claude/great-wright-3doc06
Open

hakuturu583 wants to merge 4 commits into
autowarefoundation:mainfrom
hakuturu583:claude/great-wright-3doc06

Conversation

@hakuturu583

@hakuturu583 hakuturu583 commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Description

On Ubuntu 24.04 (ROS 2 Jazzy), the system Python is marked as externally managed (PEP 668). Because of this, rosdep install fails 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:

  • The Ansible role is optional.
  • There is one venv per Autoware workspace, not one per package.
  • The venv lives at <workspace>/.venv.
  • With several Autoware versions installed, you activate the venv of the workspace you are working in.

Common approach

  • The venv is created with --system-site-packages. Python packages installed with apt (rclpy, colcon extensions and so on) stay visible.
  • rosdep and colcon-core are installed into the venv so that both run on the venv interpreter.
    • rosdep's pip installer passes its PEP 668 check only when rosdep runs inside a venv.
    • ament_python executables get the shebang of the interpreter that built them, so colcon has to run from the venv too.
    • When the tools are already installed with apt, a plain pip install treats them as satisfied and creates no entry points in the venv. So they are installed with --ignore-installed --no-deps, then a second pip install adds any missing dependencies.

Ansible: new optional role python_venv

  • Installs python3-venv and creates <workspace>/.venv. The workspace defaults to the directory ansible-playbook is run from, or set python_venv__workspace_dir.
  • Adds .venv/COLCON_IGNORE, and adds .venv/ to .gitignore.
  • The role is registered in install_dev_env.yaml with tags: [never, python_venv], so it runs only when that tag is requested.
  • Paths are passed as argv, so a path with spaces works. The workspace check follows symlinks.
cd ~/autoware
ansible-playbook autoware.dev_env.install_dev_env --tags python_venv --ask-become-pass

source .venv/bin/activate
source /opt/ros/"$ROS_DISTRO"/setup.bash
rosdep install -y --from-paths src --ignore-src --rosdistro "$ROS_DISTRO" --as-root pip:false
colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release

--as-root pip:false is required when rosdep is not run as root. Without it, rosdep runs sudo -H pip3, which resets PATH and uses the system pip. apt keys are still installed with sudo.

Docker

  • docker/base.Dockerfile creates the venv at /opt/autoware-venv, sets VIRTUAL_ENV, and puts /opt/autoware-venv/bin first on PATH. Every image inherits it, and all existing rosdep install and colcon build steps use the venv with no further changes.
  • The venv is outside /opt/autoware on purpose. Runtime images run COPY --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_python shebangs built in devel images are valid in runtime images.
  • Documented in docker/README.md.

Web.Auto CI

  • autoware-setup creates $AUTOWARE_PATH/.venv with the python_venv role.
  • autoware-build activates it and runs rosdep install ... --as-root pip:false, so colcon also runs from the venv.

How was this PR tested?

  • Docker logic on ros:jazzy-ros-base, which has /usr/lib/python3.12/EXTERNALLY-MANAGED. I ran the same commands as base.Dockerfile in a container:
    • rosdep, colcon and python3 resolve to /opt/autoware-venv. The venv's colcon still sees colcon-ros installed from apt, and rclpy imports.
    • rosdep install installed a test pip key into /opt/autoware-venv. The rosdep source was local.
    • colcon build of an ament_python package produced #!/opt/autoware-venv/bin/python3, and ros2 run imported the pip dependency from the venv.
    • The system Python does not see the pip package.
    • Building the full base image 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.
  • Ansible role on an Ubuntu 24.04 container:
    • It created .venv, and a second run reported changed=0.
    • A nonexistent workspace fails with a clear error.
    • A workspace path with a space, reached through a symlink, works.
  • Lint: ansible-lint ansible/, yamllint, prettier, markdownlint, shellcheck and shfmt pass. I could not run hadolint locally, so CI covers it.
  • CI: health-check-ansible now also runs the role on Ubuntu 22.04 and 24.04. It checks that rosdep and colcon resolve to the venv and that colcon extensions installed with apt are visible.
  • Not tested: the Web.Auto CI scripts were checked with shellcheck only. They have not been run on Web.Auto.

🤖 Generated with Claude Code

https://claude.ai/code/session_017krcQAZb8E7hVL1jGo7euG

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>
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Thank you for contributing to the Autoware project!

🚧 If your pull request is in progress, switch it to draft mode.

Please ensure:

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-04T14:03:18.873154Z b448918 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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>
@hakuturu583 hakuturu583 added the run:health-check Run health-check label Nov 4, 2026 — with Claude
- 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>
@hakuturu583 hakuturu583 changed the title feat(ansible): add optional python_venv role for workspace-local venv feat(ansible,docker,webauto-ci): use Python venv for rosdep pip keys Nov 4, 2026

@xmfcx xmfcx left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 RUN of suggestion 1, on both distros: rosdep --version printed 0.27.0, colcon --help printed its usage, and pipx install cowsay as a normal user worked with the pip.conf guard.
  • With the guard, /usr/bin/python3 -m pip install six as root printed ERROR: Could not find an activated virtualenv (required).
  • With the guard on Humble, the system rosdep install (no venv) failed at pip3 install -U typesafe-carla and installed nothing.
  • The role tasks of suggestions 4 and 5, on ansible-core 2.21.5: first run changed, second run changed=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.conf that held timeout = 60: off by default (skipped=2), first run on changed=1, second run changed=0. The file kept timeout = 60 and got require-virtualenv = true, and sudo -H pip3 install six then printed ERROR: Could not find an activated virtualenv (required).
  • A copy of the role with suggestions 3, 4 and 5 applied: the first ansible-lint 26.10.1 run failed with no-changed-when on the pip config set task. After changed_when: true it printed Passed: 0 failure(s), 0 warning(s) on the production profile, and yamllint -s exited 0.
  • The health check line of suggestion 6 passed in a fresh venv, and failed with AssertionError: ['pytest'] after pip install pytest==9.0.0 into it.
  • A workspace path with a space, with the PR as it is: colcon build finished, but the built executable failed with cannot execute: required file not found. Its shebang was #!"<path with space>/.venv/bin/python3".
  • Locked versions: the Jazzy snapshot 2026-10-05 serves python3-rosdep 0.27.0-1. The Humble snapshot 2026-05-14 serves 0.26.0-1, which works on Humble.
  • Not run: a full image build with these changes, colcon test of any Autoware package under the guard, the aw user, Web.Auto, and the role on a real host.

These review notes are AI generated.

Comment thread docker/base.Dockerfile
Comment on lines +35 to +50
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}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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-base ships 0.26.0, and the runtime stages do not run ros2_dev_tools. With 0.26.0 on Jazzy, a pip key fails with the PEP 668 error.
  • /etc/pip.conf with require-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. pipx still works, because it runs pip inside its own venvs.
Suggested change
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}"

Comment thread docker/README.md
Comment on lines +57 to +60
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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Suggested change
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.

Comment on lines +5 to +12
# 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Suggested change
# 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))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Suggested change
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"

Comment on lines +41 to +62
# 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"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Suggested change
# 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"

Comment on lines +12 to +14
- 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
- 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`. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
| `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`. |

Comment on lines +76 to +79
# 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
# 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

Comment on lines +94 to +96
rosdep --version
# colcon extensions installed from apt must be visible from the venv
colcon --help | grep -q mixin

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Suggested change
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'

Comment on lines +35 to +40
# 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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?

Suggested change
# 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

@xmfcx

xmfcx commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Please also update the Autoware documentation in the same round, if this changes what a user does. These pages run rosdep install and do not mention the venv yet:

For the source installation pages, an optional step is enough: run the python_venv role, activate .venv, and add --as-root pip:false to rosdep install. The Docker page runs rosdep as the aw user, who cannot write to the root-owned /opt/autoware-venv, so it needs a note on how to install a pip key there.

Please open the documentation PR and link it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run:health-check Run health-check

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants