Files
mars-nwe/update-submodules.sh
Mario Fetka eb64a50c60
All checks were successful
Source release / source-package (push) Successful in 56s
Update submodule pins
2026-06-05 14:34:52 +02:00

227 lines
6.9 KiB
Bash
Executable File

#!/bin/sh
# Update mars-nwe submodules.
#
# Policy:
# - mars-* / private submodules are updated to their latest configured branch;
# - external upstream submodules are checked out at release tags;
# - nested submodules are initialized, but are not blindly advanced with
# --remote --recursive.
set -eu
# -----------------------------------------------------------------------------
# Edit external release pins here.
# Add one PATH=TAG line per external repository.
# PATH is relative to the mars-nwe root.
# -----------------------------------------------------------------------------
EXTERNAL_TAG_PINS='third_party/yyjson=0.12.0
third_party/zlog=1.2.18
third_party/libsodium/libsodium=1.0.20-FINAL'
# -----------------------------------------------------------------------------
# Auto-commit root gitlink changes after a successful update.
# Set AUTO_COMMIT=0 or pass --no-commit to disable.
# -----------------------------------------------------------------------------
AUTO_COMMIT=1
AUTO_COMMIT_MESSAGE='Update submodule pins'
usage() {
cat <<USAGE
Usage: ./update-submodules.sh [options]
Options:
--init-only Do not fetch/merge remotes or checkout tag pins; only
initialize/update everything to the commits pinned by
the current superproject and submodules.
--no-default-tags Do not use the built-in external release pins.
--tag PATH=TAG Pin an external submodule to TAG after updating. Can be
given more than once. PATH is relative to the mars-nwe
root, for example third_party/yyjson or
third_party/libsodium/libsodium.
--no-commit Do not automatically commit root gitlink changes.
--commit-message TEXT Commit message used when AUTO_COMMIT is enabled.
Default tag pins:
$(printf '%s\n' "$EXTERNAL_TAG_PINS" | sed 's/^/ /')
This script intentionally does not run:
git submodule update --remote --merge --recursive
That command would also try to advance nested external submodules, e.g.
third_party/libsodium/libsodium, instead of keeping them on the selected
release tag.
USAGE
}
init_only=0
use_default_tags=1
extra_tag_pins=''
status_printed=0
print_status() {
status_printed=1
echo "Current recursive submodule status:"
git submodule status --recursive || true
}
on_exit() {
rc=$?
if [ -f .gitmodules ] && [ "$status_printed" -eq 0 ]; then
print_status
fi
if [ "$rc" -ne 0 ]; then
echo "update-submodules.sh failed with exit code $rc" >&2
fi
exit "$rc"
}
trap on_exit EXIT
add_tag_pin() {
case "$1" in
*=*) extra_tag_pins=${extra_tag_pins}${extra_tag_pins:+"
"}$1 ;;
*) echo "error: --tag expects PATH=TAG, got '$1'" >&2; exit 2 ;;
esac
}
while [ "$#" -gt 0 ]; do
case "$1" in
--init-only) init_only=1; shift ;;
--no-default-tags) use_default_tags=0; shift ;;
--no-commit) AUTO_COMMIT=0; shift ;;
--commit-message)
[ "$#" -ge 2 ] || { echo "error: --commit-message needs text" >&2; exit 2; }
AUTO_COMMIT_MESSAGE=$2
shift 2
;;
--commit-message=*) AUTO_COMMIT_MESSAGE=${1#--commit-message=}; shift ;;
--tag)
[ "$#" -ge 2 ] || { echo "error: --tag needs PATH=TAG" >&2; exit 2; }
add_tag_pin "$2"
shift 2
;;
--tag=*) add_tag_pin "${1#--tag=}"; shift ;;
-h|--help) usage; exit 0 ;;
*) usage >&2; exit 2 ;;
esac
done
tag_pins=''
if [ "$use_default_tags" -eq 1 ]; then
tag_pins=$EXTERNAL_TAG_PINS
fi
if [ -n "$extra_tag_pins" ]; then
tag_pins=${tag_pins}${tag_pins:+"
"}$extra_tag_pins
fi
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
cd "$script_dir"
if [ ! -f .gitmodules ]; then
echo "error: .gitmodules not found; run this from the mars-nwe root" >&2
exit 1
fi
pin_tag() {
path=$1
tag=$2
if [ ! -d "$path" ]; then
echo "error: cannot pin '$path': directory does not exist" >&2
exit 1
fi
echo "==> $path: checking out tag $tag"
# Fetch tags when a remote is configured. If the machine is offline but the
# tag is already present locally, continue and try the checkout anyway.
if git -C "$path" remote get-url origin >/dev/null 2>&1; then
git -C "$path" fetch --tags origin || true
fi
if git -C "$path" rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
git -C "$path" checkout -q "tags/$tag"
else
echo "error: tag '$tag' was not found in '$path'" >&2
exit 1
fi
}
is_pinned_path() {
needle=$1
printf '%s\n' "$tag_pins" | while IFS='=' read -r path _tag; do
[ -n "$path" ] || continue
[ "$path" = "$needle" ] && { echo yes; exit 0; }
done | grep -q yes
}
has_nested_submodules() {
path=$1
[ -f "$path/.gitmodules" ]
}
# First make sure the top-level submodule working trees exist.
git submodule update --init
if [ "$init_only" -eq 0 ]; then
echo "Updating own top-level submodules from their configured remotes..."
git config --file .gitmodules --get-regexp '^submodule\..*\.path$' |
while read -r _key path; do
[ -n "$path" ] || continue
if is_pinned_path "$path"; then
echo "==> $path: external tag-pinned submodule, skip latest remote update"
else
echo "==> $path"
git submodule update --remote --merge -- "$path"
fi
done
else
echo "Skipping remote updates; restoring pinned submodule commits only."
fi
# Initialize nested submodules only inside their parent submodule. Do not run a
# root-level recursive update here, because that would reset already-updated
# top-level submodules back to the commits recorded in the parent repository.
echo "Synchronizing nested submodules to pinned commits..."
git config --file .gitmodules --get-regexp '^submodule\..*\.path$' |
while read -r _key path; do
[ -n "$path" ] || continue
if has_nested_submodules "$path"; then
echo "==> $path: nested submodules"
git -C "$path" submodule update --init --recursive
fi
done
if [ "$init_only" -eq 0 ] && [ -n "$tag_pins" ]; then
echo "Applying external release tag pins..."
printf '%s\n' "$tag_pins" | while IFS='=' read -r path tag; do
[ -n "$path" ] || continue
[ -n "$tag" ] || { echo "error: empty tag for '$path'" >&2; exit 2; }
pin_tag "$path" "$tag"
done
fi
print_status
if [ "$init_only" -eq 0 ] && [ "$AUTO_COMMIT" -eq 1 ]; then
echo "Staging root submodule gitlink/script changes..."
git add update-submodules.sh
git config --file .gitmodules --get-regexp '^submodule\..*\.path$' |
while read -r _key path; do
[ -n "$path" ] || continue
git add "$path"
done
if git diff --cached --quiet; then
echo "No root changes to commit."
else
echo "Committing root changes: $AUTO_COMMIT_MESSAGE"
git commit -m "$AUTO_COMMIT_MESSAGE"
fi
fi