#!/bin/sh

# ◦ `git-push-to-s3.sh` ◦
# 
# ❝another fine script❞ by [Nate Gehringer](mailto:nate@backwater.systems)
# <https://code.backwater.systems/#scripts>

# define constants …
# … characters
CHARACTERS__QUOTATION_MARK='\x22'
# … terminal control sequences
TERMINAL_CONTROL_SEQUENCES__RESET='\x1b[0m'
TERMINAL_CONTROL_SEQUENCES__BRIGHT='\x1b[1m'
TERMINAL_CONTROL_SEQUENCES__DIM='\x1b[2m'
TERMINAL_CONTROL_SEQUENCES__FOREGROUND_RED='\x1b[31m'
TERMINAL_CONTROL_SEQUENCES__FOREGROUND_GREEN='\x1b[32m'
TERMINAL_CONTROL_SEQUENCES__FOREGROUND_YELLOW='\x1b[33m'

echo "${TERMINAL_CONTROL_SEQUENCES__DIM}→${TERMINAL_CONTROL_SEQUENCES__RESET} ${TERMINAL_CONTROL_SEQUENCES__FOREGROUND_YELLOW}@backwater-systems${TERMINAL_CONTROL_SEQUENCES__RESET}/${TERMINAL_CONTROL_SEQUENCES__FOREGROUND_GREEN}git-push-to-s3.sh${TERMINAL_CONTROL_SEQUENCES__RESET}"

echo "${TERMINAL_CONTROL_SEQUENCES__DIM}[1/6]${TERMINAL_CONTROL_SEQUENCES__RESET} Configure"
echo '… via the environment variables:'
echo '	- S3_BUCKET_NAME'
echo '	- S3_ENDPOINT_URL'
echo '	- S3_PATH'

# determine the operating system environment
macos_environment=$([ $(uname -s) = 'Darwin' ] && echo '1' || echo '0')

echo "Environment: ${TERMINAL_CONTROL_SEQUENCES__BRIGHT}$([ $macos_environment -eq 1 ] && echo 'macOS' || echo 'Linux, et al.')${TERMINAL_CONTROL_SEQUENCES__RESET}"

# define the path of the Git repository
git_repository_path=$(git rev-parse --show-toplevel)

# abort if the current folder is not within a Git repository
if [ $? -ne 0 ]
then
	echo "${TERMINAL_CONTROL_SEQUENCES__FOREGROUND_RED}[ERROR]${TERMINAL_CONTROL_SEQUENCES__RESET} The current folder is not within a Git repository."
	exit 1
fi

# define the Git repository name
git_repository_name=$(basename "${git_repository_path}")

echo "Git repository: ${TERMINAL_CONTROL_SEQUENCES__BRIGHT}${git_repository_name}${TERMINAL_CONTROL_SEQUENCES__RESET} (${TERMINAL_CONTROL_SEQUENCES__FOREGROUND_YELLOW}${git_repository_path}${TERMINAL_CONTROL_SEQUENCES__RESET})"

# define the Git bare repository name
git_bare_repository_name="${git_repository_name}.git"

# define the path for the temporary Git bare repository
git_bare_repository_path="$(dirname "${git_repository_path}")/${git_bare_repository_name}/"

# abort if the temporary folder for the Git bare repository already exists
if [ -d "${git_bare_repository_path}" ]
then
	echo "${TERMINAL_CONTROL_SEQUENCES__FOREGROUND_RED}[ERROR]${TERMINAL_CONTROL_SEQUENCES__RESET} The temporary folder for the Git bare repository (${git_bare_repository_path}) already exists."
	exit 2
fi

# attempt to define the S3 bucket name from the environment
s3_bucket_name="${S3_BUCKET_NAME}"

# prompt for the S3 bucket name if `S3_BUCKET_NAME` is undefined
s3_bucket_name_prompt='S3 bucket name: '
if [ "${s3_bucket_name}" = '' ]
then
	printf "${s3_bucket_name_prompt}"
	read s3_bucket_name
