#!/usr/bin/env bash
# * bake
# ** Usage

set -e

################################################################################
usage() {
    cat <<EOF
bake - misc project bash scripts, mainly releasing related. Cf Shake.hs, make
Commands:
./bake              - show this help
./bake relfiles     - symlink important files temporarily in .relfiles/
./bake prep VERSION - prepare this release on today's date
./bake bin          - push the current branch to CI to generate binaries

prep does the following:
- autocreates and switches to the appropriate release branch
- updates all hledger package versions
- updates all built-in docs
- updates all changelogs
VERSION is major (1.25), minor (1.25.1), fixup (1.25.1.1) or preview (1.25.99.1)
(see RELEASING.md).

Setting PAUSE=1, ECHO=1, and/or DRY=1 will cause commands to 
be run one at a time, logged, or logged without running.
EOF
    exit
}

# ** hledger version numbers

# First 0-2 parts of a dotted version number.
versionMajorPart() {
  echo "$1" | sed -E 's/([[:digit:]]+(\.[[:digit:]]+)?).*/\1/'  # seriously...
}

# Third part of a dotted version number, if any.
versionMinorPart() {
  echo "$1" | sed -E -e 's/^[[:digit:]]+(\.[[:digit:]]+(\.)?)?//' -e 's/^([[:digit:]]+).*/\1/'
}

# Fourth part of a dotted version number, if any.
versionFourthPart() {
  echo "$1" | sed -E -e 's/^[[:digit:]]+(\.[[:digit:]]+(\.[[:digit:]]+(\.)?))//' -e 's/^([[:digit:]]+).*/\1/'
}

# Does this dotted version number have a .99 third part and no fourth part ?
versionIsDev() {
  V="$1"
  test "$(versionMinorPart "$V")" = 99 -a -z "$(versionFourthPart "$V")"
}

# Does this dotted version number have a .99 third part and a fourth part ?
versionIsPreview() {
  V="$1"
  test "$(versionMinorPart "$V")" = 99 -a -n "$(versionFourthPart "$V")"
}

# Increment a major version number to the next.
majorVersionIncrement() {
  python3 -c "print($1 + 0.01)"
}

# Appropriate release branch name for the given version number.
versionReleaseBranch() {
  V="$1"
  MAJOR=$(versionMajorPart "$V")
  if versionIsDev "$V"; then
    echo "$V is not a releasable version" >&2
    exit 1
  elif versionIsPreview "$V"; then
    echo "$(majorVersionIncrement "$MAJOR")-branch"
  else
    echo "$MAJOR-branch"
  fi
}

# ** git

# Does the named branch exist in this git repo ?
gitBranchExists() {
  B="$1"
  git branch -l "$B" | grep -q "$B"
}

# Switch to the named git branch, creating it if it doesn't exist.
gitSwitchAutoCreate() {
  B="$1"
  if gitBranchExists "$B"; then
    git switch "$B"
  else
    git switch -c "$B"
  fi
}

# ** main

# Run a command with optional logging ($ECHO), dry-running ($DRY) and pausing ($PAUSE).
run() {
    if [[ -n $PAUSE ]]; then read -rp "pausing, next is: $*"
    elif [[ -n $ECHO || -n $DRY ]]; then echo "$@"
    fi
    if [[ -z $DRY ]]; then "$@"; fi
}

# Symlink important files temporarily in .relfiles/.
relfiles() {
  echo "linking important release files in .relfiles/ for convenient access..."
  mkdir -p .relfiles
  cd .relfiles
  for f in \
      ../stack.yaml \
      ../Shake.hs \
      ../CHANGELOGS.md \
      ../RELEASING.md \
      ../CHANGES.md \
      ../hledger/CHANGES.md \
      ../hledger-ui/CHANGES.md \
      ../hledger-web/CHANGES.md \
      ../hledger-lib/CHANGES.md \
      ../site/src/release-notes.md \
      ../site/src/install.md \
      ../doc/ANNOUNCE \
  ; do ln -sf $f .; done
}

# Create/switch to appropriate release branch and prepare for release.
prep() {
  VERSION="$1"
  [[ -z "$VERSION" ]] && usage
  BRANCH=$(versionReleaseBranch "$VERSION")
  COMMIT="-c"
  echo "Switching to $BRANCH, auto-creating it if needed..."
  run gitSwitchAutoCreate "$BRANCH"
  echo "Bumping all version strings to $VERSION ..."
  run ./Shake setversion "$VERSION" $COMMIT
  echo "Updating all command help texts for embedding..."
  run ./Shake cmdhelp $COMMIT
  echo "Updating all dates in man pages..."
  run ./Shake mandates
  echo "Generating all the manuals in all formats...."
  run ./Shake manuals $COMMIT
  echo "Updating CHANGES.md files with latest commits..."
  run ./Shake changelogs $COMMIT
}

# Push the current branch to the CI branches that generate platform binaries.
# Assumes the github remote is named "github".
bin() {
  run git push -f github HEAD:binaries
}

if declare -f "$1" > /dev/null; then "$@"; else usage; fi

# ** notes
# *** rerunning 
# **** creates empty doc update commits
# **** creates duplicate changelog headings
# ***** CHANGES.md's version "1.24.99.1" is not yet tagged, can't list changes
