#!/usr/bin/env bash
#
# Ensures repo is ready to release.
#
# Usage: script/preversion [-v] [--] [FILES...]
#
# Options:
#   -o     Detect unreleased changes in (and only in) FILES
#   -v     Print log since last tag
#   FILES  Files to check for changes.
#          [default: package.json#files read via $npm_package_files_*]
#
# - fetch from origin
# - ensure it isn't already tagged
# - ensure currently on main branch
# - ensure there are bin or definition changes to release

set -euo pipefail

[ -n "${DEBUG-}" ] && set -x

unset verbose strict
while getopts "ov" opt; do
  case "$opt" in
  o) strict=1 ;;
  v) verbose=1 ;;
  *) break ;;
  esac
done
shift $((OPTIND - 1))

if [ "${1-}" = -- ]; then
  shift
fi

abort() {
  echo "Aborting: $1" >&2
  exit "${2:-1}"
}

declare -a files
if [ "$#" -gt 0 ]; then
  files=("$@")
else
  IFS=" " read -r -a files <<<"$(node -p 'require("./package").files.join(" ")')"
fi

git fetch --quiet --tags origin main

existing="$(git tag --points-at HEAD)"
if [ -n "$existing" ]; then
  abort "HEAD is already tagged as '${existing}'"
fi

current_branch="$(git symbolic-ref --short HEAD)"
if [ "$current_branch" != main ]; then
  abort "Not currently on main branch" 2
fi

previous_tag="$(git describe --tags --abbrev=0)"
if git diff --quiet "${previous_tag}..HEAD" -- "${files[@]}"; then
  abort "No features to release since '${previous_tag}'"
fi

allowed_changes=("${files[@]}" .github script *.md)
allowed_changes=("${allowed_changes[@]/#/\':!\'}") # prefix pathspecs with git's "ignore"

if [ -n "${strict-}" ] &&
  ! git diff --quiet "${previous_tag}..HEAD" -- "${allowed_changes[@]}"; then
  {
    echo "git diff --stat ${previous_tag}..HEAD -- ${allowed_changes[*]}"
    git diff --stat "${previous_tag}..HEAD" -- "${allowed_changes[@]}"
  } >&2
  abort "Changes detected outside '${allowed_changes[*]#:!}'" 2
fi

if [ -n "${verbose-}" ]; then
  git log "$previous_tag"... --oneline -- "${files[@]}"
fi