else
	echo "${s3_bucket_name_prompt}${TERMINAL_CONTROL_SEQUENCES__BRIGHT}${s3_bucket_name}${TERMINAL_CONTROL_SEQUENCES__RESET}"
fi

# abort if the specified S3 bucket name is invalid
if [ "${s3_bucket_name}" = '' ]
then
	echo "${TERMINAL_CONTROL_SEQUENCES__FOREGROUND_RED}[ERROR]${TERMINAL_CONTROL_SEQUENCES__RESET} The specified \`S3 bucket name\` is invalid."
	exit 3
fi

# attempt to define the S3 endpoint URL from the environment
s3_endpoint_url="${S3_ENDPOINT_URL}"

# prompt for the S3 endpoint URL if `S3_ENDPOINT_URL` is undefined
s3_endpoint_url_prompt='S3 endpoint URL: '
if [ "${s3_endpoint_url}" = '' ]
then
	printf "${s3_endpoint_url_prompt}"
	read s3_endpoint_url
else
	echo "${s3_endpoint_url_prompt}${TERMINAL_CONTROL_SEQUENCES__BRIGHT}${s3_endpoint_url}${TERMINAL_CONTROL_SEQUENCES__RESET}"
fi

# use the default S3 endpoint URL if a value is not specified
if [ "${s3_endpoint_url}" = '' ]
then
	s3_endpoint_url='https://s3.auto.amazonaws.com/'

	echo "${TERMINAL_CONTROL_SEQUENCES__FOREGROUND_YELLOW}[WARNING]${TERMINAL_CONTROL_SEQUENCES__RESET} \`S3 endpoint URL\` is required; using the default: \`${s3_endpoint_url}\`."
fi

# normalize the S3 endpoint URL by adding a trailing '/', if not present
echo "${s3_endpoint_url}" | grep "/$" > /dev/null 2>&1
if [ $? -ne 0 ]
then
	s3_endpoint_url="${s3_endpoint_url}/"
fi

# attempt to define the S3 path from the environment
s3_path="${S3_PATH}"

# prompt for the S3 path if `S3_PATH` is undefined
s3_path_prompt='S3 path: '
if [ "${s3_path}" = '' ]
then
	printf "${s3_path_prompt}"
	read s3_path
else
	echo "${s3_path_prompt}${TERMINAL_CONTROL_SEQUENCES__BRIGHT}${s3_path}${TERMINAL_CONTROL_SEQUENCES__RESET}"
fi

# normalize the S3 path by removing the trailing '/', if present
echo "${s3_path}" | grep "/$" > /dev/null 2>&1
if [ $? -eq 0 ]
then
	s3_path=$(echo "${s3_path}" | sed 's/\/$//')
fi

echo "${TERMINAL_CONTROL_SEQUENCES__DIM}[2/6]${TERMINAL_CONTROL_SEQUENCES__RESET} Prepare"

# install and configure the AWS Command Line Interface, if necessary
# <https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-mac.html#cliv2-mac-install-cmd-all-users>
aws --version > /dev/null 2>&1
if [ $? -ne 0 ]
then
	printf $'The AWS CLI is required and does not seem to be installed. Press [^C] to cancel, or [Return] to download, install, and configure the AWS CLI (requires `sudo` privilege).\n'
	stty -echo
	read
	stty echo

	if [ $macos_environment -eq 1 ]
	then
		aws_cli_url='https://awscli.amazonaws.com/AWSCLIV2.pkg'
		aws_cli_file_name='AWSCLIV2.pkg'
	else
		aws_cli_url='https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip'
		aws_cli_file_name='awscliv2.zip'
	fi

	# download the AWS CLI
	curl "${aws_cli_url}" -o "${aws_cli_file_name}"

	# abort if downloading the AWS CLI failed
	if [ $? -ne 0 ]
	then
		echo "${TERMINAL_CONTROL_SEQUENCES__FOREGROUND_RED}[ERROR]${TERMINAL_CONTROL_SEQUENCES__RESET} Downloading the AWS CLI failed."
		exit 4
	fi

	# install the AWS CLI
	if [ $macos_environment -eq 1 ]
	then
		sudo installer -pkg "./${aws_cli_file_name}" -target /
	else
		unzip "${aws_cli_file_name}"
		sudo ./aws/install
	fi
	aws_cli_install_exit_code=$?

	# clean up the AWS CLI installer
	if [ $macos_environment -eq 0 ]
	then
		rm -Rf ./aws/
	fi
	rm "${aws_cli_file_name}"

	# abort if installing the AWS CLI failed
	if [ $aws_cli_install_exit_code -ne 0 ]
	then
		echo "${TERMINAL_CONTROL_SEQUENCES__FOREGROUND_RED}[ERROR]${TERMINAL_CONTROL_SEQUENCES__RESET} Installing the AWS CLI failed."
		exit 5
	fi

	# configure the AWS CLI
	aws configure
