Fix large GitHub release JSON handling
This commit is contained in:
committed by
latypov
parent
aa4c241f99
commit
8f84934c46
@@ -57,3 +57,11 @@ At 09:00 server-local time:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Run the monitor under a dedicated user where possible. Keep `.env` private and do not commit it. Use `./monitor.sh --test-telegram` to verify Telegram and proxy connectivity.
|
Run the monitor under a dedicated user where possible. Keep `.env` private and do not commit it. Use `./monitor.sh --test-telegram` to verify Telegram and proxy connectivity.
|
||||||
|
|
||||||
|
### Large release histories
|
||||||
|
|
||||||
|
GitHub response pages are processed through temporary files, rather than passed
|
||||||
|
through command-line arguments. The state file is read directly by `jq`.
|
||||||
|
This prevents `Argument list too long` for repositories with large release notes.
|
||||||
|
|
||||||
|
Run `bash -n monitor.sh` and `./monitor.sh --dry-run --verbose` to verify.
|
||||||
|
|||||||
Regular → Executable
+41
-16
@@ -93,29 +93,48 @@ test_telegram() {
|
|||||||
if send_telegram "$message"; then log INFO 'Test notification sent'; else log ERROR 'Test notification failed'; return 1; fi
|
if send_telegram "$message"; then log INFO 'Test notification sent'; else log ERROR 'Test notification failed'; return 1; fi
|
||||||
}
|
}
|
||||||
fetch_releases() {
|
fetch_releases() {
|
||||||
local repo="$1" page response count result='[]'
|
local repo="$1" page count response_file releases_file
|
||||||
|
response_file="$(mktemp)" || return 1
|
||||||
|
releases_file="$(mktemp)" || { rm -f "$response_file"; return 1; }
|
||||||
|
|
||||||
for ((page=1; page<=GITHUB_MAX_PAGES; page++)); do
|
for ((page=1; page<=GITHUB_MAX_PAGES; page++)); do
|
||||||
debug "Fetching $repo page=$page"
|
debug "Fetching $repo page=$page"
|
||||||
response="$(curl "${CURL_OPTIONS[@]}" "${GITHUB_HEADERS[@]}" \
|
if ! curl "${CURL_OPTIONS[@]}" "${GITHUB_HEADERS[@]}" \
|
||||||
"${GITHUB_API}/repos/${repo}/releases?per_page=${GITHUB_PER_PAGE}&page=${page}")" || return 1
|
--output "$response_file" \
|
||||||
jq -e 'type == "array"' <<< "$response" >/dev/null || return 1
|
"${GITHUB_API}/repos/${repo}/releases?per_page=${GITHUB_PER_PAGE}&page=${page}"; then
|
||||||
count="$(jq 'length' <<< "$response")"
|
rm -f "$response_file" "$releases_file"
|
||||||
result="$(jq -cn --argjson old "$result" --argjson new "$response" '$old + $new')" || return 1
|
return 1
|
||||||
|
fi
|
||||||
|
if ! count="$(jq -er 'if type == "array" then length else error("Invalid releases response") end' "$response_file")"; then
|
||||||
|
rm -f "$response_file" "$releases_file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! jq -c --argjson prereleases "$INCLUDE_PRERELEASES" '
|
||||||
|
.[] | select(.draft == false) |
|
||||||
|
select($prereleases or (.prerelease == false)) |
|
||||||
|
{id: (.id | tostring), tag: .tag_name, name: .name,
|
||||||
|
url: .html_url, published: .published_at, prerelease: .prerelease}
|
||||||
|
' "$response_file" >> "$releases_file"; then
|
||||||
|
rm -f "$response_file" "$releases_file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
if (( count < GITHUB_PER_PAGE )); then break; fi
|
if (( count < GITHUB_PER_PAGE )); then break; fi
|
||||||
done
|
done
|
||||||
jq -c --argjson prereleases "$INCLUDE_PRERELEASES" '
|
|
||||||
[.[] | select(.draft == false) | select($prereleases or (.prerelease == false)) |
|
local status=0
|
||||||
{id: (.id | tostring), tag: .tag_name, name: .name, url: .html_url,
|
jq -sc '.' "$releases_file" || status=$?
|
||||||
published: .published_at, prerelease: .prerelease}]
|
rm -f "$response_file" "$releases_file"
|
||||||
' <<< "$result"
|
return "$status"
|
||||||
}
|
}
|
||||||
|
|
||||||
write_state() {
|
write_state() {
|
||||||
local repo="$1" ids="$2" mode="$3" temp
|
local repo="$1" ids="$2" mode="$3" temp
|
||||||
temp="$(mktemp "${STATE_FILE}.tmp.XXXXXX")" || return 1
|
temp="$(mktemp "${STATE_FILE}.tmp.XXXXXX")" || return 1
|
||||||
if ! jq --arg repo "$repo" --argjson ids "$ids" --arg mode "$mode" '
|
if ! jq --arg repo "$repo" --arg mode "$mode" --slurpfile ids /dev/stdin '
|
||||||
if $mode == "replace" then .[$repo] = $ids
|
($ids[0] // []) as $new_ids |
|
||||||
else .[$repo] = ((.[$repo] // []) + $ids | unique) end
|
if $mode == "replace" then .[$repo] = $new_ids
|
||||||
' "$STATE_FILE" > "$temp"; then rm -f "$temp"; return 1; fi
|
else .[$repo] = ((.[$repo] // []) + $new_ids | unique) end
|
||||||
|
' "$STATE_FILE" <<< "$ids" > "$temp"; then rm -f "$temp"; return 1; fi
|
||||||
if ! mv -f "$temp" "$STATE_FILE"; then rm -f "$temp"; return 1; fi
|
if ! mv -f "$temp" "$STATE_FILE"; then rm -f "$temp"; return 1; fi
|
||||||
}
|
}
|
||||||
format_message() {
|
format_message() {
|
||||||
@@ -151,7 +170,13 @@ process_repo() {
|
|||||||
log INFO "Baseline initialized: $repo ($count releases)"
|
log INFO "Baseline initialized: $repo ($count releases)"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
new_releases="$(jq -c --argjson seen "$previous" '[.[] | select(.id as $id | $seen | index($id) | not)] | reverse' <<< "$releases")" || return 1
|
# Read the existing state from a file, not from argv (ARG_MAX).
|
||||||
|
local state_source="$STATE_FILE"
|
||||||
|
[[ -f "$state_source" ]] || state_source=/dev/null
|
||||||
|
new_releases="$(jq -c --slurpfile state "$state_source" --arg repo "$repo" '
|
||||||
|
($state[0][$repo] // []) as $seen |
|
||||||
|
[.[] | select(.id as $id | $seen | index($id) | not)] | reverse
|
||||||
|
' <<< "$releases")" || return 1
|
||||||
count="$(jq 'length' <<< "$new_releases")"
|
count="$(jq 'length' <<< "$new_releases")"
|
||||||
if (( count == 0 )); then log INFO "No new releases: $repo"; return 0; fi
|
if (( count == 0 )); then log INFO "No new releases: $repo"; return 0; fi
|
||||||
log INFO "Found $count new releases: $repo"
|
log INFO "Found $count new releases: $repo"
|
||||||
|
|||||||
Reference in New Issue
Block a user