reproducible_html_dashboard.sh 28 KB
Newer Older
1
2
#!/bin/bash

3
# Copyright 2014-2015 Holger Levsen <holger@layer-acht.org>
4
#         © 2015 Mattia Rizzolo <mattia@mapreri.org>
5
6
# released under the GPLv=2

Holger Levsen's avatar
Holger Levsen committed
7
DEBUG=false
Holger Levsen's avatar
Holger Levsen committed
8
9
10
. /srv/jenkins/bin/common-functions.sh
common_init "$@"

Holger Levsen's avatar
Holger Levsen committed
11
12
# common code defining db access
. /srv/jenkins/bin/reproducible_common.sh
13

14
#
15
# init some variables
16
#
Holger Levsen's avatar
Holger Levsen committed
17
# we only do stats up until yesterday... we also could do today too but not update the db yet...
18
DATE=$(date -d "1 day ago" '+%Y-%m-%d')
19
FORCE_DATE=$(date -d "3 days ago" '+%Y-%m-%d')
20
21
NOTES_GIT_PATH="/var/lib/jenkins/jobs/reproducible_html_notes/workspace"

Holger Levsen's avatar
Holger Levsen committed
22
# variables related to the stats we update
23
24
25
26
27
28
29
FIELDS[0]="datum, reproducible, unreproducible, FTBFS, other, untested"
FIELDS[1]="datum"
for i in reproducible unreproducible FTBFS other ; do
	for j in $SUITES ; do
		FIELDS[1]="${FIELDS[1]}, ${i}_${j}"
	done
done
30
FIELDS[2]="datum, oldest"
31
FIELDS[3]="datum "
32
33
# for $(sorted list of usertags)…
for TAG in $(echo "$USERTAGS" | sed -s "s# #\n#g" | sort -u | xargs echo) ; do
34
35
36
37
	# for this table (#3) bugs with ftbfs tags are ignored _now_…
	if [ "$TAG" = "ftbfs" ] ; then
		continue
	fi
38
39
	FIELDS[3]="${FIELDS[3]}, open_$TAG, done_$TAG"
done
40
41
42
# …and added at the end (so they are not ignored but rather sorted this way)
# Also note how FIELDS is only used for reading data, not writing.
FIELDS[3]="${FIELDS[3]}, open_ftbfs, done_ftbfs"
43
44
FIELDS[4]="datum, packages_with_notes"
FIELDS[5]="datum, known_issues"
45
46
47
FIELDS[7]="datum, done_bugs, open_bugs"
SUM_DONE="(0"
SUM_OPEN="(0"
48
# no need to sort the usertags, only their sum will be used
49
50
51
52
53
54
for TAG in $USERTAGS ; do
	SUM_DONE="$SUM_DONE+done_$TAG"
	SUM_OPEN="$SUM_OPEN+open_$TAG"
done
SUM_DONE="$SUM_DONE)"
SUM_OPEN="$SUM_OPEN)"
55
FIELDS[8]="datum "
56
57
# for $(sorted list of usertags)…
for TAG in $(echo "$USERTAGS" | sed -s "s# #\n#g" | sort -u | xargs echo) ; do
58
	# for this table (#8) bugs with ftbfs tags are ignored.
59
60
61
62
63
64
65
66
67
	if [ "$TAG" = "ftbfs" ] ; then
		continue
	fi
	FIELDS[8]="${FIELDS[8]}, open_$TAG, done_$TAG"
done
FIELDS[9]="datum, done_bugs, open_bugs"
REPRODUCIBLE_DONE="(0"
REPRODUCIBLE_OPEN="(0"
for TAG in $USERTAGS ; do
68
	# for this table (#9) bugs with ftbfs tags are ignored.
69
70
71
72
73
74
75
76
	if [ "$TAG" = "ftbfs" ] ; then
		continue
	fi
	REPRODUCIBLE_DONE="$REPRODUCIBLE_DONE+done_$TAG"
	REPRODUCIBLE_OPEN="$REPRODUCIBLE_OPEN+open_$TAG"
done
REPRODUCIBLE_DONE="$REPRODUCIBLE_DONE)"
REPRODUCIBLE_OPEN="$REPRODUCIBLE_OPEN)"
77
COLOR[0]=5
Holger Levsen's avatar
Holger Levsen committed
78
COLOR[1]=12
79
COLOR[2]=1
80
COLOR[3]=32
81
82
COLOR[4]=1
COLOR[5]=1
83
COLOR[7]=2
84
85
COLOR[8]=30
COLOR[9]=2
86
MAINLABEL[1]="Amount of packages built each day"
87
MAINLABEL[3]="Bugs (with all usertags) for user reproducible-builds@lists.alioth.debian.org"
88
89
MAINLABEL[4]="Packages which have notes"
MAINLABEL[5]="Identified issues"
90
91
92
MAINLABEL[7]="Open and closed bugs (with all usertags)"
MAINLABEL[8]="Bugs (with all usertags except 'ftbfs') for user reproducible-builds@lists.alioth.debian.org"
MAINLABEL[9]="Open and closed bugs (with all usertags except tagged 'ftbfs')"
93
94
95
96
97
98
YLABEL[0]="Amount (total)"
YLABEL[1]="Amount (per day)"
YLABEL[2]="Age in days"
YLABEL[3]="Amount of bugs"
YLABEL[4]="Amount of packages"
YLABEL[5]="Amount of issues"
99
YLABEL[7]="Amount of bugs open / closed"
100
101
YLABEL[8]="Amount of bugs"
YLABEL[9]="Amount of bugs open / closed"
102
103

