#!/bin/bash
#
# This script is used for stage 1 of the release process. It operates exclusively on the airlock.
# This script, for the RELEASEMANAGER (RM):
# - builds and uploads all binaries to airlock,
# - assumes all keys are available, and signs everything
# This script, for other builders:
# - builds all reproducible binaries,
# - downloads binaries built by the release manager (from airlock if SFTPUSER, else from website),
#   compares and signs them,
# - and then uploads sigs (if SFTPUSER), else they can be submitted as PR to spesmilo/electrum-signatures
# Note: the .dmg should be built separately beforehand and copied into dist/
#       (as it is built on a separate machine)
#
#
# env vars:
# - ELECBUILD_NOCACHE: if set, forces rebuild of docker images
#
# "uploadserver" is set in /etc/hosts
#
# Note: steps before doing a new release:
# - update locale:
#     1. cd /opt/electrum-locale && ./update.py && git push
#     2. cd to the submodule dir, and git pull
#     3. cd .. && git push
# - update RELEASE-NOTES and version.py
# - $ git tag -s "$VERSION" -m "$VERSION"
# - $ git push "$REMOTE_ORIGIN" tag "$VERSION"
#
# -----
# Then, typical release flow:
# - RM runs release.sh
# - Another SFTPUSER BUILDER runs `$ ./release.sh`
# - now airlock contains new binaries and two sigs for each
# - deploy.sh will verify sigs and move binaries across airlock
# - new binaries are now publicly available on uploadserver, but not linked from website yet
# - other BUILDERS can now also try to reproduce binaries and open PRs with sigs against spesmilo/electrum-signatures
#   - these PRs can get merged as they come
#   - run add_cosigner
# - after some time, RM can run release_www.sh to create and commit website-update
#   - then run WWW_DIR/publish.sh to update website
# - at least two people need to run WWW_DIR/publish.sh
#

set -e

PROJECT_ROOT="$(dirname "$(readlink -e "$0")")/.."
CONTRIB="$PROJECT_ROOT/contrib"

cd "$PROJECT_ROOT"

. "$CONTRIB"/build_tools_util.sh

# rm -rf dist/*
# rm -f .buildozer

GPGUSER=$1
if [ -z "$GPGUSER" ]; then
    fail "usage: $0 gpg_username"
fi

RELEASEMANAGER=""
if [ "$GPGUSER" == "ThomasV" ]; then
    PUBKEY="--local-user 6694D8DE7BE8EE5631BED9502BD5824B7F9470E6"
    export SSHUSER=thomasv
    RELEASEMANAGER=1
elif [ "$GPGUSER" == "sombernight_releasekey" ]; then
    PUBKEY="--local-user 0EEDCFD5CAFB459067349B23CA9EEEC43DF911DC"
    export SSHUSER=sombernight
else
    warn "unexpected GPGUSER=$GPGUSER"
    PUBKEY=""
    export SSHUSER=""
fi


if [ ! -z "$RELEASEMANAGER" ] ; then
    echo -n "Code signing passphrase:"
    read -s password
    # tests password against keystore
    keytool -list -storepass $password
    # the same password is used for windows signing
    export WIN_SIGNING_PASSWORD=$password
fi


VERSION=$("$CONTRIB"/print_electrum_version.py)
info "VERSION: $VERSION"
REV=$(git describe --tags)
info "REV: $REV"
COMMIT=$(git rev-parse HEAD)

export ELECBUILD_COMMIT="${COMMIT}^{commit}"


git_status=$(git status --porcelain)
if [ ! -z "$git_status" ]; then
    echo "$git_status"
    fail "git repo not clean, aborting"
fi

set -x

# create tarball
tarball="Electrum-$VERSION.tar.gz"
if test -f "dist/$tarball"; then
    info "file exists: $tarball"
else
    ./contrib/build-linux/sdist/build.sh
fi

# create source-only tarball
srctarball="Electrum-sourceonly-$VERSION.tar.gz"
if test -f "dist/$srctarball"; then
    info "file exists: $srctarball"
else
    OMIT_UNCLEAN_FILES=1 ./contrib/build-linux/sdist/build.sh
fi

# appimage
appimage="electrum-$REV-x86_64.AppImage"
if test -f "dist/$appimage"; then
    info "file exists: $appimage"
else
    ./contrib/build-linux/appimage/build.sh
fi


# windows
win1="electrum-$REV.exe"
win2="electrum-$REV-portable.exe"
win3="electrum-$REV-setup.exe"
if test -f "dist/$win1"; then
    info "file exists: $win1"
