1#!/usr/bin/env bash 2 3# To promote and sign a release that has been prepared by the build slaves, 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 echo 'Need at least one GPG key, please make one with `gpg --gen-key`' 49 echo 'You will also need to submit your key to a public keyserver, e.g.' 50 echo ' https://sks-keyservers.net/i/#submit' 51 exit 1 52elif [ $keycount -ne 1 ]; then 53 echo -e 'You have multiple GPG keys:\n' 54 55 gpg --list-secret-keys 56 57 while true; do 58 echo $gpgkey | awk '{ for(i = 1; i <= NF; i++) { print i ") " $i; } }' 59 echo -n 'Select a key: ' 60 read keynum 61 62 if $(test "$keynum" -eq "$keynum" > /dev/null 2>&1); then 63 _gpgkey=$(echo $gpgkey | awk '{ print $'${keynum}'}') 64 keycount=$(echo $_gpgkey | wc -w) 65 if [ $keycount -eq 1 ]; then 66 echo "" 67 gpgkey=$_gpgkey 68 break 69 fi 70 fi 71 done 72fi 73 74gpgfing=$(gpg --keyid-format 0xLONG --fingerprint $gpgkey | grep 'Key fingerprint =' | awk -F' = ' '{print $2}' | tr -d ' ') 75 76if ! test "$(grep $gpgfing README.md)"; then 77 echo 'Error: this GPG key fingerprint is not listed in ./README.md' 78 exit 1 79fi 80 81echo "Using GPG key: $gpgkey" 82echo " Fingerprint: $gpgfing" 83 84function checktag { 85 local version=$1 86 87 if ! git tag -v $version 2>&1 | grep "${gpgkey}" | grep key > /dev/null; then 88 echo "Could not find signed tag for \"${version}\" or GPG key is not yours" 89 exit 1 90 fi 91} 92 93################################################################################ 94## Create and sign checksums file for a given version 95 96function sign { 97 echo -e "\n# Creating SHASUMS256.txt ..." 98 99 local version=$1 100 101 ghtaggedversion=$(curl -sL https://raw.githubusercontent.com/nodejs/node/${version}/src/node_version.h \ 102 | awk '/define NODE_(MAJOR|MINOR|PATCH)_VERSION/{ v = v "." $3 } END{ v = "v" substr(v, 2); print v }') 103 if [ "${version}" != "${ghtaggedversion}" ]; then 104 echo "Could not find tagged version on github.com/nodejs/node, did you push your tag?" 105 exit 1 106 fi 107 108 shapath=$(ssh ${customsshkey} ${webuser}@${webhost} $signcmd nodejs $version) 109 110 if ! [[ ${shapath} =~ ^/.+/SHASUMS256.txt$ ]]; then 111 echo 'Error: No SHASUMS file returned by sign!' 112 exit 1 113 fi 114 115 echo -e "\n# Signing SHASUMS for ${version}..." 116 117 shafile=$(basename $shapath) 118 shadir=$(dirname $shapath) 119 tmpdir="/tmp/_node_release.$$" 120 121 mkdir -p $tmpdir 122 123 scp ${customsshkey} ${webuser}@${webhost}:${shapath} ${tmpdir}/${shafile} 124 125 gpg --default-key $gpgkey --clearsign --digest-algo SHA256 ${tmpdir}/${shafile} 126 gpg --default-key $gpgkey --detach-sign --digest-algo SHA256 ${tmpdir}/${shafile} 127 128 echo "Wrote to ${tmpdir}/" 129 130 echo -e "Your signed ${shafile}.asc:\n" 131 132 cat ${tmpdir}/${shafile}.asc 133 134 echo "" 135 136 while true; do 137 echo -n "Upload files? [y/n] " 138 yorn="" 139 read yorn 140 141 if [ "X${yorn}" == "Xn" ]; then 142 break 143 fi 144 145 if [ "X${yorn}" == "Xy" ]; then 146 scp ${customsshkey} ${tmpdir}/${shafile} ${tmpdir}/${shafile}.asc ${tmpdir}/${shafile}.sig ${webuser}@${webhost}:${shadir}/ 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 167echo -e "\n# Checking for releases ..." 168 169promotable=$(ssh ${customsshkey} ${webuser}@${webhost} $promotablecmd nodejs) 170 171if [ "X${promotable}" == "X" ]; then 172 echo "No releases to promote!" 173 exit 0 174fi 175 176echo -e "Found the following releases / builds ready to promote:\n" 177echo "$promotable" | sed 's/^/ * /' 178echo "" 179 180versions=$(echo "$promotable" | cut -d: -f1) 181 182################################################################################ 183## Promote releases 184 185for version in $versions; do 186 while true; do 187 files=$(echo "$promotable" | grep "^${version}" | sed 's/^'${version}': //') 188 echo -n "Promote ${version} files (${files})? [y/n] " 189 yorn="" 190 read yorn 191 192 if [ "X${yorn}" == "Xn" ]; then 193 break 194 fi 195 196 if [ "X${yorn}" != "Xy" ]; then 197 continue 198 fi 199 200 checktag $version 201 202 echo -e "\n# Promoting ${version}..." 203 204 ssh ${customsshkey} ${webuser}@${webhost} $promotecmd nodejs $version 205 206 if [ $? -eq 0 ];then 207 sign $version 208 fi 209 210 break 211 done 212done 213