#
104
# update package + build stats
105
#
106
update_suite_arch_stats() {
107
	RESULT=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT datum,suite from ${TABLE[0]} WHERE datum = \"$DATE\" AND suite = \"$SUITE\" AND architecture = \"$ARCH\"")
108
	if [ -z $RESULT ] ; then
109
110
111
112
113
114
115
116
117
118
119
120
121
		echo "Updating packages and builds stats for $SUITE/$ARCH in $DATE."
		ALL=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT count(name) FROM sources WHERE suite='${SUITE}' AND architecture='$ARCH'")
		GOOD=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT count(r.status) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE s.suite='$SUITE' AND s.architecture='$ARCH' AND r.status = 'reproducible' AND date(r.build_date)<='$DATE';")
		GOOAY=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT count(r.status) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE s.suite='$SUITE' AND s.architecture='$ARCH' AND r.status = 'reproducible' AND date(r.build_date)='$DATE';")
		BAD=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT count(r.status) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE s.suite='$SUITE' AND s.architecture='$ARCH' AND r.status = 'unreproducible' AND date(r.build_date)<='$DATE';")
		BAAY=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT count(r.status) FROM results AS r JOIN sources AS s ON r.package_id=s.id  WHERE s.suite='$SUITE' AND s.architecture='$ARCH' AND r.status = 'unreproducible' AND date(r.build_date)='$DATE';")
		UGLY=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT count(r.status) FROM results AS r JOIN sources AS s ON r.package_id=s.id  WHERE s.suite='$SUITE' AND s.architecture='$ARCH' AND r.status = 'FTBFS' AND date(r.build_date)<='$DATE';")
		UGLDAY=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT count(r.status) FROM results AS r JOIN sources AS s ON r.package_id=s.id  WHERE s.suite='$SUITE' AND s.architecture='$ARCH' AND r.status = 'FTBFS' AND date(r.build_date)='$DATE';")
		REST=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT count(r.status) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE (r.status != 'FTBFS' AND r.status != 'unreproducible' AND r.status != 'reproducible') AND s.suite='$SUITE' AND s.architecture='$ARCH' AND date(r.build_date)<='$DATE';")
		RESDAY=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT count(r.status) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE (r.status != 'FTBFS' AND r.status != 'unreproducible' AND r.status != 'reproducible') AND s.suite='$SUITE' AND s.architecture='$ARCH' AND date(r.build_date)='$DATE';")
		OLDESTG=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT r.build_date FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE r.status = 'reproducible' AND s.suite='$SUITE' AND s.architecture='$ARCH' AND NOT date(r.build_date)>='$DATE' ORDER BY r.build_date LIMIT 1;")
		OLDESTB=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT r.build_date FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE s.suite='$SUITE' AND s.architecture='$ARCH' AND r.status = 'unreproducible' AND NOT date(r.build_date)>='$DATE' ORDER BY r.build_date LIMIT 1;")
		OLDESTU=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT r.build_date FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE s.suite='$SUITE' AND s.architecture='$ARCH' AND r.status = 'FTBFS' AND NOT date(r.build_date)>='$DATE' ORDER BY r.build_date LIMIT 1;")
122
123
124
125
126
127
128
129
130
131
132
		DIFFG=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT julianday('$DATE') - julianday('$OLDESTG');")
		if [ -z $DIFFG ] ; then DIFFG=0 ; fi
		DIFFB=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT julianday('$DATE') - julianday('$OLDESTB');")
		if [ -z $DIFFB ] ; then DIFFB=0 ; fi
		DIFFU=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT julianday('$DATE') - julianday('$OLDESTU');")
		if [ -z $DIFFU ] ; then DIFFU=0 ; fi
		let "TOTAL=GOOD+BAD+UGLY+REST" || true # let FOO=0+0 returns error in bash...
		if [ "$ALL" != "$TOTAL" ] ; then
			let "UNTESTED=ALL-TOTAL"
		else
			UNTESTED=0
133
		fi
134
135
136
		sqlite3 -init ${INIT} ${PACKAGES_DB} "INSERT INTO ${TABLE[0]} VALUES (\"$DATE\", \"$SUITE\", \"$ARCH\", $UNTESTED, $GOOD, $BAD, $UGLY, $REST)" 
		sqlite3 -init ${INIT} ${PACKAGES_DB} "INSERT INTO ${TABLE[1]} VALUES (\"$DATE\", \"$SUITE\", \"$ARCH\", $GOOAY, $BAAY, $UGLDAY, $RESDAY)"
		sqlite3 -init ${INIT} ${PACKAGES_DB} "INSERT INTO ${TABLE[2]} VALUES (\"$DATE\", \"$SUITE\", \"$ARCH\", \"$DIFFG\", \"$DIFFB\", \"$DIFFU\")"
137
		# we do 3 later and 6 is special anyway...
138
		for i in 0 1 2 4 5 ; do
