blob: 829cb4a22b78047fb4ab26812c28eab6f9d800b3 [file] [log] [blame]
Joey Armstrong3134bfd2024-02-10 20:51:25 -05001#!/bin/bash
Joey Armstrongcc24b3c2024-03-22 11:12:23 -04002# -----------------------------------------------------------------------
3# Copyright 2023-2024 Open Networking Foundation Contributors
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http:#www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16# -----------------------------------------------------------------------
17# SPDX-FileCopyrightText: 2023-2024 Open Networking Foundation Contributors
18# SPDX-License-Identifier: Apache-2.0
Joey Armstrong3134bfd2024-02-10 20:51:25 -050019## --------------------------------------------------------------------
20## Intent: Construct a jira ticket query with attributes
21## --------------------------------------------------------------------
22
Joey Armstrong76f861a2024-03-13 16:01:24 -040023{ # loader
24 declare pgm=''
25 pgm="$(realpath --canonicalize-existing "$0")"
26
Joey Armstrongcc24b3c2024-03-22 11:12:23 -040027 # stack-trace-on-error
28 # interrupt handler
29 # mkdir with auto-cleanup at exit
Joey Armstrong76f861a2024-03-13 16:01:24 -040030 declare root=''
31 root="${pgm%%/jira/bin/jira-search.sh}"
32 source "$root/lf/onf-common/common.sh" '--common-args-begin--'
33
34 pgm_lib="${root}/jira/jira-search"
35 readonly pgm_lib
36
37 pgm_bin="${root}/bin"
38 readonly pgm_bin
Joey Armstrongcc24b3c2024-03-22 11:12:23 -040039
40 pgm_help="${root}/jira/jira-search/help"
41 readonly pgm_help
Joey Armstrong76f861a2024-03-13 16:01:24 -040042}
43
Joey Armstrong3134bfd2024-02-10 20:51:25 -050044# set -euo pipefail
45#source ~/.sandbox/trainlab-common/common.sh '--common-args-begin--'
46
47##-------------------##
48##---] GLOBALS [---##
49##-------------------##
50declare -g -a text=()
51declare -g -a text_and=()
52declare -g -a text_or=()
53
54declare -g -a urls_raw=()
55declare -g -a urls_filt=()
56
57declare -g -a labels_incl=()
58declare -g -a labels_excl=()
59
60declare -g -a projects=()
61
62path="$(realpath $0 --canonicalize-existing)"
Joey Armstrong76f861a2024-03-13 16:01:24 -040063# source "${path%\.sh}/utils.sh"
64source "${pgm_lib}/utils.sh"
65#source "$pgmlib/fixversion.sh"
66#source "$pgmlib/resolved.sh"
67source "${pgm_lib}/fixversion.sh"
68source "${pgm_lib}/resolved.sh"
Joey Armstrongcc24b3c2024-03-22 11:12:23 -040069source "${pgm_lib}/help/utils.sh"
Joey Armstrong3134bfd2024-02-10 20:51:25 -050070
71## --------------------------------------------------------------------
72## --------------------------------------------------------------------
73function error()
74{
75 echo "ERROR ${FUNCNAME[1]}: $@"
76 exit 1
77}
78
79## -----------------------------------------------------------------------
80## -----------------------------------------------------------------------
81function html_encode()
82{
83 local -n ref=$1; shift
84 local tmp="$ref"
85
86 tmp="${tmp//[[:space:]]/%20}"
87 tmp="${tmp//\"/%22}"
88 tmp="${tmp//\'/%27}"
89
90 ref="$tmp"
91 return
92}
93
94## -----------------------------------------------------------------------
95## Intent: Insert a conjunction into the stream when prior statements exist
96## -----------------------------------------------------------------------
97function conjunction()
98{
99 return
Joey Armstrong76f861a2024-03-13 16:01:24 -0400100
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500101 local -n ref=$1; shift
102 [[ $# -gt 0 ]] && { local literal="$1"; shift; }
103
104 ## -------------------------------
105 ## Conjunction if prior statements
106 ## -------------------------------
107 if [ ${#ref[@]} -gt 0 ]; then
108 if [[ -v literal ]]; then
109 ref+=("$literal")
110 elif [[ -v bool_and ]]; then
111 ref+=('AND')
112 else
113 ref+=('OR')
114 fi
115 fi
116
117 return
118}
119
120## -----------------------------------------------------------------------
121## Intent: Helper method
122## -----------------------------------------------------------------------
123## Usage : local path="$(join_by '/' 'lib' "${fields[@]}")"
124## -----------------------------------------------------------------------
125function join_by()
126{
127 local d=${1-} f=${2-}; if shift 2; then printf %s "$f" "${@/#/$d}"; fi;
128}
129
130## --------------------------------------------------------------------
131## Intent: Query by component name filter
132## --------------------------------------------------------------------
133## Value: helm-charts
134## --------------------------------------------------------------------
135function do_components()
136{
137 declare -n args=$1; shift
138 declare -n ans=$1; shift
139
140 # [ -z ${args+word} ] && { args=(); }
Joey Armstrong76f861a2024-03-13 16:01:24 -0400141
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500142 if [[ ${#args[@]} -gt 0 ]]; then
143
144 local modifier
145 if [[ -v bool_not ]]; then
146 modifier='NOT IN'
147 else
148 modifier='IN'
149 fi
150 ans+=("component ${modifier} (${args[@]})")
151 # alt: comp='foo' OR comp='bar'
152 fi
153
154 return
155}
156
157## --------------------------------------------------------------------
158## Intent: Query filter by labels assigned to a ticket:
159## o pods, failing, testing
160## --------------------------------------------------------------------
161# "project in (UKSCR, COMPRG) AND issuetype = Bug AND labels in (BAT)" and
162## --------------------------------------------------------------------
163function do_labels()
164{
165 declare -n incl=$1; shift # was args=
166 declare -n excl=$1; shift
167 declare -n ans=$1; shift
168
169 ## --------------------------------
170 ## Conjunction if stream tokens > 0
171 ## --------------------------------
172 conjunction ans
173
174 declare -a tokens=()
175
176 ## -----------------------------
177 ## -----------------------------
178 if [[ ${#incl[@]} -gt 0 ]]; then
179
180 local modifier
181 if [[ -v bool_not ]]; then
182 modifier='NOT IN'
183 else
184 modifier='IN'
185 fi
186
187 local labels=$(join_by ',' "${incl[@]}")
188 local -a tmp=(\
189 '('\
190 'label IS EMPTY' \
191 'OR' \
192 "labels ${modifier} ($labels)" \
193 ')'\
194 )
195 tokens+=("${tmp[@]}")
196 fi
197
198 conjunction tokens 'AND'
199
200 ## -----------------------------
201 ## -----------------------------
202 if [[ ${#excl[@]} -gt 0 ]]; then
203 local labels=$(join_by ',' "${excl[@]}")
204 tokens+=('(' "labels NOT IN ($labels)" ')')
205 fi
206
207 ans+=("${tokens[@]}")
208 return
209}
210
211## --------------------------------------------------------------------
212## Intent: Modify search query by project type (SEBA, VOL)
213## --------------------------------------------------------------------
214function do_projects()
215{
216 declare -n ref=$1; shift
217
218 [[ ${#projects[@]} -eq 0 ]] && { return; }
219
220 local terms="$(join_by ',' "${projects[@]}")"
221# local -a buffer=('(' 'project' 'IN' "($terms)" ')')
222# ref+=("$(join_by '%20' "${buffer[@]}")")
223 ref+=("(project IN ($terms))")
224 return
225}
226
227## --------------------------------------------------------------------
228## Intent: Query by compound text filters
229## --------------------------------------------------------------------
230function do_text()
231{
232 local -n ref=$1; shift
233 local -n ans=$1; shift
234 local val
235
236 ## Accumulate
237 if [[ ${#ref[@]} -gt 0 ]]; then
238
239 if [[ -v bool_and ]]; then
240 text_and+=("${ref[@]}")
241 else
242 text_or+=("${ref[@]}")
243 fi
244 fi
245
246 ## Append terms: AND
247 if [[ ${#text_and[@]} -gt 0 ]]; then
248 declare -a term=()
249 for val in "${text_and[@]}";
250 do
251 term+=("text ~ \"$val\"")
252 done
253 val=$(join_by ' AND ' "${term[@]}")
254 ans+=("($val)")
255 fi
256
257 ## Append terms: OR
258 if [[ ${#text_or[@]} -gt 0 ]]; then
259 declare -a term=()
260 for val in "${text_or[@]}";
261 do
262 term+=("text ~ \"$val\"")
263 done
264 val=$(join_by ' OR ' "${term[@]}")
265 ans+=("($val)")
266 fi
267
268 return
269}
270
271## --------------------------------------------------------------------
272## Intent: Query by assigned or requestor
273## --------------------------------------------------------------------
274## Note: Simple for now but support query by a list of suers
275## --------------------------------------------------------------------
276function do_user()
277{
278 declare -n ans=$1; shift
279
280 [[ -v argv_nobody ]] && return
281
282 local user='currentUser()'
283 if [[ -v argv_user ]]; then
284 user="$argv_user"
285 fi
286
287 if [[ -v argv_assigned ]]; then
288 ans+=("assignee=${user}")
289 fi
290
291 if [[ -v argv_reported ]]; then
292 ans+=("reporter=${user}")
293 fi
294
295 return
296}
297
298## --------------------------------------------------------------------
299## Intent: Combine filter arguments into a search query
300## --------------------------------------------------------------------
301function gen_filter()
302{
303 declare -n ans=$1; shift
304 declare -n args=$1; shift
305
306 ## -----------------------------------
307 ## Begin by joining major search terms
308 ## -----------------------------------
309 declare -a _tmp=()
310 local val
311 for val in "${args[@]}";
312 do
313 _tmp+=("$val" 'AND')
314 done
Joey Armstrong76f861a2024-03-13 16:01:24 -0400315
316 if [[ ${#_tmp[@]} -gt 0 ]]; then
317 unset _tmp[-1]
318 fi
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500319
320 ## -----------------------
321 ## Massage with html codes
322 ## -----------------------
323 ans="$(join_by '%20' "${_tmp[@]}")"
324 return
325}
326
327## --------------------------------------------------------------------
328## Intent: Combine filter arguments into a search query
329## --------------------------------------------------------------------
330function gen_url()
331{
332 declare -n ans=$1; shift
333 declare -n args=$1; shift
334
335 ## Which jira server to query (?)
336 [[ ! -v server ]] && declare -g server='jira.opennetworking.org'
337 tmp_url="https://${server}/issues/?jql="
338 tmp="${tmp_url}${args}"
339 ans="${tmp// /%20}"
340 return
341}
342
343## --------------------------------------------------------------------
344## Intent: Dispaly command usage
345## --------------------------------------------------------------------
346function usage()
347{
348 cat <<EOH
349Usage: $0 VOL-xxxx
350 --debug Enable script debug mode
351 --dry-run Simulate
Joey Armstrong76f861a2024-03-13 16:01:24 -0400352 --todo Display future enhancements
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500353
354 VOL-{xxxx} View a jira ticket by ID
355
356[SERVER]
Joey Armstrong76f861a2024-03-13 16:01:24 -0400357 --server {cord,onf}
Joey Armstrongcc24b3c2024-03-22 11:12:23 -0400358 --onf jira.opennetworking.org
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500359 --opencord jira.opencord.org
360
361[WHAT]
362 --component Search by component name assigned to ticket
363 --label Search by label name assigned to ticket.
364 --text Search string(s)
Joey Armstrongcc24b3c2024-03-22 11:12:23 -0400365EOH
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500366
Joey Armstrongcc24b3c2024-03-22 11:12:23 -0400367# declare -a topics=()
368# topics+=('fixversion.switches')
369# topics+=('resolved.switches')
370#
371# help_switch_show "${topics[@]}"#
372 #
373#[USER(s)]
374# --me Tickets assigned to or reported by me.
375# --user [u] Tickets assigned to this user.
376# --nobody Raw query, no filtering by user
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500377
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500378
Joey Armstrongcc24b3c2024-03-22 11:12:23 -0400379 cat <<EOH
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500380
381[BOOL]
382 --and Join terms using 'AND'
383 --or Join terms using 'OR'
384
385[MEMBER]
386 --in (default) Items belong (--component IN)
387 --not-in Negate item set (--component NOT IN)
388
389[Contains]
390 --text [t] (join modifer: --and, --or)
391 --text-and [t] All of these terms
392 --text-or [t] Any of these terms
393
394[RANGE]
Joey Armstrongcc24b3c2024-03-22 11:12:23 -0400395 --newer [d] Search for tickets created < [n] days ago.
396 --older [d] Search for tickets created > [n] days ago.
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500397
398[ALIASES]
Joey Armstrongcc24b3c2024-03-22 11:12:23 -0400399 --all Query for all unresolved tickets
400
401[TOPIC]
402 --fixversion Query by field: fixedversion
403 --resolved Query by field: resolved
404 --user Query by owner, requestor or 'my' jira tickets.
405
406[HELP]
407 --help This message
408 --help-{topic} Display switch help and use case. (--help-resolved)
409 --usage-{topic} Display use cases for a given switch (--usage-user)
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500410
411[USAGE]
Joey Armstrongcc24b3c2024-03-22 11:12:23 -0400412 $0 --opencord --assigned --unresolved
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500413 o Display all tickets assigned to my login
Joey Armstrongcc24b3c2024-03-22 11:12:23 -0400414
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500415 $0 --reported --or --text 'bbsim' --text 'release'
416 o Search for tickets that contain strings bbsim or release
417 $0 --cord --text-and 'release' --text-and 'voltctl'
418 o Search jira.opencord for tickets that contain release and voltctl
419 $0 --text 'bitergia' --text 'Jira' -and
420 o Search jira.opennetworking for tickets containing string bitergia and Jira
421
422 $0 --cord --label failing --label pod
423 o Search jira.opencord for tests failing due to pod/hardware issuses.
424
425 $0 --proj VOL --fixversion "VOLTHA v2.12" --resolved-is-empty
426 o Query for unresolved release tickets
427EOH
428
429 return
430}
431
432## --------------------------------------------------------------------
433# classpath=$(join_by ':' "${mypath[@]}")
434## --------------------------------------------------------------------
435function join_by()
436{
437 local d=${1-} f=${2-}; if shift 2; then printf %s "$f" "${@/#/$d}"; fi;
438}
439
440##----------------##
441##---] MAIN [---##
442##----------------##
443declare -a suffix0=()
444
445# declare -g -i debug=1
446
447while [ $# -gt 0 ]; do
448
449 if [ ${#suffix0[@]} -gt 0 ]; then
450 suffix0+=('AND')
451 fi
452
453 arg="$1"; shift
454 [[ -v debug ]] && echo "** argv=[$arg] [$*]"
455
456 case "$arg" in
457
Joey Armstrongcc24b3c2024-03-22 11:12:23 -0400458 '--help') usage; exit 0 ;;
459 '--help-'*) help_with "${arg/--help-/}" ;;
460 '--usage-'*) help_usage_show "${arg/--usage-/}"
461 ;;
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500462
463 ##-----------------##
464 ##---] MODES [---##
465 ##-----------------##
466 -*debug) declare -g -i debug=1 ;;
467 --dry-run) declare -g -i dry_run=1 ;;
468
Joey Armstrongcc24b3c2024-03-22 11:12:23 -0400469 ##------------------------##
470 ##---] SWITCH ALIAS [---##
471 ##------------------------##
472 --unresolved)
473 declare -a args=()
474 args+=('--resolved-is-empty')
475 [[ $# -gt 0 ]] && { args+=("$@"); }
476 set -- "${args[@]}"
477 ;;
478
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500479 ##-------------------##
480 ##---] BY USER [---##
481 ##-------------------##
482 --assigned) declare -g -i argv_assigned=1 ;;
483 --reported) declare -g -i argv_reported=1 ;;
484 --me) declare -g -i argv_me=1 ;;
485 --nobody) declare -g -i argv_nobody=1 ;;
486 --user)
487 arg="$1"; shift
488 declare -g argv_user="$arg"
489 ;;
490
491 ##------------------##
492 ##---] SERVER [---##
493 ##------------------##
Joey Armstrong76f861a2024-03-13 16:01:24 -0400494 --serv*)
495 arg="$1"; shift
496 case "$arg" in
497 *cord*) server='jira.opencord.org' ;;
498 *onf*) server='jira.opennetworking.org' ;;
499 *) error "--server [$arg] expected opencord or onf" ;;
500 esac
501 ;;
502
Joey Armstrongcc24b3c2024-03-22 11:12:23 -0400503 --onf) declare server='jira.opennetworking.org' ;;
504 --opencord) declare server='jira.opencord.org' ;;
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500505
506 ##---------------------##
507 ##---] SEARCH-BY [---##
508 ##---------------------##
509 --component|--comp*)
510 arg="$1"; shift
511 [[ ! -v components ]] && declare -g -a components=()
512 components+=("$arg")
513 ;;
514
515 --label-excl)
516 arg="$1"; shift
517 labels_excl+=("$arg")
518 ;;
519
520 --label|--label-incl)
521 arg="$1"; shift
522 labels_incl+=("$arg")
523 ;;
524
525 ##-----------------------##
526 ##---] Text Search [---##
527 ##-----------------------##
528 # jsearch.sh --text-and bbsim --text-and release
529 -*text-and) text_and+=("$1"); shift ;;
530 -*text-or) text_or+=("$1"); shift ;;
531
532 # % js --and --text jenkins --text cord
533 # text ~ "Jira Software"
 # [WORDs]
534 # text ~ "\"Jira Software\""
 # [STRING]
535 -*text)
536 arg="$1"; shift
537 if [[ -v bool_and ]]; then
538 text_and+=("$arg")
539 elif [[ -v bool_or ]]; then
540 text_or+=("$arg")
541 else
542 text+=("$arg")
543 fi
544 ;;
545
546 --all) set -- '--resolved-is-none' "$@" ;; # alias: --[un-]resolved
Joey Armstrong76f861a2024-03-13 16:01:24 -0400547 --todo) source "${pgm_lib}/todo.sh" ;;
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500548
549 --proj*) projects+=("$1"); shift ;;
550
551 --fixversion-*)
552 # function get_jql_fixversion()
553 case "$arg" in
554 *excl)
555 [[ ! -v fixversion_excl ]] && { declare -g -a fixversion_excl=(); }
556 val="\"$1\""; shift
557 html_encode val
558 fixversion_excl+=("$val");
559 ;;
560
561 *incl)
562 [[ ! -v fixversion_incl ]] && { declare -g -a fixversion_incl=(); }
563 val="\"$1\""; shift
564 html_encode val
565 fixversion_incl+=("$val");
566 ;;
567
568 *not-empty) declare -g -i fixversion_not_empty=1 ;;
569 *is-empty) declare -g -i fixversion_is_empty=1 ;;
570
571 *) error "Detected invalid --fixversion-* modifier" ;;
572 esac
573 ;;
574
Joey Armstrongcc24b3c2024-03-22 11:12:23 -0400575 '--resolved-'*)
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500576 # function get_jql_reasons()
577 case "$arg" in
578
579 *start) declare -g resolved_start="$1"; shift ;;
580 *end) declare -g resolved_end="$1"; shift ;;
581
582 *not-empty) declare -g resolved_not_empty="$1" ;;
583 *empty) declare -g resolved_is_empty="$1" ;;
584
585 *excl)
586 [[ ! -v resolved_excl ]] && { declare -g -a resolved_excl=(); }
587 val="\"$1\""; shift
588 html_encode val
589 resolved_excl+=("$val");
590 ;;
591 *incl)
592 [[ ! -v resolved_incl ]] && { declare -g -a resolved_incl=(); }
593 val="\"$1\""; shift
594 html_encode val
595 resolved_incl+=("$val");
596 ;;
597 *) ;;
598 *) error "Detected invalid --resolved-* modifier" ;;
599 esac
600 ;;
601
602 -*newer)
603 arg="$1"; shift
604 suffix0+=("created <= '-${arg}d'") ;;
605
606 -*older)
607 arg="$1"; shift
608 suffix0+=("created >= '-${arg}d'") ;;
609
610 ##----------------##
611 ##---] BOOL [---##
612 ##----------------##
613 --[aA][nN][dD]) declare -g -i bool_and=1 ;;
614 --[oO][rR]) declare -g -i bool_or=1 ;;
615
616 ##------------------##
617 ##---] MEMBER [---##
618 ##------------------##
619 --[iI][nN]) declare -g -i bool_in=1 ;;
620 --[nN][oO][tT]) declare -g -i bool_not=1 ;;
621
622 [A-Z][A-Z][A-Z]-[0-9]*)
623 case "$arg" in
624 CORD-[0-9]*)
625 url="https://jira.opencord.org/browse/${arg}"
626 urls_raw+=('--new-window' "$url")
627 ;;
628
629 INF-[0-9]*)
630 url="https://jira.opennetworking.org/browse/${arg}"
631 urls_raw+=('--new-window' "$url")
632 ;;
633
634 VOL-[0-9]*)
635 url="https://jira.opencord.org/browse/${arg}"
636 urls_raw+=('--new-window' "$url")
637 ;;
638
639 *) error "Detected invalid ticket [$arg]" ;;
640
641 esac
642 ;;
643
644 # -----------------------------------------------------------------------
645 # https://support.atlassian.com/jira-software-cloud/docs/search-syntax-for-text-fields/
646 # -----------------------------------------------------------------------
647 # +jira atlassian -- must contain jira, atlassian is optional
648 # -japan -- exclude term
649 # [STEM] summary ~ "customize" -- finds stem 'custom' in the Summary field
650 *)
651 declare -p text_and
652 error "Detected unknown argument $arg"
653 ;;
654 esac
655done
656
Joey Armstrong76f861a2024-03-13 16:01:24 -0400657## --------------
658## Required check
659## --------------
660[[ ! -v server ]] && { error "--server={cord,onf} is required"; }
661
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500662## ----------------------
663## Construct query filter
664## ----------------------
665do_user suffix0
666do_projects suffix0
667[[ -v components ]] && { do_components components suffix0; }
668do_labels labels_incl labels_excl suffix0
669do_text text suffix0
670do_resolved suffix0
671do_fixversion suffix0
672
673filter=''
674gen_filter filter suffix0
675
676if [[ ! -v urls_raw ]]; then
677 url=''
678 gen_url url filter
679 urls_filt+=("$url")
680elif [ ${#urls_raw} -eq 0 ]; then
681 url=''
682 gen_url url filter
683 urls_filt+=("$url")
684fi
685
686[[ -v debug ]] && [[ -v url ]] && echo "URL: $url"
Joey Armstronge4ef06a2024-03-22 11:43:54 -0400687browser="${BROWSER:-firefox}"
Joey Armstrong3134bfd2024-02-10 20:51:25 -0500688echo "$browser ${urls_filt[@]} ${urls_raw[@]}"
689
690if [[ ! -v dry_run ]]; then
691 "$browser" "${urls_filt[@]}" "${urls_raw[@]}" >/dev/null 2>/dev/null &
692fi
693
694# [SEE ALSO]
695# o https://support.atlassian.com/jira-software-cloud/docs/advanced-search-reference-jql-fields/
696
697# [EOF]