jw-pkg/scripts/scm.sh
Jan Lindemann f738951642
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 5m28s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m34s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 5m12s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m22s
CI / Packaging test (push) Successful in 0s
calculate_hash(): Derive hash from VCS file ids
The source hash is an md5sum over all versioned files in the working tree.
md5sum follows symlinks, so a project with links to files outside the tree
may produce different hashes depending on files outside the repo.

Compute the hash over the VCS file ids instead. Add a -s option to scm.sh
ls-files that lists each versioned file together with its content id. The
listing is sorted by path rather than by id, so an entry keeps its position
when its content changes.

For Git repos, git ls-tree -r HEAD reports the blob of a symlink's target
string and the pinned commit of a submodule, so the resulting hash depends
only on the committed tree, never on the working tree or on the machine.

calculate_hash() and the HASH variable in make/pkg-dist.mk feed the id
listing, minus the release metadata files, directly to md5sum.

calculate_hash() fails loudly when the pipeline fails, with its callers
refusing to proceed on an empty result.

The scheme change invalidates the stored HASH values, so every project
gets one more release the first time it runs against the new code, and the
hashes are stable again afterwards.

Signed-off-by: Jan Lindemann <jan@janware.com>
Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.85.1
2026-09-16 14:44:59 +02:00

231 lines
3.9 KiB
Shell

#!/bin/bash
cmd_mv()
{
local from="$1"
local to="$2"
case $scm in
cvs)
cp "$from" "$to"
cvs add "$to"
cvs remove -f "$from"
;;
git)
git mv "$from" "$to"
;;
esac
}
cmd_commit()
{
C=''
for i in "$@"; do
C="$C \"${i//\"/\\\"}\""
done
eval $scm commit "$C"
}
cmd_add()
{
$scm add "$@"
}
cmd_rm()
{
case $scm in
cvs)
cvs remove "$@"
;;
git)
git rm "$@"
;;
esac
}
cmd_clean()
{
case $scm in
cvs)
while [ "${1:0:1}" = - ]; do
shift
done
set +e
local file
for file in "$@"; do
if ! grep -q "/$file/" CVS/Entries; then
rm -f $file
fi
done
;;
git)
git clean -x "$@"
;;
esac
}
cmd_ls_files()
{
filter_deleted()
{
local rc_file="$1"
shift
local cand
for cand in "$@"; do
grep -q "^R ./$cand/" $rc_file && continue
echo $cand
done
}
output_find()
{
local find_opts f
[ "$zero_terminate" = 1 ] && find_opts="-print0"
find "$@" $find_opts
}
output()
{
if [ "$zero_terminate" = 1 ]; then
echo -en "$*\x00"
else
echo "$*"
fi
}
list_dirents_cvs()
{
local dirs=`sed '/^D\// !d; s%^D/%%; s%/.*%%' $1/CVS/Entries`
dirs="`filter_deleted $1/CVS/Entries $dirs`"
local cands
if [ -f $1/CVS/Entries.Log ]; then
cands="`sed '/^A D\// !d; s%^A D/%%; s%/.*%%' $1/CVS/Entries.Log`"
dirs="$dirs `filter_deleted $1/CVS/Entries.Log $cands`"
fi
local files=`sed '/^\// !d; s%/%%; s%/.*%%; s%^%%' $1/CVS/Entries`
files="`filter_deleted $1/CVS/Entries $files`"
local d f
for f in $files; do
output "$1/$f"
done
if [ "$include_vcs_files" = 1 -a -d CVS ]; then
output_find "$1/CVS"
fi
for d in $dirs; do
[ "$opt_only_regular_files" = 1 ] || output $1/$d
list_dirents_cvs $1/$d
done
}
list_dirents_git()
{
local opts="$git_ls_files_opts"
git --version | grep -q "version *1" && opt_no_submodules=1
[ "$opt_no_submodules" = 1 ] || opts="$opts --recurse-submodules"
if [ "$opt_only_regular_files" != 1 ]; then
git ls-files --recurse-submodules $opts $1
else
local entry meta
while IFS= read -r -d '' entry; do
meta=${entry%%$'\t'*}
case ${meta%% *} in
100644|100755) output "${entry#*$'\t'}" ;;
esac
done < <(git ls-files -s -z --recurse-submodules $opts $1)
fi
if [ "$include_vcs_files" = 1 -a -d .git ]; then
output_find .git
fi
}
list_dirents()
{
if [ -d $1/CVS ]; then
list_dirents_cvs $1
return
fi
git -C $1 status >/dev/null 2>&1 || {
echo "failed to list versioned files in $(realpath $1): no VCS" >&2
exit 1
}
list_dirents_git $1
}
list_dirents_git_ids()
{
local entry meta mode sha
while IFS= read -r -d '' entry; do
meta=${entry%%$'\t'*}
mode=${meta%% *}
sha=${meta##* }
[ "$opt_no_submodules" = 1 ] && [ "$mode" = 160000 ] && continue
[ "$opt_only_regular_files" = 1 ] && [ "$mode" != 100644 ] && [ "$mode" != 100755 ] && continue
output "$sha ${entry#*$'\t'}"
done < <(git ls-tree -r -z HEAD "$1")
}
list_dirents_ids()
{
if [ -d $1/CVS ]; then
echo "failed to list file ids in $(realpath $1): $scm has no file ids" >&2
exit 1
fi
git -C $1 status >/dev/null 2>&1 || {
echo "failed to list file ids in $(realpath $1): no VCS" >&2
exit 1
}
list_dirents_git_ids $1
}
set -- `getopt fnzsta "$@"`
while [ "$1" != -- ]; do
case $1 in
-f)
opt_only_regular_files=1
;;
-n)
opt_no_submodules=1
;;
-s)
show_ids=1
;;
-z)
zero_terminate=1
git_ls_files_opts="$git_ls_files_opts -z"
opt_sort="$opt_sort -z"
;;
-t)
text_files=1
;;
-a)
include_vcs_files=1
;;
esac
shift
done
shift
proj_dir="$1"
[ "$proj_dir" ] && cd $proj_dir
if [ "$text_files" ]; then
list_dirents . | sort $opt_sort | xargs file -N | grep ":.*text" | cut -d: -f1
elif [ "$show_ids" = 1 ]; then
list_dirents_ids . | sort $opt_sort -k2
else
list_dirents . | sort $opt_sort
fi
}
# ------- here we go
export LANG=POSIX
myname=`basename $0`
cmd=cmd_${1//-/_}
shift
if [ -d "CVS" ]; then
scm=cvs
else
scm=git
fi
$cmd "$@"