#!/bin/bash
# Creates a snapshot based on upstream proposal repositories.
# Derived from update-testsuite.sh in https://github.com/WebAssembly/testsuite
set -e
set -u
set -o pipefail

ignore_dirs=''

# TODO: Add the rest.
repos='
  WASI
  wasi-filesystem
  wasi-clocks
  wasi-random
'

log_and_run() {
    echo ">>" $*
    if ! $*; then
        echo "sub-command failed: $*"
        exit
    fi
}

try_log_and_run() {
    echo ">>" $*
    $*
}

pushdir() {
    pushd $1 >/dev/null || exit
}

popdir() {
    popd >/dev/null || exit
}

update_repo() {
    local repo=$1
    pushdir repos
        if [ -d ${repo} ]; then
            log_and_run git -C ${repo} fetch origin
            log_and_run git -C ${repo} reset origin/main --hard
        else
            log_and_run git clone https://github.com/WebAssembly/${repo}
        fi

        # Add upstream WASI as "WASI" remote.
        if [ "${repo}" != "WASI" ]; then
            pushdir ${repo}
                if ! git remote | grep WASI >/dev/null; then
                    log_and_run git remote add WASI https://github.com/WebAssembly/WASI
                fi

                log_and_run git fetch WASI
            popdir
        fi
    popdir
}

merge_with_WASI() {
    local repo=$1

    [ "${repo}" == "WASI" ] && return

    pushdir repos/${repo}
        # Create and checkout "try-merge" branch.
        if ! git branch | grep try-merge >/dev/null; then
            log_and_run git branch try-merge origin/main
        fi
        log_and_run git checkout try-merge

        # Attempt to merge with WASI/main.
        log_and_run git reset origin/main --hard
        try_log_and_run git merge -q WASI/main -m "merged"
        if [ $? -ne 0 ]; then
            # Ignore merge conflicts in non-test directories.
            # We don't care about those changes.
            try_log_and_run git checkout --ours ${ignore_dirs}
            try_log_and_run git add ${ignore_dirs}
            try_log_and_run git -c core.editor=true merge --continue
            if [ $? -ne 0 ]; then
                git merge --abort
                popdir
                return 1
            fi
        fi
    popdir
    return 0
}

snapshot_name=$(date --iso-8601)
snapshot_dir=snapshots/$snapshot_name
mkdir -p $snapshot_dir

commit_message_file=$PWD/commit_message
echo -e "Update repos\n" > $commit_message_file

failed_repos=

for repo in ${repos}; do
    echo "++ updating ${repo}"
    update_repo ${repo}

    if ! merge_with_WASI ${repo}; then
        echo -e "!! error merging ${repo}, skipping\n"
        failed_repos="${failed_repos} ${repo}"
        continue
    fi

    if [ "${repo}" = "WASI" ]; then
        dest_dir=$snapshot_dir
        log_and_run cp -r repos/${repo}/standard ${dest_dir}
    else
        dest_dir=$snapshot_dir/proposals/${repo}
        mkdir -p ${dest_dir}

        # Don't add tests from proposal that are the same as WASI.
        pushdir repos/${repo}
            for new in $(find standard -type f); do
                old=../../repos/WASI/${new}
                if [[ ! -f ${old} ]] || ! diff ${old} ${new} >/dev/null; then
                    log_and_run cp ${new} ../../${dest_dir}
                fi
            done
        popdir
    fi

    # Check whether any files were removed.
    for old in $(find ${dest_dir} -type f); do
      new=$(find repos/${repo}/standard -name ${old##*/})
      if [[ ! -f ${new} ]]; then
          log_and_run git rm ${old}
      fi
    done

    # Check whether any files were updated.
    if [ $(git status -s ${dest_dir} | wc -l) -ne 0 ]; then
        log_and_run git add ${dest_dir}/*

        repo_sha=$(git -C repos/${repo} log --max-count=1 --oneline origin/main| sed -e 's/ .*//')
        echo "  ${repo}:" >> $commit_message_file
        echo "    https://github.com/WebAssembly/${repo}/commit/${repo_sha}" >> $commit_message_file
    fi

    echo -e "-- ${repo}\n"
done

echo "" >> $commit_message_file
echo "This change was automatically generated by \`make-snapshot.sh\`" >> $commit_message_file
git commit -a -F $commit_message_file
# git push

echo "done"

if [ -n "${failed_repos}" ]; then
  echo "!! failed to update repos: ${failed_repos}"
fi
