qusal/scripts/spec-gen.sh
Ben Grande 224312ed42
feat: enable all optional shellcheck validations
Make shell a little bit safer with:

- add-default-case
- check-extra-masked-returns
- check-set-e-suppressed
- quote-safe-variables
- check-unassigned-uppercase

Although there are some stylistic decisions for uniformity:

- avoid-nullary-conditions
- deprecated-which
- require-variable-braces
2024-07-10 14:36:05 +02:00

184 lines
5.4 KiB
Bash
Executable File

#!/bin/sh
## SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. <ben.grande.b@gmail.com>
##
## SPDX-License-Identifier: AGPL-3.0-or-later
set -eu
usage(){
echo "Usage: ${0##*/} PROJECT [PROJECT ...]"
}
## Escape multiline strings for sed.
escape_key(){
key_type="${1}"
if test "${key_type}" = "scriptlet"; then
echo "${2}" | sed ':a;N;$!ba;s/\n/\\n /g' | sed 's/\$/\\$/'
elif test "${key_type}" = "text"; then
echo "${2}" | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/\$/\\$/'
else
return 1
fi
}
# get_scriptlet scriptlet-action
# [pre|post]-[install|upgrade], [pre|post]un-[uninstall|upgrade]
## Get scriptlet command, else fail safe.
get_scriptlet(){
scriptlet="$1"
scriptlet_begin="-- pkg:begin:${scriptlet} --"
scriptlet_end="-- pkg:end:${scriptlet} --"
scriptlet="$(sed -n \
"/^<\!${scriptlet_begin}>$/,/^<\!${scriptlet_end}>$/p" \
"${readme}" | sed '/^```.*/d;/^\S*$/d;/^<\!-- pkg:/d;s/^sudo //')"
if test -z "${scriptlet}"; then
echo true
return 0
fi
escape_key scriptlet "${scriptlet}"
}
get_spec(){
"${spec_get}" "${project}" "${1}"
}
gen_spec(){
project="$(echo "${1}" | sed "s|salt/||;s|/.*||")"
if echo "${projects_seen}" | grep -qF " ${project} "; then
return
fi
projects_seen="${projects_seen} ${project} "
if echo "${unwanted}" | grep -q "^${project}$"; then
echo "warn: skipping spec generation of untracked formula: ${project}" >&2
return 0
fi
## Test if a standard option works without error.
get_spec name >/dev/null
group="$(get_spec group)"
template="rpm_spec/template/template.spec"
target="rpm_spec/${group}-${project}.spec"
intended_target="${target}"
if test "${2-}" = "test"; then
tmpdir="$(mktemp -d)"
target="${tmpdir}/${group}-${project}.spec"
trap 'rm -rf -- "${tmpdir}"' EXIT INT HUP QUIT ABRT
fi
readme="$(get_spec readme)"
project_name="$(get_spec project)"
version="$(get_spec version)"
license_csv="$(get_spec license_csv)"
## Ideally we would query the license, but it is a heavy call.
license="$(echo "${license_csv}" | sed "s/\,/ AND /g")"
vendor="$(get_spec vendor)"
packager="$(get_spec packager)"
url="$(get_spec url)"
bug_url="$(get_spec bug_url)"
requires="$(get_spec requires)"
summary="$(get_spec summary)"
description="$(get_spec description)"
description="$(escape_key text "${description}")"
file_roots="$(get_spec file_roots)"
changelog="$(get_spec changelog)"
pre_install="$(get_scriptlet pre-install)"
pre_upgrade="$(get_scriptlet pre-upgrade)"
post_install="$(get_scriptlet post-install)"
post_upgrade="$(get_scriptlet post-upgrade)"
preun_uninstall="$(get_scriptlet preun-uninstall)"
preun_upgrade="$(get_scriptlet preun-upgrade)"
postun_uninstall="$(get_scriptlet postun-uninstall)"
postun_upgrade="$(get_scriptlet postun-upgrade)"
sed \
-e "s/@PRE_INSTALL@/${pre_install}/" \
-e "s/@PRE_UPGRADE@/${pre_upgrade}/" \
-e "s/@POST_INSTALL@/${post_install}/" \
-e "s/@POST_UPGRADE@/${post_upgrade}/" \
-e "s/@PREUN_UNINSTALL@/${preun_uninstall}/" \
-e "s/@PREUN_UPGRADE@/${preun_upgrade}/" \
-e "s/@POSTUN_UNINSTALL@/${postun_uninstall}/" \
-e "s/@POSTUN_UPGRADE@/${postun_upgrade}/" \
-e "s|@FILE_ROOTS@|${file_roots}|" \
-e "s/@NAME@/${project}/" \
-e "s/@PROJECT@/${project_name}/" \
-e "s/@VERSION@/${version}/" \
-e "s/@SUMMARY@/${summary}/" \
-e "s/@GROUP@/${group}/" \
-e "s/@PACKAGER@/${packager}/" \
-e "s/@VENDOR@/${vendor}/" \
-e "s/@LICENSE@/${license}/" \
-e "s/@LICENSE_CSV@/${license_csv}/" \
-e "s|@BUG_URL@|${bug_url}|" \
-e "s|@URL@|${url}|" \
-e "s|@DESCRIPTION@|${description}|" \
-e "/@CHANGELOG@/d" \
"${template}" | tee "${target}" >/dev/null
requires_key=""
for r in $(printf %s"${requires}" | tr " " "\n" | sort -u); do
requires_key="${requires_key:-}Requires: ${group}-${r}\n"
done
sed -i "s/@REQUIRES@/${requires_key}/" "${target}" >/dev/null
echo "${changelog}" | tee -a "${target}" >/dev/null
if test "${2-}" = "test"; then
if ! cmp -s "${target}" "${intended_target}"; then
echo "error: ${intended_target} is not up to date" >&2
diff --color=auto "${intended_target}" "${target}" || true
fail=1
else
unstaged_target="$(git diff --name-only "${intended_target}")" || true
if test -n "${unstaged_target}"; then
echo "warn: ${intended_target} is up to date but it is not staged" >&2
fi
fi
fi
}
case "${1-}" in
-h|--?help) usage; exit 1;;
*) ;;
esac
command -v git >/dev/null || { echo "Missing program: git" >&2; exit 1; }
repo_toplevel="$(git rev-parse --show-toplevel)"
test -d "${repo_toplevel}" || exit 1
unset repo_toplevel
spec_get="./scripts/spec-get.sh"
ignored="$(git ls-files --exclude-standard --others --ignored salt/)"
untracked="$(git ls-files --exclude-standard --others salt/)"
unwanted="$(printf %s"${ignored}\n${untracked}\n" \
| grep "^salt/\S\+/README.md" | cut -d "/" -f2 | sort -u)"
fail=""
gen_mode=""
if test "${1-}" = "test"; then
gen_mode="test"
shift
fi
if echo "${@}" | grep -qE "(^scripts/| scripts/|/template.spec)" ||
test -z "${1-}"
then
# shellcheck disable=SC2046,SC2312
set -- $(find salt/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' \
| sort -d | tr "\n" " ")
fi
projects_seen=""
for p in "${@}"; do
gen_spec "${p}" "${gen_mode}"
done
if test "${fail}" = "1" && test "${gen_mode}" = "test"; then
exit 1
fi