You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

windows-get-dependencies.ps1 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <#
  2. .SYNOPSIS
  3. This script aims at installing dependencies from a manifest file.
  4. .DESCRIPTION
  5. Script workflow is as follows:
  6. - Check for a cache directory
  7. - Parse input dependencies manifest
  8. - Download dependencies if: (Currently does not download!)
  9. * cache found but dependency not found in the cache
  10. * cache not found (old archives are overwritten)
  11. - Extract dependencies in destination folder (old dependencies are overwritten) if:
  12. * dependencies were never unzipped
  13. * the version of unzipped dependency is correct (read from manifest version file)
  14. - write manifest version file, named after the manifest file name and suffixed with "-version",
  15. it contains the version of lastly unzipped dependencies such as:
  16. folder_to_unzip=version_number
  17. .PARAMETER dependencies_file
  18. The manifest file containing the required archives to install.
  19. First line is the base URL of the dependencies, following lines are formatted as:
  20. archive_name;folder_to_unzip;version_number
  21. archive_name;folder_to_unzip;version_number
  22. ...
  23. The `folder_to_unzip` can be the same for several dependencies, but in this case you have to make sure
  24. that the archives share the same `version_number`.
  25. If this is not the case an error will be thrown.
  26. .PARAMETER dest_dir
  27. Optional: if unspecified then by default it will be set to $script_root\..\dependencies
  28. Destination directory for extracted archives. Each archive found in the manifest file
  29. is extracted in 'dest_dir\folder_to_unzip'.
  30. Note that if no cache is found, archives are also downloaded in 'dest_dir\arch'.
  31. .PARAMETER cache_dir
  32. Cache directory for extracted archives. Each archive found in the manifest file
  33. is extracted from cache_dir to 'dest_dir\folder_to_unzip'.
  34. .NOTES
  35. File Name : windows-get-dependencies.ps1
  36. Prerequisite : Tested with PS v4.0 on windows 10 pro.
  37. Environment variables will be used if set:
  38. * PROXYPASS: Used to set credentials, should be formed as user:passwd
  39. * ZIP_EXECUTABLE: should be set to the path to 7zip executable. If unset, script will try to read registry keys.
  40. .LINK
  41. Detailed specifications:
  42. https://jira.mensiatech.com/confluence/pages/viewpage.action?spaceKey=CT&title=Dependency+management
  43. .EXAMPLE
  44. powershell.exe -NoExit -NoProfile -ExecutionPolicy Bypass -File \absolute\path\to\windows-get-dependencies.ps1 -manifest_file .\windows-dependencies.txt
  45. .EXAMPLE
  46. powershell.exe -NoExit -NoProfile -ExecutionPolicy Bypass -File \absolute\path\to\windows-get-dependencies.ps1 -manifest_file .\windows-dependencies.txt
  47. -dest_dir \absolute\path\to\dep\ -cache_dir \absolute\path\to\cache
  48. #>
  49. #
  50. # script parameters
  51. #
  52. Param(
  53. [parameter(Mandatory=$true)][ValidateScript({Test-Path $_ })][string]$manifest_file,
  54. [parameter(Mandatory=$false)][string]$cache_dir,
  55. [parameter(Mandatory=$false)][ValidateNotNullOrEmpty()][string]$dest_dir = ".\..\dependencies"
  56. )
  57. $manifest_file = [System.IO.Path]::GetFullPath($manifest_file)
  58. $dest_dir = [System.IO.Path]::GetFullPath($dest_dir)
  59. #
  60. # input validation
  61. #
  62. Write-Host "===Input validation==="
  63. if (Test-Path $dest_dir){
  64. Write-Host "Destination directory found"
  65. } else {
  66. Write-Host "Destination directory not found"
  67. New-Item $dest_dir -itemtype directory | Out-Null
  68. Write-Host "Created destination directory: " $dest_dir
  69. }
  70. # if -cache_dir is specified, use its value, otherwise check if an environment variable exists
  71. if($cache_dir) {
  72. Write-Host "Cache directory provided by parameter: $cache_dir"
  73. } elseif ($env:DEPENDENCY_CACHE -and (Test-Path $env:DEPENDENCY_CACHE)) {
  74. Write-Host "Found cache directory: " ($env:DEPENDENCY_CACHE)
  75. $cache_dir = $env:DEPENDENCY_CACHE
  76. } else {
  77. $cache_dir = $dest_dir + "\arch"
  78. Write-Host "Found no cache directory. Set it to default value: $cache_dir"
  79. }
  80. if(-Not (Test-Path $cache_dir)) {
  81. New-Item $cache_dir -itemtype directory | Out-Null
  82. Write-Host "Created archive directory in destination: " $cache_dir
  83. }
  84. Write-Host "All archives will be downloaded in " $cache_dir
  85. Write-Host ""
  86. Write-Host "Configure Web client"
  87. # Base URL of dependency server is read in environment variable
  88. $Script:dependency_server = $env:URL
  89. $WebClient = New-Object System.Net.WebClient
  90. if($env:PROXYPASS){
  91. $Username, $Password = $env:PROXYPASS.split(':',2)
  92. Write-Host "Credentials are provided. Try to download from server with username [$Username]."
  93. $WebClient.Credentials = New-Object System.Net.Networkcredential($Username, $Password)
  94. } else {
  95. Write-Host "Credentials were not provided. If your dependencies are not up-to-date, you won't be able to download new files."
  96. }
  97. Write-Host ""
  98. if ($env:ZIP_EXECUTABLE) {
  99. $Script:7zip_executable=$env:ZIP_EXECUTABLE
  100. } else {
  101. Try {
  102. $Script:7zip_executable=(Get-ItemProperty HKLM:\Software\7-Zip).Path + "\7z.exe"
  103. } Catch {
  104. Write-Host "7-Zip was not found in register keys. Install it will fasten unzip."
  105. }
  106. }
  107. if ($Script:7zip_executable -and (Test-Path $Script:7zip_executable)) {
  108. Write-Host "Found 7-Zip in registry keys. It will be used to unzip archives."
  109. } else {
  110. Write-Host "7-Zip was not found in register keys. Install it will quicken the unzipping."
  111. $Script:7zip_executable=""
  112. }
  113. Write-Host "===Parameters==="
  114. Write-Host "Dependencies file = $manifest_file"
  115. Write-Host "Destination directory = $dest_dir"
  116. Write-Host ""
  117. #
  118. # script variables
  119. #
  120. # monitoring variables
  121. $Script:extract_count = 0
  122. $Script:download_count = 0
  123. $Script:new_versions = @{}
  124. #
  125. # script functions
  126. #
  127. function ExpandZipFile($zip, $dest)
  128. {
  129. Write-Host "Extract: [" $zip "] -> [" $dest "]"
  130. $shell = New-Object -ComObject Shell.Application
  131. if ([string]::IsNullOrEmpty($Script:7zip_executable)){
  132. # create dest if it does not exists
  133. if(-Not (Test-Path $dest)) {
  134. New-Item $dest -itemtype directory | Out-Null
  135. }
  136. $folder = $shell.NameSpace("$zip")
  137. # Progress based on count is pretty dumb. However
  138. # the sizes retrieved by $folder.GetDetailsOf($item, 2)
  139. # or $item.Size are unreliable. At least, it allows the script
  140. # user to see something is going on.
  141. $count = 0
  142. $total_count = $folder.Items().Count
  143. ForEach($item in $folder.Items()) {
  144. $item_name = $folder.GetDetailsOf($item, 0)
  145. $percent_complete = [System.Math]::Floor(100 * $count / $total_count)
  146. Write-Progress `
  147. -Status "Progress: $percent_complete%" `
  148. -Activity ("Extracting " + (Split-Path -Leaf $zip) + " to " + (Split-Path -Leaf $dest)) `
  149. -CurrentOperation "Copying $item_name (this can take a long time...)" `
  150. -PercentComplete $percent_complete
  151. # 0x14 = sum of options 4 (donnot display windows box) and 16 (answer yes to all)
  152. $shell.Namespace("$dest").CopyHere($item, 0x14)
  153. $count++
  154. }
  155. } else {
  156. $7z_Arguments = @(
  157. 'x' ## extract files with full paths
  158. '-y' ## assume Yes on all queries
  159. $zip ## archive path
  160. "`"-o$dest`"" ## dest path
  161. )
  162. & $7zip_executable $7z_Arguments
  163. }
  164. $Script:extract_count++
  165. }
  166. function InstallDeps($arch, $dir, $version)
  167. {
  168. $zip = $Script:cache_dir + "\" + $arch
  169. if(-Not (Test-Path $zip)) {
  170. if($Username -and $Script:dependency_server){
  171. $url = $Script:dependency_server + "/" + $arch
  172. } else {
  173. Write-Error "- Credentials or $Script:dependency_server are not specified, can not download dependency. "
  174. Write-Error "- They have to be set through environment variables: PROXYPASS and URL."
  175. exit
  176. }
  177. Write-Host "Download: [" $url "] -> [" $zip "]."
  178. $Script:WebClient.DownloadFile( $url, $zip )
  179. $Script:download_count++
  180. }
  181. if(-Not (Test-Path $zip)) {
  182. Write-Error "Archive [$zip] was not found in cache and could not be downloaded."
  183. exit
  184. }
  185. if(Test-Path $dest_dir\$dir-version.txt) {
  186. $old_dep_version=$(Get-Content $dest_dir\$dir-version.txt)
  187. }
  188. if((Test-Path ($Script:dest_dir + "\" + $dir)) -and ($old_dep_version -eq $version)) {
  189. Write-Host "No need to unzip dependency. Already at the good version: " $old_dep_version "."
  190. } else {
  191. Write-Host "Dependency version is not the good one: [" $old_dep_version "]. Should be " $version
  192. if((Test-Path ($Script:dest_dir + "\" + $dir)) -and (-Not $Script:new_versions.ContainsKey($dir))) {
  193. Remove-item ($Script:dest_dir + "\" + $dir) -Force -Recurse
  194. }
  195. ExpandZipFile $zip ($Script:dest_dir + "\" + $dir)
  196. }
  197. # Add new version to table
  198. if($Script:new_versions.ContainsKey($dir)) {
  199. # If (during this install process) an archive was already extracted to this folder and its version is
  200. # not the same as new version, we raise an error
  201. if($Script:new_versions[$dir] -ne $version) {
  202. Write-Error "- Several dependencies are extracted in the same folder with different versions.
  203. You have to make sure that they carry the same version."
  204. exit
  205. }
  206. } else {
  207. $Script:new_versions.Add($dir, $version)
  208. }
  209. }
  210. #
  211. # core script
  212. #
  213. Write-Host ""
  214. Write-Host "===Installing dependencies==="
  215. $Script:timer = [System.Diagnostics.Stopwatch]::StartNew()
  216. foreach ($dep in Get-Content $manifest_file) {
  217. $arch, $dir, $version = $dep.split(';',3)
  218. InstallDeps $arch $dir $version
  219. }
  220. $timer.Stop()
  221. Write-Host "Write token version files, to quicken next dependencies "
  222. foreach ($new_version in $Script:new_versions.GetEnumerator()) {
  223. $new_version.Value > $dest_dir\$($new_version.Key)-version.txt
  224. }
  225. Write-Host ""
  226. Write-Host "===Install Summary==="
  227. Write-Host "State = Success"
  228. Write-Host "Number of archives downloaded = $Script:download_count"
  229. Write-Host "Number of archives extracted = $Script:extract_count"
  230. Write-Host "Installation time = " $([string]::Format("{0:d2}h:{1:d2}mn:{2:d2}s", $timer.Elapsed.hours, $timer.Elapsed.minutes, $timer.Elapsed.seconds)) -nonewline
  231. Write-Host ""