Skip to content

Commit 5c7305a

Browse files
committed
refactor(deploy): use date-stamped db assets and resolve latest at download time
Upload always creates a date-stamped asset (explainshell-{date}.db.zst) instead of renaming the previous one. A shared download-latest-db.sh script queries the GitHub API to find the newest asset and verifies its SHA256 from release metadata. Both the Makefile target (renamed to download-latest-db) and Dockerfile use it.
1 parent f0ebb63 commit 5c7305a

6 files changed

Lines changed: 65 additions & 55 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,5 +247,5 @@ fly deploy
247247

248248
**Update the database:**
249249

250-
1. `make upload-live-db` (uploads `explainshell.db.zst` to the GitHub release)
250+
1. `make upload-live-db` (uploads a date-stamped `explainshell-{date}.db.zst` to the GitHub release)
251251
2. `fly deploy` (rebuilds the image with the new DB)

Dockerfile

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
FROM python:3.12-slim
22

3-
ARG DB_URL=https://github.com/idank/explainshell/releases/download/db-latest/explainshell.db.zst
4-
53
RUN apt-get update \
6-
&& apt-get install -y --no-install-recommends wget zstd \
4+
&& apt-get install -y --no-install-recommends wget zstd jq \
75
&& rm -rf /var/lib/apt/lists/*
86

97
WORKDIR /opt/webapp
108
COPY requirements.txt .
119
RUN pip3 install --no-cache-dir --no-warn-script-location -r requirements.txt
1210

13-
RUN wget -q -O explainshell.db.zst "$DB_URL" \
14-
&& sha256sum explainshell.db.zst | awk '{print $1}' > explainshell.db.sha256 \
15-
&& echo "db sha256: $(cat explainshell.db.sha256)" \
16-
&& zstd -d --rm explainshell.db.zst
11+
COPY tools/download-latest-db.sh tools/
12+
RUN tools/download-latest-db.sh explainshell.db
1713

1814
COPY start.sh .
1915
COPY explainshell/ explainshell/

Makefile

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,11 @@ arch-archive:
7373
--distro arch --sections 1,1p,8 --output-dir manpages
7474

7575
LIVE_DB := explainshell.db
76-
LIVE_DB_ASSET := $(LIVE_DB).zst
77-
LIVE_DB_RELEASE := db-latest
78-
LIVE_DB_REPO := idank/explainshell
79-
LIVE_DB_CDN_URL := https://github.com/$(LIVE_DB_REPO)/releases/download/$(LIVE_DB_RELEASE)/$(LIVE_DB_ASSET)
8076

81-
download-live-db:
82-
gh release download $(LIVE_DB_RELEASE) -R $(LIVE_DB_REPO) -p $(LIVE_DB_ASSET) -D . --clobber
83-
zstd -d --rm $(LIVE_DB_ASSET)
77+
download-latest-db:
78+
tools/download-latest-db.sh $(LIVE_DB)
8479

8580
upload-live-db:
8681
tools/upload-live-db.sh $(LIVE_DB)
8782

88-
.PHONY: tests e2e e2e-db e2e-update test-llm tests-all tests-quick lint serve parsing-regression parsing-update db-check ubuntu-archive arch-archive download-live-db upload-live-db
83+
.PHONY: tests e2e e2e-db e2e-update test-llm tests-all tests-quick lint serve parsing-regression parsing-update db-check ubuntu-archive arch-archive download-latest-db upload-live-db

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ $ source .venv/bin/activate
3838
$ pip install -r requirements-dev.txt
3939

4040
# Download the live db, or parse a manpage.
41-
$ make download-live-db
41+
$ make download-latest-db
4242
$ python -m explainshell.manager extract --mode source manpages/ubuntu/26.04/1/tar.1.gz
4343

4444
# Run the web server

tools/download-latest-db.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
OUTPUT="${1:-explainshell.db}"
5+
REPO="idank/explainshell"
6+
RELEASE="db-latest"
7+
API_URL="https://api.github.com/repos/$REPO/releases/tags/$RELEASE"
8+
9+
RELEASE_JSON=$(wget -qO- "$API_URL")
10+
ASSET=$(echo "$RELEASE_JSON" \
11+
| jq -r '[.assets[] | select(.name | test("^explainshell-.*\\.db\\.zst$"))] | sort_by(.created_at) | last | .name')
12+
EXPECTED_SHA=$(echo "$RELEASE_JSON" \
13+
| jq -r '[.assets[] | select(.name | test("^explainshell-.*\\.db\\.zst$"))] | sort_by(.created_at) | last | .digest' \
14+
| sed 's/^sha256://')
15+
16+
if [ -z "$ASSET" ] || [ "$ASSET" = "null" ]; then
17+
echo "No db asset found in release $RELEASE" >&2
18+
exit 1
19+
fi
20+
21+
echo "Downloading $ASSET..."
22+
wget -q -O "$ASSET" "https://github.com/$REPO/releases/download/$RELEASE/$ASSET"
23+
24+
ACTUAL_SHA=$(sha256sum "$ASSET" | awk '{print $1}')
25+
if [ "$ACTUAL_SHA" != "$EXPECTED_SHA" ]; then
26+
echo "SHA256 mismatch: expected $EXPECTED_SHA, got $ACTUAL_SHA" >&2
27+
rm -f "$ASSET"
28+
exit 1
29+
fi
30+
echo "$ACTUAL_SHA" > "$OUTPUT.sha256"
31+
echo "SHA256 verified: $ACTUAL_SHA"
32+
33+
zstd -d -f "$ASSET" -o "$OUTPUT"
34+
rm -f "$ASSET"
35+
echo "Wrote $OUTPUT"

tools/upload-live-db.sh

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,32 @@ set -euo pipefail
44
DB="${1:?Usage: $0 <db-file>}"
55
REPO="idank/explainshell"
66
RELEASE="db-latest"
7-
ASSET="explainshell.db.zst"
8-
CDN_URL="https://github.com/$REPO/releases/download/$RELEASE/$ASSET"
7+
DATE=$(date -u +%Y-%m-%d-%H%M%S)
8+
ASSET="explainshell-${DATE}.db.zst"
99

1010
test -f "$DB" || { echo "$DB not found"; exit 1; }
1111

12-
# --- Compress and check if upload is needed ---
12+
# --- Compress ---
1313
zstd -1 -f "$DB" -o "$ASSET"
14-
local_sha=$(sha256sum "$ASSET" | awk '{print $1}')
15-
remote_sha=$(gh api "repos/$REPO/releases/tags/$RELEASE" \
16-
--jq ".assets[] | select(.name == \"$ASSET\") | .digest" 2>/dev/null \
17-
| sed 's/^sha256://')
18-
19-
if [ "$local_sha" = "$remote_sha" ]; then
20-
echo "Local DB digest ($local_sha) matches release. Nothing to upload."
21-
rm -f "$ASSET"
22-
exit 0
23-
fi
2414

25-
echo "Local digest: $local_sha"
26-
echo "Release digest: ${remote_sha:-<none>}"
27-
28-
# --- Archive existing asset ---
29-
asset_id=$(gh api "repos/$REPO/releases/tags/$RELEASE" --jq ".assets[] | select(.name == \"$ASSET\") | .id")
30-
if [ -n "$asset_id" ]; then
31-
upload_date=$(gh api "repos/$REPO/releases/tags/$RELEASE" \
32-
--jq ".assets[] | select(.name == \"$ASSET\") | .updated_at" \
33-
| tr -d 'Z' | tr 'T:' '-')
34-
archive_name="explainshell-${upload_date}.db.zst"
35-
echo "Renaming existing asset to $archive_name..."
36-
gh api "repos/$REPO/releases/assets/$asset_id" -X PATCH -f name="$archive_name" --silent
15+
# --- Check if upload is needed by comparing to the newest existing asset ---
16+
newest_asset=$(gh api "repos/$REPO/releases/tags/$RELEASE" \
17+
--jq '[.assets[] | select(.name | test("^explainshell-.*\\.db\\.zst$"))] | sort_by(.created_at) | last | .name' 2>/dev/null || true)
18+
19+
if [ -n "$newest_asset" ]; then
20+
remote_sha=$(gh api "repos/$REPO/releases/tags/$RELEASE" \
21+
--jq ".assets[] | select(.name == \"$newest_asset\") | .digest" 2>/dev/null \
22+
| sed 's/^sha256://')
23+
local_sha=$(sha256sum "$ASSET" | awk '{print $1}')
24+
25+
if [ "$local_sha" = "$remote_sha" ]; then
26+
echo "Local DB digest ($local_sha) matches latest release asset ($newest_asset). Nothing to upload."
27+
rm -f "$ASSET"
28+
exit 0
29+
fi
30+
31+
echo "Local digest: $local_sha"
32+
echo "Latest asset: $newest_asset (digest: ${remote_sha:-<none>})"
3733
fi
3834

3935
# --- Upload ---
@@ -47,17 +43,5 @@ curl --progress-bar \
4743
--data-binary @"$ASSET" \
4844
"${upload_url}?name=$ASSET" | cat
4945

50-
# --- Wait for CDN ---
51-
expected_size=$(wc -c < "$ASSET")
5246
rm -f "$ASSET"
53-
54-
echo "Waiting for CDN to serve the new file ($expected_size bytes)..."
55-
while true; do
56-
cdn_size=$(curl -sI -L "$CDN_URL" | grep -i content-length | tail -1 | tr -d '[:space:]' | cut -d: -f2)
57-
if [ "$cdn_size" = "$expected_size" ]; then
58-
echo "CDN updated."
59-
break
60-
fi
61-
echo " CDN still serving $cdn_size bytes, expected $expected_size. Retrying in 10s..."
62-
sleep 10
63-
done
47+
echo "Uploaded $ASSET to release $RELEASE."

0 commit comments

Comments
 (0)