cargo-publish
1 #!/usr/bin/env bash 2 # 3 # usage: 4 # maint/cargo-publish [<OPTIONS>] VERSION 5 # 6 # options: 7 # --dry-run 8 # --force go ahead even if problems detected 9 # --branch BRANCH expect to be releasing origin/BRANCH, not HEAD 10 # --origin ORIGIN replace repo URL to look for BRANCH on 11 # 12 # Preconditions: 13 # See doc/Release.md 14 # 15 # Running a different version to the one in the arti.git being published: 16 # You can invoke this as ../PATH/TO/maint/cargo-publish. 17 # That maint/ directory must contain compatible versions of the support 18 # utilities, but it need not match the one in the CWD. 19 # The version that is published is the one from the CWD. 20 21 set -e 22 23 origin='git@gitlab.torproject.org:tpo/core/arti' 24 branch='main' 25 maint="$(dirname "$0")" 26 : "${CARGO:=cargo}" 27 28 # Note for users of nailing-cargo: use this setting: 29 # CARGO='nailing-cargo --preclean=full --no-nail --git -o' 30 31 # shellcheck source=maint/crates-io-utils.sh 32 source "$maint/crates-io-utils.sh" 33 34 #===== utilities ===== 35 36 all_ok=true 37 38 problem () { 39 echo >&2 "problem: $*" 40 all_ok=false 41 } 42 43 check_equal () { 44 local a="$1" 45 local b="$2" 46 local a_what="$3" 47 local b_what="$4" 48 if [ "x$a" != "x$b" ]; then 49 problem "mismatch: $a_what ($a) != $b_what ($b)" 50 fi 51 } 52 53 #===== temporary files ===== 54 55 tmp_trap_exit_setup 56 57 #===== argument parsing ===== 58 59 dry_run=false 60 force=false 61 62 while [ $# != 0 ]; do 63 case "$1" in 64 --) break;; 65 -*) ;; 66 *) break;; 67 esac 68 arg="$1"; shift 69 case "$arg" in 70 --dry-run) dry_run=true ;; 71 --force) force=true ;; 72 --branch|--origin) 73 eval "${arg#--}=\$1" 74 shift || fail "$arg takes an argument" 75 ;; 76 *) fail "unknown option: $1";; 77 esac 78 done 79 80 case $# in 81 1) version=$1 ;; 82 *) fail "bad usage, needs arti version" 83 esac 84 85 #===== checks and preparation ===== 86 87 jq </dev/null . || fail "jq is not installed, try apt install jq" 88 89 #----- check that we are identical to our main repo's `main` 90 91 git fetch --quiet "$origin" "$branch" 92 93 origin_head=$(git rev-parse FETCH_HEAD~0) 94 our_head=$(git rev-parse HEAD~0) 95 96 check_equal "$our_head" "$origin_head" \ 97 "our HEAD commit" "origin commit $origin" 98 99 # TODO somehow check that CI failed there! 100 101 #----- check that arti version matches 102 103 arti=$("$maint"/list_crates -p arti --version) 104 arti=${arti##* } 105 106 check_equal "$arti" "$version" \ 107 "version of the arti crate, in-tree" \ 108 "specified version to release" 109 110 #----- compare already-published versions ----- 111 112 "$maint"/list_crates --version >"$tmp/all-crates" 113 114 exec 3<"$tmp/all-crates" 115 116 # shellcheck disable=SC2162 # we don't need -r, it has no backslashes 117 while <&3 read p v; do 118 printf "checking status of %-30s %10s ..." "$p" "$v" 119 120 crates_io_api_call "v1/crates/$p/$v" .version.crate "$tmp/$p.json" 121 122 case "$http_code" in 123 200) echo ' already published.';; 124 404) 125 echo ' needs publishing.' 126 to_publish+=" $p" 127 ;; 128 esac 129 done 130 131 #===== commitment point ===== 132 133 prefix=xxxx 134 135 running () { 136 echo " $*" 137 "$@" 138 } 139 140 if $dry_run; then 141 if $all_ok; then 142 echo 'all seems OK, would run the following commands:' 143 prefix='echo' 144 elif $force; then 145 echo 'PROBLEMS, but --force passed, would run:' 146 prefix='echo' 147 else 148 echo 'PROBLEMS - would not run!' 149 prefix='echo false' 150 fi 151 else 152 if $all_ok; then 153 echo 'all OK, running publication!' 154 prefix='running' 155 elif $force; then 156 echo 'PROBLEMS, but --force passed, going ahead!' 157 prefix='running' 158 else 159 fail 'problems (see above), stopping' 160 fi 161 fi 162 163 for p in $to_publish; do 164 # shellcheck disable=SC2086 # we want to split on spaces in $prefix and $CARGO 165 $prefix $CARGO publish -p "$p" 166 done 167 168 echo 'all published.' 169 170 tmp_trap_exit_finish_ok 171