summaryrefslogtreecommitdiffstats
path: root/watch-bz
blob: a7c177c563a22c5e66ea4dfb100f9a64b5cd8e02 (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
#!/bin/bash
# silly, but good-enough bugzilla monitoring
# jpokorny@redhat.com (will be pleased to know about further enhancements)
# licensed under GPLv2+ (note: this file only, not the bundled helper)

# usage:
# 1. optionally git clone git://git.fedorahosted.org/git/python-bugzilla
#    (system-wide can also be used)
# 2. open ~/.watchbzrc for editing
#    - set BUGZILLA_ROOT when using cloned python-bugzilla
#    - optionally set STATUS and COMPONENT as per example
#      (or pass such string as a 1st argument)
#    - optionally set BZUSER to avoid the need to login manually before
#      the "watch session"; you can set BZPASSWORD as well but be careful!
# 3. optionally install expect package (-> security++)
#
# make watch work with less in order to get scrolling feature seems to be
# impossible, this might be a way forward (a bit buggy when tested):
# https://github.com/jyapayne/UnixWatchCommandOutput
#
# TODO:
# - if status is always uppercased, do the uppercasing automatically?

set -u
umask 077

HERE="$(dirname "$(readlink -f "$0")")"

BUGZILLA=$(which bugzilla 2>/dev/null)
BUGZILLA_ROOT=/usr
BUGZILLA_COOKIE=~/.watchbzcookies
BUGZILLA_LOGOUT=1

# example defaults, modify via ~/.watchbzrc
: ${WATCHBZ_DEBUG:=0}
COMPONENT=acpid,mc
STATUS=OPEN
REFRESH_INTERVAL=600  # [s], better not to drain bugzilla's power...
BZUSER=
BZPASSWORD=

# user configuration
[ -f ~/.watchbzrc ] && source ~/.watchbzrc

# Fedora and color-friendly watch: rhbz#801626
WATCHCMD=watch
PRECOLORIZE=cat
COLORIZE=cat
if watch --color -n0.1 -g date &>/dev/null; then
	WATCHCMD='watch --color'
	#PRECOLORIZE="sed -u"
	PRECOLORIZE="sed"
	PRECOLORIZE+=" 's|\( [NAPMQDVRC] \)\(.*\)\([\{]${BZUSER}[\}]\)|\x1b[1;31m\1\x1b[0m\2|'"
	COLORIZE="sed"
	COLORIZE+=" -e 's|\([0-9.A-Z]\++\+[, ]\)|\x1b[1;32m\1\x1b[0m|g'"
	COLORIZE+=" -e 's|\([0-9.A-Z]\+-\+[, ]\)|\x1b[33m\1\x1b[0m|g'"
	COLORIZE+=" -e 's|\([0-9.A-Z]\+?\+[, ]\)|\x1b[37m\1\x1b[0m|g'"
	COLORIZE+=" -e 's|\(I?[, ]\)|\x1b[1;36m\1\x1b[0m|g'"
	COLORIZE+=" -e 's|\(!!!\)|\x1b[1;31m\1\x1b[0m|'"
fi

# guess correct paths
if [ -z "$BUGZILLA" ] || [ -n "${BUGZILLA_ROOT}" ]; then
	# BUGZILLA_ROOT (path to local repo) should rather be set in ~/.watchbzrc
	BUGZILLA="${BUGZILLA_ROOT}/bin/bugzilla"
	PYTHONPATH=${PYTHONPATH:+:${PYTHONPATH}}
	# force tilde expansion
	eval export PYTHONPATH="${BUGZILLA_ROOT}${PYTHONPATH}"
fi

# hardcoded for now (note the SEP - OUTPUT_FMT relationship)
SEP=@@@
WIDTH=$(( $(stty size | cut -d' ' -f2) - 1 ))

# arguments
if [ $# -ge 1 ]; then
	if [[ "$1" = -* ]]; then
		STATUS=$(echo "$1" | cut -c2-)
		shift
	fi
	if [ $# -ge 1 ]; then
		COMPONENT="$1"
	fi
fi

# omit component if only single one queried
echo $COMPONENT | grep -q ','
if [ $? -eq 0 ]; then
	OUTPUT_FMT="%{component}$SEP%{bug_id}$SEP%{product}$SEP%{priority}$SEP%{status}$SEP%{flags}$SEP%{short_desc}\ \{%{assigned_to}\}"
else
	OUTPUT_FMT="%{bug_id}$SEP%{product}$SEP%{priority}$SEP%{status}$SEP%{flags}$SEP%{short_desc}\ \{%{assigned_to}\}"
fi


do_logout () {
	test -n "${BZUSER}" \
	  && rm "${BUGZILLA_COOKIE}" \
	  && echo "watch-bz: Authorization cookie removed"
}

do_init_login () {
	if [ -z "$BZPASSWORD" ]; then
		trap 'stty echo; exit' INT
		trap return USR1
		{
		timeout 60 /bin/sh -c \
			"while [ ! -f \"${BUGZILLA_COOKIE}\" ]; do \
				sleep 1; \
			done" \
		  && echo " entering..."
		kill -USR1 -$$
		} & read -s -p 'Password (optional with 1 min timeout): ' BZPASSWORD \
		  || return $?
		trap '' USR1
		trap '' INT
	fi

	rm -f -- ${BUGZILLA_COOKIE}.init
	if which expect &>/dev/null; then
		expect - <<-EOF
		log_user 0
		spawn $BUGZILLA --cookiefile=${BUGZILLA_COOKIE}.init login $BZUSER
		expect "Password: "
		send "${BZPASSWORD}\r"
		send_user "wait a bit..."
		expect eof
		EOF
	else
		echo "Passing password through command-line argument is DANGEROUS"
		local yn; read -p 'Continue? [yN]' yn
		[ "${yn}" -ne "y" ] && exit
		spawn $BUGZILLA --cookiefile=${BUGZILLA_COOKIE}.init login $BZUSER $BZPASSWORD
	fi
	if [ $? -ne 0 ]; then
		echo "Cannot log in"
		return 2
	fi
	cp -n -- ${BUGZILLA_COOKIE}{.init,}
	rm -f -- ${BUGZILLA_COOKIE}.init
}

# exclusively using globals
do_init () {
	# C-c handler to optionally remove cookie on exit
	test $BUGZILLA_LOGOUT -ne 0 && trap do_logout 0

	# login when appropriate
	if [ -n "$BZUSER" ] && [ ! -f "${BUGZILLA_COOKIE}" ]; then
		do_init_login || exit $?
	fi
}

# exclusively using globals
do_watch () {
	EXEC="${WATCHCMD} -t --interval $REFRESH_INTERVAL"
	[ "${WATCHBZ_DEBUG}" -ne 0 ] && EXEC="sh -c"
	TAIL="${HERE}/table-data $SEP | sort | ${PRECOLORIZE} | cut -c-$WIDTH | ${COLORIZE}"
	[ "${WATCHBZ_DEBUG}" -ne 0 ] && TAIL="cat"
	${EXEC} \
	  "$BUGZILLA --cookiefile="${BUGZILLA_COOKIE}" query -c $COMPONENT \
	             -t $STATUS --outputformat $OUTPUT_FMT \
	  | sed -u \
	    -e \"s|${SEP}Red Hat Enterprise Linux |${SEP}EL|\" \
	    -e \"s|${SEP}Fedora|${SEP} F |\" \
	    -e \"s|${SEP}NEW|${SEP}N|\" \
	    -e \"s|${SEP}ASSIGNED|${SEP}A|\" \
	    -e \"s|${SEP}POST|${SEP}P|\" \
	    -e \"s|${SEP}MODIFIED|${SEP}M|\" \
	    -e \"s|${SEP}ON_QA|${SEP}Q|\" \
	    -e \"s|${SEP}ON_DEV|${SEP}D|\" \
	    -e \"s|${SEP}VERIFIED|${SEP}V|\" \
	    -e \"s|${SEP}RELEASE_PENDING|${SEP}R|\" \
	    -e \"s|${SEP}CLOSED|${SEP}C|\" \
	    -e \"s|${SEP}urgent|${SEP}!!!|\" \
	    -e \"s|${SEP}high|${SEP}!!|\" \
	    -e \"s|${SEP}medium|${SEP} ! |\" \
	    -e \"s|${SEP}low|${SEP} ~ |\" \
	    -e \"s|${SEP}unspecified|${SEP} ? |\" \
	    -e \"s|qa_ack|QA|\" \
	    -e \"s|pm_ack|PM|\" \
	    -e \"s|devel_ack|DE|\" \
	    -e \"s|needinfo|I|\" \
	    -e \"s|exception|E|\" \
	    -e \"s|blocker|BL|\" \
	    -e \"s|requires_release_note|RN|\" \
	    -e \"s|docs_scoped|D|g\" \
	    -e \":dsloop\" \
	    -e \"s|\([^A-Z]D-\+\),D-|\1-|g;tdsloop\" \
	    -e \"s|\([^A-Z]D+\+\),D+|\1+|g;tdsloop\" \
	    -e \"s|\([^A-Z]D?\+\),D?|\1?|g;tdsloop\" \
	    -e \"s|rhel-||g\" \
	    -e \"s|qe_test_coverage[?+-][,]\?||\" \
	    -e \"s|None||\" | ${TAIL}"
}

do_init
do_watch