git.lirion.de

Of git, get, and gud

summaryrefslogtreecommitdiffstats
path: root/aptly/bin/aptly-lirionde
blob: 8933cd356097605e4043733a3ea66d14e73845e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 ! faketime "$(date -I) 13:37:08" 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 ! faketime "$(date -I) 13:37:11" 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