1<# 2.Synopsis 3 Uploads from a VSTS release build layout to python.org 4.Description 5 Given the downloaded/extracted build artifact from a release 6 build run on python.visualstudio.com, this script uploads 7 the files to the correct locations. 8.Parameter build 9 The location on disk of the extracted build artifact. 10.Parameter user 11 The username to use when logging into the host. 12.Parameter server 13 The host or PuTTY session name. 14.Parameter target 15 The subdirectory on the host to copy files to. 16.Parameter tests 17 The path to run download tests in. 18.Parameter doc_htmlhelp 19 Optional path besides -build to locate CHM files. 20.Parameter embed 21 Optional path besides -build to locate ZIP files. 22.Parameter skipupload 23 Skip uploading 24.Parameter skippurge 25 Skip purging the CDN 26.Parameter skiptest 27 Skip the download tests 28.Parameter skiphash 29 Skip displaying hashes 30#> 31param( 32 [Parameter(Mandatory=$true)][string]$build, 33 [Parameter(Mandatory=$true)][string]$user, 34 [string]$server="python-downloads", 35 [string]$target="/srv/www.python.org/ftp/python", 36 [string]$tests=${env:TEMP}, 37 [string]$doc_htmlhelp=$null, 38 [string]$embed=$null, 39 [switch]$skipupload, 40 [switch]$skippurge, 41 [switch]$skiptest, 42 [switch]$skiphash 43) 44 45if (-not $build) { throw "-build option is required" } 46if (-not $user) { throw "-user option is required" } 47 48$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; 49 50if (-not ((Test-Path "$build\win32\python-*.exe") -or (Test-Path "$build\amd64\python-*.exe"))) { 51 throw "-build argument does not look like a 'build' directory" 52} 53 54function find-putty-tool { 55 param ([string]$n) 56 $t = gcm $n -EA 0 57 if (-not $t) { $t = gcm ".\$n" -EA 0 } 58 if (-not $t) { $t = gcm "${env:ProgramFiles}\PuTTY\$n" -EA 0 } 59 if (-not $t) { $t = gcm "${env:ProgramFiles(x86)}\PuTTY\$n" -EA 0 } 60 if (-not $t) { throw "Unable to locate $n.exe. Please put it on $PATH" } 61 return gi $t.Path 62} 63 64$p = gci -r "$build\python-*.exe" | ` 65 ?{ $_.Name -match '^python-(\d+\.\d+\.\d+)((a|b|rc)\d+)?-.+' } | ` 66 select -first 1 | ` 67 %{ $Matches[1], $Matches[2] } 68 69"Uploading version $($p[0]) $($p[1])" 70" from: $build" 71" to: $($server):$target/$($p[0])" 72"" 73 74if (-not $skipupload) { 75 # Upload files to the server 76 $pscp = find-putty-tool "pscp" 77 $plink = find-putty-tool "plink" 78 79 "Upload using $pscp and $plink" 80 "" 81 82 if ($doc_htmlhelp) { 83 $chm = gci -EA 0 $doc_htmlhelp\python*.chm, $doc_htmlhelp\python*.chm.asc 84 } else { 85 $chm = gci -EA 0 $build\python*.chm, $build\python*.chm.asc 86 } 87 88 $d = "$target/$($p[0])/" 89 & $plink -batch $user@$server mkdir $d 90 & $plink -batch $user@$server chgrp downloads $d 91 & $plink -batch $user@$server chmod o+rx $d 92 if ($chm) { 93 & $pscp -batch $chm.FullName "$user@${server}:$d" 94 if (-not $?) { throw "Failed to upload $chm" } 95 } 96 97 $dirs = gci "$build" -Directory 98 if ($embed) { 99 $dirs = ($dirs, (gi $embed)) | %{ $_ } 100 } 101 102 foreach ($a in $dirs) { 103 "Uploading files from $($a.FullName)" 104 pushd "$($a.FullName)" 105 $exe = gci *.exe, *.exe.asc, *.zip, *.zip.asc 106 $msi = gci *.msi, *.msi.asc, *.msu, *.msu.asc 107 popd 108 109 if ($exe) { 110 & $pscp -batch $exe.FullName "$user@${server}:$d" 111 if (-not $?) { throw "Failed to upload $exe" } 112 } 113 114 if ($msi) { 115 $sd = "$d$($a.Name)$($p[1])/" 116 & $plink -batch $user@$server mkdir $sd 117 & $plink -batch $user@$server chgrp downloads $sd 118 & $plink -batch $user@$server chmod o+rx $sd 119 & $pscp -batch $msi.FullName "$user@${server}:$sd" 120 if (-not $?) { throw "Failed to upload $msi" } 121 & $plink -batch $user@$server chgrp downloads $sd* 122 & $plink -batch $user@$server chmod g-x,o+r $sd* 123 } 124 } 125 126 & $plink -batch $user@$server chgrp downloads $d* 127 & $plink -batch $user@$server chmod g-x,o+r $d* 128 & $pscp -ls "$user@${server}:$d" 129} 130 131if (-not $skippurge) { 132 # Run a CDN purge 133 py $tools\purge.py "$($p[0])$($p[1])" 134} 135 136if (-not $skiptest) { 137 # Use each web installer to produce a layout. This will download 138 # each referenced file and validate their signatures/hashes. 139 gci "$build\*-webinstall.exe" -r -File | %{ 140 $d = mkdir "$tests\$($_.BaseName)" -Force 141 gci $d -r -File | del 142 $ic = copy $_ $d -PassThru 143 "Checking layout for $($ic.Name)" 144 Start-Process -wait $ic "/passive", "/layout", "$d\layout", "/log", "$d\log\install.log" 145 if (-not $?) { 146 Write-Error "Failed to validate layout of $($inst.Name)" 147 } 148 } 149} 150 151if (-not $skiphash) { 152 # Display MD5 hash and size of each downloadable file 153 pushd $build 154 $files = gci python*.chm, *\*.exe, *\*.zip 155 if ($doc_htmlhelp) { 156 cd $doc_htmlhelp 157 $files = ($files, (gci python*.chm)) | %{ $_ } 158 } 159 if ($embed) { 160 cd $embed 161 $files = ($files, (gci *.zip)) | %{ $_ } 162 } 163 popd 164 165 $hashes = $files | ` 166 Sort-Object Name | ` 167 Format-Table Name, @{Label="MD5"; Expression={(Get-FileHash $_ -Algorithm MD5).Hash}}, Length -AutoSize | ` 168 Out-String -Width 4096 169 $hashes | clip 170 $hashes 171} 172