#!/usr/bin/env bash

# This script builds multiple "slim" Go binaries for Coder with the given OS and
# architecture combinations. This wraps ./build_go_matrix.sh.
#
# Usage: ./build_go_slim.sh [--version 1.2.3-devel+abcdef] [--output dist/] [--compress 22] [--agpl] os1:arch1,arch2 os2:arch1 os1:arch3
#
# If no OS:arch combinations are provided, nothing will happen and no error will
# be returned. If no version is specified, defaults to the version from
# ./version.sh
#
# The --output parameter differs from ./build_go_matrix.sh, in that it does not
# accept variables such as `{os}` and `{arch}` and only accepts a directory
# ending with `/`.
#
# The built binaries are additionally copied to the site output directory so
# they can be packaged into non-slim binaries correctly.
#
# When the --compress <level> parameter is provided, the binaries in site/bin
# will be compressed using zstd into site/bin/coder.tar.zst, this helps reduce
# final binary size significantly.
#
# If the --agpl parameter is specified, builds only the AGPL-licensed code (no
# Coder enterprise features).

set -euo pipefail
shopt -s nullglob
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"

version=""
output_path=""
compress=0
agpl=0

args="$(getopt -o "" -l version:,output:,compress:,agpl -- "$@")"
eval set -- "$args"
while true; do
	case "$1" in
	--version)
		version="$2"
		shift 2
		;;
	--output)
		output_path="$2"
		shift 2
		;;
	--compress)
		compress="$2"
		shift 2
		;;
	--agpl)
		agpl=1
		shift
		;;
	--)
		shift
		break
		;;
	*)
		error "Unrecognized option: $1"
		;;
	esac
done

# Check dependencies
dependencies go
if [[ $compress != 0 ]]; then
	dependencies openssl tar zstd

	if [[ $compress != [0-9]* ]] || [[ $compress -gt 22 ]] || [[ $compress -lt 1 ]]; then
		error "Invalid value for compress, must in in the range of [1, 22]"
	fi
fi

# Remove the "v" prefix.
version="${version#v}"
if [[ "$version" == "" ]]; then
	version="$(execrelative ./version.sh)"
fi

# Verify the output path.
if [[ "$output_path" == "" ]]; then
	# Input paths are relative, so we don't cdroot at the top, but for this case
	# we want it to be relative to the root.
	cdroot
	mkdir -p dist
	output_path="$(realpath "dist/coder-slim_{version}_{os}_{arch}")"
elif [[ "$output_path" != */ ]] || [[ "$output_path" == *"{"* ]]; then
	error "The output path '$output_path' cannot contain variables and must end with a slash"
else
	mkdir -p "$output_path"
	output_path="$(realpath "${output_path}coder-slim_{version}_{os}_{arch}")"
fi

build_args=(--slim)
if [[ "$agpl" == 1 ]]; then
	build_args+=(--agpl)
fi

./scripts/build_go_matrix.sh \
	--version "$version" \
	--output "$output_path" \
	"${build_args[@]}" \
	"$@"

cdroot
dest_dir="./site/out/bin"
mkdir -p "$dest_dir"
dest_dir="$(realpath "$dest_dir")"

# Copy the binaries to the site directory.
cd "$(dirname "$output_path")"
for f in ./coder-slim_*; do
	# Remove ./ prefix
	name="${f#./}"
	# Remove "-slim_$version"
	truncated="${name//-slim_$version/}"
	# Replace underscores with hyphens
	hyphenated="${truncated//_/-}"
	dest="$dest_dir/$hyphenated"
	cp "$f" "$dest"
done

if [[ $compress != 0 ]]; then
	pushd "$dest_dir"
	sha_file=coder.sha1
	sha_dest="$dest_dir/$sha_file"
	log "--- Generating SHA1 for coder-slim binaries ($sha_dest)"
	openssl dgst -r -sha1 coder-* | tee $sha_file
	echo "$sha_dest"
	log
	log

	tar_name=coder.tar.zst
	tar_dest="$dest_dir/$tar_name"
	log "--- Compressing coder-slim binaries using zstd level $compress ($tar_dest)"
	tar cf coder.tar $sha_file coder-*
	rm coder-*
	zstd --force --ultra --long -"${compress}" --rm --no-progress coder.tar -o $tar_name
	echo "$tar_dest"
	log
	log

	popd
fi
