<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Rich Evans</title>
    <description>The latest articles on Forem by Rich Evans (@rcbevans).</description>
    <link>https://forem.com/rcbevans</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2475330%2Fc6f7fa69-3ec7-48d5-b215-117a55cfad2a.jpg</url>
      <title>Forem: Rich Evans</title>
      <link>https://forem.com/rcbevans</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rcbevans"/>
    <language>en</language>
    <item>
      <title>Setting Up Side-by-Side Python Versions on Azure DevOps Self-Hosted Agents</title>
      <dc:creator>Rich Evans</dc:creator>
      <pubDate>Wed, 12 Nov 2025 01:18:33 +0000</pubDate>
      <link>https://forem.com/rcbevans/setting-up-side-by-side-python-versions-on-azure-devops-self-hosted-agents-15l1</link>
      <guid>https://forem.com/rcbevans/setting-up-side-by-side-python-versions-on-azure-devops-self-hosted-agents-15l1</guid>
      <description>&lt;h1&gt;
  
  
  Setting Up Side-by-Side Python Versions on Azure DevOps Self-Hosted Agents
&lt;/h1&gt;

&lt;p&gt;You've set up an Azure DevOps self-hosted agent and need Python for your pipelines. You install Python, configure your pipeline with &lt;code&gt;UsePythonVersion@0&lt;/code&gt;, and the job fails - the agent can't find it.&lt;/p&gt;

&lt;p&gt;The ADO documentation isn't clear on how to properly set up Python so the agent can use it, especially when you need multiple versions side-by-side. This guide shows you exactly how to do it using &lt;code&gt;uv&lt;/code&gt; and symlinks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;ADO agents won't use your system-installed Python. The &lt;code&gt;UsePythonVersion&lt;/code&gt; task only looks in &lt;code&gt;_work/_tool/Python/&lt;/code&gt; with this exact structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_work/_tool/
  └── Python/
      ├── 3.12.12/
      │   ├── x64/
      │   │   └── bin/
      │   └── x64.complete
      └── 3.12/
          ├── x64/
          │   └── bin/
          └── x64.complete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent needs both the full version (3.12.12) and major.minor (3.12) so pipelines can request either. Miss the &lt;code&gt;.complete&lt;/code&gt; file or put it in the wrong place, and ADO ignores the installation entirely.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Examples use Python 3.12.12 (latest 3.12.x at time of writing).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Solution &amp;amp; Why It Works
&lt;/h2&gt;

&lt;p&gt;Instead of manually installing Python or fighting with system packages, use &lt;code&gt;uv&lt;/code&gt; (the fast Python installer) and symlinks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why symlinks instead of copies?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instant setup (no file copying)&lt;/li&gt;
&lt;li&gt;Zero disk space overhead&lt;/li&gt;
&lt;li&gt;Atomic updates (change one symlink)&lt;/li&gt;
&lt;li&gt;Multiple versions coexist without conflicts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why separate full and major.minor versions?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production pipelines pin to &lt;code&gt;3.12.12&lt;/code&gt; (never changes unexpectedly)&lt;/li&gt;
&lt;li&gt;Dev pipelines use &lt;code&gt;3.12&lt;/code&gt; (gets security updates automatically)&lt;/li&gt;
&lt;li&gt;Explicit control via &lt;code&gt;--update&lt;/code&gt; flag prevents accidental breaking changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; Without the &lt;code&gt;--update&lt;/code&gt; flag, installing Python 3.12.12 creates that specific version but leaves &lt;code&gt;3.12&lt;/code&gt; pointing to 3.12.11. With &lt;code&gt;--update&lt;/code&gt;, it creates 3.12.12 and updates the &lt;code&gt;3.12&lt;/code&gt; symlink to point to it. This means you can test new patch versions before promoting them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install uv if needed&lt;/span&gt;
curl &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://astral.sh/uv/install.sh | sh

&lt;span class="c"&gt;# Run the script (see full script at end of post)&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/ado-agent
./setup-python-ado.sh 3.12

&lt;span class="c"&gt;# Restart agent&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/agent &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo&lt;/span&gt; ./svc.sh restart

&lt;span class="c"&gt;# Later, when updating to a new patch version:&lt;/span&gt;
./setup-python-ado.sh 3.12 &lt;span class="nt"&gt;--update&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Implementation Details
&lt;/h2&gt;

