unstructured/scripts/version-sync.sh
Klaijan 30b6a09bc3
fix: declare -i [SC2324 shellcheck] (#2624)
Fix SC2324 shellcheck warning by adding -i to indicate var type of
integer and tidy up the formatting.
2024-03-08 10:09:55 +00:00

172 lines
6.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -u
function usage {
echo "Usage: $(basename "$0") [-c] -f FILE_TO_CHANGE REPLACEMENT_FORMAT [-f FILE_TO_CHANGE REPLACEMENT_FORMAT ...]" 2>&1
echo 'Synchronize files to latest version in source file'
echo ' -s Specifies source file for version (default is CHANGELOG.md)'
echo ' -f Specifies a file to change and the format for searching and replacing versions'
echo ' FILE_TO_CHANGE is the file to be updated/checked for updates'
echo ' REPLACEMENT_FORMAT is one of (semver, release, api-release)'
echo ' semver indicates to look for a full semver version and replace with the latest full version'
echo ' release indicates to look for a release semver version (x.x.x) and replace with the latest release version'
echo ' api-release indicates to look for a release semver version in the context of an api route and replace with the latest release version'
echo ' -c Compare versions and output proposed changes without changing anything.'
}
function getopts-extra() {
declare -i i=1
# if the next argument is not an option, then append it to array OPTARG
while [[ ${OPTIND} -le $# && ${!OPTIND:0:1} != '-' ]]; do
OPTARG[i]=${!OPTIND}
((i += 1))
((OPTIND += 1))
done
}
# Parse input options
declare CHECK=0
declare SOURCE_FILE="CHANGELOG.md"
declare -a FILES_TO_CHECK=()
declare -a REPLACEMENT_FORMATS=()
declare args
declare OPTIND OPTARG opt
while getopts ":hcs:f:" opt; do
case $opt in
h)
usage
exit 0
;;
c)
CHECK=1
;;
s)
SOURCE_FILE="$OPTARG"
;;
f)
getopts-extra "$@"
args=("${OPTARG[@]}")
# validate length of args, should be 2
if [ ${#args[@]} -eq 2 ]; then
FILES_TO_CHECK+=("${args[0]}")
REPLACEMENT_FORMATS+=("${args[1]}")
else
echo "Exactly 2 arguments must follow -f option." >&2
exit 1
fi
;;
\?)
echo "Invalid option: -$OPTARG." >&2
usage
exit 1
;;
esac
done
# Parse REPLACEMENT_FORMATS
RE_SEMVER_FULL="(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?"
RE_RELEASE="(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)"
RE_API_RELEASE="v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)"
# Pull out semver appearing earliest in SOURCE_FILE.
LAST_VERSION=$(grep -o -m 1 -E "${RE_SEMVER_FULL}" "$SOURCE_FILE")
LAST_RELEASE=$(grep -o -m 1 -E "${RE_RELEASE}($|[^-+])" "$SOURCE_FILE" | grep -o -m 1 -E "${RE_RELEASE}")
LAST_API_RELEASE="v$(grep -o -m 1 -E "${RE_RELEASE}($|[^-+])$" "$SOURCE_FILE" | grep -o -m 1 -E "${RE_RELEASE}")"
declare -a RE_SEMVERS=()
declare -a UPDATED_VERSIONS=()
for i in "${!REPLACEMENT_FORMATS[@]}"; do
REPLACEMENT_FORMAT=${REPLACEMENT_FORMATS[$i]}
case $REPLACEMENT_FORMAT in
semver)
RE_SEMVERS+=("$RE_SEMVER_FULL")
UPDATED_VERSIONS+=("$LAST_VERSION")
;;
release)
RE_SEMVERS+=("$RE_RELEASE")
UPDATED_VERSIONS+=("$LAST_RELEASE")
;;
api-release)
RE_SEMVERS+=("$RE_API_RELEASE")
UPDATED_VERSIONS+=("$LAST_API_RELEASE")
;;
*)
echo "Invalid replacement format: \"${REPLACEMENT_FORMAT}\". Use semver, release, or api-release" >&2
exit 1
;;
esac
done
if [ -z "$LAST_VERSION" ]; then
# No match to semver regex in SOURCE_FILE, so no version to go from.
printf "Error: Unable to find latest version from %s.\n" "$SOURCE_FILE"
exit 1
fi
# Search files in FILES_TO_CHECK and change (or get diffs)
declare FAILED_CHECK=0
git fetch origin main
MAIN_VERSION=$(git show origin/main:unstructured/__version__.py | grep -o -m 1 -E "${RE_SEMVER_FULL}")
MAIN_IS_RELEASE=false
[[ $MAIN_VERSION != *"-dev"* ]] && MAIN_IS_RELEASE=true
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
for i in "${!FILES_TO_CHECK[@]}"; do
FILE_TO_CHANGE=${FILES_TO_CHECK[$i]}
RE_SEMVER=${RE_SEMVERS[$i]}
UPDATED_VERSION=${UPDATED_VERSIONS[$i]}
FILE_VERSION=$(grep -o -m 1 -E "${RE_SEMVER}" "$FILE_TO_CHANGE")
if [ -z "$FILE_VERSION" ]; then
# No match to semver regex in VERSIONFILE, so nothing to replace
printf "Error: No semver version found in file %s.\n" "$FILE_TO_CHANGE"
exit 1
else
if [[ "$MAIN_IS_RELEASE" == true && "$UPDATED_VERSION" == "$MAIN_VERSION" && "$CURRENT_BRANCH" != "main" ]]; then
# Only one commit should be associated with a particular non-dev version
if [[ "$CHECK" == 1 ]]; then
printf "Error: there is already a commit associated with version %s.\n" "$MAIN_VERSION"
exit 1
else
printf "Warning: there is already a commit associated with version %s.\n" "$MAIN_VERSION"
fi
fi
# Replace semver in VERSIONFILE with semver obtained from SOURCE_FILE
TMPFILE=$(mktemp /tmp/new_version.XXXXXX)
# Check sed version, exit if version < 4.3
if ! sed --version >/dev/null 2>&1; then
CURRENT_VERSION=1.archaic
else
CURRENT_VERSION=$(sed --version | head -n1 | cut -d" " -f4)
fi
REQUIRED_VERSION="4.3"
if [ "$(printf '%s\n' "$REQUIRED_VERSION" "$CURRENT_VERSION" | sort -V | head -n1)" != "$REQUIRED_VERSION" ]; then
echo "sed version must be >= ${REQUIRED_VERSION}" && exit 1
fi
sed -E -r "s/$RE_SEMVER/$UPDATED_VERSION/" "$FILE_TO_CHANGE" >"$TMPFILE"
if [ $CHECK == 1 ]; then
DIFF=$(diff "$FILE_TO_CHANGE" "$TMPFILE")
if [ -z "$DIFF" ]; then
printf "version sync would make no changes to %s.\n" "$FILE_TO_CHANGE"
rm "$TMPFILE"
else
FAILED_CHECK=1
printf "version sync would make the following changes to %s:\n%s\n" "$FILE_TO_CHANGE" "$DIFF"
rm "$TMPFILE"
fi
else
cp "$TMPFILE" "$FILE_TO_CHANGE"
rm "$TMPFILE"
fi
fi
done
# Exit with code determined by whether changes were needed in a check.
if [ ${FAILED_CHECK} -ne 0 ]; then
printf "\nVersions are out of sync! See above for diffs.\n"
exit 1
else
exit 0
fi