You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
255 lines
6.9 KiB
255 lines
6.9 KiB
#!/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 Bitcoin Core 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 |
|
check_tools gpg |
|
fi |
|
|
|
################ |
|
# Required env vars should be non-empty |
|
################ |
|
|
|
cmd_usage() { |
|
cat <<EOF |
|
Synopsis: |
|
|
|
env GUIX_SIGS_REPO=<path/to/guix.sigs> \\ |
|
SIGNER=GPG_KEY_NAME[=SIGNER_NAME] \\ |
|
[ NO_SIGN=1 ] |
|
./contrib/guix/guix-attest |
|
|
|
Example w/o overriding signing name: |
|
|
|
env GUIX_SIGS_REPO=/home/achow101/guix.sigs \\ |
|
SIGNER=achow101 \\ |
|
./contrib/guix/guix-attest |
|
|
|
Example overriding signing name: |
|
|
|
env GUIX_SIGS_REPO=/home/dongcarl/guix.sigs \\ |
|
SIGNER=0x96AB007F1A7ED999=dongcarl \\ |
|
./contrib/guix/guix-attest |
|
|
|
Example w/o signing, just creating SHA256SUMS: |
|
|
|
env GUIX_SIGS_REPO=/home/achow101/guix.sigs \\ |
|
SIGNER=achow101 \\ |
|
NO_SIGN=1 \\ |
|
./contrib/guix/guix-attest |
|
|
|
EOF |
|
} |
|
|
|
if [ -z "$GUIX_SIGS_REPO" ] || [ -z "$SIGNER" ]; then |
|
cmd_usage |
|
exit 1 |
|
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 guix.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 ${OUTDIR_BASE}" |
|
|
|
shopt -s nullglob |
|
sha256sum_fragments=( "$OUTDIR_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 outdir in "${sha256sum_fragments[@]}"; do |
|
echo " '$outdir'" |
|
case "$outdir" in |
|
"$OUTDIR_BASE"/*-codesigned/SHA256SUMS.part) |
|
codesigned_fragments+=("$outdir") |
|
;; |
|
*) |
|
noncodesigned_fragments+=("$outdir") |
|
;; |
|
esac |
|
done |
|
echo |
|
else |
|
echo "ERR: Could not find any build output SHA256SUMS fragments in ${OUTDIR_BASE}" |
|
exit 1 |
|
fi |
|
|
|
############## |
|
## Attest ## |
|
############## |
|
|
|
# Usage: out_name $outdir |
|
# |
|
# HOST: The output directory being attested |
|
# |
|
out_name() { |
|
basename "$(dirname "$1")" |
|
} |
|
|
|
shasum_already_exists() { |
|
cat <<EOF |
|
-- |
|
|
|
ERR: An ${1} file already exists for '${VERSION}' and attests |
|
differently. You likely previously attested to a partial build (e.g. one |
|
where you specified the HOST environment variable). |
|
|
|
See the diff above for more context. |
|
|
|
Hint: You may wish to remove the existing attestations and their signatures by |
|
invoking: |
|
|
|
rm '${PWD}/${1}'{,.asc} |
|
|
|
Then try running this script again. |
|
|
|
EOF |
|
} |
|
|
|
echo "Attesting to build outputs for version: '${VERSION}'" |
|
echo "" |
|
|
|
# Given a SHA256SUMS file as stdin that has lines like: |
|
# 0ba536819b221a91d3d42e978be016aac918f40984754d74058aa0c921cd3ea6 a/b/d/c/d/s/bitcoin-22.0rc2-riscv64-linux-gnu.tar.gz |
|
# ... |
|
# |
|
# Replace each line's file name with its basename: |
|
# 0ba536819b221a91d3d42e978be016aac918f40984754d74058aa0c921cd3ea6 bitcoin-22.0rc2-riscv64-linux-gnu.tar.gz |
|
# ... |
|
# |
|
basenameify_SHA256SUMS() { |
|
sed -E 's@(^[[:xdigit:]]{64}[[:space:]]+).+/([^/]+$)@\1\2@' |
|
} |
|
|
|
outsigdir="$GUIX_SIGS_REPO/$VERSION/$signer_name" |
|
mkdir -p "$outsigdir" |
|
( |
|
cd "$outsigdir" |
|
|
|
temp_noncodesigned="$(mktemp)" |
|
trap 'rm -rf -- "$temp_noncodesigned"' EXIT |
|
|
|
if (( ${#noncodesigned_fragments[@]} )); then |
|
cat "${noncodesigned_fragments[@]}" \ |
|
| sort -u \ |
|
| sort -k2 \ |
|
| basenameify_SHA256SUMS \ |
|
> "$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 \ |
|
| sort -k2 \ |
|
| basenameify_SHA256SUMS \ |
|
> "$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 "" |
|
)
|
|
|