139
			PREFIX=""
140
			if [ $i -eq 0 ] || [ $i -eq 2 ] ; then
Holger Levsen's avatar
Holger Levsen committed
141
				PREFIX=$SUITE/$ARCH
142
143
			fi
			# force regeneration of the image if it exists
144
			if [ -f $BASE/$PREFIX/${TABLE[$i]}.png ] ; then
145
				echo "Touching $PREFIX/${TABLE[$i]}.png..."
146
				touch -d "$FORCE_DATE 00:00" $BASE/$PREFIX/${TABLE[$i]}.png
147
148
149
150
			fi
		done
	fi
}
151
152

#
153
# update notes stats
154
#
155
update_notes_stats() {
156
	NOTES=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT COUNT(package_id) FROM notes AS n JOIN sources AS s ON n.package_id=s.id WHERE s.suite=\"unstable\"")
157
158
159
160
	ISSUES=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT COUNT(name) FROM issues")
	# the following is a hack to workaround the bad sql db design which is the issue_s_ column in the notes table...
	# it assumes we don't have packages with more than 7 issues. (we have one with 6...)
	COUNT_ISSUES=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT \
161
162
163
		(SELECT COUNT(issues) FROM notes AS n JOIN sources AS s ON n.package_id=s.id WHERE s.suite=\"unstable\" AND n.issues = \"[]\") \
		+ \
		(SELECT COUNT(issues) FROM notes AS n JOIN sources AS s ON n.package_id=s.id WHERE s.suite=\"unstable\" AND n.issues != \"[]\" AND n.issues NOT LIKE \"%,%\") \
164
165
166
167
168
169
170
171
172
173
174
		+ \
		(SELECT COUNT(issues) FROM notes AS n JOIN sources AS s ON n.package_id=s.id WHERE s.suite=\"unstable\" AND n.issues LIKE \"%,%\") \
		+ \
		(SELECT COUNT(issues) FROM notes AS n JOIN sources AS s ON n.package_id=s.id WHERE s.suite=\"unstable\" AND n.issues LIKE \"%,%,%\") \
		+ \
		(SELECT COUNT(issues) FROM notes AS n JOIN sources AS s ON n.package_id=s.id WHERE s.suite=\"unstable\" AND n.issues LIKE \"%,%,%,%\") \
		+ \
		(SELECT COUNT(issues) FROM notes AS n JOIN sources AS s ON n.package_id=s.id WHERE s.suite=\"unstable\" AND n.issues LIKE \"%,%,%,%,%\") \
		+ \
		(SELECT COUNT(issues) FROM notes AS n JOIN sources AS s ON n.package_id=s.id WHERE s.suite=\"unstable\" AND n.issues LIKE \"%,%,%,%,%,%\") \
		")
175
176
177
178
179
	RESULT=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT datum from ${TABLE[4]} WHERE datum = \"$DATE\"")
	if [ -z $RESULT ] ; then
		echo "Updating notes stats for $DATE."
		sqlite3 -init ${INIT} ${PACKAGES_DB} "INSERT INTO ${TABLE[4]} VALUES (\"$DATE\", \"$NOTES\")"
		sqlite3 -init ${INIT} ${PACKAGES_DB} "INSERT INTO ${TABLE[5]} VALUES (\"$DATE\", \"$ISSUES\")"
180
	fi
181
}
182

183
#
184
# gather suite/arch stats
185
#
186
187
188
189
190
191
192
193
194
195
gather_suite_arch_stats() {
	AMOUNT=$(sqlite3 -init $INIT $PACKAGES_DB "SELECT count(*) FROM sources WHERE suite=\"${SUITE}\" AND architecture=\"$ARCH\"")
	COUNT_TOTAL=$(sqlite3 -init $INIT $PACKAGES_DB "SELECT COUNT(*) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE s.suite=\"${SUITE}\" AND s.architecture=\"$ARCH\"")
	COUNT_GOOD=$(sqlite3 -init $INIT $PACKAGES_DB "SELECT COUNT(*) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE s.suite=\"${SUITE}\" AND s.architecture=\"$ARCH\" AND r.status=\"reproducible\"")
	COUNT_BAD=$(sqlite3 -init $INIT $PACKAGES_DB "SELECT COUNT(s.name) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE s.suite='$SUITE' AND s.architecture=\"$ARCH\" AND r.status = \"unreproducible\"")
	COUNT_UGLY=$(sqlite3 -init $INIT $PACKAGES_DB "SELECT COUNT(s.name) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE s.suite='$SUITE' AND s.architecture=\"$ARCH\" AND r.status = \"FTBFS\"")
	COUNT_SOURCELESS=$(sqlite3 -init $INIT $PACKAGES_DB "SELECT COUNT(s.name) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE s.suite='$SUITE' AND s.architecture=\"$ARCH\" AND r.status = \"404\"")
	COUNT_NOTFORUS=$(sqlite3 -init $INIT $PACKAGES_DB "SELECT COUNT(s.name) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE s.suite='$SUITE' AND s.architecture=\"$ARCH\" AND r.status = \"not for us\"")
	COUNT_BLACKLISTED=$(sqlite3 -init $INIT $PACKAGES_DB "SELECT COUNT(s.name) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE s.suite='$SUITE' AND s.architecture=\"$ARCH\" AND r.status = \"blacklisted\"")
	COUNT_DEPWAIT=$(sqlite3 -init $INIT $PACKAGES_DB "SELECT COUNT(s.name) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE s.suite='$SUITE' AND s.architecture=\"$ARCH\" AND r.status = \"depwait\"")
196
	COUNT_OTHER=$(( $COUNT_SOURCELESS+$COUNT_NOTFORUS+$COUNT_BLACKLISTED+$COUNT_DEPWAIT ))
197
	PERCENT_TOTAL=$(echo "scale=1 ; ($COUNT_TOTAL*100/$AMOUNT)" | bc)
198
199
200
201
202
203
204
	PERCENT_GOOD=$(echo "scale=1 ; ($COUNT_GOOD*100/$COUNT_TOTAL)" | bc || echo 0)
	PERCENT_BAD=$(echo "scale=1 ; ($COUNT_BAD*100/$COUNT_TOTAL)" | bc || echo 0)
	PERCENT_UGLY=$(echo "scale=1 ; ($COUNT_UGLY*100/$COUNT_TOTAL)" | bc || echo 0)
	PERCENT_NOTFORUS=$(echo "scale=1 ; ($COUNT_NOTFORUS*100/$COUNT_TOTAL)" | bc || echo 0)
	PERCENT_DEPWAIT=$(echo "scale=1 ; ($COUNT_DEPWAIT*100/$COUNT_TOTAL)" | bc || echo 0)
	PERCENT_SOURCELESS=$(echo "scale=1 ; ($COUNT_SOURCELESS*100/$COUNT_TOTAL)" | bc || echo 0)
	PERCENT_OTHER=$(echo "scale=1 ; ($COUNT_OTHER*100/$COUNT_TOTAL)" | bc || echo 0)
205
206
}