&lt;p&gt;Here are the tricky parts worth understanding—these are where things typically break:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Finding uv's Python installation&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PYTHON_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;uv python find &lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;UV_PYTHON_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="nv"&gt;$PYTHON_PATH&lt;/span&gt;&lt;span class="si"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;uv python find&lt;/code&gt; returns the path to the Python binary (e.g., &lt;code&gt;/home/user/.local/share/uv/python/cpython-3.12.12-linux-x86_64-gnu/bin/python3.12&lt;/code&gt;). We go up two directories to get the installation root we need to symlink.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Symlinking to ADO's expected structure&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;/x64
&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-sf&lt;/span&gt; &lt;span class="nv"&gt;$UV_PYTHON_DIR&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;/x64/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates the directory structure ADO expects and symlinks the entire Python installation into it. Result: &lt;code&gt;_work/_tool/Python/3.12.12/x64/bin/python3.12&lt;/code&gt; points to uv's installation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Creating the completion marker&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;/x64.complete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ADO only recognizes a Python installation if there's an &lt;code&gt;x64.complete&lt;/code&gt; file next to the &lt;code&gt;x64/&lt;/code&gt; directory. Without it, the &lt;code&gt;UsePythonVersion&lt;/code&gt; task ignores the installation entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Validating the symlink worked&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"python&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; log_error &lt;span class="s2"&gt;"Missing python binary"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After symlinking, verify the expected binary exists. If uv changes its structure, this catches it before your pipeline does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Basic Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/ado-agent
./setup-python-ado.sh 3.12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Installs Python 3.12 and configures both full version and major.minor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple Versions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./setup-python-ado.sh 3.11
./setup-python-ado.sh 3.12
./setup-python-ado.sh 3.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All versions coexist. Pipelines choose which to use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update Workflow
&lt;/h3&gt;

&lt;p&gt;When Python 3.12.13 releases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Step 1: Install the specific new version without updating major.minor&lt;/span&gt;
./setup-python-ado.sh 3.12.13

&lt;span class="c"&gt;# Step 2: Test in dev pipeline using versionSpec: '3.12.13'&lt;/span&gt;

&lt;span class="c"&gt;# Step 3: Promote to major.minor after validation&lt;/span&gt;
./setup-python-ado.sh 3.12.13 &lt;span class="nt"&gt;--update&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pipeline Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Development - gets latest 3.12.x automatically&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UsePythonVersion@0&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;versionSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.12'&lt;/span&gt;

&lt;span class="c1"&gt;# Production - pinned version, never changes&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UsePythonVersion@0&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;versionSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.12.12'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"uv not installed"&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://astral.sh/uv/install.sh | sh
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;"No write permission"&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt;:&lt;span class="nv"&gt;$USER&lt;/span&gt; /path/to/agent/_work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;"Failed to install Python X.Y"&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Invalid version: Check Python releases&lt;/li&gt;
&lt;li&gt;Network issue: Verify connectivity&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;--verbose&lt;/code&gt; for detailed error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pipeline still can't find Python&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Restart the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Method 1: Using svc.sh&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/agent &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo&lt;/span&gt; ./svc.sh restart

&lt;span class="c"&gt;# Method 2: Using systemctl&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart vsts.agent&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="c"&gt;# or: sudo systemctl restart azpipelines.agent*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /path/to/agent/_work/_tool/Python/
/path/to/agent/_work/_tool/Python/3.12/x64/bin/python &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check for &lt;code&gt;.complete&lt;/code&gt; files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find /path/to/agent/_work/_tool/Python &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.complete"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;p&gt;Inspired by Alex Kaszynski's "&lt;a href="https://dev.to/kkazala/create-an-azure-self-hosted-agent-with-python-without-going-insane-4719"&gt;Create an Azure Self-Hosted Agent with Python without going Insane&lt;/a&gt;". The original 2021 approach used Python venv. This script automates the process and uses &lt;code&gt;uv&lt;/code&gt; for faster installation and better version management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install version&lt;/span&gt;
./setup-python-ado.sh 3.12

&lt;span class="c"&gt;# Update to latest patch&lt;/span&gt;
./setup-python-ado.sh 3.12 &lt;span class="nt"&gt;--update&lt;/span&gt;

&lt;span class="c"&gt;# Debug issues&lt;/span&gt;
./setup-python-ado.sh 3.12 &lt;span class="nt"&gt;--verbose&lt;/span&gt;

&lt;span class="c"&gt;# Check versions&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /path/to/agent/_work/_tool/Python/

&lt;span class="c"&gt;# Verify&lt;/span&gt;
/path/to/agent/_work/_tool/Python/3.12/x64/bin/python &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# Restart&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/agent &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo&lt;/span&gt; ./svc.sh restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Full Script
&lt;/h2&gt;

