1function tag_release {
2   # Arguments:
3   #   $1 - Path to top level consul source
4   #   $2 - Version string to use for tagging the release
5   #   $3 - Alternative GPG key id used for signing the release commit (optional)
6   #
7   # Returns:
8   #   0 - success
9   #   * - error
10   #
11   # Notes:
12   #   If the RELEASE_UNSIGNED environment variable is set then no gpg signing will occur
13
14   if ! test -d "$1"
15   then
16      err "ERROR: '$1' is not a directory. tag_release must be called with the path to the top level source as the first argument'"
17      return 1
18   fi
19
20   if test -z "$2"
21   then
22      err "ERROR: tag_release must be called with a version number as the second argument"
23      return 1
24   fi
25
26   # determine whether the gpg key to use is being overridden
27   local gpg_key=${HASHICORP_GPG_KEY}
28   if test -n "$3"
29   then
30      gpg_key=$3
31   fi
32
33   pushd "$1" > /dev/null
34   local ret=0
35
36   local branch_to_tag=$(git_branch) || ret=1
37
38   # perform an usngined release if requested (mainly for testing locally)
39   if test ${ret} -ne 0
40   then
41      err "ERROR: Failed to determine git branch to tag"
42   elif is_set "$RELEASE_UNSIGNED"
43   then
44      (
45         git commit --allow-empty -a -m "Release v${2}" &&
46         git tag -a -m "Version ${2}" "v${2}" "${branch_to_tag}"
47      )
48      ret=$?
49   # perform a signed release (official releases should do this)
50   elif have_gpg_key ${gpg_key}
51   then
52      (
53         git commit --allow-empty -a --gpg-sign=${gpg_key} -m "Release v${2}" &&
54         git tag -a -m "Version ${2}" -s -u ${gpg_key} "v${2}" "${branch_to_tag}"
55      )
56      ret=$?
57   # unsigned release not requested and gpg key isn't useable
58   else
59      err "ERROR: GPG key ${gpg_key} is not in the local keychain - to continue set RELEASE_UNSIGNED=1 in the env"
60      ret=1
61   fi
62   popd > /dev/null
63   return $ret
64}
65
66function package_binaries {
67   # Arguments:
68   #   $1 - Path to the directory containing the built binaries
69   #   $2 - Destination path of the packaged binaries
70   #   $3 - Version
71   #
72   # Returns:
73   #   0 - success
74   #   * - error
75
76   local sdir="$1"
77   local ddir="$2"
78   local vers="$3"
79   local ret=0
80
81
82   if ! test -d "${sdir}"
83   then
84      err "ERROR: '$1' is not a directory. package_binaries must be called with the path to the directory containing the binaries"
85      return 1
86   fi
87
88   rm -rf "${ddir}" > /dev/null 2>&1
89   mkdir -p "${ddir}" >/dev/null 2>&1
90   for platform in $(find "${sdir}" -mindepth 1 -maxdepth 1 -type d )
91   do
92      local os_arch=$(basename $platform)
93      local dest="${ddir}/${CONSUL_PKG_NAME}_${vers}_${os_arch}.zip"
94      status "Compressing ${os_arch} directory into ${dest}"
95      pushd "${platform}" > /dev/null
96      zip "${ddir}/${CONSUL_PKG_NAME}_${vers}_${os_arch}.zip" ./*
97      ret=$?
98      popd > /dev/null
99
100      if test "$ret" -ne 0
101      then
102         break
103      fi
104   done
105
106   return ${ret}
107}
108
109function package_release_one {
110   # Arguments:
111   #   $1 - Path to the top level Consul source
112   #   $2 - Version to use in the names of the zip files (optional)
113   #   $3 - Subdirectory under pkg/dist to use (optional)
114   #
115   # Returns:
116   #   0 - success
117   #   * - error
118
119   if ! test -d "$1"
120   then
121      err "ERROR: '$1' is not a directory. package_release must be called with the path to the top level source as the first argument'"
122      return 1
123   fi
124
125   local sdir="$1"
126   local ret=0
127   local vers="$2"
128   local extra_dir_name="$3"
129   local extra_dir=""
130
131   if test -n "${extra_dir_name}"
132   then
133      extra_dir="${extra_dir_name}/"
134   fi
135
136   if test -z "${vers}"
137   then
138      vers=$(get_version "${sdir}" true false)
139      ret=$?
140      if test "$ret" -ne 0
141      then
142         err "ERROR: failed to determine the version."
143         return $ret
144      fi
145   fi
146
147   package_binaries "${sdir}/pkg/bin/${extra_dir}" "${sdir}/pkg/dist/${extra_dir}" "${vers}"
148   return $?
149}
150
151function package_release {
152   # Arguments:
153   #   $1 - Path to the top level Consul source
154   #   $2 - Version to use in the names of the zip files (optional)
155   #
156   # Returns:
157   #   0 - success
158   #   * - error
159
160   package_release_one "$1" "$2" ""
161   return $?
162}
163
164function shasum_release {
165   # Arguments:
166   #   $1 - Path to the dist directory
167   #   $2 - Version of the release
168   #
169   # Returns:
170   #   0 - success
171   #   * - failure
172
173   local sdir="$1"
174   local vers="$2"
175
176   if ! test -d "$1"
177   then
178      err "ERROR: sign_release requires a path to the dist dir as the first argument"
179      return 1
180   fi
181
182   if test -z "${vers}"
183   then
184      err "ERROR: sign_release requires a version to be specified as the second argument"
185      return 1
186   fi
187
188   local hfile="${CONSUL_PKG_NAME}_${vers}_SHA256SUMS"
189
190   shasum_directory "${sdir}" "${sdir}/${hfile}"
191   return $?
192}
193
194function sign_release {
195   # Arguments:
196   #   $1 - Path to distribution directory
197   #   $2 - Version
198   #   $2 - Alternative GPG key to use for signing
199   #
200   # Returns:
201   #   0 - success
202   #   * - failure
203
204   local sdir="$1"
205   local vers="$2"
206
207   if ! test -d "${sdir}"
208   then
209      err "ERROR: sign_release requires a path to the dist dir as the first argument"
210      return 1
211   fi
212
213   if test -z "${vers}"
214   then
215      err "ERROR: sign_release requires a version to be specified as the second argument"
216      return 1
217   fi
218
219   local hfile="${CONSUL_PKG_NAME}_${vers}_SHA256SUMS"
220
221   status_stage "==> Signing ${hfile}"
222   gpg_detach_sign "${1}/${hfile}" "$3" || return 1
223   return 0
224}
225
226function check_release_one {
227   # Arguments:
228   #   $1 - Path to the release files
229   #   $2 - Version to expect
230   #   $3 - boolean whether to expect the signature file
231   #   $4 - Release Name (optional)
232   #
233   # Returns:
234   #   0 - success
235   #   * - failure
236
237   declare -i ret=0
238
239   declare -a expected_files
240
241   declare log_extra=""
242
243   if test -n "$4"
244   then
245      log_extra="for $4 "
246   fi
247
248   expected_files+=("${CONSUL_PKG_NAME}_${2}_SHA256SUMS")
249   echo "check sig: $3"
250   if is_set "$3"
251   then
252      expected_files+=("${CONSUL_PKG_NAME}_${2}_SHA256SUMS.sig")
253   fi
254
255   expected_files+=("${CONSUL_PKG_NAME}_${2}_darwin_386.zip")
256   expected_files+=("${CONSUL_PKG_NAME}_${2}_darwin_amd64.zip")
257   expected_files+=("${CONSUL_PKG_NAME}_${2}_freebsd_386.zip")
258   expected_files+=("${CONSUL_PKG_NAME}_${2}_freebsd_amd64.zip")
259   expected_files+=("${CONSUL_PKG_NAME}_${2}_freebsd_arm.zip")
260   expected_files+=("${CONSUL_PKG_NAME}_${2}_linux_386.zip")
261   expected_files+=("${CONSUL_PKG_NAME}_${2}_linux_amd64.zip")
262   expected_files+=("${CONSUL_PKG_NAME}_${2}_linux_arm.zip")
263   expected_files+=("${CONSUL_PKG_NAME}_${2}_linux_arm64.zip")
264   expected_files+=("${CONSUL_PKG_NAME}_${2}_solaris_amd64.zip")
265   expected_files+=("${CONSUL_PKG_NAME}_${2}_windows_386.zip")
266   expected_files+=("${CONSUL_PKG_NAME}_${2}_windows_amd64.zip")
267
268   declare -a found_files
269
270   status_stage "==> Verifying release contents ${log_extra}- ${2}"
271   debug "Expecting Files:"
272   for fname in "${expected_files[@]}"
273   do
274      debug "    $fname"
275   done
276
277   pushd "$1" > /dev/null
278   for actual_fname in $(ls)
279   do
280      local found=0
281      for i in "${!expected_files[@]}"
282      do
283         local expected_fname="${expected_files[i]}"
284         if test "${expected_fname}" == "${actual_fname}"
285         then
286            # remove from the expected_files array
287            unset 'expected_files[i]'
288
289            # append to the list of found files
290            found_files+=("${expected_fname}")
291
292            # mark it as found so we dont error
293            found=1
294            break
295         fi
296      done
297
298      if test $found -ne 1
299      then
300         err "ERROR: Release build has an extra file: ${actual_fname}"
301         ret=1
302      fi
303   done
304
305   for fname in "${expected_files[@]}"
306   do
307      err "ERROR: Release build is missing a file: $fname"
308      ret=1
309   done
310
311   if test $ret -eq 0
312   then
313      if ! shasum -c -s "${CONSUL_PKG_NAME}_${2}_SHA256SUMS"
314      then
315         err "ERROR: Failed SHA-256 hash verification"
316         shasum -c "${CONSUL_PKG_NAME}_${2}_SHA256SUMS"
317         ret=1
318      fi
319   fi
320
321   if test $ret -eq 0 && is_set "${3}"
322   then
323      if ! gpg --verify "${CONSUL_PKG_NAME}_${2}_SHA256SUMS.sig" "${CONSUL_PKG_NAME}_${2}_SHA256SUMS" > /dev/null 2>&1
324      then
325         err "ERROR: Failed GPG verification of SHA256SUMS signature"
326         ret=1
327      fi
328   fi
329
330   if test $ret -eq 0
331   then
332      status "Release build contents:"
333      for fname in "${found_files[@]}"
334      do
335         echo "    $fname"
336      done
337   fi
338
339   popd > /dev/null
340
341   return $ret
342}
343
344function check_release {
345   # Arguments:
346   #   $1 - Path to the release files
347   #   $2 - Version to expect
348   #   $3 - boolean whether to expect the signature file
349   #
350   # Returns:
351   #   0 - success
352   #   * - failure
353
354   check_release_one "$1" "$2" "$3"
355   return ${ret}
356}
357
358
359function build_consul_release {
360   build_consul "$1" "" "$2"
361}
362
363function build_release {
364   # Arguments: (yeah there are lots)
365   #   $1 - Path to the top level Consul source
366   #   $2 - boolean whether to tag the release yet
367   #   $3 - boolean whether to build the binaries
368   #   $4 - boolean whether to generate the sha256 sums
369   #   $5 - version to set within version.go and the changelog
370   #   $6 - release date to set within the changelog
371   #   $7 - release version to set
372   #   $8 - alternative gpg key to use for signing operations (optional)
373   #
374   # Returns:
375   #   0 - success
376   #   * - error
377
378   debug "Source Dir:    $1"
379   debug "Tag Release:   $2"
380   debug "Build Release: $3"
381   debug "Sign Release:  $4"
382   debug "Version:       $5"
383   debug "Release Date:  $6"
384   debug "Release Vers:  $7"
385   debug "GPG Key:       $8"
386
387   if ! test -d "$1"
388   then
389      err "ERROR: '$1' is not a directory. build_release must be called with the path to the top level source as the first argument'"
390      return 1
391   fi
392
393   if test -z "$2" -o -z "$3" -o -z "$4"
394   then
395      err "ERROR: build_release requires 4 arguments to be specified: <path to consul source> <tag release bool?> <build binaries bool?> <shasum 256 bool?>"
396      return 1
397   fi
398
399   local sdir="$1"
400   local do_tag="$2"
401   local do_build="$3"
402   local do_sha256="$4"
403   local gpg_key="$8"
404
405   if test -z "${gpg_key}"
406   then
407      gpg_key=${HASHICORP_GPG_KEY}
408   fi
409
410   if ! is_set "${RELEASE_UNSIGNED}"
411   then
412      if ! have_gpg_key "${gpg_key}"
413      then
414         err "ERROR: Aborting build because no useable GPG key is present. Set RELEASE_UNSIGNED=1 to bypass this check"
415         return 1
416      fi
417   fi
418
419   if ! is_git_clean "${sdir}" true && ! is_set "${ALLOW_DIRTY_GIT}"
420   then
421      err "ERROR: Refusing to build because Git is dirty. Set ALLOW_DIRTY_GIT=1 in the environment to proceed anyways"
422      return 1
423   fi
424
425   local set_vers="$5"
426   local set_date="$6"
427   local set_release="$7"
428
429   if test -z "${set_vers}"
430   then
431      set_vers=$(get_version "${sdir}" false false)
432      set_release=$(parse_version "${sdir}" true false true)
433   fi
434
435   if is_set "${do_tag}" && ! set_release_mode "${sdir}" "${set_vers}" "${set_date}" "${set_release}"
436   then
437      err "ERROR: Failed to put source into release mode"
438      return 1
439   fi
440
441   local vers="$(get_version ${sdir} true false)"
442   if test $? -ne 0
443   then
444      err "Please specify a version (couldn't find one based on build tags)."
445      return 1
446   fi
447
448   # Make sure we arent in dev mode
449   unset CONSUL_DEV
450
451   if is_set "${do_build}"
452   then
453      status_stage "==> Refreshing Docker Build Images"
454      refresh_docker_images "${sdir}"
455      if test $? -ne 0
456      then
457         err "ERROR: Failed to refresh docker images"
458         return 1
459      fi
460
461      status_stage "==> Building Legacy UI for version ${vers}"
462      build_ui_legacy "${sdir}" "${UI_LEGACY_BUILD_TAG}"
463      if test $? -ne 0
464      then
465         err "ERROR: Failed to build the legacy ui"
466         return 1
467      fi
468
469      status_stage "==> Building UI for version ${vers}"
470      # passing the version to override the version determined via tags
471      build_ui "${sdir}" "${UI_BUILD_TAG}" "${vers}"
472      if test $? -ne 0
473      then
474         err "ERROR: Failed to build the ui"
475         return 1
476      fi
477      status "UI Built with Version: $(ui_version "${sdir}/pkg/web_ui/v2/index.html")"
478
479      status_stage "==> Building Static Assets for version ${vers}"
480      build_assetfs "${sdir}" "${GO_BUILD_TAG}"
481      if test $? -ne 0
482      then
483         err "ERROR: Failed to build the static assets"
484         return 1
485      fi
486
487      if is_set "${do_tag}"
488      then
489         git add "${sdir}/agent/bindata_assetfs.go"
490         if test $? -ne 0
491         then
492            err "ERROR: Failed to git add the assetfs file"
493            return 1
494         fi
495      fi
496   fi
497
498   if is_set "${do_tag}"
499   then
500      status_stage "==> Tagging version ${vers}"
501      tag_release "${sdir}" "${vers}" "${gpg_key}"
502      if test $? -ne 0
503      then
504         err "ERROR: Failed to tag the release"
505         return 1
506      fi
507
508      update_git_env "${sdir}"
509   fi
510
511   if is_set "${do_build}"
512   then
513      status_stage "==> Building Consul for version ${vers}"
514      build_consul_release "${sdir}" "${GO_BUILD_TAG}"
515      if test $? -ne 0
516      then
517         err "ERROR: Failed to build the Consul binaries"
518         return 1
519      fi
520
521      status_stage "==> Packaging up release binaries"
522      package_release "${sdir}" "${vers}"
523      if test $? -ne 0
524      then
525         err "ERROR: Failed to package the release binaries"
526         return 1
527      fi
528   fi
529
530   status_stage "==> Generating SHA 256 Hashes for Binaries"
531   shasum_release "${sdir}/pkg/dist" "${vers}"
532   if test $? -ne 0
533   then
534      err "ERROR: Failed to generate SHA 256 hashes for the release"
535      return 1
536   fi
537
538   if is_set "${do_sha256}"
539   then
540      sign_release "${sdir}/pkg/dist" "${vers}" "${gpg_key}"
541      if test $? -ne 0
542      then
543         err "ERROR: Failed to sign the SHA 256 hashes file"
544         return 1
545      fi
546   fi
547
548   check_release "${sdir}/pkg/dist" "${vers}" "${do_sha256}"
549   return $?
550}
551