diff --git a/doc/source/python-roles.rst b/doc/source/python-roles.rst index dc6303110..93e80b7ad 100644 --- a/doc/source/python-roles.rst +++ b/doc/source/python-roles.rst @@ -8,6 +8,7 @@ Python Roles .. zuul:autorole:: ensure-nox .. zuul:autorole:: ensure-pip .. zuul:autorole:: ensure-poetry +.. zuul:autorole:: ensure-pyproject-build .. zuul:autorole:: ensure-python .. zuul:autorole:: ensure-sphinx .. zuul:autorole:: ensure-tox diff --git a/roles/ensure-pyproject-build/README.rst b/roles/ensure-pyproject-build/README.rst new file mode 100644 index 000000000..8ce6b1cb6 --- /dev/null +++ b/roles/ensure-pyproject-build/README.rst @@ -0,0 +1,43 @@ +Ensure pyproject-build is installed + +Look for ``pyproject-build``, and if not found, install it via ``pip`` into a +virtual environment for the current user. + +**Role Variables** + +.. zuul:rolevar:: ensure_pyproject_build_version + :default: '' + + Version specifier to select the version of pyproject-build. The default is + the latest version. + +.. zuul:rolevar:: ensure_pyproject_build_venv_path + :default: {{ ansible_user_dir }}/.local/pyproject-build + + Directory for the Python venv where pyproject-build will be installed. + +.. zuul:rolevar:: ensure_pyproject_build_global_symlink + :default: False + + Install a symlink to the pyproject-build executable into + ``/usr/local/bin/pyproject-build``. This can be useful when scripts need to + be run that expect to find pyproject-build in a more standard location and + plumbing through the value of ``ensure_pyproject_build_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_pyproject_build_executable + :default: pyproject-build + + After running this role, ``ensure_pyproject_build_executable`` will be set + as the path to a valid ``pyproject-build``. + + At role runtime, look for an existing ``pyproject-build`` at this specific + path. Note the default (``pyproject-build``) effectively means to find + pyproject-build in the current ``$PATH``. For example, if your base image + pre-installs pyproject-build in an out-of-path environment, set this so the + role does not attempt to install the user version. diff --git a/roles/ensure-pyproject-build/defaults/main.yaml b/roles/ensure-pyproject-build/defaults/main.yaml new file mode 100644 index 000000000..d944d4035 --- /dev/null +++ b/roles/ensure-pyproject-build/defaults/main.yaml @@ -0,0 +1,4 @@ +ensure_pyproject_build_global_symlink: false +ensure_pyproject_build_version: "" +ensure_pyproject_build_executable: pyproject-build +ensure_pyproject_build_venv_path: "{{ ansible_user_dir }}/.local/pyproject-build" diff --git a/roles/ensure-pyproject-build/tasks/main.yaml b/roles/ensure-pyproject-build/tasks/main.yaml new file mode 100644 index 000000000..f46d4c799 --- /dev/null +++ b/roles/ensure-pyproject-build/tasks/main.yaml @@ -0,0 +1,44 @@ +- name: Install pip + include_role: + name: ensure-pip + +- name: Check if pyproject-build is installed + shell: | + command -v {{ ensure_pyproject_build_executable }} {{ ensure_pyproject_build_venv_path }}/bin/pyproject-build || exit 1 + args: + executable: /bin/bash + register: pyproject_build_preinstalled + failed_when: false + +- name: Export preinstalled ensure_pyproject_build_executable + set_fact: + ensure_pyproject_build_executable: "{{ pyproject_build_preinstalled.stdout_lines[0] }}" + cacheable: true + when: pyproject_build_preinstalled.rc == 0 + +- name: Install pyproject-build to local env + when: pyproject_build_preinstalled.rc != 0 + block: + - name: Create local venv + command: "{{ ensure_pip_virtualenv_command }} {{ ensure_pyproject_build_venv_path }}" + + - name: Install pyproject-build to local venv + command: "{{ ensure_pyproject_build_venv_path }}/bin/pip install build{{ ensure_pyproject_build_version }}" + + - name: Export installed ensure_pyproject_build_executable path + set_fact: + ensure_pyproject_build_executable: "{{ ensure_pyproject_build_venv_path }}/bin/pyproject-build" + cacheable: true + +- name: Output pyproject-build version + command: "{{ ensure_pyproject_build_executable }} --version" + +- name: Make global symlink + when: + - ensure_pyproject_build_global_symlink + - ensure_pyproject_build_executable != '/usr/local/bin/pyproject-build' + file: + state: link + src: "{{ ensure_pyproject_build_executable }}" + dest: /usr/local/bin/pyproject-build + become: yes diff --git a/test-playbooks/ensure-pyproject-build.yaml b/test-playbooks/ensure-pyproject-build.yaml new file mode 100644 index 000000000..0abb0f013 --- /dev/null +++ b/test-playbooks/ensure-pyproject-build.yaml @@ -0,0 +1,35 @@ +- hosts: all + name: Test ensure-pyproject-build installs into user environment + tasks: + - name: Verify pyproject-build is not installed + command: "pyproject-build --version" + register: result + failed_when: result.rc == 0 + - name: Run ensure-pyproject-build with pyproject-build not installed + include_role: + name: ensure-pyproject-build + - name: Verify ensure_pyproject_build_executable is set + assert: + that: + - ensure_pyproject_build_executable == ansible_user_dir + '/.local/pyproject-build/bin/pyproject-build' + - name: Verify pyproject-build is installed + command: "{{ ensure_pyproject_build_executable }} --version" + register: result + failed_when: result.rc != 0 + +- hosts: all + name: Test ensure-pyproject-build when ensure_pyproject_build_executable is set to an already installed pyproject-build + tasks: + - name: Create a virtualenv + command: '{{ ensure_pip_virtualenv_command }} {{ ansible_user_dir }}/pyproject-build-venv' + - name: Install pyproject-build to local venv + command: '{{ ansible_user_dir }}/pyproject-build-venv/bin/pip install build' + - name: Run ensure-pyproject-build pointing to an already installed pyproject-build + include_role: + name: ensure-pyproject-build + vars: + ensure_pyproject_build_executable: "{{ ansible_user_dir }}/pyproject-build-venv/bin/pyproject-build" + - name: Verify ensure_pyproject_build_executable is set to the virtualenv pyproject-build + assert: + that: + - ensure_pyproject_build_executable == ansible_user_dir + '/pyproject-build-venv/bin/pyproject-build' diff --git a/zuul-tests.d/python-jobs.yaml b/zuul-tests.d/python-jobs.yaml index 06847127f..c31e907e9 100644 --- a/zuul-tests.d/python-jobs.yaml +++ b/zuul-tests.d/python-jobs.yaml @@ -213,6 +213,75 @@ - name: ubuntu-noble label: ubuntu-noble +- job: + name: zuul-jobs-test-ensure-pyproject-build + description: Test the ensure-pyproject-build role + files: + - roles/ensure-pyproject-build/.* + - test-playbooks/ensure-pyproject-build.yaml + run: test-playbooks/ensure-pyproject-build.yaml + tags: all-platforms + +- job: + name: zuul-jobs-test-ensure-pyproject-build-centos-9-stream + description: Test the ensure-pyproject-build role on centos-9-stream + parent: zuul-jobs-test-ensure-pyproject-build + tags: auto-generated + nodeset: + nodes: + - name: centos-9-stream + label: centos-9-stream + +- job: + name: zuul-jobs-test-ensure-pyproject-build-debian-bookworm + description: Test the ensure-pyproject-build role on debian-bookworm + parent: zuul-jobs-test-ensure-pyproject-build + tags: auto-generated + nodeset: + nodes: + - name: debian-bookworm + label: debian-bookworm + +- job: + name: zuul-jobs-test-ensure-pyproject-build-debian-bullseye + description: Test the ensure-pyproject-build role on debian-bullseye + parent: zuul-jobs-test-ensure-pyproject-build + tags: auto-generated + nodeset: + nodes: + - name: debian-bullseye + label: debian-bullseye + +- job: + name: zuul-jobs-test-ensure-pyproject-build-ubuntu-focal + description: Test the ensure-pyproject-build role on ubuntu-focal + parent: zuul-jobs-test-ensure-pyproject-build + tags: auto-generated + nodeset: + nodes: + - name: ubuntu-focal + label: ubuntu-focal + +- job: + name: zuul-jobs-test-ensure-pyproject-build-ubuntu-jammy + description: Test the ensure-pyproject-build role on ubuntu-jammy + parent: zuul-jobs-test-ensure-pyproject-build + tags: auto-generated + nodeset: + nodes: + - name: ubuntu-jammy + label: ubuntu-jammy + +- job: + name: zuul-jobs-test-ensure-pyproject-build-ubuntu-noble + description: Test the ensure-pyproject-build role on ubuntu-noble + parent: zuul-jobs-test-ensure-pyproject-build + tags: auto-generated + nodeset: + nodes: + - name: ubuntu-noble + label: ubuntu-noble + - job: name: zuul-jobs-test-ensure-sphinx description: Test the ensure-sphinx role @@ -495,6 +564,12 @@ - zuul-jobs-test-ensure-poetry-ubuntu-focal - zuul-jobs-test-ensure-poetry-ubuntu-jammy - zuul-jobs-test-ensure-poetry-ubuntu-noble + - zuul-jobs-test-ensure-pyproject-build-centos-9-stream + - zuul-jobs-test-ensure-pyproject-build-debian-bookworm + - zuul-jobs-test-ensure-pyproject-build-debian-bullseye + - zuul-jobs-test-ensure-pyproject-build-ubuntu-focal + - zuul-jobs-test-ensure-pyproject-build-ubuntu-jammy + - zuul-jobs-test-ensure-pyproject-build-ubuntu-noble - zuul-jobs-test-ensure-sphinx - zuul-jobs-test-ensure-tox-centos-9-stream - zuul-jobs-test-ensure-tox-debian-bookworm