From 1c6085e931dc11b6cbaabea9c789153e975c3fa2 Mon Sep 17 00:00:00 2001 From: Henry Borchers Date: Tue, 31 Mar 2026 12:49:50 -0500 Subject: [PATCH 1/2] ci: extend cache lifespan --- ci/jenkins/scripts/create_uv_config.sh | 28 ++++++ ci/jenkins/scripts/new-uv-global-config.ps1 | 27 ++++++ vars/runJenkinsPipeline.groovy | 97 +++++++++++++++------ 3 files changed, 124 insertions(+), 28 deletions(-) create mode 100644 ci/jenkins/scripts/create_uv_config.sh create mode 100644 ci/jenkins/scripts/new-uv-global-config.ps1 diff --git a/ci/jenkins/scripts/create_uv_config.sh b/ci/jenkins/scripts/create_uv_config.sh new file mode 100644 index 000000000..da49ad4e7 --- /dev/null +++ b/ci/jenkins/scripts/create_uv_config.sh @@ -0,0 +1,28 @@ +#! /usr/bin/env bash +# This script creates a global configuration file for Jenkins to make the UV cache metadata for longer than standard + +set -e + +MAX_AGE_API=6000 +MAX_AGE_FILES=365000000 + +GLOBAL_CONFIG_FILE=uv.toml + +echo "# This file is auto-generated by $(basename $0)" > $GLOBAL_CONFIG_FILE + +echo '' >> $GLOBAL_CONFIG_FILE +echo '[[index]]' >> $GLOBAL_CONFIG_FILE +echo "url = \"https://pypi.org/simple\"" >> $GLOBAL_CONFIG_FILE +echo 'default = true' >> $GLOBAL_CONFIG_FILE +echo "cache-control = { api = \"max-age=$MAX_AGE_API\", files = \"max-age=$MAX_AGE_FILES, immutable\" }" >> $GLOBAL_CONFIG_FILE + +index=1 +for arg in "$@"; do + echo '' >> $GLOBAL_CONFIG_FILE + echo '[[index]]' >> $GLOBAL_CONFIG_FILE + echo "name = \"extra-index-$index\"" >> $GLOBAL_CONFIG_FILE + echo "url = \"$arg\"" >> $GLOBAL_CONFIG_FILE + echo "cache-control = { api = \"max-age=$MAX_AGE_API\", files = \"max-age=$MAX_AGE_FILES, immutable\" }" >> $GLOBAL_CONFIG_FILE +done + +echo "$(realpath $GLOBAL_CONFIG_FILE)" diff --git a/ci/jenkins/scripts/new-uv-global-config.ps1 b/ci/jenkins/scripts/new-uv-global-config.ps1 new file mode 100644 index 000000000..fad17d09c --- /dev/null +++ b/ci/jenkins/scripts/new-uv-global-config.ps1 @@ -0,0 +1,27 @@ +$GLOBAL_CONFIG_FILE = "uv.toml" +$MAX_AGE_API="6000" +$MAX_AGE_FILES="365000000" + +$scriptName = $MyInvocation.MyCommand.Name +"# This file is auto-generated by $scriptName" | Set-Content -Path $GLOBAL_CONFIG_FILE -Encoding UTF8 + +'' | Add-Content -Path $GLOBAL_CONFIG_FILE -Encoding UTF8 +'[[index]]' | Add-Content -Path $GLOBAL_CONFIG_FILE -Encoding UTF8 +"url = `"https://pypi.org/simple`"" | Add-Content -Path $GLOBAL_CONFIG_FILE -Encoding UTF8 +'default = true' | Add-Content -Path $GLOBAL_CONFIG_FILE -Encoding UTF8 +"cache-control = { api = `"max-age=$MAX_AGE_API`", files = `"max-age=$MAX_AGE_FILES, immutable`" }" | Add-Content -Path $GLOBAL_CONFIG_FILE -Encoding UTF8 + + +$index = 0 +foreach ($arg in $args) { + if ([string]::IsNullOrWhiteSpace($arg)) { continue } + + '' | Add-Content -Path $GLOBAL_CONFIG_FILE -Encoding UTF8 + '[[index]]' | Add-Content -Path $GLOBAL_CONFIG_FILE -Encoding UTF8 + "name = `"extra-index-$index`"" | Add-Content -Path $GLOBAL_CONFIG_FILE -Encoding UTF8 + "url = `"$arg`"" | Add-Content -Path $GLOBAL_CONFIG_FILE -Encoding UTF8 + 'default = true' | Add-Content -Path $GLOBAL_CONFIG_FILE -Encoding UTF8 + "cache-control = { api = `"max-age=$MAX_AGE_API`", files = `"max-age=$MAX_AGE_FILES, immutable`" }" | Add-Content -Path $GLOBAL_CONFIG_FILE -Encoding UTF8 +} + +Write-Output (Get-ChildItem -Path $GLOBAL_CONFIG_FILE).FullName \ No newline at end of file diff --git a/vars/runJenkinsPipeline.groovy b/vars/runJenkinsPipeline.groovy index bd5370feb..e0b558469 100644 --- a/vars/runJenkinsPipeline.groovy +++ b/vars/runJenkinsPipeline.groovy @@ -85,6 +85,27 @@ def installMSVCRuntime(cacheLocation){ } +def createWindowUVConfig(){ + def scriptFile = "ci\\jenkins\\scripts\\new-uv-global-config.ps1" + if(! fileExists(scriptFile)){ + checkout scm + } + return powershell( + label: 'Setting up uv.toml config file', + script: "& ${scriptFile} \$env:UV_INDEX_URL \$env:UV_EXTRA_INDEX_URL", + returnStdout: true + ).trim() +} + +def createUnixUvConfig(){ + + def scriptFile = 'ci/jenkins/scripts/create_uv_config.sh' + if(! fileExists(scriptFile)){ + checkout scm + } + return sh(label: 'Setting up uv.toml config file', script: "sh ${scriptFile} " + '$UV_INDEX_URL $UV_EXTRA_INDEX_URL', returnStdout: true).trim() +} + def hasSonarCreds(credentialsId){ try{ @@ -210,6 +231,7 @@ def call(){ UV_CACHE_DIR='/tmp/uvcache' UV_PYTHON='3.11' QT_QPA_PLATFORM='offscreen' + UV_CONFIG_FILE=createUnixUvConfig() UV_FROZEN='1' } options { @@ -464,11 +486,13 @@ def call(){ checkout scm docker.image('ghcr.io/astral-sh/uv:debian').inside('--mount source=python-tmp-speedwagon,target=/tmp'){ try{ - envs = sh( - label: 'Get tox environments', - script: 'uv run --isolated --only-group=tox --frozen --quiet tox list -d --no-desc', - returnStdout: true, - ).trim().split('\n') + withEnv(["UV_CONFIG_FILE=${createUnixUvConfig()}"]){ + envs = sh( + label: 'Get tox environments', + script: 'uv run --isolated --only-group=tox --frozen --quiet tox list -d --no-desc', + returnStdout: true, + ).trim().split('\n') + } } finally{ cleanWs( patterns: [ @@ -492,11 +516,13 @@ def call(){ def image = docker.build(UUID.randomUUID().toString(), '-f ci/docker/python/linux/jenkins/Dockerfile .') try{ image.inside('--mount source=python-tmp-speedwagon,target=/tmp --tmpfs /.local/share:exec --tmpfs /.local/bin:exec'){ - sh( label: 'Running Tox', - script: """uv python install cpython-${version} - uv run -p ${version} --frozen --only-group=tox-uv --isolated tox run --runner uv-venv-lock-runner -e ${toxEnv} - """ - ) + withEnv(["UV_CONFIG_FILE=${createUnixUvConfig()}"]){ + sh( label: 'Running Tox', + script: """uv python install cpython-${version} + uv run -p ${version} --frozen --only-group=tox-uv --isolated tox run --runner uv-venv-lock-runner -e ${toxEnv} + """ + ) + } } } finally { sh "docker image rm --force ${image.imageName()}" @@ -533,14 +559,16 @@ def call(){ def envs = [] node('docker && windows'){ try{ + checkout scm docker.image(env.DEFAULT_PYTHON_DOCKER_IMAGE ? env.DEFAULT_PYTHON_DOCKER_IMAGE: 'python').inside("--mount source=uv_python_cache_dir,target=${env.UV_PYTHON_CACHE_DIR}"){ - checkout scm - bat(script: 'python -m venv venv && venv\\Scripts\\pip install --disable-pip-version-check uv') - envs = bat( - label: 'Get tox environments', - script: '@.\\venv\\Scripts\\uv run --isolated --only-group tox --frozen --quiet tox list -d --no-desc', - returnStdout: true, - ).trim().split('\r\n') + withEnv(["UV_CONFIG_FILE=${createWindowUVConfig()}"]){ + bat(script: 'python -m venv venv && venv\\Scripts\\pip install --disable-pip-version-check uv') + envs = bat( + label: 'Get tox environments', + script: '@.\\venv\\Scripts\\uv run --isolated --only-group tox --frozen --quiet tox list -d --no-desc', + returnStdout: true, + ).trim().split('\r\n') + } } } finally{ bat "${tool(name: 'Default', type: 'git')} clean -dfx" @@ -562,7 +590,10 @@ def call(){ ){ retry(3){ try{ - withEnv(["TOX_UV_PATH=${env.WORKSPACE}\\venv\\Scripts\\uv.exe"]){ + withEnv([ + "TOX_UV_PATH=${env.WORKSPACE}\\venv\\Scripts\\uv.exe", + "UV_CONFIG_FILE=${createWindowUVConfig()}" + ]){ bat(label: 'Running Tox', script: """python -m venv venv && venv\\Scripts\\pip install --disable-pip-version-check uv venv\\Scripts\\uv python install cpython-${version} @@ -731,6 +762,7 @@ def call(){ 'UV_TOOL_DIR=/tmp/uvtools', 'UV_PYTHON_CACHE_DIR=/tmp/uvpython', 'UV_CACHE_DIR=/tmp/uvcache', + "UV_CONFIG_FILE=${createUnixUvConfig()}" ]){ sh( label: 'Testing with tox', @@ -746,7 +778,8 @@ def call(){ 'UV_PYTHON_CACHE_DIR=c:\\Users\\ContainerUser\\Documents\\cache\\uvpython', 'UV_CACHE_DIR=c:\\Users\\ContainerUser\\Documents\\cache\\uvcache', 'UV_LINK_MODE=copy', - "TOX_UV_PATH=${env.WORKSPACE}\\venv\\Scripts\\uv.exe" + "TOX_UV_PATH=${env.WORKSPACE}\\venv\\Scripts\\uv.exe", + "UV_CONFIG_FILE=${createWindowUVConfig()}" ]){ installMSVCRuntime('c:\\msvc_runtime\\') bat( @@ -762,7 +795,10 @@ def call(){ } } else { if(isUnix()){ - withEnv(["TOX_UV_PATH=${env.WORKSPACE}/venv/bin/uv"]){ + withEnv([ + "TOX_UV_PATH=${env.WORKSPACE}/venv/bin/uv", + "UV_CONFIG_FILE=${createUnixUvConfig()}" + ]){ sh( label: 'Testing with tox', script: """python3 -m venv venv @@ -772,14 +808,19 @@ def call(){ ) } } else { - bat( - label: 'Testing with tox', - script: """python -m venv venv - .\\venv\\Scripts\\pip install --disable-pip-version-check uv - .\\venv\\Scripts\\uv python install cpython-${entry.PYTHON_VERSION} - .\\venv\\Scripts\\uv run --only-group=tox-uv --isolated --frozen tox --installpkg ${findFiles(glob: entry.PACKAGE_TYPE == 'wheel' ? 'dist/*.whl' : 'dist/*.tar.gz')[0].path} -e py${entry.PYTHON_VERSION.replace('.', '')} - """ - ) + withEnv([ + "TOX_UV_PATH=${env.WORKSPACE}\\venv\\Scripts\\uv.exe", + "UV_CONFIG_FILE=${createWindowUVConfig()}" + ]){ + bat( + label: 'Testing with tox', + script: """python -m venv venv + .\\venv\\Scripts\\pip install --disable-pip-version-check uv + .\\venv\\Scripts\\uv python install cpython-${entry.PYTHON_VERSION} + .\\venv\\Scripts\\uv run --only-group=tox-uv --isolated --frozen tox --installpkg ${findFiles(glob: entry.PACKAGE_TYPE == 'wheel' ? 'dist/*.whl' : 'dist/*.tar.gz')[0].path} -e py${entry.PYTHON_VERSION.replace('.', '')} + """ + ) + } } } } finally{ From 13c57e864ed62cafba022ee820ab1641c4a20f12 Mon Sep 17 00:00:00 2001 From: Henry Borchers Date: Tue, 31 Mar 2026 13:42:21 -0500 Subject: [PATCH 2/2] ci: remove references to unsafe-best-match --- .github/workflows/tox_matrix.yml | 1 - vars/runJenkinsPipeline.groovy | 9 --------- 2 files changed, 10 deletions(-) diff --git a/.github/workflows/tox_matrix.yml b/.github/workflows/tox_matrix.yml index 4c67bc465..f2ceca0c7 100644 --- a/.github/workflows/tox_matrix.yml +++ b/.github/workflows/tox_matrix.yml @@ -40,7 +40,6 @@ jobs: run: uv run --only-group=tox-uv tox --runner uv-venv-lock-runner -e ${{ steps.tox-env.outputs.result }} env: UV_EXTRA_INDEX_URL: ${{vars.PIP_EXTRA_INDEX_URL}} - UV_INDEX_STRATEGY: "unsafe-best-match" QT_QPA_PLATFORM: "offscreen" DISPLAY: ':99.0' diff --git a/vars/runJenkinsPipeline.groovy b/vars/runJenkinsPipeline.groovy index e0b558469..e11a5b2c3 100644 --- a/vars/runJenkinsPipeline.groovy +++ b/vars/runJenkinsPipeline.groovy @@ -158,7 +158,6 @@ def call(){ } environment{ PIP_CACHE_DIR = '/tmp/pipcache' - UV_INDEX_STRATEGY = 'unsafe-best-match' UV_TOOL_DIR = '/tmp/uvtools' UV_PYTHON_CACHE_DIR = '/tmp/uvpython' UV_CACHE_DIR = '/tmp/uvcache' @@ -225,7 +224,6 @@ def call(){ } environment{ PIP_CACHE_DIR='/tmp/pipcache' - UV_INDEX_STRATEGY='unsafe-best-match' UV_TOOL_DIR='/tmp/uvtools' UV_PYTHON_CACHE_DIR='/tmp/uvpython' UV_CACHE_DIR='/tmp/uvcache' @@ -474,7 +472,6 @@ def call(){ } environment{ PIP_CACHE_DIR='/tmp/pipcache' - UV_INDEX_STRATEGY='unsafe-best-match' UV_TOOL_DIR='/tmp/uvtools' UV_PYTHON_CACHE_DIR='/tmp/uvpython' UV_CACHE_DIR='/tmp/uvcache' @@ -547,7 +544,6 @@ def call(){ expression {return nodesByLabel('windows && docker && x86').size() > 0} } environment{ - UV_INDEX_STRATEGY='unsafe-best-match' PIP_CACHE_DIR='C:\\Users\\ContainerUser\\Documents\\pipcache' UV_TOOL_DIR='C:\\Users\\ContainerUser\\Documents\\uvtools' UV_PYTHON_CACHE_DIR='C:\\Users\\ContainerUser\\Documents\\uvpython' @@ -645,7 +641,6 @@ def call(){ } environment{ PIP_CACHE_DIR='/tmp/pipcache' - UV_INDEX_STRATEGY='unsafe-best-match' UV_CACHE_DIR='/tmp/uvcache' } options { @@ -679,9 +674,6 @@ def call(){ when{ equals expected: true, actual: params.TEST_PACKAGES } - environment{ - UV_INDEX_STRATEGY='unsafe-best-match' - } stages{ stage('Twine Check'){ agent{ @@ -848,7 +840,6 @@ def call(){ stage('Deploy to pypi') { environment{ PIP_CACHE_DIR='/tmp/pipcache' - UV_INDEX_STRATEGY='unsafe-best-match' UV_TOOL_DIR='/tmp/uvtools' UV_PYTHON_CACHE_DIR='/tmp/uvpython' UV_CACHE_DIR='/tmp/uvcache'