fi

# abort if the S3 bucket does not exist or is not accessible
aws s3 ls "s3://${s3_bucket_name}" --endpoint-url "${s3_endpoint_url}" > /dev/null 2>&1
if [ $? -ne 0 ]
then
	echo "${TERMINAL_CONTROL_SEQUENCES__FOREGROUND_RED}[ERROR]${TERMINAL_CONTROL_SEQUENCES__RESET} Accessing the S3 bucket failed."
	exit 6
fi

echo "${TERMINAL_CONTROL_SEQUENCES__DIM}[3/6]${TERMINAL_CONTROL_SEQUENCES__RESET} Clone the Git repository (\`--bare\`)"

# clone the Git repository to a temporary Git bare repository
git clone --bare "${git_repository_path}" "${git_bare_repository_path}"

# abort if cloning the Git bare repository failed
if [ $? -ne 0 ]
then
	echo "${TERMINAL_CONTROL_SEQUENCES__FOREGROUND_RED}[ERROR]${TERMINAL_CONTROL_SEQUENCES__RESET} Cloning the Git bare repository failed."
	exit 7
fi

echo "${TERMINAL_CONTROL_SEQUENCES__DIM}[4/6]${TERMINAL_CONTROL_SEQUENCES__RESET} Update the Git bare repository for the “Dumb HTTP” protocol"

# define the current working path
current_working_path=$(pwd)

# update the Git bare repository for the [_Dumb HTTP_ protocol](https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols#_dumb_http)
cd "${git_bare_repository_path}"
# <https://git-scm.com/docs/git-update-server-info>
git update-server-info

echo "${TERMINAL_CONTROL_SEQUENCES__DIM}[5/6]${TERMINAL_CONTROL_SEQUENCES__RESET} Upload the Git bare repository to S3"

# upload the Git bare repository to S3
aws s3 sync . "s3://${s3_bucket_name}/$([ "${s3_path}" = '' ] && echo '' || echo "${s3_path}/")${git_bare_repository_name}/" --delete --endpoint-url "${s3_endpoint_url}"
aws_s3_sync_exit_code=$?

echo "${TERMINAL_CONTROL_SEQUENCES__DIM}[6/6]${TERMINAL_CONTROL_SEQUENCES__RESET} Clean up"

# clean up
cd "${current_working_path}"
rm -Rf "${git_bare_repository_path}"

# abort if uploading the Git bare repository to S3 failed
if [ $aws_s3_sync_exit_code -ne 0 ]
then
	echo "${TERMINAL_CONTROL_SEQUENCES__FOREGROUND_RED}[ERROR]${TERMINAL_CONTROL_SEQUENCES__RESET} Uploading the Git bare repository to S3 failed."
	exit 8
fi

echo "${TERMINAL_CONTROL_SEQUENCES__DIM}→${TERMINAL_CONTROL_SEQUENCES__RESET} Done."
echo "\`${TERMINAL_CONTROL_SEQUENCES__BRIGHT}git clone ${CHARACTERS__QUOTATION_MARK}${s3_endpoint_url}${s3_bucket_name}/$([ "${s3_path}" = '' ] && echo '' || echo "${s3_path}/")${git_bare_repository_name}/${CHARACTERS__QUOTATION_MARK}${TERMINAL_CONTROL_SEQUENCES__RESET}\`"
