run.sh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. #
  4. # variables
  5. #
  6. RESET="\033[0m"
  7. RED="\033[0;31m"
  8. YELLOW="\033[0;33m"
  9. MAGENTA="\033[0;95m"
  10. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
  11. verbose=false
  12. update=false
  13. reinstall=false
  14. repo_path="$DIR"
  15. channel=''
  16. tools_source=''
  17. ci=false
  18. package_version_props_url=''
  19. asset_root_url=''
  20. access_token_suffix=''
  21. restore_sources=''
  22. product_build_id=''
  23. msbuild_args=()
  24. #
  25. # Functions
  26. #
  27. __usage() {
  28. echo "Usage: $(basename "${BASH_SOURCE[0]}") command [options] [[--] <Arguments>...]"
  29. echo ""
  30. echo "Arguments:"
  31. echo " command The command to be run."
  32. echo " <Arguments>... Arguments passed to the command. Variable number of arguments allowed."
  33. echo ""
  34. echo "Options:"
  35. echo " --verbose Show verbose output."
  36. echo " -c|--channel <CHANNEL> The channel of KoreBuild to download. Overrides the value from the config file.."
  37. echo " --config-file <FILE> The path to the configuration file that stores values. Defaults to korebuild.json."
  38. echo " -d|--dotnet-home <DIR> The directory where .NET Core tools will be stored. Defaults to '\$DOTNET_HOME' or '\$HOME/.dotnet."
  39. echo " --path <PATH> The directory to build. Defaults to the directory containing the script."
  40. echo " -s|--tools-source|-ToolsSource <URL> The base url where build tools can be downloaded. Overrides the value from the config file."
  41. echo " --package-version-props-url <URL> The url of the package versions props path containing dependency versions."
  42. echo " --access-token <Token> The query string to append to any blob store access for PackageVersionPropsUrl, if any."
  43. echo " --restore-sources <Sources> Semi-colon delimited list of additional NuGet feeds to use as part of restore."
  44. echo " --product-build-id <ID> The product build ID for correlation with orchestrated builds."
  45. echo " -u|--update Update to the latest KoreBuild even if the lock file is present."
  46. echo " --reinstall Reinstall KoreBuild."
  47. echo " --ci Apply CI specific settings and environment variables."
  48. echo ""
  49. echo "Description:"
  50. echo " This function will create a file \$DIR/korebuild-lock.txt. This lock file can be committed to source, but does not have to be."
  51. echo " When the lockfile is not present, KoreBuild will create one using latest available version from \$channel."
  52. if [[ "${1:-}" != '--no-exit' ]]; then
  53. exit 2
  54. fi
  55. }
  56. get_korebuild() {
  57. local version
  58. local lock_file="$repo_path/korebuild-lock.txt"
  59. if [ ! -f "$lock_file" ] || [ "$update" = true ]; then
  60. __get_remote_file "$tools_source/korebuild/channels/$channel/latest.txt" "$lock_file"
  61. fi
  62. version="$(grep 'version:*' -m 1 "$lock_file")"
  63. if [[ "$version" == '' ]]; then
  64. __error "Failed to parse version from $lock_file. Expected a line that begins with 'version:'"
  65. return 1
  66. fi
  67. version="$(echo "${version#version:}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
  68. local korebuild_path="$DOTNET_HOME/buildtools/korebuild/$version"
  69. if [ "$reinstall" = true ] && [ -d "$korebuild_path" ]; then
  70. rm -rf "$korebuild_path"
  71. fi
  72. {
  73. if [ ! -d "$korebuild_path" ]; then
  74. mkdir -p "$korebuild_path"
  75. local remote_path="$tools_source/korebuild/artifacts/$version/korebuild.$version.zip"
  76. tmpfile="$(mktemp)"
  77. echo -e "${MAGENTA}Downloading KoreBuild ${version}${RESET}"
  78. if __get_remote_file "$remote_path" "$tmpfile"; then
  79. unzip -q -d "$korebuild_path" "$tmpfile"
  80. fi
  81. rm "$tmpfile" || true
  82. fi
  83. source "$korebuild_path/KoreBuild.sh"
  84. } || {
  85. if [ -d "$korebuild_path" ]; then
  86. echo "Cleaning up after failed installation"
  87. rm -rf "$korebuild_path" || true
  88. fi
  89. return 1
  90. }
  91. }
  92. __error() {
  93. echo -e "${RED}error: $*${RESET}" 1>&2
  94. }
  95. __warn() {
  96. echo -e "${YELLOW}warning: $*${RESET}"
  97. }
  98. __machine_has() {
  99. hash "$1" > /dev/null 2>&1
  100. return $?
  101. }
  102. __get_remote_file() {
  103. local remote_path=$1
  104. local local_path=$2
  105. if [[ "$remote_path" != 'http'* ]]; then
  106. cp "$remote_path" "$local_path"
  107. return 0
  108. fi
  109. local failed=false
  110. if __machine_has wget; then
  111. wget --tries 10 --quiet -O "$local_path" "$remote_path" || failed=true
  112. else
  113. failed=true
  114. fi
  115. if [ "$failed" = true ] && __machine_has curl; then
  116. failed=false
  117. curl --retry 10 -sSL -f --create-dirs -o "$local_path" "$remote_path" || failed=true
  118. fi
  119. if [ "$failed" = true ]; then
  120. __error "Download failed: $remote_path" 1>&2
  121. return 1
  122. fi
  123. }
  124. #
  125. # main
  126. #
  127. command="${1:-}"
  128. shift
  129. while [[ $# -gt 0 ]]; do
  130. case $1 in
  131. -\?|-h|--help)
  132. __usage --no-exit
  133. exit 0
  134. ;;
  135. -c|--channel|-Channel)
  136. shift
  137. channel="${1:-}"
  138. [ -z "$channel" ] && __error "Missing value for parameter --channel" && __usage
  139. ;;
  140. --config-file|-ConfigFile)
  141. shift
  142. config_file="${1:-}"
  143. [ -z "$config_file" ] && __error "Missing value for parameter --config-file" && __usage
  144. if [ ! -f "$config_file" ]; then
  145. __error "Invalid value for --config-file. $config_file does not exist."
  146. exit 1
  147. fi
  148. ;;
  149. -d|--dotnet-home|-DotNetHome)
  150. shift
  151. DOTNET_HOME="${1:-}"
  152. [ -z "$DOTNET_HOME" ] && __error "Missing value for parameter --dotnet-home" && __usage
  153. ;;
  154. --path|-Path)
  155. shift
  156. repo_path="${1:-}"
  157. [ -z "$repo_path" ] && __error "Missing value for parameter --path" && __usage
  158. ;;
  159. -s|--tools-source|-ToolsSource)
  160. shift
  161. tools_source="${1:-}"
  162. [ -z "$tools_source" ] && __error "Missing value for parameter --tools-source" && __usage
  163. ;;
  164. --package-version-props-url|-PackageVersionPropsUrl)
  165. shift
  166. # This parameter can be an empty string, but it should be set
  167. [ -z "${1+x}" ] && __error "Missing value for parameter --package-version-props-url" && __usage
  168. package_version_props_url="$1"
  169. ;;
  170. --access-token-suffix|-AccessTokenSuffix)
  171. shift
  172. # This parameter can be an empty string, but it should be set
  173. [ -z "${1+x}" ] && __error "Missing value for parameter --access-token-suffix" && __usage
  174. access_token_suffix="$1"
  175. ;;
  176. --restore-sources|-RestoreSources)
  177. shift
  178. # This parameter can be an empty string, but it should be set
  179. [ -z "${1+x}" ] && __error "Missing value for parameter --restore-sources" && __usage
  180. restore_sources="$1"
  181. ;;
  182. --asset-root-url|-AssetRootUrl)
  183. shift
  184. # This parameter can be an empty string, but it should be set
  185. [ -z "${1+x}" ] && __error "Missing value for parameter --asset-root-url" && __usage
  186. asset_root_url="$1"
  187. ;;
  188. --product-build-id|-ProductBuildId)
  189. shift
  190. # This parameter can be an empty string, but it should be set
  191. [ -z "${1+x}" ] && __error "Missing value for parameter --product-build-id" && __usage
  192. product_build_id="$1"
  193. ;;
  194. -u|--update|-Update)
  195. update=true
  196. ;;
  197. --reinstall|-Reinstall)
  198. reinstall=true
  199. ;;
  200. --ci|-[Cc][Ii])
  201. ci=true
  202. if [[ -z "${DOTNET_HOME:-}" ]]; then
  203. DOTNET_HOME="$DIR/.dotnet"
  204. fi
  205. ;;
  206. --verbose|-Verbose)
  207. verbose=true
  208. ;;
  209. *)
  210. msbuild_args[${#msbuild_args[*]}]="$1"
  211. ;;
  212. esac
  213. shift
  214. done
  215. if ! __machine_has unzip; then
  216. __error 'Missing required command: unzip'
  217. exit 1
  218. fi
  219. if ! __machine_has curl && ! __machine_has wget; then
  220. __error 'Missing required command. Either wget or curl is required.'
  221. exit 1
  222. fi
  223. [ -z "${config_file:-}" ] && config_file="$repo_path/korebuild.json"
  224. if [ -f "$config_file" ]; then
  225. if __machine_has jq ; then
  226. if jq '.' "$config_file" >/dev/null ; then
  227. config_channel="$(jq -r 'select(.channel!=null) | .channel' "$config_file")"
  228. config_tools_source="$(jq -r 'select(.toolsSource!=null) | .toolsSource' "$config_file")"
  229. else
  230. __error "$config_file is invalid JSON. Its settings will be ignored."
  231. exit 1
  232. fi
  233. elif __machine_has python ; then
  234. if python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'))" >/dev/null ; then
  235. config_channel="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")"
  236. config_tools_source="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")"
  237. else
  238. __error "$config_file is invalid JSON. Its settings will be ignored."
  239. exit 1
  240. fi
  241. else
  242. __error 'Missing required command: jq or python. Could not parse the JSON file. Its settings will be ignored.'
  243. exit 1
  244. fi
  245. [ ! -z "${config_channel:-}" ] && channel="$config_channel"
  246. [ ! -z "${config_tools_source:-}" ] && tools_source="$config_tools_source"
  247. fi
  248. [ -z "${DOTNET_HOME:-}" ] && DOTNET_HOME="$HOME/.dotnet"
  249. if [ ! -z "$package_version_props_url" ]; then
  250. intermediate_dir="$repo_path/obj"
  251. props_file_path="$intermediate_dir/external-dependencies.props"
  252. mkdir -p "$intermediate_dir"
  253. __get_remote_file "$package_version_props_url" "$props_file_path"
  254. msbuild_args[${#msbuild_args[*]}]="-p:DotNetPackageVersionPropsPath=$props_file_path"
  255. fi
  256. if [ ! -z "$restore_sources" ]; then
  257. msbuild_args[${#msbuild_args[*]}]="-p:DotNetAdditionalRestoreSources=$restore_sources"
  258. fi
  259. if [ ! -z "$asset_root_url" ]; then
  260. msbuild_args[${#msbuild_args[*]}]="-p:DotNetAssetRootUrl=$asset_root_url"
  261. fi
  262. if [ ! -z "$access_token_suffix" ]; then
  263. msbuild_args[${#msbuild_args[*]}]="-p:DotNetAssetRootAccessTokenSuffix=$access_token_suffix"
  264. fi
  265. if [ ! -z "$product_build_id" ]; then
  266. msbuild_args[${#msbuild_args[*]}]="-p:DotNetProductBuildId=$product_build_id"
  267. fi
  268. [ -z "$channel" ] && channel='master'
  269. [ -z "$tools_source" ] && tools_source='https://aspnetcore.blob.core.windows.net/buildtools'
  270. get_korebuild
  271. set_korebuildsettings "$tools_source" "$DOTNET_HOME" "$repo_path" "$config_file" "$ci"
  272. # This incantation avoids unbound variable issues if msbuild_args is empty
  273. # https://stackoverflow.com/questions/7577052/bash-empty-array-expansion-with-set-u
  274. invoke_korebuild_command "$command" ${msbuild_args[@]+"${msbuild_args[@]}"}