tasks/build.tasks.ps1
# <copyright file="build.tasks.ps1" company="Endjin Limited"> # Copyright (c) Endjin Limited. All rights reserved. # </copyright> . $PSScriptRoot/build.properties.ps1 # Synopsis: Checks whether Python is installed and available on the PATH environment variable. If not, throws an error. task EnsurePython -If { $PythonProjectDir -ne "" } { if (!(Get-Command python -ErrorAction SilentlyContinue)) { throw "A Python installation could not be found. Please install Python and ensure it is available on the PATH environment variable." } } # Synopsis: Installs Python Poetry if it is not already installed. Poetry is a dependency management tool for Python. task InstallPythonPoetry -If { !$SkipInstallPythonPoetry } EnsurePython,{ $existingPoetry = Get-Command poetry -ErrorAction SilentlyContinue if (!$existingPoetry -and !$PoetryPath) { # The install script will honour this environment variable. If not explicitly set, we set it to: # - On build servers we install within the working directory to ensure it's part of the build agent caching # - Otherwise, we install to the user profile directory in a cross-platform way $env:POETRY_HOME ??= $IsRunningOnCICDServer ? (Join-Path $here ".poetry") : (Join-Path ($IsWindows ? $env:USERPROFILE : $env:HOME) ".poetry") $env:POETRY_VERSION = $PythonPoetryVersion $poetryBinPath = Join-Path $env:POETRY_HOME "bin" # If the poetry binary is not found, install it if (!(Test-Path (Join-Path $poetryBinPath "poetry"))) { Write-Build White "Installing Poetry $env:POETRY_VERSION: $env:POETRY_HOME" Invoke-WebRequest -Uri https://install.python-poetry.org/ -OutFile get-poetry.py exec { & python get-poetry.py --yes } Remove-Item get-poetry.py -Force } # Ensure the poetry tool is availiable to the rest of the build process $script:PoetryPath = Join-Path $poetryBinPath "poetry" Write-Build Green "Poetry now available: $PoetryPath" if ($poetryBinPath -notin ($env:PATH -split [System.IO.Path]::PathSeparator)) { Write-Build White "Adding Poetry to PATH: $poetryBinPath" $env:PATH = "$poetryBinPath{0}$env:PATH" -f [System.IO.Path]::PathSeparator } } else { if (!$PoetryPath) { # Ensure $PoetryPath is set if poetry was already available in the PATH $script:PoetryPath = $existingPoetry.Path } Write-Build Green "Poetry already installed: $PoetryPath" } } # Synopsis: Updates the Poetry lockfile without updating any packages. This is useful for local development scenarios to ensure that the lockfile is in sync with the pyproject.toml file. task UpdatePoetryLockfile -If { !$IsRunningOnCICDServer } InstallPythonPoetry,{ Write-Build White "Ensuring poetry.lock is up-to-date - no packages will be updated" # Extract the Poetry version from the output of the --version command # Example output: Poetry (version 1.8.0) $poetryVersionMsg = & $script:PoetryPath --version [semver]$poetryVersion = $poetryVersionMsg.Replace("Poetry (version ", "").Replace(")", "") Set-Location $PythonProjectDir if ($poetryVersion.Major -lt 2) { # Poetry versions less v2.0.0 default to updating package versions & $script:PoetryPath lock --no-update } else { # Poetry v2 and later will not update package versions by default & $script:PoetryPath lock } } # Synopsis: Initialise the Python Poetry virtual environment. task InitialisePythonPoetry -If { $PythonProjectManager -eq "poetry" -and !$SkipInitialisePythonPoetry } InstallPythonPoetry,UpdatePoetryLockfile,{ if (!(Test-Path (Join-Path $PythonProjectDir "pyproject.toml"))) { throw "pyproject.toml not found in $PythonProjectDir" } # Default to using virtual environments in the project directory, unless already set $env:POETRY_VIRTUALENVS_IN_PROJECT ??= "true" # Define the global poetry arguments we will use for all poetry commands $script:poetryGlobalArgs = @( "--no-interaction" "--directory=$PythonProjectDir" "-v" ) Write-Build White "poetryGlobalArgs: $poetryGlobalArgs" # Handle the addition of the preferred 'sync' command in later versions of Poetry if ($poetryVersion.Major -lt 2) { $poetryInstallCmd = "install" } else { $poetryInstallCmd = "sync" } Write-Build White "poetryInstallCommand: $poetryInstallCmd" if ($IsRunningOnCICDServer ) { Write-Build Green "Installing dependencies for CI environment ('$($PoetryInstallCicdArgs -join " ")')" exec { & $script:PoetryPath $poetryInstallCmd @poetryGlobalArgs @PoetryInstallCicdArgs } } else { Write-Build Green "Installing dependencies for local environment ('$($PoetryInstallArgs -join " ")')" exec { & $script:PoetryPath $poetryInstallCmd @poetryGlobalArgs @PoetryInstallArgs } } } # Synopsis: Installs UV if it is not already installed. UV is a dependency management tool for Python. task InstallPythonUv -If { !$SkipInstallPythonUv } EnsurePython,{ # The install script will honour this environment variable. If not explicitly set, we set it to: # - On build servers we install within the working directory to ensure it's part of the build agent caching # - Otherwise, we install to the user profile directory in a cross-platform way $env:UV_INSTALL_DIR ??= $IsRunningOnCICDServer ? (Join-Path $here ".uv") : (Join-Path ($IsWindows ? $env:USERPROFILE : $env:HOME) ".uv") $uvBinPath = $env:UV_INSTALL_DIR $existingUv = Get-Command uv -ErrorAction SilentlyContinue if (!$existingUv) { # If the uv binary is not found, install it if (!(Test-Path (Join-Path $uvBinPath "uv"))) { Write-Build White "Installing uv $PythonUvVersion" if ($IsWindows) { Invoke-RestMethod https://astral.sh/uv/$PythonUvVersion/install.ps1 | Invoke-Expression } else { Invoke-RestMethod https://astral.sh/uv/$PythonUvVersion/install.sh | bash } # Ensure the uv tool is available to the rest of the build process $script:PythonUvPath = Join-Path $uvBinPath "uv" Write-Build Green "uv now available: $PythonUvPath" } else { # Ensure the uv tool is available to the rest of the build process $script:PythonUvPath = Join-Path $uvBinPath "uv" Write-Build Green "uv already installed: $PythonUvPath" } if ($uvBinPath -notin ($env:PATH -split [System.IO.Path]::PathSeparator)) { Write-Build White "Adding uv to PATH: $uvBinPath" $env:PATH = "$uvBinPath{0}$env:PATH" -f [System.IO.Path]::PathSeparator } } else { # Ensure $PythonUvPath is set if uv was already available in the PATH $script:PythonUvPath = $existingUv.Path Write-Build Green "uv already installed: $PythonUvPath" } } # Synopsis: Updates the UV lockfile without updating any packages. This is useful for local development scenarios to ensure that the lockfile is in sync with the pyproject.toml file. task UpdateUvLockfile -If { !$IsRunningOnCICDServer } InstallPythonUv,{ Write-Build White "Ensuring uv.lock is up-to-date - no packages will be updated" exec { & $script:PythonUvPath lock --project=$PythonProjectDir } } # Synopsis: Initialise the UV virtual environment. task InitialisePythonUv -If { $PythonProjectManager -eq "uv" -and !$SkipInitialisePythonUv } InstallPythonUv,UpdateUvLockfile,{ if (!(Test-Path (Join-Path $PythonProjectDir "pyproject.toml"))) { throw "pyproject.toml not found in $PythonProjectDir" } # Define the global uv arguments we will use for all uv commands $script:uvGlobalArgs = @( "--project=$PythonProjectDir" ) Write-Build White "uvGlobalArgs: $uvGlobalArgs" if ($IsRunningOnCICDServer ) { Write-Build Green "Installing dependencies for CI environment ('$($UvSyncCicdArgs -join " ")')" exec { & $script:PythonUvPath sync @uvGlobalArgs @UvSyncCicdArgs } } else { Write-Build Green "Installing dependencies for local environment ('$($UvSyncArgs -join " ")')" exec { & $script:PythonUvPath sync @uvGlobalArgs @UvSyncArgs } } } # Synopsis: Run the flake8 linter on the Python source code. task RunFlake8 -If { $PythonProjectDir -ne "" -and !$SkipRunFlake8 } InitialisePythonPoetry,InitialisePythonUv,{ Write-Build White "Running flake8" # Explicitly change directory as Flake8 does not run when Poetry has the '--directory' argument Set-Location $PythonProjectDir if ($PythonProjectManager -eq "uv") { exec { & $script:PythonUvPath run flake8 src $PythonFlake8Args } } elseif ($PythonProjectManager -eq "poetry") { exec { & $script:PoetryPath run --no-interaction -v flake8 src $PythonFlake8Args } } } # Synopsis: Wrapper task for the overall Python build process. task BuildPython -If { $PythonProjectDir -ne "" } -After BuildCore BuildPythonPoetry,BuildPythonUv,RunFlake8 # Synopsis: Wrapper task for the Poetry-based build process. task BuildPythonPoetry -If { $PythonProjectManager -eq "poetry" -and $PythonProjectDir -ne "" } InitialisePythonPoetry # Synopsis: Wrapper task for the UV-based build process. task BuildPythonUv -If { $PythonProjectManager -eq "uv" -and $PythonProjectDir -ne "" } InitialisePythonUv |