&lt;p&gt;The complete, production-ready script with error handling and verbose logging. Save as &lt;code&gt;setup-python-ado.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Colors for output&lt;/span&gt;
&lt;span class="nv"&gt;RED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\033[0;31m'&lt;/span&gt;
&lt;span class="nv"&gt;GREEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\033[0;32m'&lt;/span&gt;
&lt;span class="nv"&gt;YELLOW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\033[1;33m'&lt;/span&gt;
&lt;span class="nv"&gt;BLUE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\033[0;34m'&lt;/span&gt;
&lt;span class="nv"&gt;NC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\033[0m'&lt;/span&gt;

&lt;span class="nv"&gt;PYTHON_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nv"&gt;UPDATE_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false
&lt;/span&gt;&lt;span class="nv"&gt;VERBOSE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false

&lt;/span&gt;log_error&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RED&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;Error: &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NC&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
log_success&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GREEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NC&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
log_info&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BLUE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NC&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
log_warning&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;YELLOW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NC&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

show_usage&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;python-version&amp;gt; [--update] [--verbose]"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Arguments:"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  &amp;lt;python-version&amp;gt;  Python version (e.g., 3.12, 3.11, 3.13)"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  --update          Force update major.minor symlink"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  --verbose         Show detailed output"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Parse arguments&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$# &lt;/span&gt;&lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    case&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="nt"&gt;--update&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;UPDATE_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;shift&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
        &lt;span class="nt"&gt;--verbose&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;VERBOSE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;shift&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
        &lt;span class="nt"&gt;-h&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;--help&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; show_usage &lt;span class="p"&gt;;;&lt;/span&gt;
        &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
                &lt;/span&gt;&lt;span class="nv"&gt;PYTHON_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
            &lt;span class="k"&gt;else
                &lt;/span&gt;log_error &lt;span class="s2"&gt;"Unknown argument '&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;
                show_usage
            &lt;span class="k"&gt;fi
            &lt;/span&gt;&lt;span class="nb"&gt;shift&lt;/span&gt;
            &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# Validate version&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;log_error &lt;span class="s2"&gt;"Python version required"&lt;/span&gt;
    show_usage
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ ^[0-9]+&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;0-9]+&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;0-9]+&lt;span class="o"&gt;)&lt;/span&gt;?&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;log_error &lt;span class="s2"&gt;"Invalid version format. Use X.Y or X.Y.Z"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Python &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt;&lt;span class="s2"&gt; Setup for ADO Agent"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=========================================="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;

&lt;span class="c"&gt;# Setup directories&lt;/span&gt;
&lt;span class="nv"&gt;AGENT_ROOT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ADO_AGENT_DIR&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
log_info &lt;span class="s2"&gt;"Agent directory: &lt;/span&gt;&lt;span class="nv"&gt;$AGENT_ROOT_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AGENT_ROOT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/_work"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;log_warning &lt;span class="s2"&gt;"No '_work' folder found"&lt;/span&gt;
    &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Continue? (y/N): "&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 &lt;span class="nt"&gt;-r&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt;
    &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$REPLY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ ^[Yy]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;AGENT_TOOL_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AGENT_ROOT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/_work/_tool"&lt;/span&gt;

&lt;span class="c"&gt;# Check permissions&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;log_error &lt;span class="s2"&gt;"No write permission"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Fix: sudo chown -R &lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;USER:&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;USER &lt;/span&gt;&lt;span class="nv"&gt;$AGENT_ROOT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/_work"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Verify uv is installed&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; uv &amp;amp;&amp;gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;log_error &lt;span class="s2"&gt;"uv not installed"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Install: curl -LsSf https://astral.sh/uv/install.sh | sh"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;UV_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;uv &lt;span class="nt"&gt;--version&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"unknown"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
log_success &lt;span class="s2"&gt;"Found uv (&lt;/span&gt;&lt;span class="nv"&gt;$UV_VERSION&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;

&lt;span class="c"&gt;# Install Python if needed&lt;/span&gt;
log_info &lt;span class="s2"&gt;"Checking for Python &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; uv python find &lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt; &amp;amp;&amp;gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;log_info &lt;span class="s2"&gt;"Installing Python &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VERBOSE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;uv python &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            log_error &lt;span class="s2"&gt;"Installation failed"&lt;/span&gt;
            &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Possible causes: invalid version, network issues, disk space"&lt;/span&gt;
            &lt;span class="nb"&gt;exit &lt;/span&gt;1
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;uv python &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            log_error &lt;span class="s2"&gt;"Installation failed"&lt;/span&gt;
            &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Run with --verbose: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt;&lt;span class="s2"&gt; --verbose"&lt;/span&gt;
            &lt;span class="nb"&gt;exit &lt;/span&gt;1
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fi

    &lt;/span&gt;log_success &lt;span class="s2"&gt;"Installed Python &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;log_success &lt;span class="s2"&gt;"Found Python &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Get Python info&lt;/span&gt;
&lt;span class="nv"&gt;PYTHON_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;uv python find &lt;span class="nv"&gt;$PYTHON_VERSION&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; log_error &lt;span class="s2"&gt;"Not executable: &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;PYTHON_FULL_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_PATH&lt;/span&gt; &lt;span class="nt"&gt;--version&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; log_error &lt;span class="s2"&gt;"Could not determine version"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;,2&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;UV_PYTHON_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="nv"&gt;$PYTHON_PATH&lt;/span&gt;&lt;span class="si"&gt;))&lt;/span&gt;

log_info &lt;span class="s2"&gt;"Full version: &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
log_info &lt;span class="s2"&gt;"Major.minor: &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;

&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$UV_PYTHON_DIR&lt;/span&gt;&lt;span class="s2"&gt;/bin"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; log_error &lt;span class="s2"&gt;"Missing bin directory"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Check existing installation&lt;/span&gt;
&lt;span class="nv"&gt;MAJOR_MINOR_EXISTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;&lt;span class="s2"&gt;/Python/&lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;MAJOR_MINOR_EXISTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
    &lt;/span&gt;&lt;span class="nv"&gt;EXISTING_LINK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;readlink&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;&lt;span class="s2"&gt;/Python/&lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="s2"&gt;/x64/bin/python&lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXISTING_LINK&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXISTING_LINK&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;EXISTING_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$EXISTING_LINK&lt;/span&gt; &lt;span class="nt"&gt;--version&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
        log_warning &lt;span class="s2"&gt;"Python &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="s2"&gt; exists (currently: &lt;/span&gt;&lt;span class="nv"&gt;$EXISTING_VERSION&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$UPDATE_MODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
            &lt;/span&gt;log_info &lt;span class="s2"&gt;"Use --update to replace with &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
            log_info &lt;span class="s2"&gt;"Configuring only: &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
            &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
        &lt;span class="k"&gt;else
            &lt;/span&gt;log_info &lt;span class="s2"&gt;"Update mode: replacing with &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
            &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
        &lt;span class="k"&gt;fi
    fi
fi&lt;/span&gt;

&lt;span class="c"&gt;# Configure full version&lt;/span&gt;
log_info &lt;span class="s2"&gt;"Configuring &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;/x64

&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-sf&lt;/span&gt; &lt;span class="nv"&gt;$UV_PYTHON_DIR&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;/x64/ &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    log_error &lt;span class="s2"&gt;"Symlink failed for &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;/x64/bin
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"python&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; log_error &lt;span class="s2"&gt;"Missing python binary"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; python &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-sf&lt;/span&gt; python&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; python
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; python3 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-sf&lt;/span&gt; python&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; python3

&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;/x64.complete
log_success &lt;span class="s2"&gt;"Configured &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Configure major.minor if needed&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MAJOR_MINOR_EXISTS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$UPDATE_MODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
    log_info &lt;span class="s2"&gt;"Configuring &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;

    &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$UPDATE_MODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MAJOR_MINOR_EXISTS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;

    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;/x64

    &lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-sf&lt;/span&gt; &lt;span class="nv"&gt;$UV_PYTHON_DIR&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;/x64/ &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        log_error &lt;span class="s2"&gt;"Symlink failed for &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;/x64/bin
    &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; python &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-sf&lt;/span&gt; python&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; python
    &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; python3 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-sf&lt;/span&gt; python&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; python3

    &lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;/x64.complete
    log_success &lt;span class="s2"&gt;"Configured &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Verify&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Verification"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"============"&lt;/span&gt;
&lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;/x64/bin/python &lt;span class="nt"&gt;--version&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    log_error &lt;span class="s2"&gt;"Verification failed"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MAJOR_MINOR_EXISTS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$UPDATE_MODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nv"&gt;$AGENT_TOOL_DIR&lt;/span&gt;/Python/&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;/x64/bin/python &lt;span class="nt"&gt;--version&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        log_error &lt;span class="s2"&gt;"Verification failed"&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
log_success &lt;span class="s2"&gt;"Setup complete"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Available versions:"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MAJOR_MINOR_EXISTS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$UPDATE_MODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="s2"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_FULL_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MAJOR_MINOR_EXISTS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  &lt;/span&gt;&lt;span class="nv"&gt;$PYTHON_MAJOR_MINOR&lt;/span&gt;&lt;span class="s2"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$EXISTING_VERSION&lt;/span&gt;&lt;span class="s2"&gt; (unchanged)"&lt;/span&gt;
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Next steps:"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  1. Restart: cd /path/to/agent &amp;amp;&amp;amp; sudo ./svc.sh restart"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  2. Use in pipeline: UsePythonVersion@0"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>uv</category>
      <category>ado</category>
      <category>devops</category>
      <category>python</category>
    </item>
  </channel>
</rss>