else
    pushd .
    if test -f "contrib/build-wine/dist/$win1"; then
        info "unsigned file exists: $win1"
    else
        ./contrib/build-wine/build.sh
    fi
    cd contrib/build-wine/
    if [ ! -z "$RELEASEMANAGER" ] ; then
        ./sign.sh
        cp ./signed/*.exe "$PROJECT_ROOT/dist/"
    else
        cp ./dist/*.exe "$PROJECT_ROOT/dist/"
    fi
    popd
fi

# android
apk1="Electrum-$VERSION-armeabi-v7a-release.apk"
apk2="Electrum-$VERSION-arm64-v8a-release.apk"
apk3="Electrum-$VERSION-x86_64-release.apk"
for arch in armeabi-v7a arm64-v8a x86_64
do
    apk="Electrum-$VERSION-$arch-release.apk"
    apk_unsigned="Electrum-$VERSION-$arch-release-unsigned.apk"
    if test -f "dist/$apk"; then
        info "file exists: $apk"
    else
        info "file does not exists: $apk"
        if [ ! -z "$RELEASEMANAGER" ] ; then
            ./contrib/android/build.sh qml $arch release $password
        else
            if test -f "dist/$apk_unsigned"; then
                # has already been built separately before
                info "found unsigned: $apk_unsigned"
            else
                ./contrib/android/build.sh qml $arch release-unsigned
            fi
            mv "dist/$apk_unsigned" "dist/$apk"
        fi
    fi
done

# the macos binary is built on a separate machine.
# the file that needs to be copied over is the codesigned release binary (regardless of builder role)
dmg="electrum-$VERSION.dmg"
if ! test -f "dist/$dmg"; then
    if [ ! -z "$RELEASEMANAGER" ] ; then  # RM
        fail "dmg is missing, aborting. Please build and codesign the dmg on a mac and copy it over."
    else  # other builders
        fail "dmg is missing, aborting. Please build the unsigned dmg on a mac, compare it with file built by RM, and if matches, copy RM's dmg."
    fi
fi

# now that we have all binaries, if we are the RM, sign them.
if [ ! -z "$RELEASEMANAGER" ] ; then
    if test -f "dist/$dmg.asc"; then
        info "packages are already signed"
    else
        info "signing packages"
        ./contrib/sign_packages "$GPGUSER"
    fi
fi

info "build complete"
sha256sum dist/*.tar.gz
sha256sum dist/*.AppImage
sha256sum contrib/build-wine/dist/*.exe

echo -n "proceed (y/n)? "
read answer

if [ "$answer" != "y" ]; then
    echo "exit"
    exit 1
fi


if [ -z "$RELEASEMANAGER" ] ; then
    # people OTHER THAN release manager.
    # download binaries built by RM
    rm -rf "$PROJECT_ROOT/dist/releasemanager"
    mkdir --parent "$PROJECT_ROOT/dist/releasemanager"
    cd "$PROJECT_ROOT/dist/releasemanager"

    if [ -z "$SSHUSER" ]; then
        info "No SFTP access, downloading binaries from website"
        BASE_URL="https://download.electrum.org/$VERSION"
        FILES_TO_DOWNLOAD=(
            "$tarball"
            "$srctarball"
            "$appimage"
            "$win1"
            "$win2"
            "$win3"
            "$apk1"
            "$apk2"
            "$apk3"
            "$dmg"
        )

        for filename in "${FILES_TO_DOWNLOAD[@]}"; do
            if [ ! -f "$filename" ]; then
                info "Downloading $filename..."
                wget -q "$BASE_URL/$filename" -O "$filename" || fail "Failed to download $filename"
            else
                info "File already exists: $filename"
            fi
        done
    else
        # TODO check somehow that RM had finished uploading
        sftp -oBatchMode=no -b - "$SSHUSER@uploadserver" <<-EOF
           cd electrum-downloads-airlock
           cd "$VERSION"
           mget *
           bye
EOF
    fi

    # check we have each binary
    test -f "$tarball"    || fail "tarball not found among sftp downloads"
    test -f "$srctarball" || fail "srctarball not found among sftp downloads"
    test -f "$appimage"   || fail "appimage not found among sftp downloads"
    test -f "$win1"       || fail "win1 not found among sftp downloads"
    test -f "$win2"       || fail "win2 not found among sftp downloads"
    test -f "$win3"       || fail "win3 not found among sftp downloads"
    test -f "$apk1"       || fail "apk1 not found among sftp downloads"
    test -f "$apk2"       || fail "apk2 not found among sftp downloads"
    test -f "$apk3"       || fail "apk3 not found among sftp downloads"
    test -f "$dmg"        || fail "dmg not found among sftp downloads"
    test -f "$PROJECT_ROOT/dist/$tarball"    || fail "tarball not found among built files"
    test -f "$PROJECT_ROOT/dist/$srctarball" || fail "srctarball not found among built files"
    test -f "$PROJECT_ROOT/dist/$appimage"   || fail "appimage not found among built files"
    test -f "$CONTRIB/build-wine/dist/$win1" || fail "win1 not found among built files"
    test -f "$CONTRIB/build-wine/dist/$win2" || fail "win2 not found among built files"
    test -f "$CONTRIB/build-wine/dist/$win3" || fail "win3 not found among built files"
    test -f "$PROJECT_ROOT/dist/$apk1"       || fail "apk1 not found among built files"
    test -f "$PROJECT_ROOT/dist/$apk2"       || fail "apk2 not found among built files"
    test -f "$PROJECT_ROOT/dist/$apk3"       || fail "apk3 not found among built files"
    test -f "$PROJECT_ROOT/dist/$dmg"        || fail "dmg not found among built files"
    # compare downloaded binaries against ones we built
    cmp --silent "$tarball"    "$PROJECT_ROOT/dist/$tarball"    || fail "files are different. tarball."
    cmp --silent "$srctarball" "$PROJECT_ROOT/dist/$srctarball" || fail "files are different. srctarball."
    cmp --silent "$appimage"   "$PROJECT_ROOT/dist/$appimage"   || fail "files are different. appimage."
    rm -rf "$CONTRIB/build-wine/signed/" && mkdir --parents "$CONTRIB/build-wine/signed/"
    cp -f "$win1" "$win2" "$win3" "$CONTRIB/build-wine/signed/"
    "$CONTRIB/build-wine/unsign.sh" || fail "files are different. windows."
    "$CONTRIB/android/apkdiff.py" "$apk1" "$PROJECT_ROOT/dist/$apk1" || fail "files are different. android."
    "$CONTRIB/android/apkdiff.py" "$apk2" "$PROJECT_ROOT/dist/$apk2" || fail "files are different. android."
    "$CONTRIB/android/apkdiff.py" "$apk3" "$PROJECT_ROOT/dist/$apk3" || fail "files are different. android."
    cmp --silent "$dmg" "$PROJECT_ROOT/dist/$dmg" || fail "files are different. macos."
    # all files matched. sign them.
    rm -rf "$PROJECT_ROOT/dist/sigs/"
    mkdir --parents "$PROJECT_ROOT/dist/sigs/"
    for fname in "$tarball" "$srctarball" "$appimage" "$win1" "$win2" "$win3" "$apk1" "$apk2" "$apk3" "$dmg" ; do
        signame="$fname.$GPGUSER.asc"
        gpg --sign --armor --detach $PUBKEY --output "$PROJECT_ROOT/dist/sigs/$signame" "$fname"
    done

    if [ -z "$SSHUSER" ]; then
        info "Signing successfully, now open a pull request with your signatures to spesmilo/electrum-signatures"
        exit 0
    else
        # upload sigs
        ELECBUILD_UPLOADFROM="$PROJECT_ROOT/dist/sigs/" "$CONTRIB/upload.sh"
    fi

else
    # ONLY release manager

    cd "$PROJECT_ROOT"

    # check we have each binary
    test -f "$PROJECT_ROOT/dist/$tarball"    || fail "tarball not found among built files"
    test -f "$PROJECT_ROOT/dist/$srctarball" || fail "srctarball not found among built files"
    test -f "$PROJECT_ROOT/dist/$appimage"   || fail "appimage not found among built files"
    test -f "$PROJECT_ROOT/dist/$win1"       || fail "win1 not found among built files"
    test -f "$PROJECT_ROOT/dist/$win2"       || fail "win2 not found among built files"
    test -f "$PROJECT_ROOT/dist/$win3"       || fail "win3 not found among built files"
    test -f "$PROJECT_ROOT/dist/$apk1"       || fail "apk1 not found among built files"
    test -f "$PROJECT_ROOT/dist/$apk2"       || fail "apk2 not found among built files"
    test -f "$PROJECT_ROOT/dist/$apk3"       || fail "apk3 not found among built files"
    test -f "$PROJECT_ROOT/dist/$dmg"        || fail "dmg not found among built files"

    if [ "$REV" != "$VERSION" ]; then
        fail "versions differ, not uploading"
    fi

    # upload the files
    ./contrib/upload.sh

fi

set +x

info "release.sh finished successfully."
info "After two people ran release.sh, the binaries will be publicly available on uploadserver."
info "Then, we wait for additional signers, and run add_cosigner for them."
info "Finally, release_www.sh needs to be run, for the website to be updated."
