Skip to content

Instantly share code, notes, and snippets.

@dotysan
Last active October 7, 2025 19:08
Show Gist options
  • Select an option

  • Save dotysan/520a48851368751de642dbc8136caecd to your computer and use it in GitHub Desktop.

Select an option

Save dotysan/520a48851368751de642dbc8136caecd to your computer and use it in GitHub Desktop.
Scan *all* debian image tags from Docker Hub and report which ones result in `ldd /usr/bin/mc` showing a broken libcom_err.so.2 after installing mc.
#!/usr/bin/env bash
#
# find-mc-libcomerr-bug.sh
# Scan *all* debian image tags from Docker Hub and report
# which ones result in `ldd /usr/bin/mc` showing a broken libcom_err.so.2
# after installing mc.
IMAGE_REPO='library/debian'
PAGE_SIZE=100 # API max page size
REPO_API='https://registry.hub.docker.com/v2/repositories'
TMP_DIR="$(mktemp -d)"
trap 'rm --force --recursive -- "$TMP_DIR"' EXIT
declare -a TAGS BAD_TAGS
GOOD_TAG='' BAD_TAG=''
#DEBUG=yep
main() {
# set +x
# fetch_and_filter_tags '1 year'
fetch_and_filter_tags '9 months'
# fetch_and_filter_tags '6 months'
# fetch_and_filter_tags '2 months'
# set -x
scan_all_tags
# bisect_tags
report_results
}
################################################################################
fetch_and_filter_tags() {
local ago="$1"
echo "๐Ÿ“ฅ Fetching debian image tags..."
local cutoff=$(date -d "$ago ago" +%Y%m%d)
local json
local page=1
while :
do
json=$(get_page "$page")
jq -r '.results[].name' <<<"$json" >>"$TMP_DIR/tags.raw"
if jq -e '.next == null' <<<"$json" >/dev/null
then break
fi
((page++))
done
awk -F'-' -v cutoff="$cutoff" '
# all slim images with datestamps
/^[a-z]+-[0-9]{8}-slim$/ {
# /^sid-[0-9]{8}-slim$/ {
# /^[a-z]+-202502..-slim$/ {
# # June 2025 only
# /^[a-z]+-202506..$/ {
date = $2
if (date >= cutoff)
printf "%s-%s\n", date, $0
}
' "$TMP_DIR/tags.raw" |
sort |
cut -d'-' -f2- >"$TMP_DIR/tags.sorted"
# cat "$TMP_DIR/tags.sorted"
mapfile -t TAGS <"$TMP_DIR/tags.sorted"
echo "๐Ÿงฎ Filtered, date-sorted tags: ${#TAGS[@]}"
}
get_page() {
local page="$1"
curl -sSL "$REPO_API/$IMAGE_REPO/tags?page_size=$PAGE_SIZE&page=$page"
}
scan_all_tags() {
for tag in "${TAGS[@]}"
do
if ! has_bug "$tag"
then BAD_TAG="$tag"
else GOOD_TAG="$tag"
fi
done
}
has_bug() {
local tag="$1"
echo -n "๐Ÿงช Testing debian:$tag ... "
if docker run --rm -i "debian:$tag" bash <<-EOF
set -e
export DEBIAN_FRONTEND=noninteractive
apt-get -qq update
apt-get -qq install -y mc --no-install-recommends &>/dev/null
ldd /usr/bin/mc |grep -q 'libcom_err\.so\.2 => not found'
EOF
then
echo "โŒ bug present"
BAD_TAGS+=("$tag")
return 1
else
echo "โœ… OK"
return 0
fi
}
todo_bisect_tags() {
local lo=0 hi=$(( ${#TAGS[@]} - 1 )) mid
while (( lo <= hi ))
do
mid=$(( (lo + hi) / 2 ))
local tag="${TAGS[mid]}"
if ! has_bug "$tag"
then
BAD_TAG="$tag"
hi=$(( mid - 1 ))
else
GOOD_TAG="$tag"
lo=$(( mid + 1 ))
fi
done
}
report_results() {
echo
if (( ${#BAD_TAGS[@]} == 0 ))
then
echo "๐ŸŽ‰ No affected images found."
else
echo "๐Ÿšจ Affected Debian tags where 'mc' has broken libcom_err.so.2:"
printf '%s\n' "${BAD_TAGS[@]}"
fi
}
todo_wtf() {
if [[ -n "$BAD_TAG" ]]
then
echo "โœ… First bad tag : $BAD_TAG"
if [[ -n "$GOOD_TAG" ]]
then
echo "โœ… Last known good: $GOOD_TAG"
else
echo "โš  No good tag found before regression."
fi
list_affected_from_bad
else
echo "๐ŸŽ‰ No affected tags found."
fi
}
list_affected_from_bad() {
echo
echo "๐Ÿšจ All affected slim tags from '$BAD_TAG' onward:"
local print=0
for tag in "${TAGS[@]}"; do
if [[ "$tag" == "$BAD_TAG" ]]; then print=1; fi
if (( print )); then echo "$tag"; fi
done
}
#=======================================================================
if [ -z "${BASH_VERSINFO[0]}" ] || [ ${BASH_VERSINFO[0]} -lt 4 ]
then
echo "ERROR: Only tested on Bourne-Again SHell v4/v5."
exit 1
fi >&2
# poor man's __main__
return 2>/dev/null ||:
if [ "$(type -t "$1")" = "function" ]
then
func="$1"
shift 1
$func "$@"
exit 0
fi
set -o errexit
set -o nounset
set -o pipefail
if [[ "${DEBUG:-}" ]]
then
PS4func() {
local lineno="$1"
if [[ $lineno == 1 ]]
then lineno=0
fi
local src d
if [[ ${BASH_SOURCE[0]} == main ]]
then
src='<stdin>'
d=$((${#FUNCNAME[@]}-1))
else
src="${BASH_SOURCE[0]##*/}"
d=$((${#FUNCNAME[@]}-2))
fi
local i f=''
for ((i=d; i>0; i--))
do f+="${FUNCNAME[i]}()"
done
local c="\033[0;36m" y="\033[0;33m" n="\033[0m"
printf "$y%s:%04d$c%s$n " "$src" "$lineno" "$f"
}
PS4='\r$(PS4func $LINENO)'
set -o xtrace
fi
main "$@"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment