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
|