From 506e7a9025d31747f523911161683ec5e3b376f1 Mon Sep 17 00:00:00 2001 From: Aurelio Jargas Date: Mon, 27 Jan 2025 21:33:18 +0100 Subject: [PATCH] Add ensure-uv role Uv (https://docs.astral.sh/uv/) is not declared as a dependency for a Python project, it must be available somehow in the system. This role installs it if missing. - Latest version is installed, unless `ensure_uv_version` is informed. - The installed executable path is set as the `uv_executable` fact. - The `/usr/local/bin/uv` symlink can also be created if `ensure_uv_global_symlink: true`. This new role is a verbatim copy of the `ensure-poetry` role, just doing a `s/poetry/uv/g`. Even this commit is a replay of the commit adding that role: 524b7e7b95dcd6adc311e74dd7f0e6da8a3cce58. Change-Id: I55bc5e1d273045d0978b09f719bf79a875336e30 --- doc/source/python-roles.rst | 1 + roles/ensure-uv/README.rst | 42 +++++++++++++++++ roles/ensure-uv/defaults/main.yaml | 4 ++ roles/ensure-uv/tasks/main.yaml | 44 ++++++++++++++++++ test-playbooks/ensure-uv.yaml | 35 ++++++++++++++ zuul-tests.d/python-jobs.yaml | 75 ++++++++++++++++++++++++++++++ 6 files changed, 201 insertions(+) create mode 100644 roles/ensure-uv/README.rst create mode 100644 roles/ensure-uv/defaults/main.yaml create mode 100644 roles/ensure-uv/tasks/main.yaml create mode 100644 test-playbooks/ensure-uv.yaml diff --git a/doc/source/python-roles.rst b/doc/source/python-roles.rst index dc6303110..cb812f86d 100644 --- a/doc/source/python-roles.rst +++ b/doc/source/python-roles.rst @@ -12,6 +12,7 @@ Python Roles .. zuul:autorole:: ensure-sphinx .. zuul:autorole:: ensure-tox .. zuul:autorole:: ensure-twine +.. zuul:autorole:: ensure-uv .. zuul:autorole:: ensure-virtualenv .. zuul:autorole:: fetch-coverage-output .. zuul:autorole:: fetch-python-sdist-output diff --git a/roles/ensure-uv/README.rst b/roles/ensure-uv/README.rst new file mode 100644 index 000000000..66f9c6bdb --- /dev/null +++ b/roles/ensure-uv/README.rst @@ -0,0 +1,42 @@ +Ensure uv is installed + +Look for ``uv``, and if not found, install it via ``pip`` into a +virtual environment for the current user. + +**Role Variables** + +.. zuul:rolevar:: ensure_uv_version + :default: '' + + Version specifier to select the version of uv. The default is the + latest version. + +.. zuul:rolevar:: ensure_uv_venv_path + :default: {{ ansible_user_dir }}/.local/uv + + Directory for the Python venv where uv will be installed. + +.. zuul:rolevar:: ensure_uv_global_symlink + :default: False + + Install a symlink to the uv executable into ``/usr/local/bin/uv``. + This can be useful when scripts need to be run that expect to find + uv in a more standard location and plumbing through the value + of ``ensure_uv_executable`` would be onerous. + + Setting this requires root access, so should only be done in + circumstances where root access is available. + +**Output Variables** + +.. zuul:rolevar:: ensure_uv_executable + :default: uv + + After running this role, ``ensure_uv_executable`` will be set as the path + to a valid ``uv``. + + At role runtime, look for an existing ``uv`` at this specific + path. Note the default (``uv``) effectively means to find uv in + the current ``$PATH``. For example, if your base image + pre-installs uv in an out-of-path environment, set this so the + role does not attempt to install the user version. diff --git a/roles/ensure-uv/defaults/main.yaml b/roles/ensure-uv/defaults/main.yaml new file mode 100644 index 000000000..a6060f028 --- /dev/null +++ b/roles/ensure-uv/defaults/main.yaml @@ -0,0 +1,4 @@ +ensure_uv_global_symlink: false +ensure_uv_version: "" +ensure_uv_executable: uv +ensure_uv_venv_path: "{{ ansible_user_dir }}/.local/uv" diff --git a/roles/ensure-uv/tasks/main.yaml b/roles/ensure-uv/tasks/main.yaml new file mode 100644 index 000000000..87dd44e8a --- /dev/null +++ b/roles/ensure-uv/tasks/main.yaml @@ -0,0 +1,44 @@ +- name: Install pip + include_role: + name: ensure-pip + +- name: Check if uv is installed + shell: | + command -v {{ ensure_uv_executable }} {{ ensure_uv_venv_path }}/bin/uv || exit 1 + args: + executable: /bin/bash + register: uv_preinstalled + failed_when: false + +- name: Export preinstalled ensure_uv_executable + set_fact: + ensure_uv_executable: "{{ uv_preinstalled.stdout_lines[0] }}" + cacheable: true + when: uv_preinstalled.rc == 0 + +- name: Install uv to local env + when: uv_preinstalled.rc != 0 + block: + - name: Create local venv + command: "{{ ensure_pip_virtualenv_command }} {{ ensure_uv_venv_path }}" + + - name: Install uv to local venv + command: "{{ ensure_uv_venv_path }}/bin/pip install uv{{ ensure_uv_version }}" + + - name: Export installed ensure_uv_executable path + set_fact: + ensure_uv_executable: "{{ ensure_uv_venv_path }}/bin/uv" + cacheable: true + +- name: Output uv version + command: "{{ ensure_uv_executable }} --version" + +- name: Make global symlink + when: + - ensure_uv_global_symlink + - ensure_uv_executable != '/usr/local/bin/uv' + file: + state: link + src: "{{ ensure_uv_executable }}" + dest: /usr/local/bin/uv + become: yes diff --git a/test-playbooks/ensure-uv.yaml b/test-playbooks/ensure-uv.yaml new file mode 100644 index 000000000..ce20ee05e --- /dev/null +++ b/test-playbooks/ensure-uv.yaml @@ -0,0 +1,35 @@ +- hosts: all + name: Test ensure-uv installs into user environment + tasks: + - name: Verify uv is not installed + command: "uv --version" + register: result + failed_when: result.rc == 0 + - name: Run ensure-uv with uv not installed + include_role: + name: ensure-uv + - name: Verify ensure_uv_executable is set + assert: + that: + - ensure_uv_executable == ansible_user_dir + '/.local/uv/bin/uv' + - name: Verify uv is installed + command: "{{ ensure_uv_executable }} --version" + register: result + failed_when: result.rc != 0 + +- hosts: all + name: Test ensure-uv when ensure_uv_executable is set to an already installed uv + tasks: + - name: Create a virtualenv + command: "{{ ensure_pip_virtualenv_command }} {{ ansible_user_dir }}/uv-venv" + - name: Install uv to local venv + command: "{{ ansible_user_dir }}/uv-venv/bin/pip install uv" + - name: Run ensure-uv pointing to an already installed uv + include_role: + name: ensure-uv + vars: + ensure_uv_executable: "{{ ansible_user_dir }}/uv-venv/bin/uv" + - name: Verify ensure_uv_executable is set to the virtualenv uv + assert: + that: + - ensure_uv_executable == ansible_user_dir + '/uv-venv/bin/uv' diff --git a/zuul-tests.d/python-jobs.yaml b/zuul-tests.d/python-jobs.yaml index 06847127f..d9c1611c9 100644 --- a/zuul-tests.d/python-jobs.yaml +++ b/zuul-tests.d/python-jobs.yaml @@ -291,6 +291,75 @@ - name: ubuntu-noble label: ubuntu-noble +- job: + name: zuul-jobs-test-ensure-uv + description: Test the ensure-uv role + files: + - roles/ensure-uv/.* + - test-playbooks/ensure-uv.yaml + run: test-playbooks/ensure-uv.yaml + tags: all-platforms + +- job: + name: zuul-jobs-test-ensure-uv-centos-9-stream + description: Test the ensure-uv role on centos-9-stream + parent: zuul-jobs-test-ensure-uv + tags: auto-generated + nodeset: + nodes: + - name: centos-9-stream + label: centos-9-stream + +- job: + name: zuul-jobs-test-ensure-uv-debian-bookworm + description: Test the ensure-uv role on debian-bookworm + parent: zuul-jobs-test-ensure-uv + tags: auto-generated + nodeset: + nodes: + - name: debian-bookworm + label: debian-bookworm + +- job: + name: zuul-jobs-test-ensure-uv-debian-bullseye + description: Test the ensure-uv role on debian-bullseye + parent: zuul-jobs-test-ensure-uv + tags: auto-generated + nodeset: + nodes: + - name: debian-bullseye + label: debian-bullseye + +- job: + name: zuul-jobs-test-ensure-uv-ubuntu-focal + description: Test the ensure-uv role on ubuntu-focal + parent: zuul-jobs-test-ensure-uv + tags: auto-generated + nodeset: + nodes: + - name: ubuntu-focal + label: ubuntu-focal + +- job: + name: zuul-jobs-test-ensure-uv-ubuntu-jammy + description: Test the ensure-uv role on ubuntu-jammy + parent: zuul-jobs-test-ensure-uv + tags: auto-generated + nodeset: + nodes: + - name: ubuntu-jammy + label: ubuntu-jammy + +- job: + name: zuul-jobs-test-ensure-uv-ubuntu-noble + description: Test the ensure-uv role on ubuntu-noble + parent: zuul-jobs-test-ensure-uv + tags: auto-generated + nodeset: + nodes: + - name: ubuntu-noble + label: ubuntu-noble + - job: name: zuul-jobs-test-fetch-sphinx-tarball description: Test the fetch-sphinx-tarball role @@ -502,6 +571,12 @@ - zuul-jobs-test-ensure-tox-ubuntu-focal - zuul-jobs-test-ensure-tox-ubuntu-jammy - zuul-jobs-test-ensure-tox-ubuntu-noble + - zuul-jobs-test-ensure-uv-centos-9-stream + - zuul-jobs-test-ensure-uv-debian-bookworm + - zuul-jobs-test-ensure-uv-debian-bullseye + - zuul-jobs-test-ensure-uv-ubuntu-focal + - zuul-jobs-test-ensure-uv-ubuntu-jammy + - zuul-jobs-test-ensure-uv-ubuntu-noble - zuul-jobs-test-fetch-sphinx-tarball-centos-9-stream - zuul-jobs-test-fetch-sphinx-tarball-debian-bookworm - zuul-jobs-test-fetch-sphinx-tarball-debian-bullseye