From 3813544f25d40702c8a3381fc725c26fc6974f2f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 21 May 2026 20:03:51 -0500 Subject: [PATCH 1/3] ci: install astral managed Python so prerelease setup is cached --- .github/actions/setup-uv-python/action.yml | 42 ++++++++++++++++++++++ .github/workflows/ci.yml | 41 ++++++++++----------- 2 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 .github/actions/setup-uv-python/action.yml diff --git a/.github/actions/setup-uv-python/action.yml b/.github/actions/setup-uv-python/action.yml new file mode 100644 index 00000000..0990a5b5 --- /dev/null +++ b/.github/actions/setup-uv-python/action.yml @@ -0,0 +1,42 @@ +name: Set up uv and managed Python +description: >- + Pins uv (avoids the raw.githubusercontent.com manifest fetch on cache miss) + and proactively installs the requested Python so cached venvs resolve their + interpreter symlinks in jobs that only restore the venv. setup-uv alone + only sets UV_PYTHON, it does not actually fetch the interpreter until uv + first uses it, so jobs that just activate a cached venv blow up with + broken symlinks on cache hit. + +inputs: + python-version: + description: The Python version uv should install and use. + required: true + uv-version: + description: The uv version setup-uv should install. + required: true + +outputs: + python-version: + description: The Python version uv reports as installed. + value: ${{ steps.uv.outputs.python-version }} + +runs: + using: composite + steps: + - name: Set up uv + id: uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + with: + version: ${{ inputs.uv-version }} + python-version: ${{ inputs.python-version }} + # Persist astral's managed Python across jobs so 'uv venv' / poetry + # env use below is fast on the second job onwards. + cache-python: true + # Jobs that only configure the toolchain (no deps to cache) would + # otherwise abort with "Nothing to cache" on the post step. + ignore-nothing-to-cache: true + - name: Install Python interpreter + shell: bash + env: + PYTHON_VERSION: ${{ inputs.python-version }} + run: uv python install "${PYTHON_VERSION}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e34eeb86..17bbd66a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,7 @@ concurrency: env: POETRY_VIRTUALENVS_IN_PROJECT: "true" UV_PYTHON_PREFERENCE: only-managed + UV_VERSION: "0.11.16" jobs: lint: @@ -55,19 +56,15 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 - - name: Set up uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + - name: Set up uv and Python ${{ matrix.python-version }} + id: python + uses: ./.github/actions/setup-uv-python with: - enable-cache: true + uv-version: ${{ env.UV_VERSION }} + python-version: ${{ matrix.python-version }} - name: Install poetry run: uv tool install poetry - - name: Set up Python - id: setup-python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v5 - with: - python-version: ${{ matrix.python-version }} - cache: "poetry" - allow-prereleases: true + shell: bash - name: Cache poetry venv id: cache-venv uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 @@ -75,10 +72,13 @@ jobs: path: | .venv src/habluetooth/**/*.so - key: venv-v2-${{ runner.os }}-py${{ steps.setup-python.outputs.python-version }}-${{ matrix.extension }}-${{ hashFiles('poetry.lock', 'pyproject.toml', 'build_ext.py', 'src/habluetooth/**/*.py', 'src/habluetooth/**/*.pxd') }} + key: venv-v3-${{ runner.os }}-py${{ steps.python.outputs.python-version }}-${{ matrix.extension }}-${{ hashFiles('poetry.lock', 'pyproject.toml', 'build_ext.py', 'src/habluetooth/**/*.py', 'src/habluetooth/**/*.pxd') }} - name: Install Dependencies if: steps.cache-venv.outputs.cache-hit != 'true' + env: + PYTHON_VERSION: ${{ matrix.python-version }} run: | + poetry env use "$(uv python find "${PYTHON_VERSION}")" if [ "${{ matrix.extension }}" = "skip_cython" ]; then SKIP_CYTHON=1 poetry install --only=main,dev else @@ -97,19 +97,15 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 - - name: Set up uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + - name: Set up uv and Python 3.14 + id: python + uses: ./.github/actions/setup-uv-python with: - enable-cache: true + uv-version: ${{ env.UV_VERSION }} + python-version: "3.14" - name: Install poetry run: uv tool install poetry - - name: Setup Python 3.14 - id: setup-python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v5 - with: - python-version: "3.14" - cache: "poetry" - allow-prereleases: true + shell: bash - name: Cache poetry venv id: cache-venv uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 @@ -117,10 +113,11 @@ jobs: path: | .venv src/habluetooth/**/*.so - key: venv-v2-${{ runner.os }}-benchmark-py${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('poetry.lock', 'pyproject.toml', 'build_ext.py', 'src/habluetooth/**/*.py', 'src/habluetooth/**/*.pxd') }} + key: venv-v3-${{ runner.os }}-benchmark-py${{ steps.python.outputs.python-version }}-${{ hashFiles('poetry.lock', 'pyproject.toml', 'build_ext.py', 'src/habluetooth/**/*.py', 'src/habluetooth/**/*.pxd') }} - name: Install Dependencies if: steps.cache-venv.outputs.cache-hit != 'true' run: | + poetry env use "$(uv python find 3.14)" REQUIRE_CYTHON=1 poetry install --only=main,dev shell: bash - name: Run benchmarks From 6be66a0349c513723e0b425853971b11f0528988 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 21 May 2026 20:10:47 -0500 Subject: [PATCH 2/3] ci: keep benchmark job on actions/setup-python for stable history --- .github/workflows/ci.yml | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17bbd66a..7c526b8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,18 +94,25 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} benchmark: + # Keep actions/setup-python here so codspeed history stays comparable; + # astral's managed Python ships PGO/LTO/BOLT/mimalloc which would shift + # the baseline. runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 - - name: Set up uv and Python 3.14 - id: python - uses: ./.github/actions/setup-uv-python + - name: Set up uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: - uv-version: ${{ env.UV_VERSION }} - python-version: "3.14" + enable-cache: true - name: Install poetry run: uv tool install poetry - shell: bash + - name: Setup Python 3.14 + id: setup-python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v5 + with: + python-version: "3.14" + cache: "poetry" + allow-prereleases: true - name: Cache poetry venv id: cache-venv uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 @@ -113,11 +120,10 @@ jobs: path: | .venv src/habluetooth/**/*.so - key: venv-v3-${{ runner.os }}-benchmark-py${{ steps.python.outputs.python-version }}-${{ hashFiles('poetry.lock', 'pyproject.toml', 'build_ext.py', 'src/habluetooth/**/*.py', 'src/habluetooth/**/*.pxd') }} + key: venv-v2-${{ runner.os }}-benchmark-py${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('poetry.lock', 'pyproject.toml', 'build_ext.py', 'src/habluetooth/**/*.py', 'src/habluetooth/**/*.pxd') }} - name: Install Dependencies if: steps.cache-venv.outputs.cache-hit != 'true' run: | - poetry env use "$(uv python find 3.14)" REQUIRE_CYTHON=1 poetry install --only=main,dev shell: bash - name: Run benchmarks From dfbb4d33d2e6614f0c82d1c9d24e8f3a911aa723 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 21 May 2026 20:13:20 -0500 Subject: [PATCH 3/3] ci: skip uv python install when interpreter already cached --- .github/actions/setup-uv-python/action.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/actions/setup-uv-python/action.yml b/.github/actions/setup-uv-python/action.yml index 0990a5b5..4103b7b1 100644 --- a/.github/actions/setup-uv-python/action.yml +++ b/.github/actions/setup-uv-python/action.yml @@ -39,4 +39,10 @@ runs: shell: bash env: PYTHON_VERSION: ${{ inputs.python-version }} - run: uv python install "${PYTHON_VERSION}" + # 'uv python install' on Windows blows up with a reparse-point tag + # mismatch (os error 4394) when cache-python: true already restored + # the install dir, so only install when find says we have nothing yet. + run: | + if ! uv python find "${PYTHON_VERSION}" >/dev/null 2>&1; then + uv python install "${PYTHON_VERSION}" + fi