207
#
208
# update bug stats
209
#
210
211
212
213
214
215
update_bug_stats() {
	RESULT=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT * from ${TABLE[3]} WHERE datum = \"$DATE\"")
	if [ -z $RESULT ] ; then
		echo "Updating bug stats for $DATE."
		declare -a DONE
		declare -a OPEN
216
		GOT_BTS_RESULTS=false
217
218
219
220
221
222
223
224
225
226
227
		SQL="INSERT INTO ${TABLE[3]} VALUES (\"$DATE\" "
		for TAG in $USERTAGS ; do
			OPEN[$TAG]=$(bts select usertag:$TAG users:reproducible-builds@lists.alioth.debian.org status:open status:forwarded 2>/dev/null|wc -l)
			DONE[$TAG]=$(bts select usertag:$TAG users:reproducible-builds@lists.alioth.debian.org status:done archive:both 2>/dev/null|wc -l)
			# test if both values are integers
			if ! ( [[ ${DONE[$TAG]} =~ ^-?[0-9]+$ ]] && [[ ${OPEN[$TAG]} =~ ^-?[0-9]+$ ]] ) ; then
				echo "Non-integers value detected, exiting."
				echo "Usertag: $TAG"
				echo "Open: ${OPEN[$TAG]}"
				echo "Done: ${DONE[$TAG]}"
				exit 1
Holger Levsen's avatar
Holger Levsen committed
228
			elif [ ! "${DONE[$TAG]}" = "0" ] || [ ! "${OPEN[$TAG]}" = "0" ] ; then
229
				GOT_BTS_RESULTS=true
230
231
232
233
234
			fi
			SQL="$SQL, ${OPEN[$TAG]}, ${DONE[$TAG]}"
		done
		SQL="$SQL)"
		echo $SQL
235
236
237
238
		if $GOT_BTS_RESULTS ; then
			echo "Updating ${PACKAGES_DB} with bug stats for $DATE."
			sqlite3 -init ${INIT} ${PACKAGES_DB} "$SQL"
			# force regeneration of the image
239
240
241
242
243
			local i=0
			for i in 3 7 8 9 ; do
				echo "Touching ${TABLE[$i]}.png..."
				touch -d "$FORCE_DATE 00:00" $BASE/${TABLE[$i]}.png
			done
244
		fi
245
246
	fi
}
247

248
249
250
#
# gather bugs stats and generate html table
#
251
252
write_usertag_table() {
	RESULT=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT * from ${TABLE[3]} WHERE datum = \"$DATE\"")
Holger Levsen's avatar
Holger Levsen committed
253
	if [ ! -z "$RESULT" ] ; then
254
		COUNT=0
255
		TOPEN=0 ; TDONE=0 ; TTOTAL=0
256
257
258
259
		for FIELD in $(echo ${FIELDS[3]} | tr -d ,) ; do
			let "COUNT+=1"
			VALUE=$(echo $RESULT | cut -d "|" -f$COUNT)
			if [ $COUNT -eq 1 ] ; then
260
				write_page "<table class=\"main\" id=\"usertagged-bugs\"><tr><th>Usertagged bugs</th><th>Open</th><th>Done</th><th>Total</th></tr>"
261
			elif [ $((COUNT%2)) -eq 0 ] ; then
