From 44871fd2904363b2fdd1491771e0f668f3036605 Mon Sep 17 00:00:00 2001 From: LangLangbart <92653266+LangLangBart@users.noreply.github.com> Date: Thu, 18 Jul 2024 05:03:50 +0200 Subject: [PATCH] style: improve code structure and comments --- gh-notify | 187 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 103 insertions(+), 84 deletions(-) diff --git a/gh-notify b/gh-notify index 64fb759..bef21ce 100755 --- a/gh-notify +++ b/gh-notify @@ -2,27 +2,31 @@ set -o errexit -o nounset -o pipefail # https://www.gnu.org/software/bash/manual/bash.html#The-Set-Builtin -# ====================== Infos ======================= +############################################################################### +# Information +############################################################################### # https://docs.github.com/en/rest/activity/notifications +# https://docs.github.com/en/graphql/reference/queries # NotificationReason: # assign, author, comment, invitation, manual, mention, review_requested, security_alert, state_change, subscribed, team_mention, ci_activity # NotificationSubjectTypes: # CheckSuite, Commit, Discussion, Issue, PullRequest, Release, RepositoryVulnerabilityAlert, ... -# ====================== set variables ======================= +############################################################################### +# Set Variables +############################################################################### -# The minimum fzf version that the user needs to run all interactive commands. -MIN_FZF_VERSION="0.29.0" - -# export variables for use in child processes +# Export variables for use in child processes. +set -o allexport # https://docs.github.com/en/rest/overview/api-versions -export GH_REST_API_VERSION="X-GitHub-Api-Version:2022-11-28" +GH_REST_API_VERSION="X-GitHub-Api-Version:2022-11-28" # Enable terminal-style output even when the output is redirected. -export GH_FORCE_TTY=1 +# shellcheck disable=SC2034 +GH_FORCE_TTY=1 +# The maximum number of notifications per page set by GitHub. +GH_NOTIFY_PER_PAGE_LIMIT=50 -# Need to be exported because of its use in the 'print_help_text' function -set -o allexport # Customize the fzf keys using environment variables : "${GH_NOTIFY_MARK_ALL_READ_KEY:=ctrl-a}" : "${GH_NOTIFY_OPEN_BROWSER_KEY:=ctrl-b}" @@ -36,12 +40,46 @@ set -o allexport : "${GH_NOTIFY_VIEW_KEY:=enter}" : "${GH_NOTIFY_TOGGLE_PREVIEW_KEY:=tab}" : "${GH_NOTIFY_TOGGLE_HELP_KEY:=?}" -set +o allexport -# The maximum number of notifications per page (set by GitHub) -export GH_NOTIFY_PER_PAGE_LIMIT=50 # Assign 'GH_NOTIFY_DEBUG_MODE' with 'true' to see more information -export GH_NOTIFY_DEBUG_MODE=${GH_NOTIFY_DEBUG_MODE:-false} +: "${GH_NOTIFY_DEBUG_MODE:=false}" + +# 'SHLVL' variable represents the nesting level of the current shell +NESTED_START_LVL="$SHLVL" +FINAL_MSG='All caught up!' + +# color codes +GREEN='\033[0;32m' +DARK_GRAY='\033[0;90m' +NC='\033[0m' +WHITE_BOLD='\033[1m' + +exclusion_string='XXX_BOGUS_STRING_THAT_SHOULD_NOT_EXIST_XXX' +filter_string='' +num_notifications=0 +only_participating_flag=false +include_all_flag=false +preview_window_visibility='hidden' +python_executable='' +set +o allexport + +# No need to export, since they aren't used in any child process. +print_static_flag=false +mark_read_flag=false +update_subscription_url='' + +# The minimum fzf version that the user needs to run all interactive commands. +MIN_FZF_VERSION="0.29.0" + +############################################################################### +# Debugging and Error Handling Configuration +############################################################################### + +die() { + echo ERROR: "$*" >&2 + exit 1 +} + if $GH_NOTIFY_DEBUG_MODE; then export gh_notify_debug_log="${BASH_SOURCE[0]%/*}/gh_notify_debug.log" @@ -68,6 +106,11 @@ if $GH_NOTIFY_DEBUG_MODE; then # Redirect possible errors and debug information from 'gh api' calls to a file # exec 5> >(tee -a "$gh_notify_debug_log") + # Ensure Bash 4.1+ for BASH_XTRACEFD support. + if [[ ${BASH_VERSINFO[0]} -lt 4 || (${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 1) ]]; then + die "Bash 4.1 or newer is required for debugging. Current version: ${BASH_VERSION}" + fi + # Redirect xtrace output to a file exec 6>>"$gh_notify_debug_log" # Write the trace output to file descriptor 6 @@ -76,36 +119,11 @@ if $GH_NOTIFY_DEBUG_MODE; then export PS4='+$(date +%Y-%m-%d:%H:%M:%S) ${FUNCNAME[0]:-}:L${LINENO:-}: ' set -o xtrace fi -# 'SHLVL' variable represents the nesting level of the current shell -export NESTED_START_LVL="$SHLVL" -export FINAL_MSG='All caught up!' - -# color codes -export GREEN='\033[0;32m' -export DARK_GRAY='\033[0;90m' -export NC='\033[0m' -export WHITE_BOLD='\033[1m' - -export exclusion_string='XXX_BOGUS_STRING_THAT_SHOULD_NOT_EXIST_XXX' -export filter_string='' -export num_notifications=0 -export only_participating_flag=false -export include_all_flag=false -export preview_window_visibility='hidden' -export python_executable='' -# not necessarily to be exported, since they are not used in any child process -print_static_flag=false -mark_read_flag=false -update_subscription_url='' - -# ===================== basic functions ===================== -die() { - echo ERROR: "$*" >&2 - exit 1 -} +############################################################################### +# Helper Functions +############################################################################### -# Create help message with colored text # IMPORTANT: Keep it synchronized with the README, but without the Examples. print_help_text() { local help_text @@ -160,37 +178,6 @@ EOF echo -e "$help_text" } -# ====================== parse command-line options ======================= - -while getopts 'e:f:n:u:pawhsr' flag; do - case "${flag}" in - e) - FINAL_MSG="No results found." - exclusion_string="${OPTARG}" - ;; - f) - FINAL_MSG="No results found." - filter_string="${OPTARG}" - ;; - n) num_notifications="${OPTARG}" ;; - p) only_participating_flag=true ;; - u) update_subscription_url="${OPTARG}" ;; - a) include_all_flag=true ;; - w) preview_window_visibility='nohidden' ;; - s) print_static_flag=true ;; - r) mark_read_flag=true ;; - h) - print_help_text - exit 0 - ;; - *) - die "see 'gh notify -h' for help" - ;; - esac -done - -# ===================== helper functions ========================== - gh_rest_api() { command gh api --header "$GH_REST_API_VERSION" --method GET --cache=0s "$@" } @@ -515,9 +502,16 @@ mark_all_read() { mark_individual_read() { local thread_id thread_state + # TODO: Investigate potential rate-limiting issues when sending too many requests in short + # succession. I didn't encounter any rate-limiting when testing with 100 unread notifications, + # but further testing is needed to be sure. + + # Running commands in the background of a script can cause it to hang, especially if the + # command outputs to stdout: https://tldp.org/LDP/abs/html/x9644.html#WAITHANG while IFS=' ' read -r _ thread_id thread_state _; do if [[ $thread_state == "UNREAD" ]]; then - gh_rest_api --silent --method PATCH "notifications/threads/${thread_id}" + # https://docs.github.com/en/rest/activity/notifications#mark-a-thread-as-read + gh_rest_api --silent --method PATCH "notifications/threads/${thread_id}" &>/dev/null & fi done <"$1" } @@ -534,14 +528,12 @@ select_notif() { # a failed 'print_notifs' call, but does not display the message. # See the man page (man fzf) for an explanation of the arguments. - output=$( - # shellcheck disable=SC2086 - SHELL="$(which bash)" command fzf ${GH_NOTIFY_FZF_OPTS:-} \ + SHELL="$(which bash)" FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS-} ${GH_NOTIFY_FZF_OPTS-}" command fzf \ --ansi \ --bind "${GH_NOTIFY_RESIZE_PREVIEW_KEY}:change-preview-window(75%:nohidden|75%:down:nohidden:border-top|nohidden)" \ --bind "change:first" \ - --bind "${GH_NOTIFY_MARK_ALL_READ_KEY}:select-all+execute-silent([[ -z {q} ]] && mark_all_read {} || mark_individual_read {+f})+reload:print_notifs || true" \ + --bind "${GH_NOTIFY_MARK_ALL_READ_KEY}:select-all+execute-silent(if [[ -z {q} ]]; then mark_all_read {}; else mark_individual_read {+f}; fi)+reload:print_notifs || true" \ --bind "${GH_NOTIFY_OPEN_BROWSER_KEY}:execute-silent:open_in_browser {}" \ --bind "${GH_NOTIFY_VIEW_DIFF_KEY}:toggle-preview+change-preview:if command grep -q PullRequest <<<{10}; then command gh pr diff {11} --repo {5} | highlight_output; else view_notification {}; fi" \ --bind "${GH_NOTIFY_VIEW_PATCH_KEY}:toggle-preview+change-preview:if command grep -q PullRequest <<<{10}; then command gh pr diff {11} --patch --repo {5} | highlight_output; else view_notification {}; fi" \ @@ -683,8 +675,35 @@ update_subscription() { fi } -gh_notify() { +main() { local python_version notifs + # CLI Options + while getopts 'e:f:n:u:pawsrh' flag; do + case "${flag}" in + e) + FINAL_MSG="No results found." + exclusion_string="${OPTARG}" + ;; + f) + FINAL_MSG="No results found." + filter_string="${OPTARG}" + ;; + n) num_notifications="${OPTARG}" ;; + p) only_participating_flag=true ;; + u) update_subscription_url="${OPTARG}" ;; + a) include_all_flag=true ;; + w) preview_window_visibility='nohidden' ;; + s) print_static_flag=true ;; + r) mark_read_flag=true ;; + h) + print_help_text + exit 0 + ;; + *) + die "see 'gh notify -h' for help" + ;; + esac + done if ! command -v gh >/dev/null; then die "install 'gh'" @@ -714,7 +733,6 @@ gh_notify() { if ! command -v fzf >/dev/null; then die "install 'fzf' or use the -s flag" fi - check_version fzf "$MIN_FZF_VERSION" fi @@ -731,7 +749,8 @@ gh_notify() { fi } -# This will call the function only when the script is run, not when it's sourced -if [[ ${BASH_SOURCE[0]} == "${0}" ]]; then - gh_notify -fi +############################################################################### +# Script Execution +############################################################################### + +main "$@"