Skip to content

Commit

Permalink
get-github-release, waiter: wait for rate limits
Browse files Browse the repository at this point in the history
- waiter:
    - supports --message for speedy custom updates
    - if no timeout is provided, it now waits forever, and counts up
    - will now output seconds and minutes
    - if --exists is provided, a exit status of a timeout will now default to 60
- mount-helper:
    - now that waiter supports waiting forever, we now wait forever for a mount to complete
- get-github-release:
    - wait for the rate limit to complete before proceeding

/closes #204
  • Loading branch information
balupton committed Jan 12, 2024
1 parent 0d18a6e commit bb57786
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 39 deletions.
2 changes: 1 addition & 1 deletion commands/btrfs-helper
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ function btrfs_helper() (
while ! eval-helper --no-quiet --wrap \
-- sudo-helper \
-- btrfs balance status --verbose "$existing_mount"; do
waiter
waiter 5
done

# usage: btrfs device remove <device>|<devid> [<device>|<devid>...] <path>
Expand Down
79 changes: 56 additions & 23 deletions commands/get-github-release
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ function get_github_release() (
Fetch the json of the release.
--releases
Fetch the releases of the repository.
--rate-limit
Fetch the current rate limits.
> If no specific action is provided, then --tag is used.
--gh
Expand Down Expand Up @@ -243,6 +245,7 @@ function get_github_release() (
'--api-url') action='api-url' ;;
'--release') action='release' ;;
'--releases') action='releases' ;;
'--rate-limit') action='rate-limit' ;;
# technique
'--gh') techniques+=('gh') ;;
'--jq') techniques+=('jq') ;;
Expand Down Expand Up @@ -277,6 +280,55 @@ function get_github_release() (
fi
fi

# essential helpers
function fetch_json {
fetch --bearer-token="$GITHUB_TOKEN" "$1"
}
function fetch_json_of_rate_limit {
fetch_json "$GITHUB_API_URL/rate_limit"
}
function extract_value_from_json_property_from_stdin {
local property="$1"
tr -d '\n' | awk -v property="$property" '
{
while ( match($0,/"[^"]*"|: false|: true|: [0-9]+/) ) {
inner = substr($0,RSTART,RLENGTH)
if ( inner ~ /^: / ) {
inner = substr(inner,3)
} else if ( inner ~ /^"/ ) {
inner = substr(inner,2,length(inner)-2)
}
if ( inner == property ) {
found = 1
} else if ( found ) {
print inner
exit
}
$0 = substr($0,RSTART+RLENGTH)
}
}'
}
function wait_on_rate_limit {
local json
local now remaining reset seconds
json="$(fetch_json_of_rate_limit)"
# limit="$(extract_value_from_json_property_from_stdin 'limit' <<<"$json")"
remaining="$(extract_value_from_json_property_from_stdin 'remaining' <<<"$json")"
if test "$remaining" -gt 5; then # we need at least a few requests to do anything, so 5 seems a reasonable limit
return 0
fi
now="$(date-helper --unix)"
reset="$(extract_value_from_json_property_from_stdin 'reset' <<<"$json")"
seconds="$((reset - now))"
waiter "$seconds" --message="$(echo-style --notice='Waiting for the GitHub API Rate Limit to reset...' ' ' --code_notice+blink="%s remaining")"
}

# rate-limit action is here, to avoid the slug check for everything else
if test "$action" = 'rate-limit'; then
fetch_json_of_rate_limit
return
fi

# enforcements
if test -z "$slug"; then
help 'A GitHub repository slug is required.'
Expand Down Expand Up @@ -342,33 +394,10 @@ function get_github_release() (
# =====================================
# Helpers

function extract_value_from_json_property_from_stdin {
local property="$1"
tr -d '\n' | awk -v property="$property" '
{
while ( match($0,/"[^"]*"|false|true/) ) {
inner = substr($0,RSTART,RLENGTH)
if ( inner ~ /^"/ ) {
inner = substr(inner,2,length(inner)-2)
}
if ( inner == property ) {
found = 1
} else if ( found ) {
print inner
exit
}
$0 = substr($0,RSTART+RLENGTH)
}
}'
}

# fetch releases
local RELEASES_API_URL="$GITHUB_API_URL/repos/$slug/releases"
local RELEASE_API_URL_FROM_ALIAS="$GITHUB_API_URL/repos/$slug/releases/$alias"
local RELEASE_API_URL_FROM_ID="$GITHUB_API_URL/repos/$slug/releases/$id"
function fetch_json {
fetch --bearer-token="$GITHUB_TOKEN" "$1"
}
function fetch_json_of_releases {
fetch_json "$RELEASES_API_URL"
}
Expand Down Expand Up @@ -511,6 +540,10 @@ function get_github_release() (
# =====================================
# Action

# wait on rate limits if necessary
wait_on_rate_limit

# continue with the action
if test "$action" = 'releases'; then
for technique in "${techniques[@]}"; do
if test "$technique" = 'gh'; then
Expand Down
2 changes: 1 addition & 1 deletion commands/mount-helper
Original file line number Diff line number Diff line change
Expand Up @@ -1625,7 +1625,7 @@ function mount_helper() (

# wait
if test "$uses_wait" = 'yes'; then
waiter --timeout=60 --exists="$option_target"
waiter --exists="$option_target"
fi

# chown
Expand Down
82 changes: 68 additions & 14 deletions commands/waiter
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@ function waiter_() (
waiter [...options]
OPTIONS:
--timeout=<timeout> | <timeout>
How many seconds to wait for before timing out.
Defaults to 5 seconds.
<timeout> | --timeout=<timeout>
If provided, will wait for this amount of secons, and count down.
If not provided, will wait indefinitely, and count up.
--message=<message>
A message where %s will be replaced with the time remaining or the time waited.
Defaults to "Waiting %s..." (if counting down / timeout provide) and "Waited %s..." (if counting up / no timeout provided).
--exists=<path>
If the path exists, then skip waiting.
If the path exists, skip any more waiting, and return success exit status (0).
--status=<exit-status>
If provided, once done waiting, return this status code.
Defaults to 0.
If timed out, return this exit status.
Defaults to 0, however if --exists is provided, this will default to [ETIMEDOUT 60 Operation timed out] instead.
--stdout=<stdout>
If provided, once done waiting, output this to stdout.
Expand All @@ -45,13 +49,14 @@ function waiter_() (
}

# process
local item option_timeout='' option_exists='' option_status=0 option_stdout='' option_stderr='' option_tty='' option_magic='yes'
local item option_timeout='' option_message='' option_exists='' option_status='' option_stdout='' option_stderr='' option_tty='' option_magic='yes'
while test "$#" -ne 0; do
item="$1"
shift
case "$item" in
'--help' | '-h') help ;;
'--timeout='*) option_timeout="${item#*--timeout=}" ;;
'--message='*) option_message="${item#*--message=}" ;;
'--exists='*) option_exists="${item#*--exists=}" ;;
'--status='*) option_status="${item#*--status=}" ;;
'--stdout='*) option_stdout="${item#*--stdout=}" ;;
Expand All @@ -69,9 +74,27 @@ function waiter_() (
esac
done

# default timeout here, to allow [waiter <timeout>] usage
if test -z "$option_timeout"; then
option_timeout=5
# validate
if test -n "$option_timeout" && ! is-integer "$option_timeout"; then
help "If <timeout> is provided, it must be an integer, this is not an integer: $option_timeout"
fi

# status
if test -z "$option_status"; then
if test -n "$option_exists"; then
option_status=60 # ETIMEDOUT 60 Operation timed out
else
option_status=0
fi
fi

# message
if test -z "$option_message"; then
if test -n "$option_timeout"; then
option_message="$(echo-style --dim='Waiting %s...')"
else
option_message="$(echo-style --dim='Waited %s...')"
fi
fi

# =====================================
Expand All @@ -82,13 +105,44 @@ function waiter_() (
tty_target="$(is-tty --fallback)"
fi

local count
for ((count = option_timeout; count > 0; count--)); do
# wait
# trunk-ignore-all(shellcheck/SC2059)
local -i delta waited=0
while true; do
if test -n "$option_exists" -a -e "$option_exists"; then
option_status=0
break
fi
echo-style --dim="Waiting $count seconds..." >"$tty_target"
sleep 1
if test -n "$option_timeout"; then
if test "$option_timeout" -eq 0; then
break
fi
if test "$option_timeout" -gt 120; then
delta="$((option_timeout / 60))"
printf "$option_message"$'\n' "$delta minutes" >"$tty_target"
if test "$option_timeout" -gt 130; then
option_timeout="$((option_timeout - 10))"
sleep 10
else
delta="$((option_timeout - 120))"
option_timeout="$((option_timeout - delta))"
sleep "$delta"
fi
else
printf "$option_message"$'\n' "$option_timeout seconds" >"$tty_target"
option_timeout="$((option_timeout - 1))"
sleep 1
fi
elif test "$waited" -gt 120; then
delta="$((waited / 60))"
printf "$option_message"$'\n' "$delta minutes" >"$tty_target"
waited="$((waited + 10))"
sleep 1
else
printf "$option_message"$'\n' "$waited seconds" >"$tty_target"
waited="$((waited + 1))"
sleep 1
fi
if test "$option_magic" = 'yes'; then
echo-clear-line >"$tty_target"
fi
Expand Down

0 comments on commit bb57786

Please sign in to comment.