Ctrl K
ComunidadGuías…

scripts/change.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env bash
set -euo pipefail

CHANGE_DIRECTORY=.changes
TYPES=(added changed deprecated removed fixed security maintenance)
SECTIONS=('Breaking Changes' Security Added Changed Deprecated Removed Fixed Maintenance)

BOLD=$'\033[1m' GREEN=$'\033[32m' RED=$'\033[31m' DIM=$'\033[2m' RESET=$'\033[0m'
[ -t 1 ] || BOLD='' GREEN='' RED='' DIM='' RESET=''

heading() { printf '\n  %s%s%s\n' "$BOLD" "$*" "$RESET"; }
ok() { printf '  %s✔%s %s\n' "$GREEN" "$RESET" "$*"; }
die() {
	printf '  %s✖%s %s\n' "$RED" "$RESET" "$*" >&2
	exit 1
}
confirm() {
	local answer
	printf '\n  %s [y/N] ' "$*"
	read -r answer
	[[ "$answer" =~ ^[Yy]$ ]]
}

cd "$(dirname "$0")/.."
trap 'tput cnorm 2> /dev/null || true' EXIT

main() {
	read_arguments "$@"
	if [ -n "$notes" ]; then
		print_notes
		return
	fi
	[ -n "$kind" ] && [ -n "$summary" ] || ask_questions
	write_change
}

read_arguments() {
	kind='' summary='' commit='' breaking='' notes='' details=()
	while [ $# -gt 0 ]; do
		case "$1" in
			--notes) notes=true ;;
			--breaking) breaking=true ;;
			--type | --summary | --detail | --commit)
				[ $# -ge 2 ] || die "$1 has no value, expected one"
				case "$1" in
					--type) kind="$2" ;;
					--summary) summary="$2" ;;
					--detail) details+=("$2") ;;
					--commit) commit="$2" ;;
				esac
				shift
				;;
			*) die "'$1' is not an option, expected --type, --summary, --detail, --breaking, --commit or --notes" ;;
		esac
		shift
	done
}

ask_questions() {
	local selected detail
	[ -t 0 ] || die "--type and --summary are required without a terminal"
	if [ -z "$kind" ]; then
		heading "Select the type"
		selected="$(menu 0 "${TYPES[@]}")"
		kind="${TYPES[$selected]}"
	fi
	if [ -z "$summary" ]; then
		heading "Summary, one sentence with the heading"
		printf '  '
		read -r summary
	fi
	heading "Details, an empty line ends them"
	while printf '  - ' && read -r detail && [ -n "$detail" ]; do
		details+=("$detail")
	done
	if confirm "Breaking change?"; then
		breaking=true
	fi
	printf '\n'
}

write_change() {
	local path detail
	printf '%s\n' "${TYPES[@]}" | grep -qx -- "$kind" || die "the type is '$kind', expected ${TYPES[*]}"
	[ -n "$summary" ] || die "the summary is empty, expected one sentence"
	if [ -n "$commit" ]; then
		git rev-parse --verify --quiet "$commit^{commit}" > /dev/null || die "$commit is not a commit of this repository"
		commit="$(git rev-parse --short "$commit")"
	fi
	mkdir -p "$CHANGE_DIRECTORY"
	path="$CHANGE_DIRECTORY/$(file_name).md"
	{
		printf -- '---\ntype: %s\n' "$kind"
		[ -z "$breaking" ] || printf 'breaking: true\n'
		[ -z "$commit" ] || printf 'commit: %s\n' "$commit"
		printf -- '---\n%s\n' "$summary"
		[ "${#details[@]}" = 0 ] || printf '\n'
		for detail in ${details[@]+"${details[@]}"}; do
			printf -- '- %s\n' "$detail"
		done
	} > "$path"
	ok "$path describes the $BOLD$kind$RESET change"
}

print_notes() {
	local section entries
	compgen -G "$CHANGE_DIRECTORY/*.md" > /dev/null || die "$CHANGE_DIRECTORY has no change files"
	for section in "${SECTIONS[@]}"; do
		entries="$(awk -v section="$section" '
			FNR == 1 { fences = 0; type = ""; breaking = 0 }
			fences < 2 {
				if ($0 == "---") fences++
				else if ($1 == "type:") type = $2
				else if ($0 == "breaking: true") breaking = 1
				next
			}
			NF && tolower(section) == (breaking ? "breaking changes" : type) {
				prefix = /^- / ? "  " : "- "
				print prefix $0
			}
		' "$CHANGE_DIRECTORY"/*.md)"
		[ -z "$entries" ] || printf '### %s\n\n%s\n\n' "$section" "$entries"
	done
}

file_name() {
	local stem name number=2
	stem="$(printf '%s' "$summary" | LC_ALL=C tr -cs '[:alnum:]' '\n' | LC_ALL=C tr '[:upper:]' '[:lower:]' | awk 'NF && ++count <= 6' | paste -sd - -)"
	name="$stem"
	while [ -e "$CHANGE_DIRECTORY/$name.md" ]; do
		name="$stem-$number"
		number=$((number + 1))
	done
	echo "$name"
}

menu() {
	local selected="$1" options key i
	shift
	options=("$@")
	tput civis >&2 2> /dev/null || true
	while true; do
		for i in "${!options[@]}"; do
			if [ "$i" = "$selected" ]; then
				printf '  %s❯ %s%s\n' "$GREEN" "${options[$i]}" "$RESET" >&2
			else
				printf '  %s  %s%s\n' "$DIM" "${options[$i]}" "$RESET" >&2
			fi
		done
		IFS= read -rsn1 key
		if [ "$key" = $'\x1b' ]; then
			IFS= read -rsn2 key
			if [ "$key" = '[A' ] && [ "$selected" -gt 0 ]; then
				selected=$((selected - 1))
			elif [ "$key" = '[B' ] && [ "$selected" -lt $((${#options[@]} - 1)) ]; then
				selected=$((selected + 1))
			fi
		elif [ -z "$key" ]; then
			break
		fi
		printf '\033[%dA\033[J' "${#options[@]}" >&2
	done
	tput cnorm >&2 2> /dev/null || true
	echo "$selected"
}

main "$@"