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?
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.