git.lirion.de

Of git, get, and gud

aboutsummaryrefslogtreecommitdiffstats
path: root/.githooks
ModeNameSize
-rwxr-xr-xpost-checkout616logstatsplain
-rwxr-xr-xpre-commit197logstatsplain
> 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
#!/usr/bin/env bash

# Manages aptly input repositories and publishes a merged output repository.
# Structure is as follows:
# repo1 ───> snapshot "repo1-%Y-%m-%d" ─┬─> snapshot "%Y-%m-%d" ───> publish inside $TBASE/public
#                                       │
# repo2 ───> snapshot "repo2-%Y-%m-%d" ─┘
# 
# Furthermore, our PACKAGE input folder is /tmp/aptly with repository names as subfolder, so if
# you want a new package added inside repo1, you would place a file inside /tmp/aptly/repo1/.
# The package would then be added and its file inside /tmp be removed.
#
# Why bash?
# 1. This script is INTERACTIVE, it asks for your GPG passphrase before signing packages
# 2. It still uses a lot of syscalls like aptly
# 3. The steps we do are still not that resource-hungry, so the downsides while using bash are rather low
# The combination of these three led to bash in the first iteration.
#
# What doesn't this script do?
# 1. It does not create the source repos or the aptly config.
# 2. It does not repair if one of the parts of the structure above is missing; in that case,
#    we assume an extraordinary failure and will fail ourselves. Exception: We skip failing
#    on removal if a published repo or a snapshot does not exist.
# 3. It does not manage multiple snapshots yet. Nor does it cleanup snapshot remainders in case the script
#    is interrupted. Both can be considered a TODO.


MYCONF="/etc/lirion/aptly.conf"
if [ ! -r "$MYCONF" ];then
	printf '%b cannot be read, exiting!\n' "$MYCONF" >&2
	exit 254
else
	# shellcheck disable=SC1091,SC1090
	source "$MYCONF" || exit 254
fi

MALFORMED=0
[ -z "$MYREPS" ] && MALFORMED=1
[ -z "$GPGKEY" ] && MALFORMED=1
[ -z "$GPGTESTKEY" ] && GPGKEY="$GPGTESTKEY"
[ -z "$PBASE" ] && MALFORMED=1
[ -z "$TBASE" ] && MALFORMED=1

[ "$MALFORMED" -eq 1 ] && printf '%b malformed, exiting.\n' "$MYCONF" >&2 && exit 253


printf -v repjoined "%s-$(date -I) " "${MYREPS[@]}"

# shellcheck disable=SC1091
source /usr/lib/lirion/ln-initfunctions || exit 10

SNDATE="$(aptly snapshot list -raw | head -n1)"
if ! printf '%b' "$SNDATE" | grep -P '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' > /dev/null; then
	SNDATE="$(date -I)"
fi
printf -v repoldjoined "%s-$SNDATE " "${MYREPS[@]}"
printf 'Snapshot suffix that will be deleted: %b\n' "$SNDATE" || exit 11

printf 'Have you added all packages? :)\n'
printf 'Starting snapshots and publication\033[s in '
for ((i=5;i>0;--i)); do
	printf '\r\033[u\033[K in %b...' "$i"
	sleep 1
done
printf '\r\033[u\033[K:\n'

for rep in "${MYREPS[@]}"; do
	if ! aptly repo list -raw 2>/dev/null | grep -P "^${rep}$" >/dev/null; then
		lnfail "repository ${rep} does not exist!"
	fi
	lnbegin "Adding packages to repo $rep"
	if [ ! -d "${PBASE}/$rep" ]; then
		lnskip "source directory not existing"
		continue
	fi
	readarray -t debfiles < <(
		find "${PBASE}/$rep" -type l -name "*deb" 2>/dev/null
		find "${PBASE}/$rep" -type f -name "*deb" 2>/dev/null
	)
	case "${#debfiles[@]}" in
		0)
			lnskip "no files in ${PBASE}/$rep"
		;;
		*)
			for debfile in "${debfiles[@]}"; do
				lnprog "$(basename "$debfile")"
				sleep 0.271828
				if ! aptly repo add "$rep" "$debfile" >/dev/null 2>&1; then
					lnfail "adding file failed"
					exit 100
				fi
				if ! rm -f "$debfile" >/dev/null 2>&1; then
					lnfail "source file removal failed"
					exit 101
				fi
			done
			lnok
		;;
	esac
done

