diff --git a/.ansible-lint b/.ansible-lint index 0f05d83..d66c4dd 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -9,5 +9,4 @@ exclude_paths: - molecule/jupyter-system offline: false use_default_rules: true -parseable: true verbosity: 1 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2991f52 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,21 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). + +## [Unreleased] + +### Changed +- Python installation now uses [`uv`](https://docs.astral.sh/uv/) instead of Posit's prebuilt binaries, which are no longer being built by Posit for new Python versions. +- `uv` itself is installed by downloading a pinned release archive from GitHub releases, rather than running the upstream install script. +- The symlink from `/opt/python/{{ version }}` to the actual `uv`-managed install directory is now created with the `ansible.builtin.file` module instead of a raw shell command. +- Installation tasks shared between Debian and RedHat families were extracted into a common `tasks/install-python.yml`, included by both `tasks/debian/main.yml` and `tasks/redhat/main.yml`. +- `pip` calls that install directly into the `uv`-managed Python now set `PIP_BREAK_SYSTEM_PACKAGES`/`PIP_ROOT_USER_ACTION` as environment variables (needed for PEP 668 "externally managed environment" enforcement on newer Python/pip versions). + +### Added +- New `uv_version` variable controlling which `uv` release gets installed. +- `curl` added to the system dependencies list (needed by the `uv` install process). + +### Removed +- `python_download_url` variable (no longer used now that Python isn't downloaded as a distro-specific package). diff --git a/README.md b/README.md index 0fbd3df..05e9496 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Ansible Python Install -Install Python (with Jupyter) on Ubuntu and RedHat based systems with Posit's pre-compiled binaries ([docs](https://docs.posit.co/resources/install-python/)) +Install Python (with Jupyter) on Ubuntu and RedHat based systems via `uv` ([docs](https://docs.posit.co/resources/install-python-uv.html)) [![CI](https://github.com/Appsilon/ansible-python-install/workflows/CI/badge.svg)](https://github.com/Appsilon/ansible-python-install/actions/workflows/ci.yml) [![Ansible Galaxy](https://img.shields.io/badge/ansible--galaxy-appsilon.python_install-blue.svg)](https://galaxy.ansible.com/appsilon/python_install) @@ -19,6 +19,7 @@ If you want to install Workbench's plugins, then the Python used for Jupyter ins | python_jupyter_install_method | virtualenv | String: virtualenv, system | Method of Jupyter installation | | python_jupyter_python_version | `"{{ python_versions[0] }}"` | One of the versions passed in python_versions | Used only for virtualenv method | | python_jupyter_workbench | true | Boolean: true, false | Whether to install Workbench's plugins for Jupyter | +| uv_version | "0.13.4" | String | Version of `uv` to install from GitHub releases | ## Dependencies diff --git a/defaults/main.yml b/defaults/main.yml index 210bbf4..d20b5e6 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -18,3 +18,4 @@ python_jupyter_workbench: true python_jupyter_workbench_packages: - rsp_jupyter - workbench_jupyterlab==1.1.315 +uv_version: "0.13.4" diff --git a/meta/main.yml b/meta/main.yml index 875bd57..6fe01cf 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -4,7 +4,7 @@ galaxy_info: role_name: python_install author: appsilon company: Appsilon - description: Install Python with Posit's pre-compiled binaries + description: Install Python via uv (Posit-recommended python-build-standalone distributions) license: MIT min_ansible_version: 2.11.1 platforms: diff --git a/tasks/debian/main.yml b/tasks/debian/main.yml index 717f7a4..c9db687 100644 --- a/tasks/debian/main.yml +++ b/tasks/debian/main.yml @@ -6,30 +6,4 @@ update_cache: true cache_valid_time: "{{ apt_update_cache_valid_time | default(3600) }}" tags: - - python-install-system-dependecies - -- name: Install Python | Set release version - ansible.builtin.set_fact: - ubuntu_release: "{{ ansible_facts['distribution_version'] | replace('.', '') }}" - when: ansible_facts['distribution'] == "Ubuntu" - tags: - - python-install-set-facts - - python-install-setup-versions - -- name: Install Python | Download deb package - ansible.builtin.get_url: - url: "https://cdn.rstudio.com/python/ubuntu-{{ ubuntu_release }}/pkgs/python-{{ item }}_1_amd64.deb" - dest: "/tmp/Python-{{ item }}.deb" - mode: "0644" - loop: "{{ python_versions }}" - tags: - - python-install-download-archives - - python-install-setup-versions - -- name: Install Python | Install deb package - ansible.builtin.apt: - deb: "/tmp/Python-{{ item }}.deb" - loop: "{{ python_versions }}" - tags: - - python-install-install-archives - - python-install-setup-versions + - python-install-system-dependencies diff --git a/tasks/install-jupyter.yml b/tasks/install-jupyter.yml index d9828cb..f4f9978 100644 --- a/tasks/install-jupyter.yml +++ b/tasks/install-jupyter.yml @@ -15,6 +15,9 @@ ansible.builtin.pip: name: "{{ python_jupyter_packages }}" executable: pip3 + environment: + PIP_BREAK_SYSTEM_PACKAGES: "1" + PIP_ROOT_USER_ACTION: ignore when: python_jupyter_install_method == "system" - name: Install Python | Set path to jupyter-nbextension diff --git a/tasks/install-python.yml b/tasks/install-python.yml new file mode 100644 index 0000000..019f679 --- /dev/null +++ b/tasks/install-python.yml @@ -0,0 +1,45 @@ +--- +- name: Install Python | Download uv release archive + ansible.builtin.get_url: + url: "https://github.com/astral-sh/uv/releases/download/{{ uv_version }}/uv-x86_64-unknown-linux-gnu.tar.gz" + dest: /tmp/uv.tar.gz + mode: "0644" + +- name: Install Python | Extract uv binary + ansible.builtin.unarchive: + src: /tmp/uv.tar.gz + dest: /usr/local/bin + remote_src: true + extra_opts: + - --strip-components=1 + - --wildcards + - "*/uv" + creates: /usr/local/bin/uv + +- name: Install Python | Install Python versions via uv + ansible.builtin.command: /usr/local/bin/uv python install {{ item }} --install-dir=/opt/python + loop: "{{ python_versions }}" + register: command_result + changed_when: "'Installed' in command_result.stdout" + tags: + - python-install-install-archives + - python-install-setup-versions + +- name: Install Python | Find installed build directory + ansible.builtin.find: + paths: /opt/python + patterns: "cpython-{{ item }}-*" + file_type: directory + loop: "{{ python_versions }}" + register: python_build_dirs + tags: + - python-install-setup-versions + +- name: Install Python | Symlink versioned path + ansible.builtin.file: + src: "{{ (item.files | first).path }}" + dest: "/opt/python/{{ item.item }}" + state: link + loop: "{{ python_build_dirs.results }}" + tags: + - python-install-setup-versions diff --git a/tasks/main.yml b/tasks/main.yml index 34359b4..672b530 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -22,10 +22,16 @@ - name: Install Python | Include installation tasks ansible.builtin.include_tasks: '{{ ansible_os_family | lower }}/main.yml' +- name: Install Python | Include shared installation tasks + ansible.builtin.include_tasks: install-python.yml + - name: Install Python | Upgrade tools ansible.builtin.command: > /opt/python/{{ item }}/bin/pip install --upgrade \ pip setuptools wheel + environment: + PIP_BREAK_SYSTEM_PACKAGES: "1" + PIP_ROOT_USER_ACTION: ignore loop: "{{ python_versions }}" register: command_result changed_when: "'Successfully installed' in command_result.stdout" @@ -41,6 +47,9 @@ ansible.builtin.pip: executable: "/opt/python/{{ item }}/bin/pip" name: ipykernel + environment: + PIP_BREAK_SYSTEM_PACKAGES: "1" + PIP_ROOT_USER_ACTION: ignore loop: "{{ python_versions }}" - name: Install Python | Check if kernel.json exists diff --git a/tasks/redhat/main.yml b/tasks/redhat/main.yml index 331104a..dcc7c68 100644 --- a/tasks/redhat/main.yml +++ b/tasks/redhat/main.yml @@ -3,25 +3,5 @@ ansible.builtin.dnf: name: "{{ system_dependecies }}" state: present - when: system_dependecies is defined - - -- name: Install Python | Download rpm package - ansible.builtin.get_url: - url: "{{ python_download_url }}/python-{{ item }}-1-1.x86_64.rpm" - dest: "{{ downloads_path }}/python-{{ item }}-1-1.x86_64.rpm" - mode: "0644" - loop: "{{ python_versions }}" - tags: - - python-install-download-archives - - python-install-setup-versions - -- name: Install Python | Install rpm package - ansible.builtin.dnf: - name: "{{ downloads_path }}/python-{{ item }}-1-1.x86_64.rpm" - disable_gpg_check: true - state: present - loop: "{{ python_versions }}" - tags: - - python-install-install-archives - - python-install-setup-versions + allowerasing: true + when: system_dependecies is defined diff --git a/vars/_redhat-9.yml b/vars/_redhat-9.yml index 7c88672..af9f4af 100644 --- a/vars/_redhat-9.yml +++ b/vars/_redhat-9.yml @@ -1,5 +1,5 @@ # vars file --- system_dependecies: + - curl - libev-devel -python_download_url: "https://cdn.rstudio.com/python/rhel-9/pkgs" diff --git a/vars/_rocky-8.yml b/vars/_rocky-8.yml index 48b2783..af9f4af 100644 --- a/vars/_rocky-8.yml +++ b/vars/_rocky-8.yml @@ -1,5 +1,5 @@ # vars file --- system_dependecies: + - curl - libev-devel -python_download_url: "https://cdn.rstudio.com/python/centos-8/pkgs" diff --git a/vars/_ubuntu.yml b/vars/_ubuntu.yml index 430c01d..a89b0a1 100644 --- a/vars/_ubuntu.yml +++ b/vars/_ubuntu.yml @@ -2,6 +2,7 @@ --- python_system_dependecies: - build-essential + - curl - libffi-dev - libgdbm-dev - libsqlite3-dev