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:
@@ -33,4 +33,10 @@ jobs:
|
|||||||
git add PKGBUILD .SRCINFO
|
git add PKGBUILD .SRCINFO
|
||||||
git commit -m "pkgbuild: Bump grok-bot-bin to ${after}"
|
git commit -m "pkgbuild: Bump grok-bot-bin to ${after}"
|
||||||
git tag "v${after}"
|
git tag "v${after}"
|
||||||
git push origin "HEAD:${GITHUB_REF_NAME}" "v${after}"
|
# Proven on this forge: run 1190 pushed master + tag v0.35.0.
|
||||||
|
# Still fail the job if push is denied rather than reporting a landed bump.
|
||||||
|
if ! git push origin "HEAD:${GITHUB_REF_NAME}" "v${after}"; then
|
||||||
|
echo "error: could not push commit/tag to ${GITHUB_REF_NAME} (v${after})." >&2
|
||||||
|
echo "error: Manual path: ./scripts/update.sh, commit, PR/merge, then tag v${after} and push the tag so build.yml can attach the release asset." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|||||||
@@ -68,14 +68,16 @@ The tag triggers `.gitea/workflows/build.yml`, which builds the Arch package and
|
|||||||
|
|
||||||
Actions **can** push and tag on this forge. [Run 1190](https://git.s1d3sw1ped.com/s1d3sw1ped/grok-bot-bin/actions/runs/1190) (schedule on `master`) bumped 0.30.0 → 0.35.0 and pushed `master` plus tag `v0.35.0`.
|
Actions **can** push and tag on this forge. [Run 1190](https://git.s1d3sw1ped.com/s1d3sw1ped/grok-bot-bin/actions/runs/1190) (schedule on `master`) bumped 0.30.0 → 0.35.0 and pushed `master` plus tag `v0.35.0`.
|
||||||
|
|
||||||
If the script cannot parse the feed, or `curl` / `jq` / `python3` is missing, the job fails. A no-op is only “already at this `pkgver`”.
|
If the script cannot parse the feed, the `.deb` download fails, or `curl` / `python3` / `sha256sum` is missing, the job fails (it is not a silent “up to date”). `jq` is optional; without it the script parses the feed with `python3`. The workflow still `apt-get install`s `jq` for the run. A no-op is only “already at this `pkgver`”.
|
||||||
|
|
||||||
|
Scheduled jobs run on the default branch (`master`). Product PRs land on `develop`; promote or run the manual path below if `develop` is ahead of `master`.
|
||||||
|
|
||||||
### Manual force path
|
### Manual force path
|
||||||
|
|
||||||
When you do not want to wait for cron, or you are landing a bump on `develop`:
|
When you do not want to wait for cron, or you are landing a bump on `develop`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# requires curl, jq, python3
|
# requires curl, python3, sha256sum; jq optional
|
||||||
./scripts/update.sh
|
./scripts/update.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+106
-18
@@ -1,20 +1,69 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Bump PKGBUILD/.SRCINFO when Cursor publishes a newer linux-x64 Grok Bot.
|
# 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
|
set -euo pipefail
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
for cmd in curl jq python3 sha256sum; do
|
need_cmd() { command -v "$1" >/dev/null 2>&1; }
|
||||||
command -v "$cmd" >/dev/null || { echo "error: missing required command: $cmd" >&2; exit 1; }
|
|
||||||
done
|
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'
|
FEED='https://api2.cursor.sh/updates/api/update/linux-x64/sand/0.0.0/00000000-0000-0000-0000-000000000000/stable'
|
||||||
resp=$(curl -fsSL "$FEED")
|
echo "Fetching update feed..."
|
||||||
version=$(jq -r .version <<<"$resp")
|
if ! resp=$(curl -fsSL --retry 3 --retry-delay 2 "$FEED"); then
|
||||||
commit=$(jq -r .url <<<"$resp" | sed -E 's#.*/stable/([^/]+)/.*#\1#')
|
echo "error: failed to fetch update feed: $FEED" >&2
|
||||||
current=$(sed -n 's/^pkgver=//p' PKGBUILD)
|
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
|
feed_field() {
|
||||||
echo "error: could not parse update feed: $resp" >&2
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [[ -z "$current" ]]; then
|
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"
|
deb_url="https://downloads.cursor.com/grokbot/stable/${commit}/linux/x64/grok-bot_${version}_amd64.deb"
|
||||||
tmp=$(mktemp)
|
tmp=$(mktemp)
|
||||||
trap 'rm -f "$tmp"' EXIT
|
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)
|
deb_sha=$(sha256sum "$tmp" | cut -d' ' -f1)
|
||||||
shim_sha=$(sha256sum grok-bot-launch.sh | 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.
|
# Reset pkgrel on upstream version bumps.
|
||||||
sed -i \
|
sed -i \
|
||||||
@@ -41,9 +109,11 @@ sed -i \
|
|||||||
-e "s/^pkgrel=.*/pkgrel=1/" \
|
-e "s/^pkgrel=.*/pkgrel=1/" \
|
||||||
PKGBUILD
|
PKGBUILD
|
||||||
|
|
||||||
newver=$(sed -n 's/^pkgver=//p' PKGBUILD)
|
newver=$(sed -n 's/^pkgver=//p' PKGBUILD | head -1)
|
||||||
if [[ "$newver" != "$version" ]]; then
|
newcommit=$(sed -n 's/^_commit=//p' PKGBUILD | head -1)
|
||||||
echo "error: PKGBUILD pkgver rewrite failed (got ${newver@Q}, expected ${version@Q})" >&2
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -60,20 +130,29 @@ p.write_text(text2)
|
|||||||
PY
|
PY
|
||||||
|
|
||||||
python3 - "$version" "$commit" "$deb_sha" "$shim_sha" <<'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]
|
version, commit, deb_sha, shim_sha = sys.argv[1:5]
|
||||||
p = pathlib.Path(".SRCINFO")
|
p = pathlib.Path(".SRCINFO")
|
||||||
|
if not p.is_file():
|
||||||
|
raise SystemExit(".SRCINFO missing")
|
||||||
lines = []
|
lines = []
|
||||||
saw_sha = False
|
saw_sha = False
|
||||||
|
saw_pkgver = saw_pkgrel = saw_source = saw_noextract = False
|
||||||
for line in p.read_text().splitlines(True):
|
for line in p.read_text().splitlines(True):
|
||||||
if line.startswith("\tpkgver ="):
|
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 ="):
|
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/"):
|
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_"):
|
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 line.startswith("\tsha256sums ="):
|
||||||
if not saw_sha:
|
if not saw_sha:
|
||||||
lines.append(f"\tsha256sums = {deb_sha}\n")
|
lines.append(f"\tsha256sums = {deb_sha}\n")
|
||||||
@@ -81,6 +160,15 @@ for line in p.read_text().splitlines(True):
|
|||||||
saw_sha = True
|
saw_sha = True
|
||||||
continue
|
continue
|
||||||
lines.append(line)
|
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))
|
p.write_text("".join(lines))
|
||||||
PY
|
PY
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user