scripts/admin/publish-meteor-tool-on-arch.sh
#!/usr/bin/env bash

# This scripts automates the release process of Meteor Tool.

# Normally, after publishing a new Meteor release from checkout, you need to ssh
# to a machine running every supported platform and publish a build from it.
# This script automates ssh'ing into machines and running the publish command.

set -e
set -u

TEMP_PRIV_KEY=
TEMP_KEY=
SESSION_FILE=

main () {
  if [ $# -ne 3 ]; then
    echo "usage: $0 <git sha> <platform> <path to meteor session>" 1>&2
    echo "The passed sha1 is checked out and published from the machines." 1>&2
    echo "Options for platform:" 1>&2
    echo "  os.osx.x86_64 os.linux.x86_64 os.linux.x86_32 os.linux.aarch64" 1>&2
    echo "  os.windows.x86_32 os.windows.x86_64" 1>&2
    exit 1
  fi

  GITSHA=$1
  PLATFORM=$2
  SESSION_FILE=$3

  ADMIN_DIR="`dirname "$0"`"
  SCRIPTS_DIR="`dirname "$ADMIN_DIR"`"
  CHECKOUT_DIR="`dirname "$SCRIPTS_DIR"`"

  METEOR="$CHECKOUT_DIR/meteor"

  UNIX_PLATFORMS=( os.osx.x86_64 os.linux.x86_64 os.linux.x86_32 os.linux.aarch64 )
  WINDOWS_PLATFORMS=( os.windows.x86_32 os.windows.x86_64 )

  if [[ $PLATFORM =~ ^(os\.linux|os\.osx) ]] ; then
    echo "${green}Publishing from unixy platform.${NC}"

    parse_keys

    echo "${green}Going to ssh into machine running $PLATFORM and publish the release${NC}"
    trap 'echo "${red}Failed to publish from $PLATFORM${NC}"; clean_up' EXIT
    # copy the meteor session file to the remote host
    scp -oUserKnownHostsFile="$TEMP_KEY" -P "$PORT" -i "$TEMP_PRIV_KEY" -q "$SESSION_FILE" $USERNAME@$HOST:~/session

    METEOR_SESSION_FILE="$SESSION_FILE" "$METEOR" admin get-machine --minutes 30 $PLATFORM <<'END'
set -e
set -u
if [ -d meteor ]; then
  rm -rf meteor
fi
git clone --recursive https://github.com/meteor/meteor.git
cd meteor
git fetch --tags
END

    # checkout the SHA1 we want to publish
    echo "cd meteor; git checkout $GITSHA; git submodule update --init --recursive" | METEOR_SESSION_FILE="$SESSION_FILE" "$METEOR" admin get-machine "$PLATFORM"
    # publish the release
    echo "cd meteor/packages/meteor-tool && env METEOR_SESSION_FILE=~/session ../../meteor publish --existing-version" | METEOR_SESSION_FILE=$SESSION_FILE $METEOR admin get-machine $PLATFORM

    trap - EXIT
  else
    echo "${green}Publishing from Windowsy platform.${NC}"

    parse_keys

    echo "${green}Going to ssh into machine running $PLATFORM and publish the release${NC}"
    trap 'echo "${red}Failed to publish from $PLATFORM${NC}"; clean_up' EXIT

    # copy the meteor session file to the remote host
    SESSION_CONTENT=$(cat $SESSION_FILE | tr '\n' ' ')
    ssh $USERNAME@$HOST -oUserKnownHostsFile="$TEMP_KEY" -p "$PORT" -i "$TEMP_PRIV_KEY" "cmd /c echo $SESSION_CONTENT > C:\\meteor-session" 2>/dev/null

    # delete existing batch script if it exists
    ssh $USERNAME@$HOST -oUserKnownHostsFile="$TEMP_KEY" -p "$PORT" -i "$TEMP_PRIV_KEY" "cmd /c del C:\\publish-tool.bat || exit 0" 2>/dev/null

    # copy batch script to windows machine, in a funky way, by splicing each
    # line of publish-meteor-tool.bat into a Windows `echo` command, with no
    # escaping.
    # therefore, the lines of publish-meteor-tool.bat must already be
    # escaped, for example `>` as `^>` and `%` as `^%`.

    BAT_FILENAME="$ADMIN_DIR/publish-meteor-tool.bat"

    # we need to use file descriptor 10 because otherwise SSH will conflict with
    # the while loop
    while read -u10 -r line
    do
      line="${line/\$GITSHA/$GITSHA}"

      # skip empty lines and comments
      if [[ x$line == x ]] || [[ $line == "REM "* ]]; then
        continue
      fi

      echo $line
      ssh $USERNAME@$HOST -oUserKnownHostsFile="$TEMP_KEY" -p "$PORT" -i "$TEMP_PRIV_KEY" "cmd /c echo $line>> C:\\publish-tool.bat" 2>/dev/null
    done 10< "$BAT_FILENAME"

    ssh $USERNAME@$HOST -oUserKnownHostsFile=$TEMP_KEY -p $PORT -i $TEMP_PRIV_KEY "C:\\publish-tool.bat"

    trap - EXIT
  fi

  clean_up
}

# get keys from "meteor admin get-machine" command
# inputs: SESSION_FILE, METEOR, PLATFORM, CHECKOUT_DIR
# outputs: USERNAME, HOST, PORT, TEMP_KEY, TEMP_PRIV_KEY
parse_keys () {
  trap 'echo "${red}Failed to parse the machine credentials${NC}";clean_up' EXIT
  CREDS=$(METEOR_SESSION_FILE="$SESSION_FILE" "$METEOR" admin get-machine $PLATFORM --json)

  # save host key and login private key to temp files
  echo "$CREDS" | get_from_json "key" > "$CHECKOUT_DIR/temp_priv_key_$PLATFORM"
  TEMP_PRIV_KEY="$CHECKOUT_DIR/temp_priv_key_$PLATFORM"
  chmod 600 "$TEMP_PRIV_KEY"

  USERNAME=$(echo $CREDS | get_from_json "username")
  HOST=$(echo $CREDS | get_from_json "host")
  PORT=$(echo $CREDS | get_from_json "port")
  echo -n "$HOST " > "$CHECKOUT_DIR/temp_key_$PLATFORM"
  echo $CREDS | get_from_json "hostKey" >> "$CHECKOUT_DIR/temp_key_$PLATFORM"

  TEMP_KEY="$CHECKOUT_DIR/temp_key_$PLATFORM"

  trap - EXIT
}

# print a value from a JSON file by key
get_from_json () {
  "$CHECKOUT_DIR/dev_bundle/bin/node" -e "console.log(JSON.parse(require('fs').readFileSync('/dev/stdin').toString())['$1'])"
}

clean_up () {
  if [[ "x$TEMP_KEY" != x ]]; then
    echo "Removing remaining keys"
    rm "$TEMP_KEY"
  fi
  if [[ "x$TEMP_PRIV_KEY" != x ]]; then
    rm "$TEMP_PRIV_KEY"
  fi

  exit 1
}

# '|| true' so that we don't fail on terminals without colors
red=`tput setaf 1 || true`
green=`tput setaf 2 || true`
NC=`tput sgr0 || true`

main $@

