init-tools-native.sh 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. #!/usr/bin/env bash
  2. source="${BASH_SOURCE[0]}"
  3. scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
  4. base_uri='https://netcorenativeassets.blob.core.windows.net/resource-packages/external'
  5. install_directory=''
  6. clean=false
  7. force=false
  8. download_retries=5
  9. retry_wait_time_seconds=30
  10. global_json_file="$(dirname "$(dirname "${scriptroot}")")/global.json"
  11. declare -A native_assets
  12. . $scriptroot/pipeline-logging-functions.sh
  13. . $scriptroot/native/common-library.sh
  14. while (($# > 0)); do
  15. lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")"
  16. case $lowerI in
  17. --baseuri)
  18. base_uri=$2
  19. shift 2
  20. ;;
  21. --installdirectory)
  22. install_directory=$2
  23. shift 2
  24. ;;
  25. --clean)
  26. clean=true
  27. shift 1
  28. ;;
  29. --force)
  30. force=true
  31. shift 1
  32. ;;
  33. --donotabortonfailure)
  34. donotabortonfailure=true
  35. shift 1
  36. ;;
  37. --donotdisplaywarnings)
  38. donotdisplaywarnings=true
  39. shift 1
  40. ;;
  41. --downloadretries)
  42. download_retries=$2
  43. shift 2
  44. ;;
  45. --retrywaittimeseconds)
  46. retry_wait_time_seconds=$2
  47. shift 2
  48. ;;
  49. --help)
  50. echo "Common settings:"
  51. echo " --installdirectory Directory to install native toolset."
  52. echo " This is a command-line override for the default"
  53. echo " Install directory precedence order:"
  54. echo " - InstallDirectory command-line override"
  55. echo " - NETCOREENG_INSTALL_DIRECTORY environment variable"
  56. echo " - (default) %USERPROFILE%/.netcoreeng/native"
  57. echo ""
  58. echo " --clean Switch specifying not to install anything, but cleanup native asset folders"
  59. echo " --donotabortonfailure Switch specifiying whether to abort native tools installation on failure"
  60. echo " --donotdisplaywarnings Switch specifiying whether to display warnings during native tools installation on failure"
  61. echo " --force Clean and then install tools"
  62. echo " --help Print help and exit"
  63. echo ""
  64. echo "Advanced settings:"
  65. echo " --baseuri <value> Base URI for where to download native tools from"
  66. echo " --downloadretries <value> Number of times a download should be attempted"
  67. echo " --retrywaittimeseconds <value> Wait time between download attempts"
  68. echo ""
  69. exit 0
  70. ;;
  71. esac
  72. done
  73. function ReadGlobalJsonNativeTools {
  74. # happy path: we have a proper JSON parsing tool `jq(1)` in PATH!
  75. if command -v jq &> /dev/null; then
  76. # jq: read each key/value pair under "native-tools" entry and emit:
  77. # KEY="<entry-key>" VALUE="<entry-value>"
  78. # followed by a null byte.
  79. #
  80. # bash: read line with null byte delimeter and push to array (for later `eval`uation).
  81. while IFS= read -rd '' line; do
  82. native_assets+=("$line")
  83. done < <(jq -r '. |
  84. select(has("native-tools")) |
  85. ."native-tools" |
  86. keys[] as $k |
  87. @sh "KEY=\($k) VALUE=\(.[$k])\u0000"' "$global_json_file")
  88. return
  89. fi
  90. # Warning: falling back to manually parsing JSON, which is not recommended.
  91. # Following routine matches the output and escaping logic of jq(1)'s @sh formatter used above.
  92. # It has been tested with several weird strings with escaped characters in entries (key and value)
  93. # and results were compared with the output of jq(1) in binary representation using xxd(1);
  94. # just before the assignment to 'native_assets' array (above and below).
  95. # try to capture the section under "native-tools".
  96. if [[ ! "$(cat "$global_json_file")" =~ \"native-tools\"[[:space:]\:\{]*([^\}]+) ]]; then
  97. return
  98. fi
  99. section="${BASH_REMATCH[1]}"
  100. parseStarted=0
  101. possibleEnd=0
  102. escaping=0
  103. escaped=0
  104. isKey=1
  105. for (( i=0; i<${#section}; i++ )); do
  106. char="${section:$i:1}"
  107. if ! ((parseStarted)) && [[ "$char" =~ [[:space:],:] ]]; then continue; fi
  108. if ! ((escaping)) && [[ "$char" == "\\" ]]; then
  109. escaping=1
  110. elif ((escaping)) && ! ((escaped)); then
  111. escaped=1
  112. fi
  113. if ! ((parseStarted)) && [[ "$char" == "\"" ]]; then
  114. parseStarted=1
  115. possibleEnd=0
  116. elif [[ "$char" == "'" ]]; then
  117. token="$token'\\\''"
  118. possibleEnd=0
  119. elif ((escaping)) || [[ "$char" != "\"" ]]; then
  120. token="$token$char"
  121. possibleEnd=1
  122. fi
  123. if ((possibleEnd)) && ! ((escaping)) && [[ "$char" == "\"" ]]; then
  124. # Use printf to unescape token to match jq(1)'s @sh formatting rules.
  125. # do not use 'token="$(printf "$token")"' syntax, as $() eats the trailing linefeed.
  126. printf -v token "'$token'"
  127. if ((isKey)); then
  128. KEY="$token"
  129. isKey=0
  130. else
  131. line="KEY=$KEY VALUE=$token"
  132. native_assets+=("$line")
  133. isKey=1
  134. fi
  135. # reset for next token
  136. parseStarted=0
  137. token=
  138. elif ((escaping)) && ((escaped)); then
  139. escaping=0
  140. escaped=0
  141. fi
  142. done
  143. }
  144. native_base_dir=$install_directory
  145. if [[ -z $install_directory ]]; then
  146. native_base_dir=$(GetNativeInstallDirectory)
  147. fi
  148. install_bin="${native_base_dir}/bin"
  149. installed_any=false
  150. ReadGlobalJsonNativeTools
  151. if [[ ${#native_assets[@]} -eq 0 ]]; then
  152. echo "No native tools defined in global.json"
  153. exit 0;
  154. else
  155. native_installer_dir="$scriptroot/native"
  156. for index in "${!native_assets[@]}"; do
  157. eval "${native_assets["$index"]}"
  158. installer_path="$native_installer_dir/install-$KEY.sh"
  159. installer_command="$installer_path"
  160. installer_command+=" --baseuri $base_uri"
  161. installer_command+=" --installpath $install_bin"
  162. installer_command+=" --version $VALUE"
  163. echo $installer_command
  164. if [[ $force = true ]]; then
  165. installer_command+=" --force"
  166. fi
  167. if [[ $clean = true ]]; then
  168. installer_command+=" --clean"
  169. fi
  170. if [[ -a $installer_path ]]; then
  171. $installer_command
  172. if [[ $? != 0 ]]; then
  173. if [[ $donotabortonfailure = true ]]; then
  174. if [[ $donotdisplaywarnings != true ]]; then
  175. Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Execution Failed"
  176. fi
  177. else
  178. Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Execution Failed"
  179. exit 1
  180. fi
  181. else
  182. $installed_any = true
  183. fi
  184. else
  185. if [[ $donotabortonfailure == true ]]; then
  186. if [[ $donotdisplaywarnings != true ]]; then
  187. Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Execution Failed: no install script"
  188. fi
  189. else
  190. Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Execution Failed: no install script"
  191. exit 1
  192. fi
  193. fi
  194. done
  195. fi
  196. if [[ $clean = true ]]; then
  197. exit 0
  198. fi
  199. if [[ -d $install_bin ]]; then
  200. echo "Native tools are available from $install_bin"
  201. echo "##vso[task.prependpath]$install_bin"
  202. else
  203. if [[ $installed_any = true ]]; then
  204. Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Native tools install directory does not exist, installation failed"
  205. exit 1
  206. fi
  207. fi
  208. exit 0