lnbegin "Unpublishing repo \"all\""
if [ "$(aptly publish list | grep -cv '^No snapshots/local repos')" -lt 1 ]; then
	lnskip "No snapshots / published repos"
else
	if ! aptly publish drop all >/dev/null 2>&1; then
		lnfail
		exit 110
	fi
fi
lnok

lnbegin "Dropping snapshots"
for ss in "$SNDATE" ${repoldjoined%,};do
	lnprog "$ss"
	sleep 0.271828
	if ! aptly snapshot list -raw | grep "^$ss\$" > /dev/null; then
		lnprog "skipping $ss, not present"
		sleep 0.314159
	else
		if ! aptly snapshot drop "$ss" >/dev/null 2>&1; then
			lnfail
			exit 111
		fi
	fi
done
lnok

lnbegin "Creating fresh snapshots"
for rep in "${MYREPS[@]}"; do
	lnprog "$rep"
	sleep 0.271828
	if ! aptly snapshot create \
			"${rep}-$(date -I)" from repo "$rep" >/dev/null 2>&1; then
		lnfail
		exit 120
	fi
done
lnok

lnbegin "Merging snapshots"
printf -v repjoined "%s-$(date -I) " "${MYREPS[@]}"
# shellcheck disable=SC2086
if ! aptly snapshot merge "$(date -I)" ${repjoined%,} >/dev/null 2>&1; then
	lnfail
	exit 121
fi
lnok

printf 'GPG pseudo operation...\n'
MYLEL="$(mktemp --tmpdir lel.XXX)"
printf 'lel\n' > "$MYLEL" || exit 122
gpg -eu "$GPGKEY" -r "$GPGTESTKEY" "$MYLEL" || exit 122
gpg -qd "${MYLEL}.gpg" > /dev/null || exit 122
rm "$MYLEL" "${MYLEL}.gpg" || exit 122
printf '...done.\n'

lnbegin "Publishing snapshot result"
if ! aptly publish snapshot \
		-gpg-key="$GPGKEY" -distribution='all' "$(date -I)" >/dev/null 2>&1; then
	lnfail
	exit 123
fi
lnok

lnbegin "Creating aptly graphs"
for layout in horizontal vertical; do
	lnprog "$layout"
	if ! aptly graph -layout="$layout" \
			-output="${TBASE}/public/aptly-graph-${layout}.png" >/dev/null 2>&1; then
		lnfail
		exit 130
	fi
done
lnok

lnbegin "Creating sha256 checksums"
lnprog "directories"
if ! mkdir -p "${TBASE}/public/dists/all/utils"/binary-{amd64,arm64}/by-hash/SHA{256,512} 2>/dev/null; then
	lnfail "directory creation failed"
	exit 140
fi
lnprog "old hash removal"
for dir in "${TBASE}/public/dists/all/utils"/binary-{amd64,arm64}/by-hash/SHA{256,512}; do
	if ! find "$dir" -type l -exec rm -f '{}' \; ; then
		lnfail "hash file removal failed in $dir"
		exit 141
	fi
done
lnprog "hash creation"
for file in "${TBASE}/public/dists/all/utils"/binary-{amd64,arm64}/{Packages,Release}*;do
	S256="$(sha256sum "$file"|awk '{print $1}')"
	S512="$(sha512sum "$file"|awk '{print $1}')"
	PROGSTR="SHA256 hash for $(dirname "$file")/$(basename "$file")"
	PROGSTR="$(printf '%b' "$PROGSTR" | sed 's#.*\(/dists/\)#...\1#')"
	lnprog "$PROGSTR"
	unset PROGSTR
	sleep 0.271828
	if ! ln -fs "../../$(basename "$file")" "$(dirname "$file")/by-hash/SHA256/$S256" 2>/dev/null; then
		lnfail "hash failed for $(dirname "$file")/$(basename "$file")"
		exit 142
	fi
	PROGSTR="SHA512 hash for $(dirname "$file")/$(basename "$file")"
	PROGSTR="$(printf '%b' "$PROGSTR" | sed 's#.*\(/dists/\)#...\1#')"
	lnprog "$PROGSTR"
	unset PROGSTR
	sleep 0.271828
	if ! ln -fs "../../$(basename "$file")" "$(dirname "$file")/by-hash/SHA512/$S512" 2>/dev/null; then
		lnfail "hash failed for $(dirname "$file")/$(basename "$file")"
		exit 142
	fi
done
lnok