1#!/usr/bin/env bash
2
3# Stop script on NZEC
4# set -e
5# Stop script if unbound variable found (use ${var:-} if intentional)
6set -u
7# By default cmd1 | cmd2 returns exit code of cmd2 regardless of cmd1 success
8# This is causing it to fail
9set -o pipefail
10
11# Use in the the functions: eval $invocation
12invocation='say_verbose "Calling: ${FUNCNAME[0]}"'
13
14# standard output may be used as a return value in the functions
15# we need a way to write text on the screen in the functions so that
16# it won't interfere with the return value.
17# Exposing stream 3 as a pipe to standard output of the script itself
18exec 3>&1
19
20say_err() {
21    printf "%b\n" "bootstrap: Error: $1" >&2
22}
23
24say() {
25    # using stream 3 (defined in the beginning) to not interfere with stdout of functions
26    # which may be used as return value
27    printf "%b\n" "bootstrap: $1" >&3
28}
29
30say_verbose() {
31    if [ "$verbose" = true ]; then
32        say "$1"
33    fi
34}
35
36machine_has() {
37    eval $invocation
38
39    hash "$1" > /dev/null 2>&1
40    return $?
41}
42
43check_min_reqs() {
44    if ! machine_has "curl"; then
45        say_err "curl is required to download dotnet. Install curl to proceed."
46        return 1
47    fi
48
49    return 0
50}
51
52# args:
53# remote_path - $1
54# [out_path] - $2 - stdout if not provided
55download() {
56    eval $invocation
57
58    local remote_path=$1
59    local out_path=${2:-}
60
61    local failed=false
62    if [ -z "$out_path" ]; then
63        curl --retry 10 -sSL --create-dirs $remote_path || failed=true
64    else
65        curl --retry 10 -sSL --create-dirs -o $out_path $remote_path || failed=true
66    fi
67
68    if [ "$failed" = true ]; then
69        say_err "Download failed"
70        return 1
71    fi
72}
73
74verbose=false
75repoRoot=`pwd`
76toolsLocalPath="<auto>"
77cliLocalPath="<auto>"
78symlinkPath="<auto>"
79sharedFxVersion="<auto>"
80force=
81forcedCliLocalPath="<none>"
82architecture="<auto>"
83dotNetInstallBranch="release/2.0.0"
84
85while [ $# -ne 0 ]
86do
87    name=$1
88    case $name in
89        -r|--repositoryRoot|-[Rr]epositoryRoot)
90            shift
91            repoRoot="$1"
92            ;;
93        -t|--toolsLocalPath|-[Tt]oolsLocalPath)
94            shift
95            toolsLocalPath="$1"
96            ;;
97        -c|--cliInstallPath|--cliLocalPath|-[Cc]liLocalPath)
98            shift
99            cliLocalPath="$1"
100            ;;
101        -u|--useLocalCli|-[Uu]seLocalCli)
102            shift
103            forcedCliLocalPath="$1"
104            ;;
105        -a|--architecture|-[Aa]rchitecture)
106            shift
107            architecture="$1"
108            ;;
109        --dotNetInstallBranch|-[Dd]ot[Nn]et[Ii]nstall[Bb]ranch)
110            shift
111            dotNetInstallBranch="$1"
112            ;;
113        --sharedFrameworkSymlinkPath|--symlink|-[Ss]haredFrameworkSymlinkPath)
114            shift
115            symlinkPath="$1"
116            ;;
117        --sharedFrameworkVersion|-[Ss]haredFrameworkVersion)
118            sharedFxVersion="$1"
119            ;;
120        --force|-[Ff]orce)
121            force=true
122            ;;
123        -v|--verbose|-[Vv]erbose)
124            verbose=true
125            ;;
126        *)
127            say_err "Unknown argument \`$name\`"
128            exit 1
129            ;;
130    esac
131
132    shift
133done
134
135if [ $toolsLocalPath = "<auto>" ]; then
136    toolsLocalPath="$repoRoot/Tools"
137fi
138
139if [ $cliLocalPath = "<auto>" ]; then
140    if [ $forcedCliLocalPath = "<none>" ]; then
141        cliLocalPath="$toolsLocalPath/dotnetcli"
142    else
143        cliLocalPath=$forcedCliLocalPath
144    fi
145fi
146
147if [ $symlinkPath = "<auto>" ]; then
148    symlinkPath="$toolsLocalPath/dotnetcli/shared/Microsoft.NETCore.App/version"
149fi
150
151
152rootCliVersion="$repoRoot/.cliversion"
153globalJson="$repoRoot/global.json"
154bootstrapComplete="$toolsLocalPath/bootstrap.complete"
155
156# if the force switch is specified delete the semaphore file if it exists
157if [[ $force && -f $bootstrapComplete ]]; then
158    rm -f $bootstrapComplete
159fi
160
161# if the semaphore file exists and is identical to the specified version then exit
162if [[ -f $bootstrapComplete && ! `cmp $bootstrapComplete $rootCliVersion` ]]; then
163    say "$bootstrapComplete appears to show that bootstrapping is complete.  Use --force if you want to re-bootstrap."
164    exit 0
165fi
166
167initCliScript="dotnet-install.sh"
168dotnetInstallPath="$toolsLocalPath/$initCliScript"
169
170# blow away the tools directory so we can start from a known state
171if [ -d $toolsLocalPath ]; then
172    # if the bootstrap.sh script was downloaded to the tools directory don't delete it
173    find $toolsLocalPath -type f -not -name bootstrap.sh -exec rm -f {} \;
174else
175    mkdir $toolsLocalPath
176fi
177
178if [ $forcedCliLocalPath = "<none>" ]; then
179    check_min_reqs
180
181    # download CLI boot-strapper script
182    download "https://raw.githubusercontent.com/dotnet/cli/$dotNetInstallBranch/scripts/obtain/dotnet-install.sh" "$dotnetInstallPath"
183    chmod u+x "$dotnetInstallPath"
184
185    # load the version of the CLI
186    dotNetCliVersion=`cat $rootCliVersion`
187
188    if [ ! -e $cliLocalPath ]; then
189        mkdir -p "$cliLocalPath"
190    fi
191
192    # now execute the script
193    say_verbose "installing CLI: $dotnetInstallPath --version \"$dotNetCliVersion\" --install-dir $cliLocalPath --architecture \"$architecture\""
194    $dotnetInstallPath --version "$dotNetCliVersion" --install-dir $cliLocalPath --architecture "$architecture"
195    if [ $? != 0 ]; then
196        say_err "The .NET CLI installation failed with exit code $?"
197        exit $?
198    fi
199
200    echo "{ \"sdk\": { \"version\": \"$dotNetCliVersion\" } }" > $globalJson
201fi
202
203cp $rootCliVersion $bootstrapComplete
204
205say "Bootstrap finished successfully."
206
207