#!/usr/bin/env bash export LC_ALL=C set -e -o pipefail # Source the common prelude, which: # 1. Checks if we're at the top directory of the Feather Wallet repository # 2. Defines a few common functions and variables # # shellcheck source=libexec/prelude.bash source "$(dirname "${BASH_SOURCE[0]}")/libexec/prelude.bash" ################### ## Sanity Checks ## ################### ################ # Required non-builtin commands should be invokable ################ check_tools cat env basename mkdir diff sort if [ -z "$NO_SIGN" ]; then # make it possible to override the gpg binary GPG=${GPG:-gpg} # $GPG can contain extra arguments passed to the binary # so let's check only the existence of arg[0] # shellcheck disable=SC2206 GPG_ARRAY=($GPG) check_tools "${GPG_ARRAY[0]}" fi ################ # Required env vars should be non-empty ################ cmd_usage() { cat < \\ SIGNER=GPG_KEY_NAME[=SIGNER_NAME] \\ [ NO_SIGN=1 ] ./contrib/guix/guix-attest Example w/o overriding signing name: env GUIX_SIGS_REPO=/home/user/feather-sigs \\ SIGNER=achow101 \\ ./contrib/guix/guix-attest Example overriding signing name: env GUIX_SIGS_REPO=/home/user/feather-sigs \\ SIGNER=0x96AB007F1A7ED999=dongcarl \\ ./contrib/guix/guix-attest Example w/o signing, just creating SHA256SUMS: env GUIX_SIGS_REPO=/home/user/feather-sigs \\ SIGNER=achow101 \\ NO_SIGN=1 \\ ./contrib/guix/guix-attest EOF } if [ -z "${GUIX_SIGS_REPO}" ]; then echo "[HINT] Fork and clone the feather-sigs repo, if you haven't already:" echo "https://github.com/feather-wallet/feather-sigs" echo "" printf "Enter path to 'feather-sigs' repo: " read -r repo if [ ! -d "${repo}" ]; then echo "ERR: directory does not exist" exit 1 fi export GUIX_SIGS_REPO="$repo" wizard=1 fi if [ -z "${SIGNER}" ]; then printf "Enter your GitHub username: " read -r signer if [ -z "$signer" ]; then echo "ERR: signer name can't be empty" exit 1 fi echo "" echo "[HINT] To find your GPG fingerprint use:" echo "gpg --list-secret-keys --keyid-format=long" echo "It should look like: E87BD921CDD885C9D78A38C5E45B10DD027D2472" echo "" printf "Enter fingerprint of your GPG key: " read -r fingerprint export SIGNER="${fingerprint}=${signer}" wizard=1 fi if [ -n "$wizard" ]; then echo "" echo "Next time, invoke this command as:" echo "env GUIX_SIGS_REPO=${GUIX_SIGS_REPO} SIGNER=${SIGNER} make attest" echo "" fi ################ # GUIX_SIGS_REPO should exist as a directory ################ if [ ! -d "$GUIX_SIGS_REPO" ]; then cat << EOF ERR: The specified GUIX_SIGS_REPO is not an existent directory: '$GUIX_SIGS_REPO' Hint: Please clone the feather-sigs repository and point to it with the GUIX_SIGS_REPO environment variable. EOF cmd_usage exit 1 fi ################ # The key specified in SIGNER should be usable ################ IFS='=' read -r gpg_key_name signer_name <<< "$SIGNER" if [ -z "${signer_name}" ]; then signer_name="$gpg_key_name" fi if [ -z "$NO_SIGN" ] && ! ${GPG} --dry-run --list-secret-keys "${gpg_key_name}" >/dev/null 2>&1; then echo "ERR: GPG can't seem to find any key named '${gpg_key_name}'" exit 1 fi ################ # We should be able to find at least one output ################ echo "Looking for build output SHA256SUMS fragments in ${LOGDIR_BASE}" shopt -s nullglob sha256sum_fragments=( "$LOGDIR_BASE"/*/SHA256SUMS.part ) # This expands to an array of directories... shopt -u nullglob noncodesigned_fragments=() codesigned_fragments=() if (( ${#sha256sum_fragments[@]} )); then echo "Found build output SHA256SUMS fragments:" for logdir in "${sha256sum_fragments[@]}"; do echo " '$logdir'" case "$logdir" in "$LOGDIR_BASE"/*-codesigned/SHA256SUMS.part) codesigned_fragments+=("$logdir") ;; *) noncodesigned_fragments+=("$logdir") ;; esac done echo else echo "ERR: Could not find any build output SHA256SUMS fragments in ${LOGDIR_BASE}" exit 1 fi ############## ## Attest ## ############## # Usage: out_name $logdir # # HOST: The output directory being attested # out_name() { basename "$(dirname "$1")" } shasum_already_exists() { cat < "$temp_noncodesigned" if [ -e noncodesigned.SHA256SUMS ]; then # The SHA256SUMS already exists, make sure it's exactly what we # expect, error out if not if diff -u noncodesigned.SHA256SUMS "$temp_noncodesigned"; then echo "A noncodesigned.SHA256SUMS file already exists for '${VERSION}' and is up-to-date." else shasum_already_exists noncodesigned.SHA256SUMS exit 1 fi else mv "$temp_noncodesigned" noncodesigned.SHA256SUMS fi else echo "ERR: No noncodesigned outputs found for '${VERSION}', exiting..." exit 1 fi temp_all="$(mktemp)" trap 'rm -rf -- "$temp_all"' EXIT if (( ${#codesigned_fragments[@]} )); then # Note: all.SHA256SUMS attests to all of $sha256sum_fragments, but is # not needed if there are no $codesigned_fragments cat "${sha256sum_fragments[@]}" \ | sort -u \ | basenameify_SHA256SUMS \ | sort -k2 \ > "$temp_all" if [ -e all.SHA256SUMS ]; then # The SHA256SUMS already exists, make sure it's exactly what we # expect, error out if not if diff -u all.SHA256SUMS "$temp_all"; then echo "An all.SHA256SUMS file already exists for '${VERSION}' and is up-to-date." else shasum_already_exists all.SHA256SUMS exit 1 fi else mv "$temp_all" all.SHA256SUMS fi else # It is fine to have the codesigned outputs be missing (perhaps the # detached codesigs have not been published yet), just print a log # message instead of erroring out echo "INFO: No codesigned outputs found for '${VERSION}', skipping..." fi if [ -z "$NO_SIGN" ]; then echo "Signing SHA256SUMS to produce SHA256SUMS.asc" for i in *.SHA256SUMS; do if [ ! -e "$i".asc ]; then ${GPG} --detach-sign \ --digest-algo sha256 \ --local-user "$gpg_key_name" \ --armor \ --output "$i".asc "$i" else echo "Signature already there" fi done else echo "Not signing SHA256SUMS as \$NO_SIGN is not empty" fi echo "" )