263 lines
6.0 KiB
Bash
Executable File
263 lines
6.0 KiB
Bash
Executable File
#!/bin/bash
|
|
set -e
|
|
|
|
# config options
|
|
declare -A keep
|
|
REMOTE="origin"
|
|
BASE_BRANCH="main"
|
|
PACKAGE_BASE_URL=
|
|
PACKAGE_RELATIVE_URL="../../rpm"
|
|
DATE="$(date "+%s %z")"
|
|
PACKAGES_KEEP=
|
|
|
|
# command line only
|
|
packages_from=
|
|
single=
|
|
verbose=0
|
|
cfg_file=
|
|
|
|
###################
|
|
|
|
# constant needed to aid quoting
|
|
nl=$'\n'
|
|
|
|
declare -A packages
|
|
declare -A new
|
|
declare -A drop
|
|
declare -A revs
|
|
gitmodules_rev=
|
|
|
|
helpandquit()
|
|
{
|
|
cat <<-EOF
|
|
Usage: $0 [OPTIONS] [<module> ...]
|
|
OPTIONS:
|
|
--packages-from=FILE read list of packges to sync from FILE
|
|
--single create single commit for all changes
|
|
-h help screen
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
show_status()
|
|
{
|
|
git for-each-ref 'refs/pq/*'
|
|
exit 0
|
|
}
|
|
|
|
clear_queue()
|
|
{
|
|
while read -r ref; do
|
|
git update-ref -d "$ref"
|
|
done < <(git for-each-ref 'refs/pq/*' --format '%(refname)')
|
|
exit 0
|
|
}
|
|
|
|
isnew()
|
|
{
|
|
local p="${1:?}"
|
|
[ -n "${new[$p]}" ]
|
|
}
|
|
|
|
todrop()
|
|
{
|
|
local p="${1:?}"
|
|
[ -n "${drop[$p]}" ]
|
|
}
|
|
|
|
log_info()
|
|
{
|
|
[ "$verbose" -gt 0 ] || return 0
|
|
echo "$@"
|
|
}
|
|
|
|
makedict()
|
|
{
|
|
local dict="$1"
|
|
shift
|
|
mapfile -t a < <("$@")
|
|
for k in "${a[@]}"; do
|
|
eval "$dict"["$k"]=1
|
|
done
|
|
}
|
|
|
|
getopttmp=$(getopt -o hc:v --long help,single,branch:,config:,date:,remote:,status,verbose,clear,packages-from: -n "${0##*/}" -- "$@")
|
|
eval set -- "$getopttmp"
|
|
|
|
while true ; do
|
|
case "$1" in
|
|
-h|--help) helpandquit; shift ;;
|
|
-v|--verbose) verbose=$((++verbose)); shift ;;
|
|
-c|--config) cfg_file="$2"; shift 2 ;;
|
|
--single) single=1; shift ;;
|
|
--packages-from) packages_from="$2"; shift 2 ;;
|
|
--remote) REMOTE="$2"; shift 2 ;;
|
|
--branch) BASE_BRANCH="$2"; shift 2 ;;
|
|
--status) show_status; exit 0 ;;
|
|
--clear) clear_queue; exit 0 ;;
|
|
--date) DATE="$2"; shift 2 ;;
|
|
--) shift ; break ;;
|
|
*) echo "Internal error!" ; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
modules=("$@")
|
|
|
|
PACKAGE_BASE_URL="$(git config --get remote."$REMOTE".url)"
|
|
|
|
# shellcheck disable=SC1090
|
|
. "${cfg_file:-.settings}"
|
|
|
|
for p in $PACKAGES_KEEP; do
|
|
keep["$p"]=1
|
|
done
|
|
|
|
if [ "${DATE:0:1}" = '@' ]; then
|
|
DATE="$(stat -c %Y "${DATE:1}") +0100"
|
|
fi
|
|
|
|
export GIT_AUTHOR_NAME="Auto"
|
|
export GIT_AUTHOR_EMAIL="auto@suse.de"
|
|
export GIT_AUTHOR_DATE="$DATE"
|
|
export GIT_COMMITTER_NAME="Auto"
|
|
export GIT_COMMITTER_EMAIL="auto@suse.de"
|
|
export GIT_COMMITTER_DATE="$DATE"
|
|
|
|
tmpfile=$(mktemp updatemodules.XXXXXX)
|
|
tmpfile2=$(mktemp updatemodules.XXXXXX)
|
|
cleanup()
|
|
{
|
|
rm -f "$tmpfile" "$tmpfile2"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# read all submodules
|
|
while read -r m t cid p; do
|
|
if [ "$t" = blob ] && [ "$p" = ".gitmodules" ]; then
|
|
gitmodules_rev="$cid"
|
|
fi
|
|
[ "$t" = "commit" ] || continue
|
|
revs["$p"]="$cid"
|
|
done < <(git cat-file -p "$REMOTE/$BASE_BRANCH^{tree}")
|
|
|
|
if [ -n "$packages_from" ]; then
|
|
while read -r p; do
|
|
packages["$p"]=1
|
|
[ -n "${revs[$p]}" ] || new["$p"]=1
|
|
done < "$packages_from"
|
|
for m in "${!revs[@]}"; do
|
|
[ -n "${packages[$m]}" -o -n "${keep[$m]}" ] || drop["$m"]=1
|
|
done
|
|
fi
|
|
|
|
[ -z "${new[*]}" ] || log_info "new: ${!new[*]}"
|
|
[ -z "${drop[*]}" ] || log_info "drop: ${!drop[*]}"
|
|
|
|
if [ "${#modules[@]}" = 0 ]; then
|
|
modules=("${!revs[@]}" "${!new[@]}")
|
|
fi
|
|
|
|
# push queue
|
|
declare -A pq
|
|
makedict pq git for-each-ref 'refs/pq/*' --format '%(refname)'
|
|
|
|
for r in "${!pq[@]}"; do
|
|
m="${r##*/}"
|
|
if [ -z "${revs[$m]}" ] && ! isnew "$m" && ! todrop "$m"; then
|
|
log_info "remove stale update entry for $m"
|
|
git update-ref -d "$r"
|
|
fi
|
|
done
|
|
|
|
# check remotes for updates
|
|
declare -A commits
|
|
treetext=$(git cat-file -p "$REMOTE/$BASE_BRANCH^{tree}")
|
|
git cat-file -p "$REMOTE/$BASE_BRANCH":.gitmodules > "$tmpfile"
|
|
cat "$tmpfile" > "$tmpfile2"
|
|
for m in "${modules[@]}"; do
|
|
if isnew "$m"; then
|
|
path="$m"
|
|
url="$PACKAGE_RELATIVE_URL/$m"
|
|
smbranch=
|
|
git config -f "$tmpfile" --add "submodule.$m.path" "$path"
|
|
git config -f "$tmpfile" --add "submodule.$m.url" "$url"
|
|
else
|
|
path="$(git config -f "$tmpfile" --get "submodule.$m.path")"
|
|
url="$(git config -f "$tmpfile" --get "submodule.$m.url")"
|
|
smbranch="$(git config -f "$tmpfile" --get "submodule.$m.branch" || :)"
|
|
fi
|
|
if [ -z "$path" ] || [ -z "$url" ]; then
|
|
echo "$m unknown" >&2
|
|
continue
|
|
fi
|
|
if [ "${url:0:3}" = '../' ]; then
|
|
url="$PACKAGE_BASE_URL/$url"
|
|
fi
|
|
|
|
cid=
|
|
if ! todrop "$m"; then
|
|
read -r cid _d < <(GIT_ASKPASS=/bin/true git ls-remote "$url" "${smbranch:-HEAD}" 2>/dev/null) || true
|
|
if [ -z "$cid" ]; then
|
|
echo "Warning: $path not in pool, ignored" >&2
|
|
continue
|
|
fi
|
|
fi
|
|
|
|
# create a new commit for this package
|
|
if [ "${revs[$path]}" != "$cid" ]; then
|
|
log_info "Needs update: $path@${revs[$path]:-NEW} -> ${cid:-DROP}"
|
|
if isnew "$m"; then
|
|
newtreetext="$treetext${nl}160000 commit $cid $m"
|
|
elif todrop "$m"; then
|
|
newtreetext="${treetext/160000 commit ${revs[$path]} $path$nl/}"
|
|
git config -f "$tmpfile" --remove-section "submodule.$m"
|
|
else
|
|
newtreetext="${treetext/${revs[$path]} $path/$cid $path}"
|
|
fi
|
|
if isnew "$m" || todrop "$m"; then
|
|
nh=$(git hash-object -w --path .gitmodules "$tmpfile")
|
|
newtreetext="${newtreetext/$gitmodules_rev .gitmodules/$nh .gitmodules}"
|
|
fi
|
|
|
|
if [ "$single" = 1 ]; then
|
|
if isnew "$m" || todrop "$m"; then
|
|
gitmodules_rev="$nh"
|
|
fi
|
|
treetext="$newtreetext"
|
|
# fall through
|
|
else
|
|
newtree=$(echo "$newtreetext" | git mktree)
|
|
msg="Update $m"
|
|
isnew "$m" && msg="Add $m"
|
|
todrop "$m" && msg="Remove $m"
|
|
newcid="$(git commit-tree -p "$REMOTE/$BASE_BRANCH" -m "$msg" "$newtree")"
|
|
commits["$m"]="$newcid"
|
|
cat "$tmpfile2" > "$tmpfile"
|
|
continue
|
|
fi
|
|
# fall through
|
|
fi
|
|
if [ -n "${pq[refs/pq/$m]}" ]; then
|
|
log_info "remove pq for $m"
|
|
git update-ref -d "refs/pq/$m"
|
|
fi
|
|
done
|
|
|
|
if [ "$single" = 1 ]; then
|
|
newtree="$(echo "$treetext" | git mktree)"
|
|
newcid="$(git commit-tree -p "$REMOTE/$BASE_BRANCH" -m "Update all" "$newtree")"
|
|
commits["all"]="$newcid"
|
|
elif [ -n "${pq[refs/pq/all]}" ]; then
|
|
log_info "remove pq for single commit"
|
|
git update-ref -d "refs/pq/all"
|
|
fi
|
|
|
|
for m in "${!commits[@]}"; do
|
|
ref="refs/pq/$m"
|
|
if [ -n "${pq[$ref]}" ]; then
|
|
cid="$(git rev-parse "$ref")"
|
|
[ "$cid" = "${commits[$m]}" ] || echo "Warning: previous commit $cid for $m" >&2
|
|
fi
|
|
git update-ref "$ref" "${commits[$m]}"
|
|
done
|