xo-sdlc/bin/xo-sdlc.in

562 lines
16 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<EOF
$0 [-n|--dry-run] [--dirty] [-u|--usage|-h|--help] [-fj|--forgejo] [--list] [--root] [-u2|--umbrella2] [--sat|--satellite] [--repo] [--xo-clone] [--xo-add-fj-remote] [--xo-push-remote] [--verify-satellite-list] [--u2-subrepo-status] [--u2-fix-xobuild-subsystem-list] [--u2-fix-cmake-bootstrap-macros] [--u2-subrepo-clone|--u2-push-satellite-remote|--u2-pull-satellite-remote] [--xoname=XONAME|XONAME]
EOF
}
help() {
usage
cat <<EOF
SLDC support for XO
Options:
-u | --usage brief help message
-h | --help this longer help message
-n | --dry-run print what would be done without making changes
--list list known xo subsystems
--root print project root directory XO_ROOT
-u2 | --umbrella2 print xo-umbrella2 directory XO_UMBRELLA2
--sat | --satellite print xo satellite root XO_SATELLITE_ROOT
--repo print repo URL for NAME
-fj | --forgejo with --repo or --xo-clone: use forgejo URL instead of github URL
--verify-satellite-list audit subsystem-list against XO_SATELLITE_ROOT and XO_UMBRELLA2
--xo-clone clone NAME into XO_SATELLITE_ROOT
--xo-add-fj-remote add forgejo remote to XO_SATELLITE_ROOT/NAME
--xo-push-remote push XO_SATELLITE_ROOT/NAME (with -fj: push to forgejo remote)
--u2-fix-xobuild-subsystem-list append xo-* dirs present in XO_UMBRELLA2 but missing from xobuild subsystem-list
--u2-fix-cmake-bootstrap-macros copy xo-cmake bootstrap macros to NAME (or --all) if not already matching
--u2-subrepo-clone clone satellite into XO_UMBRELLA2 as subrepo (NAME)
--u2-push-satellite-remote push satellite to upstream remote (NAME or --all; with --dirty: skip clean)
--dirty with --u2-push-satellite-remote --all: only push subrepos with outgoing commits
--u2-pull-satellite-remote pull satellite from upstream remote (NAME or --all with .gitrepo)
--u2-subrepo-status show git subrepo status for all satellites
--xoname=NAME satellite name for remote operations
EOF
}
cmd=
dry_run=0
xoname=
all_flag=0
dirty_flag=0
fj_flag=0
while [[ $# > 0 ]]; do
case "$1" in
-n | --dry-run)
dry_run=1
;;
-u | --usage)
cmd='usage'
;;
-h | --help)
cmd='help'
;;
--list)
cmd='list'
;;
--root)
cmd='root'
;;
-u2 | --umbrella2)
cmd='umbrella2'
;;
--sat | --satellite)
cmd='satellite'
;;
--repo)
cmd='repo'
;;
--xo-clone)
cmd='xo-clone'
;;
--xo-add-fj-remote)
cmd='xo-add-fj-remote'
;;
--xo-push-remote)
cmd='xo-push-remote'
;;
-fj | --forgejo)
fj_flag=1
;;
--verify-satellite-list)
cmd='verify-satellite-list'
;;
--u2-fix-xobuild-subsystem-list)
cmd='u2-fix-xobuild-subsystem-list'
;;
--u2-fix-cmake-bootstrap-macros)
cmd='u2-fix-cmake-bootstrap-macros'
;;
--u2-subrepo-clone)
cmd='u2-subrepo-clone'
;;
--u2-push-satellite-remote)
cmd='u2-push-satellite-remote'
;;
--u2-pull-satellite-remote)
cmd='u2-pull-satellite-remote'
;;
--u2-subrepo-status)
cmd='u2-subrepo-status'
;;
--all)
all_flag=1
;;
--dirty)
dirty_flag=1
;;
--xoname=*)
xoname="${1#*=}"
;;
xo-*)
xoname="$1"
;;
*)
usage
exit 1
;;
esac
shift
done
XO_ROOT=@XO_ROOT@
XO_UMBRELLA2=@XO_UMBRELLA2@
XO_SATELLITE_ROOT=@XO_SATELLITE_ROOT@
SUBSYSTEMLIST_FILE=@CMAKE_INSTALL_FULL_DATADIR@/etc/xo/sdlc/subsystem-list
XOBUILD_SUBSYSTEMLIST_FILE=${XO_UMBRELLA2}/xo-cmake/etc/xo/subsystem-list
XO_REPO_STEM=
if [[ $(whoami) == "roland" ]]; then
XO_REPO_STEM="git@github.com:Rconybea"
else
XO_REPO_STEM="https://github.com/Rconybea"
fi
subsystem_list() {
cat ${SUBSYSTEMLIST_FILE}
}
FJ_REPO_STEM="ssh://forgejo@conybeare.us:2222/roland"
print_forgejo_repo() {
local name=$1
if grep -qx "${name}" "${XOBUILD_SUBSYSTEMLIST_FILE}" || grep -qx "${name}" "${SUBSYSTEMLIST_FILE}"; then
echo "${FJ_REPO_STEM}/${name}.git"
else
>&2 echo "xo-sdlc: unknown xo component [${name}]"
return 1
fi
}
print_github_repo() {
local name=$1
case "${name}" in
xo-indentlog) echo "${XO_REPO_STEM}/indentlog.git" ;;
xo-refcnt) echo "${XO_REPO_STEM}/refcnt.git" ;;
xo-subsys) echo "${XO_REPO_STEM}/subsys.git" ;;
xo-randomgen) echo "${XO_REPO_STEM}/randomgen.git" ;;
xo-reflect) echo "${XO_REPO_STEM}/reflect.git" ;;
*)
if grep -qx "${name}" "${XOBUILD_SUBSYSTEMLIST_FILE}"; then
echo "${XO_REPO_STEM}/${name}.git"
else
>&2 echo "xo-sdlc: unknown xo component [${name}]"
return 1
fi
;;
esac
}
u2_push_one() {
local name=$1
local cmd="git -C ${XO_UMBRELLA2} subrepo push ${name}"
if [[ ${dry_run} == 1 ]]; then
echo "${cmd}"
else
echo -n "pushing ${name}..."
local t0=$SECONDS
${cmd}
local rc=$?
echo " $((SECONDS - t0))s"
return ${rc}
fi
}
u2_push_satellite_remote() {
if [[ ${all_flag} == 1 ]]; then
local rc=0
for dir in "${XO_UMBRELLA2}"/xo-*/; do
local name
name=$(basename "${dir}")
[[ -f "${XO_UMBRELLA2}/${name}/.gitrepo" ]] || continue
if [[ ${dirty_flag} == 1 ]]; then
local outgoing
outgoing=$(u2_subrepo_outgoing "${name}")
[[ -n "${outgoing}" ]] || continue
fi
u2_push_one "${name}" || rc=$?
done
return ${rc}
fi
if [[ -z "${xoname}" ]]; then
>&2 echo "xo-sdlc: --u2-push-satellite-remote requires --xoname=NAME or --all"
return 1
fi
u2_push_one "${xoname}"
}
u2_pull_one() {
local name=$1
local cmd="git -C ${XO_UMBRELLA2} subrepo pull ${name}"
if [[ ${dry_run} == 1 ]]; then
echo "${cmd}"
else
${cmd}
fi
}
u2_pull_satellite_remote() {
if [[ ${all_flag} == 1 ]]; then
local rc=0
for dir in "${XO_UMBRELLA2}"/xo-*/; do
local name
name=$(basename "${dir}")
[[ -f "${XO_UMBRELLA2}/${name}/.gitrepo" ]] || continue
u2_pull_one "${name}" || rc=$?
done
return ${rc}
fi
if [[ -z "${xoname}" ]]; then
>&2 echo "xo-sdlc: --u2-pull-satellite-remote requires --xoname=NAME or --all"
return 1
fi
u2_pull_one "${xoname}"
}
u2_subrepo_outgoing() {
local name=$1
[[ -f "${XO_UMBRELLA2}/${name}/.gitrepo" ]] || return 1
local parent
parent=$(git -C "${XO_UMBRELLA2}" config -f "${name}/.gitrepo" subrepo.parent)
git -C "${XO_UMBRELLA2}" log --oneline --grep="^git subrepo " --invert-grep "${parent}..HEAD" -- "${name}"
}
u2_subrepo_status_one() {
local name=$1
if [[ ! -f "${XO_UMBRELLA2}/${name}/.gitrepo" ]]; then
>&2 echo "xo-sdlc: ${name} is not a subrepo"
return 1
fi
local commits
commits=$(u2_subrepo_outgoing "${name}")
if [[ -z "${commits}" ]]; then
printf "%-32s (clean)\n" "${name}"
else
printf "%-32s\n" "${name}"
while IFS= read -r line; do
printf " %s\n" "${line}"
done <<< "${commits}"
fi
}
u2_subrepo_status() {
if [[ -n "${xoname}" ]]; then
u2_subrepo_status_one "${xoname}"
return $?
fi
for dir in "${XO_UMBRELLA2}"/xo-*/; do
local name
name=$(basename "${dir}")
u2_subrepo_status_one "${name}" 2>/dev/null
done
}
u2_subrepo_clone() {
if [[ -z "${xoname}" ]]; then
>&2 echo "xo-sdlc: --u2-subrepo-clone requires --xoname=NAME"
return 1
fi
local url
url=$(print_github_repo "${xoname}") || return 1
local cmd="git -C ${XO_UMBRELLA2} subrepo clone ${url} ${xoname}"
if [[ ${dry_run} == 1 ]]; then
echo "${cmd}"
else
${cmd}
fi
}
verify_satellite_list() {
local rc=0
# collect union of names from all sources
local all_names
all_names=$(
# nullglob: an empty/missing dir yields nothing rather than a literal '*'
shopt -s nullglob
{
cat "${SUBSYSTEMLIST_FILE}"
cat "${XOBUILD_SUBSYSTEMLIST_FILE}"
for dir in "${XO_SATELLITE_ROOT}"/*/ "${XO_UMBRELLA2}"/xo-*/; do
basename "${dir}"
done
} | sed '/^[[:space:]]*$/d' | sort -u
)
local boot_ref="${XO_UMBRELLA2}/xo-cmake/share/xo-macros/xo-bootstrap-macros.cmake"
printf "%-32s %s %s %s %s %s %s %s\n" "name" "u2" "sr" "xo" "fj" "sdlc" "bld" "boot"
printf "%-32s %s %s %s %s %s %s %s\n" "----" "--" "--" "--" "--" "----" "---" "----"
while IFS= read -r name; do
local in_u2=0 in_sr=0 in_xo=0 in_fj=0 in_sdlc=0 in_bld=0 in_boot=0
[[ -d "${XO_UMBRELLA2}/${name}" ]] && in_u2=1
[[ -f "${XO_UMBRELLA2}/${name}/.gitrepo" ]] && in_sr=1
[[ -d "${XO_SATELLITE_ROOT}/${name}" ]] && in_xo=1
if [[ ${in_xo} == 1 ]]; then
git -C "${XO_SATELLITE_ROOT}/${name}" remote | grep -qx "forgejo" && in_fj=1
fi
if [[ ${in_u2} == 1 ]]; then
local sat_boot="${XO_UMBRELLA2}/${name}/cmake/xo-bootstrap-macros.cmake"
if [[ -f "${sat_boot}" ]] && diff -q "${boot_ref}" "${sat_boot}" &>/dev/null; then
in_boot=1
fi
fi
grep -qx "${name}" "${SUBSYSTEMLIST_FILE}" && in_sdlc=1
grep -qx "${name}" "${XOBUILD_SUBSYSTEMLIST_FILE}" && in_bld=1
printf "%-32s %2d %2d %2d %2d %4d %3d %4d\n" "${name}" "${in_u2}" "${in_sr}" "${in_xo}" "${in_fj}" "${in_sdlc}" "${in_bld}" "${in_boot}"
if [[ ${in_u2} == 0 || ${in_sr} == 0 || ${in_xo} == 0 || ${in_fj} == 0 || ${in_sdlc} == 0 || ${in_bld} == 0 || ${in_boot} == 0 ]]; then
rc=1
fi
done <<< "${all_names}"
return ${rc}
}
u2_fix_xobuild_subsystem_list() {
local added=0
for dir in "${XO_UMBRELLA2}"/xo-*/; do
local name
name=$(basename "${dir}")
if ! grep -qx "${name}" "${XOBUILD_SUBSYSTEMLIST_FILE}"; then
if [[ ${dry_run} == 1 ]]; then
echo "echo ${name} >> ${XOBUILD_SUBSYSTEMLIST_FILE}"
else
echo "${name}" >> "${XOBUILD_SUBSYSTEMLIST_FILE}"
fi
added=1
fi
done
if [[ ${added} == 0 ]]; then
echo "xo-sdlc: xobuild subsystem-list already up to date"
fi
}
fix_cmake_bootstrap_macros_one() {
local name=$1
local boot_ref="${XO_UMBRELLA2}/xo-cmake/share/xo-macros/xo-bootstrap-macros.cmake"
local repo_dir="${XO_UMBRELLA2}/${name}"
local dest_dir="${repo_dir}/cmake"
local dest="${dest_dir}/xo-bootstrap-macros.cmake"
# require the subrepo dir to exist (do not create it); but create cmake/ if needed
if [[ ! -d "${repo_dir}" ]]; then
>&2 echo "xo-sdlc: --u2-fix-cmake-bootstrap-macros: no such subrepo: ${repo_dir}"
return 1
fi
if [[ ! -f "${dest}" ]] || ! diff -q "${boot_ref}" "${dest}" &>/dev/null; then
if [[ ${dry_run} == 1 ]]; then
[[ -d "${dest_dir}" ]] || echo "mkdir -p ${dest_dir}"
echo "cp ${boot_ref} ${dest}"
else
mkdir -p "${dest_dir}"
cp "${boot_ref}" "${dest}"
fi
fi
}
u2_fix_cmake_bootstrap_macros() {
if [[ ${all_flag} == 1 ]]; then
local rc=0
for dir in "${XO_UMBRELLA2}"/xo-*/; do
local name
name=$(basename "${dir}")
[[ "${name}" == "xo-cmake" ]] && continue
fix_cmake_bootstrap_macros_one "${name}" || rc=$?
done
return ${rc}
fi
if [[ -z "${xoname}" ]]; then
>&2 echo "xo-sdlc: --u2-fix-cmake-bootstrap-macros requires --xoname=NAME or --all"
return 1
fi
fix_cmake_bootstrap_macros_one "${xoname}"
}
if [[ $cmd == 'usage' ]]; then
echo -n "usage: "
usage
exit 0
fi
if [[ $cmd == 'help' ]]; then
help
exit 0
fi
if [[ $cmd == 'list' ]]; then
subsystem_list
exit 0
fi
if [[ -z ${XO_ROOT} ]]; then
2>&1 echo "xo-sdlc: expected non-empty XO_ROOT path"
exit 1
fi
if [[ -z ${XO_UMBRELLA2} ]]; then
2>&1 echo "xo-sdlc: expected non-empty XO_UMBRELLA2 path"
exit 1
fi
if [[ $cmd == 'root' ]]; then
echo ${XO_ROOT}
exit 0
fi
if [[ $cmd == 'umbrella2' ]]; then
echo ${XO_UMBRELLA2}
exit 0
fi
if [[ $cmd == 'satellite' ]]; then
echo ${XO_SATELLITE_ROOT}
exit 0
fi
if [[ $cmd == 'repo' ]]; then
if [[ -z "${xoname}" ]]; then
>&2 echo "xo-sdlc: --repo requires --xoname=NAME or a NAME argument"
exit 1
fi
if [[ ${fj_flag} -eq 1 ]]; then
print_forgejo_repo "${xoname}"
else
print_github_repo "${xoname}"
fi
exit $?
fi
if [[ $cmd == 'xo-clone' ]]; then
if [[ -z "${xoname}" ]]; then
>&2 echo "xo-sdlc: --xo-clone requires --xoname=NAME or a NAME argument"
exit 1
fi
if [[ ${fj_flag} -eq 1 ]]; then
clone_url=$(print_forgejo_repo "${xoname}") || exit 1
else
clone_url=$(print_github_repo "${xoname}") || exit 1
fi
clone_dest="${XO_SATELLITE_ROOT}/${xoname}"
clone_cmd="git clone ${clone_url} ${clone_dest}"
if [[ ${dry_run} -eq 1 ]]; then
echo "${clone_cmd}"
else
${clone_cmd}
fi
exit $?
fi
if [[ $cmd == 'xo-add-fj-remote' ]]; then
if [[ -z "${xoname}" ]]; then
>&2 echo "xo-sdlc: --xo-add-fj-remote requires --xoname=NAME or a NAME argument"
exit 1
fi
fj_url=$(print_forgejo_repo "${xoname}") || exit 1
fj_cmd="git -C ${XO_SATELLITE_ROOT}/${xoname} remote add forgejo ${fj_url}"
if [[ ${dry_run} -eq 1 ]]; then
echo "${fj_cmd}"
else
${fj_cmd}
fi
exit $?
fi
if [[ $cmd == 'xo-push-remote' ]]; then
if [[ -z "${xoname}" ]]; then
>&2 echo "xo-sdlc: --xo-push-remote requires --xoname=NAME or a NAME argument"
exit 1
fi
if [[ ${fj_flag} -eq 1 ]]; then
push_cmd="git -C ${XO_SATELLITE_ROOT}/${xoname} push forgejo"
else
push_cmd="git -C ${XO_SATELLITE_ROOT}/${xoname} push"
fi
if [[ ${dry_run} -eq 1 ]]; then
echo "${push_cmd}"
else
${push_cmd}
fi
exit $?
fi
if [[ $cmd == 'verify-satellite-list' ]]; then
verify_satellite_list
exit $?
fi
if [[ $cmd == 'u2-fix-xobuild-subsystem-list' ]]; then
u2_fix_xobuild_subsystem_list
exit $?
fi
if [[ $cmd == 'u2-fix-cmake-bootstrap-macros' ]]; then
u2_fix_cmake_bootstrap_macros
exit $?
fi
if [[ $cmd == 'u2-subrepo-clone' ]]; then
u2_subrepo_clone
exit $?
fi
if [[ $cmd == 'u2-push-satellite-remote' ]]; then
u2_push_satellite_remote
exit $?
fi
if [[ $cmd == 'u2-pull-satellite-remote' ]]; then
u2_pull_satellite_remote
exit $?
fi
if [[ $cmd == 'u2-subrepo-status' ]]; then
u2_subrepo_status
exit $?
fi