Holger Levsen's avatar
Holger Levsen committed
262
				write_page "<tr><td><a href=\"https://bugs.debian.org/cgi-bin/pkgreport.cgi?tag=${FIELD:5};users=reproducible-builds@lists.alioth.debian.org&amp;archive=both\">${FIELD:5}</a></td><td>$VALUE</td>"
263
				TOTAL=$VALUE
264
				let "TOPEN=TOPEN+VALUE" || TOPEN=0
265
			else
266
				write_page "<td>$VALUE</td>"
267
				let "TOTAL=TOTAL+VALUE" || true # let FOO=0+0 returns error in bash...
268
				let "TDONE=TDONE+VALUE"
269
				write_page "<td>$TOTAL</td></tr>"
270
				let "TTOTAL=TTOTAL+TOTAL"
271
272
			fi
		done
273
		write_page "<tr><td>Total number of <a href=\"https://wiki.debian.org/ReproducibleBuilds/Contribute#How_to_report_bugs\">usertags related to reproducible builds</a> on $DATE<br />(this is not the number of bugs as bugs can have several tags)</td><td>$TOPEN</td><td>$TDONE</td><td>$TTOTAL</td></tr>"
274
275
276
277
		write_page "</table>"
	fi
}

Holger Levsen's avatar
Holger Levsen committed
278
#
279
# write build performance stats
Holger Levsen's avatar
Holger Levsen committed
280
#
281
write_build_performance_stats() {
Holger Levsen's avatar
Holger Levsen committed
282
	if [ "$ARCH" = "amd64" ] || [ "$ARCH" = "armhf" ] ; then
283
284
285
		TIMESPAN_VERBOSE="4 weeks"
		TIMESPAN_RAW="28"
	else
286
287
		TIMESPAN_VERBOSE="1 week"
		TIMESPAN_RAW="7"
288
	fi
Holger Levsen's avatar
Holger Levsen committed
289
290
	write_page "<table class=\"main\"><tr><th colspan=\"2\">Build statistics for $ARCH</th></tr>"
	AGE_UNSTABLE=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT CAST(max(oldest_reproducible, oldest_unreproducible, oldest_FTBFS) AS INTEGER) FROM ${TABLE[2]} WHERE suite='unstable' AND architecture='$ARCH' AND datum='$DATE'")
291
292
293
294
295
296
297
	if [ "$ARCH" != "armhf" ] ; then
		AGE_TESTING=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT CAST(max(oldest_reproducible, oldest_unreproducible, oldest_FTBFS) AS INTEGER) FROM ${TABLE[2]} WHERE suite='testing' AND architecture='$ARCH' AND datum='$DATE'")
		AGE_EXPERIMENTAL=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT CAST(max(oldest_reproducible, oldest_unreproducible, oldest_FTBFS) AS INTEGER) FROM ${TABLE[2]} WHERE suite='experimental' AND architecture='$ARCH' AND datum='$DATE'")
		write_page "<tr><td>oldest $ARCH build result in testing / unstable / experimental</td><td>$AGE_TESTING / $AGE_UNSTABLE / $AGE_EXPERIMENTAL days</td></tr>"
	else
		write_page "<tr><td>oldest $ARCH build result in unstable </td><td>$AGE_UNSTABLE days</td></tr>"
	fi
298
	RESULT=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT CAST(AVG(r.build_duration) AS INTEGER) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE r.build_duration!='' AND r.build_duration!='0' AND r.build_date LIKE '%$DATE%' AND s.architecture='$ARCH'")
Holger Levsen's avatar
Holger Levsen committed
299
300
301
	MIN=$(echo $RESULT/60|bc)
	SEC=$(echo "$RESULT-($MIN*60)"|bc)
	write_page "<tr><td>average test duration (on $DATE)</td><td>$MIN minutes, $SEC seconds</td></tr>"
302
	RESULT=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT CAST(AVG(r.build_duration) AS INTEGER) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE r.build_duration!='' AND r.build_duration!='0' AND r.build_date > datetime('$DATE', '-$TIMESPAN_RAW days') AND s.architecture='$ARCH'")
Holger Levsen's avatar
Holger Levsen committed
303
304
	MIN=$(echo $RESULT/60|bc)
	SEC=$(echo "$RESULT-($MIN*60)"|bc)
305
	write_page "<tr><td>average test duration (in the last $TIMESPAN_VERBOSE)</td><td>$MIN minutes, $SEC seconds</td></tr>"
306
	RESULT=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT COUNT(r.build_date) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE r.build_date LIKE '%$DATE%' AND s.architecture='$ARCH'")
Holger Levsen's avatar
Holger Levsen committed
307
	write_page "<tr><td>packages tested on $DATE</td><td>$RESULT</td></tr>"
308
309
	RESULT=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT COUNT(r.build_date) FROM results AS r JOIN sources AS s ON r.package_id=s.id WHERE r.build_date > datetime('$DATE', '-$TIMESPAN_RAW days') AND s.architecture='$ARCH'")
	RESULT="$(echo $RESULT/$TIMESPAN_RAW|bc)"
310
	write_page "<tr><td>packages tested on average per day in the last $TIMESPAN_VERBOSE</td><td>$RESULT</td></tr>"
Holger Levsen's avatar
Holger Levsen committed
311
312
313
	write_page "</table>"
}

