diff --git a/scripts/npm-dist-tag.sh b/scripts/npm-dist-tag.sh index 8327e50a602c..b1f5543805f1 100755 --- a/scripts/npm-dist-tag.sh +++ b/scripts/npm-dist-tag.sh @@ -1,7 +1,7 @@ #! /bin/bash usage() { cat << END_USAGE -Usage: $0 [--dry-run] [lerna] []... +Usage: $0 [--dry-run|--otp-stream=] [lerna] []... Commands: add [-] @@ -15,6 +15,12 @@ add [-] With "--dry-run", npm commands are printed to standard error rather than executed. +With "--otp-stream=", for each npm command the value of an "--otp" option +is read from the specified file (generally expected to be a named pipe created +by \`mkfifo\`) and failing commands are retried until the read value is empty. +Alternatively, environment variable configuration \`npm_config_auth_type=legacy\` +causes npm itself to prompt for and read OTP values from standard input. + If the first operand is "lerna", the operation is extended to all packages. END_USAGE exit 1 @@ -29,15 +35,34 @@ fail() { # Exit on any errors. set -ueo pipefail -# Check for `--dry-run`. +# Check for `--dry-run` and `--otp-stream`. npm=npm -dryrun= -if test "${1:-}" = "--dry-run"; then - dryrun=$1 - npm="echo-to-stderr npm" - shift -fi +style= +otpfile= +case "${1:-}" in + --dry-run) + style="$1" + npm="echo-to-stderr npm" + shift + ;; + --otp-stream=*) + style="$1" + otpfile="${1##--otp-stream=}" + npm="npm-otp" + shift + ;; +esac echo-to-stderr() { echo "$@"; } 1>&2 +npm-otp() { + printf 1>&2 "Reading OTP from %s ... " "$otpfile" + otp=$(head -n 1 "$otpfile") + if test -z "$otp"; then + echo 1>&2 "No OTP" + return 66 + fi + echo 1>&2 OK + npm --otp="$otp" "$@" +} # Check for `lerna`. case "${1-}" in @@ -52,7 +77,7 @@ case "${1-}" in # Strip the first argument (`lerna`), so that `$@` gives us remaining args. shift - exec npm run -- lerna exec --concurrency=1 --no-bail -- "$thisdir/$thisprog" "$dryrun" "$@" + exec npm run -- lerna exec --concurrency=1 --no-bail -- "$thisdir/$thisprog" "$style" "$@" ;; esac CMD="${1-"--help"}" @@ -98,19 +123,31 @@ case "$CMD" in # Add $TAG to dist-tags. test -n "$TAG" || fail "Missing tag!" test "$#" -le 3 || fail "Too many arguments!" - $npm dist-tag add "$pkg@$version" "$TAG" + while true; do + $npm dist-tag add "$pkg@$version" "$TAG" && break || ret=$? + [[ "$style" =~ --otp-stream ]] || exit $ret + [ $ret -ne 66 ] && continue + echo Aborting + exit 1 + done ;; remove | rm) # Remove $TAG from dist-tags. test -n "$TAG" || fail "Missing tag!" test "$#" -le 2 || fail "Too many arguments!" - $npm dist-tag rm "$pkg" "$TAG" + while true; do + $npm dist-tag rm "$pkg" "$TAG" && break || ret=$? + [[ "$style" =~ --otp-stream ]] || exit $ret + [ $ret -ne 66 ] && continue + echo Aborting + exit 1 + done ;; list | ls) # List either all dist-tags or just the specific $TAG. test "$#" -le 2 || fail "Too many arguments!" if test -n "$TAG"; then - if test -n "$dryrun"; then + if test "$style" = "--dry-run"; then # Print the entire pipeline. $npm dist-tag ls "$pkg" \| awk -vP="$TAG" -F: '$1==P' else