1#!/bin/sh 2 3# To promote and sign a release that has been prepared by the build jobs, use: 4# release.sh 5 6# To _only_ sign an existing release, use: 7# release.sh -s vx.y.z 8 9set -e 10 11webhost=direct.nodejs.org 12webuser=dist 13promotablecmd=dist-promotable 14promotecmd=dist-promote 15signcmd=dist-sign 16customsshkey="" # let ssh and scp use default key 17signversion="" 18 19while getopts ":i:s:" option; do 20 case "${option}" in 21 i) 22 customsshkey="-i ${OPTARG}" 23 ;; 24 s) 25 signversion="${OPTARG}" 26 ;; 27 \?) 28 echo "Invalid option -$OPTARG." 29 exit 1 30 ;; 31 *) 32 echo "Option -$OPTARG takes a parameter." 33 exit 1 34 ;; 35 esac 36done 37shift $((OPTIND-1)) 38 39################################################################################ 40## Select a GPG key to use 41 42echo "# Selecting GPG key ..." 43 44gpgkey=$(gpg --list-secret-keys --keyid-format SHORT | awk -F'( +|/)' '/^(sec|ssb)/{print $3}') 45keycount=$(echo "$gpgkey" | wc -w) 46 47if [ "$keycount" -eq 0 ]; then 48 # shellcheck disable=SC2016 49 echo 'Need at least one GPG key, please make one with `gpg --gen-key`' 50 echo 'You will also need to submit your key to a public keyserver, e.g.' 51 echo ' https://sks-keyservers.net/i/#submit' 52 exit 1 53elif [ "$keycount" -ne 1 ]; then 54 printf "You have multiple GPG keys:\n\n" 55 56 gpg --list-secret-keys 57 58 keynum= 59 while [ -z "${keynum##*[!0-9]*}" ] || [ "$keynum" -le 0 ] || [ "$keynum" -gt "$keycount" ]; do 60 echo "$gpgkey" | awk '{ print NR ") " $0; }' 61 printf 'Select a key: ' 62 read -r keynum 63 done 64 echo "" 65 gpgkey=$(echo "$gpgkey" | sed -n "${keynum}p") 66fi 67 68gpgfing=$(gpg --keyid-format 0xLONG --fingerprint "$gpgkey" | grep 'Key fingerprint =' | awk -F' = ' '{print $2}' | tr -d ' ') 69 70grep -q "$gpgfing" README.md || (\ 71 echo 'Error: this GPG key fingerprint is not listed in ./README.md' && \ 72 exit 1 \ 73) 74 75 76echo "Using GPG key: $gpgkey" 77echo " Fingerprint: $gpgfing" 78 79checktag() { 80 # local version=$1 81 82 if ! git tag -v "$1" 2>&1 | grep "${gpgkey}" | grep key > /dev/null; then 83 echo "Could not find signed tag for \"$1\" or GPG key is not yours" 84 exit 1 85 fi 86} 87 88################################################################################ 89## Create and sign checksums file for a given version 90 91sign() { 92 printf "\n# Creating SHASUMS256.txt ...\n" 93 94 # local version=$1 95 96 ghtaggedversion=$(curl -sL "https://raw.githubusercontent.com/nodejs/node/$1/src/node_version.h" \ 97 | awk '/define NODE_(MAJOR|MINOR|PATCH)_VERSION/{ v = v "." $3 } END{ v = "v" substr(v, 2); print v }') 98 if [ "$1" != "${ghtaggedversion}" ]; then 99 echo "Could not find tagged version on github.com/nodejs/node, did you push your tag?" 100 exit 1 101 fi 102 103 # shellcheck disable=SC2086,SC2029 104 shapath=$(ssh ${customsshkey} "${webuser}@${webhost}" $signcmd nodejs $1) 105 106 echo "${shapath}" | grep -q '^/.*/SHASUMS256.txt$' || (\ 107 echo 'Error: No SHASUMS file returned by sign!' &&\ 108 exit 1) 109 110 echo "" 111 echo "# Signing SHASUMS for $1..." 112 113 shafile=$(basename "$shapath") 114 shadir=$(dirname "$shapath") 115 tmpdir="/tmp/_node_release.$$" 116 117 mkdir -p $tmpdir 118 119 # shellcheck disable=SC2086 120 scp ${customsshkey} "${webuser}@${webhost}:${shapath}" "${tmpdir}/${shafile}" 121 122 gpg --default-key "$gpgkey" --clearsign --digest-algo SHA256 "${tmpdir}/${shafile}" 123 gpg --default-key "$gpgkey" --detach-sign --digest-algo SHA256 "${tmpdir}/${shafile}" 124 125 echo "Wrote to ${tmpdir}/" 126 127 echo "Your signed ${shafile}.asc:" 128 echo "" 129 130 cat "${tmpdir}/${shafile}.asc" 131 132 echo "" 133 134 while true; do 135 printf "Upload files? [y/n] " 136 yorn="" 137 read -r yorn 138 139 if [ "X${yorn}" = "Xn" ]; then 140 break 141 fi 142 143 if [ "X${yorn}" = "Xy" ]; then 144 # shellcheck disable=SC2086 145 scp ${customsshkey} "${tmpdir}/${shafile}" "${tmpdir}/${shafile}.asc" "${tmpdir}/${shafile}.sig" "${webuser}@${webhost}:${shadir}/" 146 # shellcheck disable=SC2086,SC2029 147 ssh ${customsshkey} "${webuser}@${webhost}" chmod 644 "${shadir}/${shafile}.asc" "${shadir}/${shafile}.sig" 148 break 149 fi 150 done 151 152 rm -rf $tmpdir 153} 154 155 156if [ -n "${signversion}" ]; then 157 checktag "$signversion" 158 sign "$signversion" 159 exit 0 160fi 161 162# else: do a normal release & promote 163 164################################################################################ 165## Look for releases to promote 166 167printf "\n# Checking for releases ...\n" 168 169# shellcheck disable=SC2086,SC2029 170promotable=$(ssh ${customsshkey} "$webuser@$webhost" $promotablecmd nodejs) 171 172if [ "X${promotable}" = "X" ]; then 173 echo "No releases to promote!" 174 exit 0 175fi 176 177echo "Found the following releases / builds ready to promote:" 178echo "" 179echo "$promotable" | sed 's/^/ * /' 180echo "" 181 182versions=$(echo "$promotable" | cut -d: -f1) 183 184################################################################################ 185## Promote releases 186 187for version in $versions; do 188 while true; do 189 files=$(echo "$promotable" | grep "^${version}" | sed 's/^'"${version}"': //') 190 printf "Promote %s files (%s)? [y/n] " "${version}" "${files}" 191 yorn="" 192 read -r yorn 193 194 if [ "X${yorn}" = "Xn" ]; then 195 break 196 fi 197 198 if [ "X${yorn}" != "Xy" ]; then 199 continue 200 fi 201 202 checktag "$version" 203 204 echo "" 205 echo "# Promoting ${version}..." 206 207 # shellcheck disable=SC2086,SC2029 208 ssh ${customsshkey} "$webuser@$webhost" $promotecmd nodejs $version && \ 209 sign "$version" 210 211 break 212 done 213done 214