Issue-ID: DOC-599
[doc.git] / tools / warnstats.sh
1 #!/bin/bash
2
3 #set -x # uncomment for bash script debugging
4
5 # ============================================================================
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 #       http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 # ============LICENSE_END=====================================================
18
19 ###
20 ### warnstats
21 ###
22 ### AUTHOR(S):
23 ### Thomas Kulik, Deutsche Telekom AG, 2020
24 ###
25 ### DESCRIPTION:
26 ### warnstat helps to find the onap modules (projects) and rst-files which are
27 ### responsible for the most warnings during the documentation build process.
28 ### it requires a tox build logfile, parses it line by line, prints out some
29 ### statistics and provides links to the local rst file, its html version, the
30 ### related link to readthedocs and as well the doc8 test result for the rst.
31 ###
32
33 ###
34 ### CHANGELOG (LATEST ON TOP)
35 ###
36 ### 1.6.0 (2020-04-03) - extended detection of docs pathes in case they are not
37 ###                      below the submodules directory
38 ### 1.5.0 (2020-03-23) - doc8 test now executed for every rst file. result is
39 ###                      provided in the output as "doc8_(nnnnn)" where nnnnn
40 ###                      is the total number of accumulated doc8 errors.
41 ###                    - improved readability of output
42 ### 1.4.0 (2020-03-18) - the link to the local html and rst file is provided in
43 ###                      the output. this may help to ease the debug process.
44 ###                      use mouse-over/context menu functionality of bash to
45 ###                      easily open files with your browser or rst editor.
46 ###                    - improved handling for module names (in case they are
47 ###                      no real onap projects/modules but directories which
48 ###                      contain additional documentation in rst format).
49 ### 1.3.1 (2020-03-10) - fixed minor typo in usage message
50 ### 1.3.0 (2020-03-09) - initial release
51 ###
52
53 script_version="1.6.0 (2020-04-03)"
54 doc8_dir=$(pwd)/doc8_results
55 logfile=$1;
56 doc8_command="doc8 --verbose"; #add options if required
57 web_base_url="https://docs.onap.org/en/latest";
58
59 echo " ";
60 echo " warnstats version ${script_version}";
61
62 declare -A module_array
63 declare -A message_short_array
64 declare -A message_long_array
65 declare -A rstfile_array
66 declare -A rstfilepath_array
67 declare -A htmlfilepath_array
68 declare -A webpath_array
69 declare -A doc8_result_array
70
71 ###
72 ### simple script argument handling
73 ###
74
75 # check if there is an argument at all
76 if [[ "$logfile" == "" ]] ; then
77     echo 'Usage: warnstats [tox-logfile]'
78     exit 1
79 fi
80
81 # check if argument is a file
82 if [ ! -f $logfile ] ; then
83     echo "Error: can't find tox-logfile \"$logfile\""
84     exit 1
85 fi
86
87 # create and clean doc8 directory
88 if [ ! -d "$doc8_dir" ]; then
89   mkdir $doc8_dir;
90 else
91   rm ${doc8_dir}/*.txt 2>/dev/null;
92 fi
93
94 # get local html build directory
95 html_build_dir=$(grep "Generated docs available in" $logfile);
96 html_build_dir=$(echo "$html_build_dir" | grep -oP " /.*/doc/docs/_build/html$");
97 html_build_dir=$(echo "$html_build_dir" | sed -r 's:^ ::');
98 echo " html build directory ..... $html_build_dir"
99 echo " web base url ............. $web_base_url";
100 echo " doc8 command ............. $doc8_command";
101 echo " doc8 results directory ... $doc8_dir";
102 echo " tox logfile .............. $logfile";
103
104 # read in the tox build logfile - use only lines which contain a warning
105 readarray -t logfile_array < <(grep ": WARNING:" $logfile);
106
107 # process filtered logfile line by line
108 for line in "${logfile_array[@]}"
109 do
110
111     # count warning lines
112     (( counter++ ));
113     echo -n -e " lines processed .......... $counter (doc8 check may take a while ...)\r";
114
115     #
116     # extract path to local rst file
117     #
118     path_rst=$line;
119     path_rst_debug=$line;
120     #echo "DBUG      line: $line"
121     # remove problematic text in line that causes regex to fail
122     path_rst=$(echo "$path_rst" | sed -r 's:, other instance in.*::');
123     #echo "DBUG path_rst: $path_rst"
124     # grep the rst file path
125     path_rst=$(echo "$path_rst" | grep -oP "^(/|docs).*\.rst");
126     #echo "DBUG path_rst: $path_rst"
127     if [[ "$path_rst" == "" ]] ; then
128       path_rst="path_to_rst_missing"
129       #echo "DBUG       path_rst: $path_rst"
130       #echo "DBUG path_rst_debug: $path_rst_debug"
131     fi
132     # finally embed the full rst path in a message to use mouse-over/context menu of bash to open file
133     path_rst_link='\e]8;;file:'$path_rst'\arst\e]8;;\a';
134     #echo -e "DBUG path_rst: "$path_rst;
135
136     #
137     # extract path to the html version of the local rst file
138     #
139     path_html=$line;
140     #echo "DBUG      line: $line"
141     # remove problematic text in line that causes regex to fail
142     path_html=$(echo "$path_html" | sed -r 's:, other instance in.*::');
143     #echo "DBUG path_html: $path_html"
144     # grep the rst file path and modify it so we get the local html build path; grep a little bit more to be save
145     path_html=$(echo "$path_html" | grep -oP "(^|/)docs(/.*|)/[\w -]*\.rst");
146     #echo "DBUG path_html: $path_html"
147     path_html=$(echo "$path_html" | sed -r 's:^/docs::');
148     #echo "DBUG path_html: $path_html"
149     path_html=$(echo "$path_html" | sed -r 's:.rst:.html:');
150     #echo "DBUG path_html: $path_html"
151     # create also the path to the web version
152     path_web_link='\e]8;;'${web_base_url}${path_html}'\aweb\e]8;;\a';
153     #echo "DBUG path_web_link: $path_web_link"
154     # finally embed the full html path in a message to use mouse-over/context menu of bash to open file
155     path_html_link='\e]8;;file:'${html_build_dir}${path_html}'\ahtml\e]8;;\a';
156     #echo -e "DBUG path_html_link: "$path_html_link;
157
158     # extract module name from line (remove all text before module name; then cut out module name)
159     module=$(echo "$line" | sed -r 's:(^.*/doc/docs/submodules/|^docs/submodules/|checking consistency... )::' | cut -f1 -d\/);
160     #echo "DBUG   line: $line"
161     #echo "DBUG module: $module"
162
163     # in case the extraction has not lead to a valid module name do some additional investigation
164     if [[ "$module" == "" ]] ; then
165
166       if [[ $line =~ doc/docs/release ]] ; then
167           module="docs_release"
168           #echo "DBUG   line: $line"
169           #echo "DBUG module: $module"
170       elif [[ $line =~ doc/docs/use-cases ]] ; then
171           module="docs_use-cases"
172           #echo "DBUG   line: $line"
173           #echo "DBUG module: $module"
174       elif [[ $line =~ doc/docs/guides/onap-developer ]] ; then
175           module="docs_guides_onap-developer"
176           #echo "DBUG   line: $line"
177           #echo "DBUG module: $module"
178       elif [[ $line =~ doc/docs/guides/onap-operator ]] ; then
179           module="docs_guides_onap-operator"
180           #echo "DBUG   line: $line"
181           #echo "DBUG module: $module"
182       elif [[ $line =~ doc/docs/guides/onap-provider ]] ; then
183           module="docs_guides_onap-provider"
184           #echo "DBUG   line: $line"
185           #echo "DBUG module: $module"
186       elif [[ $line =~ doc/docs/guides/onap-user ]] ; then
187           module="docs_guides_onap-user"
188           #echo "DBUG   line: $line"
189           #echo "DBUG module: $module"
190       elif [[ $line =~ doc/docs/guides/overview ]] ; then
191           module="docs_guides_overview"
192           #echo "DBUG   line: $line"
193           #echo "DBUG module: $module"
194       elif [[ $line =~ doc/docs/templates ]] ; then
195           module="docs_templates"
196           #echo "DBUG   line: $line"
197           #echo "DBUG module: $module"
198       elif [[ $line =~ doc/docs/guides ]] ; then
199           module="docs_guides"
200           #echo "DBUG   line: $line"
201           #echo "DBUG module: $module"
202       else
203           module="docs"
204           #echo "DBUG   line: $line"
205           #echo "DBUG module: $module"
206       fi
207
208     fi
209     #echo "DBUG   line: $line";
210     #echo "DBUG module: $module";
211
212     # get the maximum length of the variable entries to adjust table width later on
213     if [[ ${#module} -gt "$maxlength_module" ]]; then
214       maxlength_module=${#module};
215     fi
216     #echo "DBUG maxlength_module=$maxlength_module";
217
218     # extract rst file name from line and do some formatting to use it later as an array name
219     #echo "DBUG line: $line";
220     rstfile=$(echo "$line" | grep -oP "[\w -]*\.rst");
221     rstfile=$(echo -e ${rstfile} | tr '[:blank:]' '_');
222     #echo "DBUG rst-file: $rstfile";
223
224     # get the maximum length of the variable entries to adjust table width later on
225     if [[ ${#rstfile} -gt "$maxlength_rstfile" ]]; then
226       maxlength_rstfile=${#rstfile};
227     fi
228     #echo "DBUG maxlength_rstfile=$maxlength_rstfile";
229
230     # count the number of warnings for the module/rstfile combination
231     (( rstfile_array[$module | $rstfile]++ ));
232
233     # count the number of warnings for the single module
234     #echo "DBUG $module | $rstfile | $message";
235     (( module_array[$module]++ ));
236
237     # now we have all the information to fill the html/rst/web (file) path arrays
238     htmlfilepath_array[$module | $rstfile]=$path_html_link;
239      rstfilepath_array[$module | $rstfile]=$path_rst_link;
240          webpath_array[$module | $rstfile]=$path_web_link;
241
242     # extract the warning message and do some formatting
243     #message=$(echo "$line" | sed -r 's:^/.+WARNING\:\ ::');
244     message=$(echo "$line" | sed -r 's:^.+WARNING\:\ ::');
245     message=$(echo -e ${message} | tr '[:blank:]' '_');
246     message=$(echo -e ${message} | tr '/' '_');
247     message=$(echo -e ${message} | tr '.' '_');
248
249     # remove all characters from message which may cause problems in the shell
250     message="$(echo -e "${message}" | sed -e 's/[^A-Za-z0-9_-]//g')";
251     #echo "DBUG message=\"$message\""
252
253     # count the number of warnings for the single message (long version)
254     message_long="$(echo -e "${message}")";
255     (( message_long_array[$message_long]++ ))
256
257     # reduce length of message to group them more easily and then ...
258     # count the number of warnings for the single message (short version)
259     message_short="$(echo -e "${message}" | cut -c -16)";
260     (( message_short_array[$message_short]++ ))
261
262     # check rst files with doc8 and store results
263     doc8_result_path="${doc8_dir}/${module}-${rstfile}.txt";
264     #echo "DBUG ---------------------------------------------"
265     #echo "DBUG doc8_result_path=\"$doc8_result_path\""
266     # doc8 check only if result file does not exists yet AND if rst file is valid (exists)
267     if [[ ! -f "$doc8_result_path" && -f "$path_rst" ]] ; then
268         echo "FILE:$path_rst" >$doc8_result_path;
269         $doc8_command "$path_rst" >>$doc8_result_path;
270         total_acc_err=$(grep "Total accumulated errors = " $doc8_result_path);
271         #echo "DBUG total_acc_err=$total_acc_err";
272         total_acc_err=$(echo $total_acc_err | sed 's:Total accumulated errors = ::');
273         #echo "DBUG total_acc_err=$total_acc_err";
274         total_acc_err=$(printf "%05d" $total_acc_err);
275         #echo "DBUG command:doc8 ${path_rst} >>${doc8_result_path}";
276         #echo "DBUG total_acc_err=$total_acc_err";
277     fi
278     doc8_result='\e]8;;file:'${doc8_result_path}'\adoc8_('$total_acc_err')\e]8;;\a';
279     doc8_result_array[$module | $rstfile]=$doc8_result;
280
281 done
282
283 #format counter to have always x digits
284 counter=$(printf "%05d" $counter);
285 echo "                                                                      ";
286 echo " $counter LINES WITH WARNING IN FILE '$logfile'";
287
288 echo " ";
289 echo "################################################################################";
290 echo "~~~ MESSAGES LONG ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
291 echo "################################################################################";
292 echo " ";
293
294 #print array content and append to temporary outfile
295 for i in "${!message_long_array[@]}"
296 do
297   m=$i;
298   n=${message_long_array[$i]};
299   ((nc += n))
300   #format counter to have always x digits
301   n=$(printf "%05d" $n);
302   echo " $n | $m" >>tempoutfile;
303 done
304
305 #format counter to have always x digits
306 nc=$(printf "%05d" $nc);
307 echo " $nc WARNINGS IN TOTAL WITH ${#message_long_array[@]} UNIQUE MESSAGES" >>tempoutfile;
308
309 #print a sorted version of the temporary outfile
310 sort -br tempoutfile
311
312 # clean up
313 rm tempoutfile
314 nc=0
315
316 echo " ";
317 echo "################################################################################";
318 echo "~~~ MESSAGES SHORTENED (FOR SIMPLE GROUPING) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
319 echo "################################################################################";
320 echo " ";
321
322 #print array content and append to temporary outfile
323 for i in "${!message_short_array[@]}"
324 do
325   m=$i;
326   n=${message_short_array[$i]};
327   ((nc += n))
328   #format counter to have always x digits
329   n=$(printf "%05d" $n);
330   echo " $n | $m" >>tempoutfile;
331 done
332
333 #format counter to have always x digits
334 nc=$(printf "%05d" $nc);
335 echo " $nc WARNINGS IN TOTAL WITH ${#message_short_array[@]} UNIQUE MESSAGES" >>tempoutfile;
336
337 #print a sorted version of the temporary outfile
338 sort -br tempoutfile
339
340 # clean up
341 rm tempoutfile
342 nc=0
343
344 echo " ";
345 echo "################################################################################";
346 echo "~~~ MODULES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
347 echo "################################################################################";
348 echo " ";
349
350 #create temporary outfile
351 for i in "${!module_array[@]}"
352 do
353   m=$i;
354   n=${module_array[$i]};
355   ((nc += n))
356   n=$(printf "%05d" $n);
357   echo " $n | $m" >>tempoutfile;
358 done
359
360 #format counter to have always x digits
361 nc=$(printf "%05d" $nc);
362 echo " $nc WARNINGS IN TOTAL IN ${#module_array[@]} MODULES" >>tempoutfile;
363
364 #print a sorted version of the temporary outfile
365 sort -br tempoutfile
366 rm tempoutfile
367 nc=0
368
369 echo " ";
370 echo "################################################################################";
371 echo "~~~ MODULES WITH RSTFILES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
372 echo "################################################################################";
373 echo " ";
374
375 #print array content and append to temporary outfile
376 for i in "${!rstfile_array[@]}"
377 do
378   m=$i;
379   n=${rstfile_array[$i]};
380   p=${htmlfilepath_array[$i]}
381   r=${rstfilepath_array[$i]}
382   w=${webpath_array[$i]}
383   d=${doc8_result_array[$i]};
384   #echo "DBUG -------------------------------"
385   #echo "DBUG i=$i"
386   #echo "DBUG m=$m"
387   #echo "DBUG n=$n"
388   #echo "DBUG p=$p"
389   #echo -e "DBUG p=$p"
390   ((nc += n))
391   #format counter to have always x digits
392   n=$(printf "%05d" $n);
393
394   # extend module name to the max for better readability
395   tmp_mod=$(echo "$m" | sed -r 's: \|.+$::');
396   #echo "DBUG tmp_mod=$tmp_mod"
397   len_tmp_mod=${#tmp_mod}
398   to_add="$(($maxlength_module-$len_tmp_mod))"
399   #echo "DBUG to_add=$to_add"
400   while [ $to_add -gt 0 ]; do
401       tmp_mod="${tmp_mod} ";
402       ((to_add--));
403       #echo "DBUG  to_add=$to_add"
404       #echo "DBUG tmp_mod=\"$tmp_mod\""
405   done
406
407   # extend rst name to the max for better readability
408   tmp_rst=$(echo "$m" | sed -r 's:^.+ \| ::');
409   #echo "DBUG tmp_rst=$tmp_rst"
410   len_tmp_rst=${#tmp_rst}
411   to_add="$(($maxlength_rstfile-$len_tmp_rst))"
412   #echo "DBUG to_add=$to_add"
413   while [ $to_add -gt 0 ]; do
414       tmp_rst="${tmp_rst} ";
415       ((to_add--));
416       #echo "DBUG  to_add=$to_add"
417       #echo "DBUG tmp_rst=\"$tmp_rst\""
418   done
419
420   # recombine module and rst names
421   m="${tmp_mod} | ${tmp_rst}";
422
423   # print out to temp file
424   echo -e " $m | $r  $p  $w  $d | $n" >>tempoutfile;
425 done
426
427 #format counter to have always x digits
428 nc=$(printf "%05d" $nc);
429 #in case the name (e.g) index.rst is used multiple times in the same module warnings are combined
430 echo " $nc WARNINGS IN TOTAL IN APPROX. ${#rstfile_array[@]} RST FILES" >>tempoutfile;
431
432 #print a sorted version of the temporary outfile
433 sort -b tempoutfile
434
435 # clean up
436 rm tempoutfile
437 nc=0
438
439 echo " ";
440 echo "################################################################################";
441 echo "~~~ RSTFILES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
442 echo "################################################################################";
443 echo " ";
444
445 #print array content and append to temporary outfile
446 for i in "${!rstfile_array[@]}"
447 do
448   m=$i;
449   n=${rstfile_array[$i]};
450   p=${htmlfilepath_array[$i]}
451   r=${rstfilepath_array[$i]}
452   w=${webpath_array[$i]}
453   d=${doc8_result_array[$i]};
454   ((nc += n))
455   #format counter to have always x digits
456   n=$(printf "%05d" $n);
457
458   # extend module name to the max for better readability
459   tmp_mod=$(echo "$m" | sed -r 's: \|.+$::');
460   #echo "DBUG tmp_mod=$tmp_mod"
461   len_tmp_mod=${#tmp_mod}
462   to_add="$(($maxlength_module-$len_tmp_mod))"
463   #echo "DBUG to_add=$to_add"
464   while [ $to_add -gt 0 ]; do
465       tmp_mod="${tmp_mod} ";
466       ((to_add--));
467       #echo "DBUG  to_add=$to_add"
468       #echo "DBUG tmp_mod=\"$tmp_mod\""
469   done
470
471   # extend rst name to the max for better readability
472   tmp_rst=$(echo "$m" | sed -r 's:^.+ \| ::');
473   #echo "DBUG tmp_rst=$tmp_rst"
474   len_tmp_rst=${#tmp_rst}
475   to_add="$(($maxlength_rstfile-$len_tmp_rst))"
476   #echo "DBUG to_add=$to_add"
477   while [ $to_add -gt 0 ]; do
478       tmp_rst="${tmp_rst} ";
479       ((to_add--));
480       #echo "DBUG  to_add=$to_add"
481       #echo "DBUG tmp_rst=\"$tmp_rst\""
482   done
483
484   # recombine module and rst names
485   m="${tmp_mod} | ${tmp_rst}";
486
487   # print out to temp file
488   echo -e " $n | $m | $r  $p  $w  $d" >>tempoutfile;
489 done
490
491 #format counter to have always x digits
492 nc=$(printf "%05d" $nc);
493 #in case the name (e.g) index.rst is used multiple times in the same module warnings are combined
494 echo " $nc WARNINGS IN TOTAL IN APPROX. ${#rstfile_array[@]} RST FILES" >>tempoutfile;
495
496 #print a sorted version of the temporary outfile
497 sort -br tempoutfile
498
499 # clean up
500 rm tempoutfile
501 nc=0
502
503 echo " ";
504 exit
505
506 ###
507 ### backup code for future extensions
508 ###
509
510 #
511 # Block_quote_ends_without_a_blank_line_unexpected_unindent
512 # Bullet_list_ends_without_a_blank_line_unexpected_unindent
513 # Citation_[\w-]_is_not_referenced
514 # Citation_unit_test_is_not_referenced
515 # Content_block_expected_for_the_code_directive_none_found
516 # Content_block_expected_for_the_container_directive_none_found
517 # Could_not_lex_literal_block_as_bash__Highlighting_skipped
518 # Could_not_lex_literal_block_as_console__Highlighting_skipped
519 # Could_not_lex_literal_block_as_guess__Highlighting_skipped
520 # Could_not_lex_literal_block_as_json__Highlighting_skipped
521 # Could_not_lex_literal_block_as_yaml__Highlighting_skipped
522 # Definition_list_ends_without_a_blank_line_unexpected_unindent
523 # document_isnt_included_in_any_toctree
524 # download_file_not_readable
525 # Duplicate_explicit_target_name
526 # duplicate_label
527 # Enumerated_list_ends_without_a_blank_line_unexpected_unindent
528 # Error_in_code_directive
529 # Error_in_code-block_directive
530 # Error_in_image_directive
531 # Explicit_markup_ends_without_a_blank_line_unexpected_unindent
532 # Field_list_ends_without_a_blank_line_unexpected_unindent
533 # Footnote_[0-9.*]_is_not_referenced
534 # image_file_not_readable
535 # Include_file
536 # Inconsistent_literal_block_quoting
537 # Inline_emphasis_start-string_without_end-string
538 # Inline_interpreted_text_or_phrase_reference_start-string_without_end-string
539 # Inline_strong_start-string_without_end-string
540 # Inline_substitution_reference_start-string_without_end-string
541 # Literal_block_ends_without_a_blank_line_unexpected_unindent
542 # Literal_block_expected_none_found
543 # Malformed_table
544 # Pygments_lexer_name_asn_is_not_known
545 # Title_level_inconsistent
546 # Title_overline__underline_mismatch
547 # Title_overline_too_short
548 # Title_underline_too_short
549 # toctree_contains_reference_to_nonexisting_document
550 # Too_many_autonumbered_footnote_references_only_0_corresponding_footnotes_available
551 # undecodable_source_characters_replacing_with
552 # undefined_label
553 # Unexpected_indentation
554 # Unknown_directive_type_clode-block
555 # unknown_document
556 # Unknown_target_name