314
315
316
317
318
319
320
321
322
323
324
#
# write suite table
#
write_suite_table() {
	write_page "<p>"
	write_page "<table class=\"main\"><tr><th>suite</th><th>all sources packages</th><th>reproducible packages</th><th>unreproducible packages</th><th>packages failing to build</th><th>other packages</th></tr>"
	for SUITE in $SUITES ; do
		if [ "$ARCH" = "armhf" ] && [ "$SUITE" != "unstable" ] ; then
			continue
		fi
		gather_suite_arch_stats
325
		write_page "<tr><td>$SUITE/$ARCH</td><td>$AMOUNT"
326
327
328
329
330
331
332
333
334
		if [ $(echo $PERCENT_TOTAL/1|bc) -lt 98 ] ; then
			write_page "<span style=\"font-size:0.8em;\">($PERCENT_TOTAL% tested)</span>"
		fi
		write_page "</td><td>$COUNT_GOOD / $PERCENT_GOOD%</td><td>$COUNT_BAD / $PERCENT_BAD%</td><td>$COUNT_UGLY / $PERCENT_UGLY%</td><td>$COUNT_OTHER / $PERCENT_OTHER%</td></tr>"
	done
        write_page "</table>"
	write_page "</p><p style=\"clear:both;\">"
}

335
336
337
#
# create suite stats page
#
338
339
create_suite_arch_stats_page() {
	VIEW=suite_${ARCH}_stats
340
	PAGE=index_${VIEW}.html
341
342
	MAINLABEL[0]="Reproducibility status for packages in '$SUITE' for '$ARCH'"
	MAINLABEL[2]="Age in days of oldest reproducible build result in '$SUITE' for '$ARCH'"
343
	echo "$(date) - starting to write $PAGE page."
344
	write_page_header $VIEW "Overview of reproducible builds for packages in $SUITE for $ARCH"
345
	if [ $(echo $PERCENT_TOTAL/1|bc) -lt 98 ] ; then
346
		write_page "<p>$COUNT_TOTAL packages have been attempted to be build so far, that's $PERCENT_TOTAL% of $AMOUNT source packages in Debian $SUITE/$ARCH.</p>"
347
348
349
	fi
	write_page "<p>"
	set_icon reproducible
350
	write_icon
351
352
353
	write_page "$COUNT_GOOD packages ($PERCENT_GOOD%) successfully built reproducibly in $SUITE/$ARCH."
	set_icon unreproducible
	write_icon
354
	write_page "$COUNT_BAD packages ($PERCENT_BAD%) failed to build reproducibly."
355
356
357
358
	set_icon FTBFS
	write_icon
	write_page "$COUNT_UGLY packages ($PERCENT_UGLY%) failed to build from source.</p>"
	write_page "<p>"
359
360
361
362
363
364
	if [ $COUNT_DEPWAIT -gt 0 ] ; then
		write_page "For "
		set_icon depwait
		write_icon
		write_page "$COUNT_DEPWAIT ($PERCENT_DEPWAIT%) source packages the build-depends cannot be satisfied."
	fi
365
366
367
368
	if [ $COUNT_SOURCELESS -gt 0 ] ; then
		write_page "For "
		set_icon 404
		write_icon
369
		write_page "$COUNT_SOURCELESS ($PERCENT_SOURCELESS%) source packages could not be downloaded,"
370
	fi
371
372
	set_icon not_for_us
	write_icon
373
374
375
376
	if [ "$ARCH" = "armhf" ] ; then
		ARMSPECIALARCH=" 'any-arm',"
	fi
	write_page "$COUNT_NOTFORUS ($PERCENT_NOTFORUS%) packages which are neither Architecture: 'any', 'all', '$ARCH', 'linux-any', 'linux-$ARCH'$ARMSPECIALARCH nor 'any-$ARCH' will not be build here"
377
378
379
380
381
	write_page "and those "
	set_icon blacklisted
	write_icon
	write_page "$COUNT_BLACKLISTED blacklisted packages neither.</p>"
	write_page "<p>"
382
	write_page " <a href=\"/$SUITE/$ARCH/${TABLE[0]}.png\"><img src=\"/$SUITE/$ARCH/${TABLE[0]}.png\" alt=\"${MAINLABEL[0]}\"></a>"
383
	for i in 0 2 ; do
384
		# recreate png once a day
385
386
		if [ ! -f $BASE/$SUITE/$ARCH/${TABLE[$i]}.png ] || [ ! -z $(find $BASE/$SUITE/$ARCH -maxdepth 1 -mtime +0 -name ${TABLE[$i]}.png) ] ; then
			create_png_from_table $i $SUITE/$ARCH/${TABLE[$i]}.png
387
388
		fi
	done
389
390
391
392
393
394
	write_page "</p>"
	write_page_footer
	publish_page $SUITE
}

