pythonpython-3.xwindowspowershell

Trying to Install latest python3 version using powershell script


I am currently installing python3 by hardcoding the version in my script. I am trying to figure out what I can do to always install the latest stable version of python on the windows agent.

$url = "https://www.python.org/ftp/python/3.9.10/python-3.9.10.exe"
$pythonPath = "C:/Program Files/python/python-3.9.10.exe"

If ((Test-Path C:) -and !(Test-Path "C:\Program Files\python"))
{
    New-Item -Path "C:\Program Files\" -Name "python" -ItemType "directory"
}

[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072

Invoke-WebRequest -Uri $url -OutFile $pythonPath

Above is what I am doing currently, this is working fine though but what can I do not to hardcode the version and install the latest stable python version?


Solution

  • We were using @mklement0 powershell method (thanks!), but it broke on us today because Python changed their download page. (There was a warning about the fragility of the web scraping method after all...)

    We did find a simple change could make it work again (only returns the url, not the version), like this:

    if ((Invoke-RestMethod 'https://www.python.org/downloads/windows/') -notmatch '\bhref="(?<url>.+?\.exe)"\s*>\s*Windows installer \(64-bit\)') {
        throw "Could not determine latest Python version and download URL ... "
        exit 1
    }
    

    However, I think I stumbled upon perhaps a "better" method. In a github discussion, someone mentioned lastversion ^1 ^2 ^3, so I tried it out to see how they were getting the latest python version.

    When I ran it in verbose mode (lastversion python --verbose), it was referencing this RSS/Atom feed https://github.com/python/cpython/releases.atom which I didn't know existed...

    In our situation, we cannot really use lastversion because it is a python application and we are trying to determine the latest version of python and install it on a fresh system. So I thought I could use that same feed to determine the latest, and work out the link from there. Here's what I came up with. I'm sure it could be improved upon, but thought it might be useful for someone to use or build upon.

    $TmpDir = "${env:SystemDrive}\Temp"
    if (!(Test-Path $TmpDir)) { New-Item -ItemType Directory $TmpDir -Force }
    $PyReleases = Invoke-RestMethod 'https://github.com/python/cpython/releases.atom'
    # Drop the "v" and filter out versions with letters in it, cast to a version, sort descending and select the first result
    $PyLatestVersion = ($PyReleases.title) -replace "^v" -notmatch "[a-z]" | Sort-Object { [version] $_ } -Descending | Select-Object -First 1
    $PyLatestBaseUrl = "https://www.python.org/ftp/python/${PyLatestVersion}"
    $PyUrl = "${PyLatestBaseUrl}/python-${PyLatestVersion}-amd64.exe"
    $PyPkg = $PyUrl | Split-Path -Leaf
    # Also could use: $PyPkg = "python-${PyLatestVersion}-amd64.exe"
    $PyVerDir = ($PyPkg -replace "\.exe" -replace "-amd64" -replace "-").Split(".")
    $PyVerDir = $PyVerDir[0] + $PyVerDir[-2]
    $PyVerDir = $PyVerDir.Substring(0, 1).ToUpper() + $PyVerDir.Substring(1).ToLower()
    $PyCmd = "${env:ProgramFiles}\${PyVerDir}\python.exe"
    Invoke-WebRequest -UseBasicParsing -Uri $PyUrl -OutFile "${TmpDir}\${PyPkg}"
    Start-Process "${TmpDir}\${PyPkg}" -ArgumentList "/passive", "InstallAllUsers=1", "PrependPath=1", "Include_test=0" -Wait -NoNewWindow
    

    Hopefully this is useful. Also, I'm open to hearing about improvements that could be made.