scripts: Parse update feed with python3 if jq is missing
Jumpboxes may lack jq; requiring it made the documented manual path fail closed for the wrong reason. Keep python3 as the rewrite tool and the feed parser fallback, and fail loudly when neither parser nor curl/sha256sum is present. Reject tiny/non-deb downloads so a failed fetch cannot look like a successful bump.
This commit is contained in:
+106
-18
@@ -1,20 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
# Bump PKGBUILD/.SRCINFO when Cursor publishes a newer linux-x64 Grok Bot.
|
||||
# Requires curl, sha256sum, python3. jq is optional (python3 parses the feed).
|
||||
# Fail loudly; do not leave a half-applied bump that looks like "up to date".
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
for cmd in curl jq python3 sha256sum; do
|
||||
command -v "$cmd" >/dev/null || { echo "error: missing required command: $cmd" >&2; exit 1; }
|
||||
done
|
||||
need_cmd() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
missing=()
|
||||
need_cmd curl || missing+=(curl)
|
||||
need_cmd sha256sum || missing+=(sha256sum)
|
||||
need_cmd python3 || missing+=(python3)
|
||||
if ((${#missing[@]})); then
|
||||
echo "error: missing required tools: ${missing[*]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! need_cmd jq; then
|
||||
echo "note: jq not found; parsing update feed with python3" >&2
|
||||
fi
|
||||
|
||||
FEED='https://api2.cursor.sh/updates/api/update/linux-x64/sand/0.0.0/00000000-0000-0000-0000-000000000000/stable'
|
||||
resp=$(curl -fsSL "$FEED")
|
||||
version=$(jq -r .version <<<"$resp")
|
||||
commit=$(jq -r .url <<<"$resp" | sed -E 's#.*/stable/([^/]+)/.*#\1#')
|
||||
current=$(sed -n 's/^pkgver=//p' PKGBUILD)
|
||||
echo "Fetching update feed..."
|
||||
if ! resp=$(curl -fsSL --retry 3 --retry-delay 2 "$FEED"); then
|
||||
echo "error: failed to fetch update feed: $FEED" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "$resp" ]]; then
|
||||
echo "error: empty update feed from $FEED" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$version" || "$version" == "null" || ! "$commit" =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "error: could not parse update feed: $resp" >&2
|
||||
feed_field() {
|
||||
local field="$1"
|
||||
if need_cmd jq; then
|
||||
jq -er --arg f "$field" '.[$f] | select(. != null and . != "")' <<<"$resp"
|
||||
else
|
||||
python3 -c '
|
||||
import json, sys
|
||||
d = json.loads(sys.stdin.read())
|
||||
v = d.get(sys.argv[1])
|
||||
if not isinstance(v, (str, int, float)) or v == "":
|
||||
raise SystemExit(1)
|
||||
print(v)
|
||||
' "$field" <<<"$resp"
|
||||
fi
|
||||
}
|
||||
|
||||
if ! version=$(feed_field version); then
|
||||
echo "error: could not parse .version from update feed: $resp" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! url=$(feed_field url); then
|
||||
echo "error: could not parse .url from update feed: $resp" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
commit=$(sed -E 's#.*/stable/([^/]+)/.*#\1#' <<<"$url")
|
||||
current=$(sed -n 's/^pkgver=//p' PKGBUILD | head -1)
|
||||
|
||||
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z]+)*$ ]]; then
|
||||
echo "error: feed version looks wrong: $version" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! "$commit" =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "error: could not parse 40-char commit from feed url: $url" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "$current" ]]; then
|
||||
@@ -30,9 +79,28 @@ echo "Bump $current -> $version (commit $commit)"
|
||||
deb_url="https://downloads.cursor.com/grokbot/stable/${commit}/linux/x64/grok-bot_${version}_amd64.deb"
|
||||
tmp=$(mktemp)
|
||||
trap 'rm -f "$tmp"' EXIT
|
||||
curl -fsSL --retry 3 "$deb_url" -o "$tmp"
|
||||
echo "Downloading $deb_url"
|
||||
if ! curl -fL --retry 3 --retry-delay 2 -o "$tmp" "$deb_url"; then
|
||||
echo "error: failed to download deb: $deb_url" >&2
|
||||
exit 1
|
||||
fi
|
||||
sz=$(wc -c <"$tmp")
|
||||
if ((sz < 1000000)); then
|
||||
echo "error: downloaded deb too small (${sz} bytes): $deb_url" >&2
|
||||
exit 1
|
||||
fi
|
||||
magic=$(head -c 8 "$tmp" | tr -d '\0')
|
||||
if [[ "$magic" != '!<arch>'* ]]; then
|
||||
echo "error: download is not a .deb (missing ar magic): $deb_url" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
deb_sha=$(sha256sum "$tmp" | cut -d' ' -f1)
|
||||
shim_sha=$(sha256sum grok-bot-launch.sh | cut -d' ' -f1)
|
||||
if [[ ! "$deb_sha" =~ ^[0-9a-f]{64}$ || ! "$shim_sha" =~ ^[0-9a-f]{64}$ ]]; then
|
||||
echo "error: sha256sum failed (deb=$deb_sha shim=$shim_sha)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Reset pkgrel on upstream version bumps.
|
||||
sed -i \
|
||||
@@ -41,9 +109,11 @@ sed -i \
|
||||
-e "s/^pkgrel=.*/pkgrel=1/" \
|
||||
PKGBUILD
|
||||
|
||||
newver=$(sed -n 's/^pkgver=//p' PKGBUILD)
|
||||
if [[ "$newver" != "$version" ]]; then
|
||||
echo "error: PKGBUILD pkgver rewrite failed (got ${newver@Q}, expected ${version@Q})" >&2
|
||||
newver=$(sed -n 's/^pkgver=//p' PKGBUILD | head -1)
|
||||
newcommit=$(sed -n 's/^_commit=//p' PKGBUILD | head -1)
|
||||
newrel=$(sed -n 's/^pkgrel=//p' PKGBUILD | head -1)
|
||||
if [[ "$newver" != "$version" || "$newcommit" != "$commit" || "$newrel" != "1" ]]; then
|
||||
echo "error: PKGBUILD rewrite did not stick (pkgver=$newver _commit=$newcommit pkgrel=$newrel)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -60,20 +130,29 @@ p.write_text(text2)
|
||||
PY
|
||||
|
||||
python3 - "$version" "$commit" "$deb_sha" "$shim_sha" <<'PY'
|
||||
import pathlib, re, sys
|
||||
import pathlib, sys
|
||||
version, commit, deb_sha, shim_sha = sys.argv[1:5]
|
||||
p = pathlib.Path(".SRCINFO")
|
||||
if not p.is_file():
|
||||
raise SystemExit(".SRCINFO missing")
|
||||
lines = []
|
||||
saw_sha = False
|
||||
saw_pkgver = saw_pkgrel = saw_source = saw_noextract = False
|
||||
for line in p.read_text().splitlines(True):
|
||||
if line.startswith("\tpkgver ="):
|
||||
lines.append(f"\tpkgver = {version}\n"); continue
|
||||
lines.append(f"\tpkgver = {version}\n"); saw_pkgver = True; continue
|
||||
if line.startswith("\tpkgrel ="):
|
||||
lines.append("\tpkgrel = 1\n"); continue
|
||||
lines.append("\tpkgrel = 1\n"); saw_pkgrel = True; continue
|
||||
if line.startswith("\tsource = https://downloads.cursor.com/grokbot/stable/"):
|
||||
lines.append(f"\tsource = https://downloads.cursor.com/grokbot/stable/{commit}/linux/x64/grok-bot_{version}_amd64.deb\n"); continue
|
||||
lines.append(
|
||||
f"\tsource = https://downloads.cursor.com/grokbot/stable/{commit}/linux/x64/grok-bot_{version}_amd64.deb\n"
|
||||
)
|
||||
saw_source = True
|
||||
continue
|
||||
if line.startswith("\tnoextract = grok-bot_"):
|
||||
lines.append(f"\tnoextract = grok-bot_{version}_amd64.deb\n"); continue
|
||||
lines.append(f"\tnoextract = grok-bot_{version}_amd64.deb\n")
|
||||
saw_noextract = True
|
||||
continue
|
||||
if line.startswith("\tsha256sums ="):
|
||||
if not saw_sha:
|
||||
lines.append(f"\tsha256sums = {deb_sha}\n")
|
||||
@@ -81,6 +160,15 @@ for line in p.read_text().splitlines(True):
|
||||
saw_sha = True
|
||||
continue
|
||||
lines.append(line)
|
||||
missing = [n for n, ok in (
|
||||
("pkgver", saw_pkgver),
|
||||
("pkgrel", saw_pkgrel),
|
||||
("source", saw_source),
|
||||
("noextract", saw_noextract),
|
||||
("sha256sums", saw_sha),
|
||||
) if not ok]
|
||||
if missing:
|
||||
raise SystemExit(f".SRCINFO rewrite missed fields: {', '.join(missing)}")
|
||||
p.write_text("".join(lines))
|
||||
PY
|
||||
|
||||
|
||||
Reference in New Issue
Block a user