#
395
# create dashboard page
396
#
397
398
create_dashboard_page() {
	VIEW=dashboard
399
400
401
	PAGE=index_${VIEW}.html
	echo "$(date) - starting to write $PAGE page."
	write_page_header $VIEW "Overview of various statistics about reproducible builds"
402
	write_suite_table
Holger Levsen's avatar
Holger Levsen committed
403
	# write suite graphs
404
	for SUITE in $SUITES ; do
405
		write_page " <a href=\"/$SUITE\"><img src=\"/$SUITE/$ARCH/${TABLE[0]}.png\" class=\"overview\" alt=\"$SUITE/$ARCH stats\"></a>"
406
	done
Holger Levsen's avatar
Holger Levsen committed
407
	write_page "</p><p style=\"clear:both;\">"
408
	write_page "<center>"
409
410
	# write meta pkg graphs per suite
	for SUITE in $SUITES ; do
411
		if [ "$SUITE" != "unstable" ] ; then
Holger Levsen's avatar
Holger Levsen committed
412
			# only show pkg sets from unstable
413
414
415
			continue
		fi
		for i in $(seq 1 ${#META_PKGSET[@]}) ; do
416
			THUMB=${TABLE[6]}_${META_PKGSET[$i]}-thumbnail.png
417
			LABEL="Reproducibility status for packages in $SUITE/$ARCH from '${META_PKGSET[$i]}'"
418
			write_page "<a href=\"/$SUITE/$ARCH/pkg_set_${META_PKGSET[$i]}.html\"><img src=\"/$SUITE/$ARCH/$THUMB\" class=\"metaoverview\" alt=\"$LABEL\"></a>"
419
		done
420
	done
421
	write_page "</center></p><p>"
422
	# write inventory table
423
	write_page "<table class=\"main\"><tr><th colspan=\"2\">Various reproducibility statistics</th></tr>"
424
	write_page "<tr><td>identified <a href=\"/index_issues.html\">distinct and categorized issues</a></td><td>$ISSUES</td></tr>"
425
	write_page "<tr><td>total number of identified issues in packages</td><td>$COUNT_ISSUES</td></tr>"
426
	write_page "<tr><td>packages with notes about these issues</td><td>$NOTES</td></tr>"
427
	SUITE="unstable"
428
	gather_suite_arch_stats
429
	RESULT=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT COUNT(*) FROM (SELECT s.id FROM sources AS s JOIN results AS r ON r.package_id=s.id WHERE r.status IN ('unreproducible', 'FTBFS', 'blacklisted') AND s.id NOT IN (SELECT package_id FROM notes) AND s.suite='$SUITE' AND s.architecture='$ARCH')")
Holger Levsen's avatar
Holger Levsen committed
430
	write_page "<tr><td>packages in $SUITE with issues but <a href=\"/$SUITE/$ARCH/index_no_notes.html\">without identified ones</a></td><td>$RESULT / $(echo "scale=1 ; ($RESULT*100/$COUNT_TOTAL)" | bc)%</td></tr>"
431
432
433
434
	RESULT=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT COUNT(*) FROM (SELECT s.id FROM sources AS s JOIN results AS r ON r.package_id=s.id WHERE r.status='unreproducible' AND s.id NOT IN (SELECT package_id FROM notes) AND s.suite='$SUITE' AND s.architecture='$ARCH')")
	write_page "<tr><td>&nbsp;&nbsp;- unreproducible ones</a></td><td>$RESULT / $(echo "scale=1 ; ($RESULT*100/$COUNT_TOTAL)" | bc)%</td></tr>"
	RESULT=$(sqlite3 -init ${INIT} ${PACKAGES_DB} "SELECT COUNT(*) FROM (SELECT s.id FROM sources AS s JOIN results AS r ON r.package_id=s.id WHERE r.status='FTBFS' AND s.id NOT IN (SELECT package_id FROM notes) AND s.suite='$SUITE' AND s.architecture='$ARCH')")
	write_page "<tr><td>&nbsp;&nbsp;- failing to build</a></td><td>$RESULT / $(echo "scale=1 ; ($RESULT*100/$COUNT_TOTAL)" | bc)%</td></tr>"
435
	write_page "<tr><td>packages in $SUITE which need to be fixed</td><td>$(echo $COUNT_BAD + $COUNT_UGLY |bc) / $(echo $PERCENT_BAD + $PERCENT_UGLY|bc)%</td></tr>"
436
	SUITE="testing"
437
	gather_suite_arch_stats
438
439
	write_page "<tr><td>&nbsp;&nbsp;- in $SUITE</td><td>$(echo $COUNT_BAD + $COUNT_UGLY |bc) / $(echo $PERCENT_BAD + $PERCENT_UGLY|bc)%</td></tr>"
	SUITE="unstable"
440
	if [ -f ${NOTES_GIT_PATH}/packages.yml ] && [ -f ${NOTES_GIT_PATH}/issues.yml ] ; then
Holger Levsen's avatar
Holger Levsen committed
441
442
		write_page "<tr><td>committers to <a href=\"https://anonscm.debian.org/cgit/reproducible/notes.git\" target=\"_parent\">notes.git</a> (in the last three months)</td><td>$(cd ${NOTES_GIT_PATH} ; git log --since="3 months ago"|grep Author|sort -u |wc -l)</td></tr>"
		write_page "<tr><td>committers to notes.git (in total)</td><td>$(cd ${NOTES_GIT_PATH} ; git log |grep Author|sort -u |wc -l)</td></tr>"
443
	fi
444
	RESULT=$(cat /srv/reproducible-results/modified_in_sid.txt || echo "unknown")	# written by reproducible_html_repository_comparison.sh
445
	write_page "<tr><td>packages <a href=\"/index_repositories.html\">modified in our toolchain</a> (in unstable)</td><td>$(echo $RESULT)</td></tr>"
446
447
	RESULT=$(cat /srv/reproducible-results/modified_in_exp.txt || echo "unknown")	# written by reproducible_html_repository_comparison.sh
	write_page "<tr><td>&nbsp;&nbsp;- (in experimental)</td><td>$(echo $RESULT)</td></tr>"
448
449
	RESULT=$(cat /srv/reproducible-results/binnmus_needed.txt || echo "unknown")	# written by reproducible_html_repository_comparison.sh
	if [ "$RESULT" != "0" ] ; then
450
		write_page "<tr><td>&nbsp;&nbsp;- which need to be build on some archs</td><td>$(echo $RESULT)</td></tr>"
451
	fi
452
	write_page "</table>"
453
454
455
456
	# write bugs with usertags table
	write_usertag_table
	write_page "</p><p style=\"clear:both;\">"
	# do other global graphs
457
	for i in 8 9 3 7 4 5 ; do
458
		write_page " <a href=\"/${TABLE[$i]}.png\"><img src=\"/${TABLE[$i]}.png\" class="halfview" alt=\"${MAINLABEL[$i]}\"></a>"
459
		# redo pngs once a day
460
		if [ ! -f $BASE/${TABLE[$i]}.png ] || [ ! -z $(find $BASE -maxdepth 1 -mtime +0 -name ${TABLE[$i]}.png) ] ; then
461
462
463
			create_png_from_table $i ${TABLE[$i]}.png
		fi
	done
464
	write_page "</p>"
465
	# explain setup
466
	write_explaination_table debian
467
	# write build per day graph
468
	write_page "<p style=\"clear:both;\">"
469
	write_page " <a href=\"/${TABLE[1]}_$ARCH.png\"><img src=\"/${TABLE[1]}_$ARCH.png\" alt=\"${MAINLABEL[$i]}\"></a>"
470
	# redo arch specific pngs once a day
471
472
473
474
475
476
	for ARCH in ${ARCHS} ; do
		if [ ! -f $BASE/${TABLE[1]}_$ARCH.png ] || [ ! -z $(find $BASE -maxdepth 1 -mtime +0 -name ${TABLE[1]}_$ARCH.png) ] ; then
				create_png_from_table 1 ${TABLE[1]}_$ARCH.png
		fi
	done
	ARCH="amd64"
477
	# write suite builds age graphs
478
	write_page "</p><p style=\"clear:both;\">"
479
	for SUITE in $SUITES ; do
480
		write_page " <a href=\"/$SUITE/\"><img src=\"/$SUITE/$ARCH/${TABLE[2]}.png\" class=\"overview\" alt=\"age of oldest reproducible build result in $SUITE/$ARCH\"></a>"
481
	done
482
	write_build_performance_stats
483
484
	# other archs: armhf
	ARCH="armhf"
Holger Levsen's avatar
Holger Levsen committed
485
	write_page "</p><p style=\"clear:both;\">"
486
	write_page " <hr />"
487
	write_suite_table
488
	SUITE="unstable"
489
	write_page " <a href=\"/$SUITE/index_suite_${ARCH}_stats.html\"><img src=\"/$SUITE/$ARCH/${TABLE[0]}.png\" class=\"overview\" alt=\"$SUITE/$ARCH stats\"></a>"
Holger Levsen's avatar
Holger Levsen committed
490
	write_page " <a href=\"/${TABLE[1]}_$ARCH.png\"><img src=\"/${TABLE[1]}_$ARCH.png\" class=\"overview\" alt=\"${MAINLABEL[$i]}\"></a>"
491
	write_page " <a href=\"/$SUITE/$ARCH/${TABLE[2]}.png\"><img src=\"/$SUITE/$ARCH/${TABLE[2]}.png\" class=\"overview\" alt=\"age of oldest reproducible build result in $SUITE/$ARCH\"></a>"
492
	write_build_performance_stats
493
	write_page "</p><p style=\"clear:both;\">"
494
	write_page " <hr />"
495
	# link to index_breakages
Holger Levsen's avatar
Holger Levsen committed
496
	write_page "<br />There are <a href=\"$BASEURL/index_breakages.html\">some problems in this setup</a> too. And there is <a href=\"https://jenkins.debian.net/userContent/about.html#_reproducible_builds_jobs\">documentation</a> too, in case you missed the link at the top. More feedback is always welcome!</p>"
497
	# the end
498
	write_page_footer
499
	cp $PAGE $BASE/reproducible.html
500
501
502
503
504
505
	publish_page
}

#
# main
#
506
SUITE="unstable"
507
508
update_bug_stats
update_notes_stats
509
510
for ARCH in ${ARCHS} ; do
	for SUITE in $SUITES ; do
511
512
513
514
		if [ "$SUITE" != "unstable" ] && [ "$ARCH" = "armhf" ] ; then
			# we only test unstable on armhf atm
			continue
		fi
515
		update_suite_arch_stats
516
		gather_suite_arch_stats
517
		create_suite_arch_stats_page
518
	done
519
done
520
ARCH="amd64"
521
SUITE="unstable"
522
create_dashboard_page
523