Skip to content

Commit 7b32203

Browse files
steveyeggeseanbeardenclaude
committed
feat: add run.sh for dolt-archive plugin, fix DB name beads to bd
Adds deterministic JSONL backup script with git push and dolt push support. Fixes production database name from 'beads' to 'bd' in plugin documentation. Closes #2644 Co-Authored-By: Sean Bearden <72461227+seanbearden@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2abc36d commit 7b32203

2 files changed

Lines changed: 225 additions & 1 deletion

File tree

plugins/dolt-archive/plugin.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ whether the other layers work.
3232

3333
```bash
3434
DOLT_DATA_DIR="$HOME/gt/.dolt-data"
35-
PROD_DBS=("hq" "beads" "gastown")
35+
PROD_DBS=("hq" "bd" "gastown")
3636
JSONL_EXPORT_DIR="$HOME/gt/.dolt-archive/jsonl"
3737
DOLT_HOST="127.0.0.1"
3838
DOLT_PORT=3307

plugins/dolt-archive/run.sh

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
#!/usr/bin/env bash
2+
# dolt-archive/run.sh — Deterministic JSONL backup + git push + dolt push.
3+
#
4+
# Exports production databases to JSONL, commits to git backup repo,
5+
# and pushes Dolt remotes. JSONL is the last-resort recovery layer.
6+
#
7+
# Usage: ./run.sh [--databases db1,db2,...] [--skip-git] [--skip-dolt-push]
8+
9+
set -euo pipefail
10+
11+
# --- Configuration -----------------------------------------------------------
12+
13+
DOLT_HOST="${DOLT_HOST:-127.0.0.1}"
14+
DOLT_PORT="${DOLT_PORT:-3307}"
15+
DOLT_USER="${DOLT_USER:-root}"
16+
DOLT_DATA_DIR="${DOLT_DATA_DIR:-$HOME/gt/.dolt-data}"
17+
JSONL_EXPORT_DIR="$HOME/gt/.dolt-archive/jsonl"
18+
BACKUP_REPO="$HOME/gt/.dolt-archive/git"
19+
DEFAULT_DBS="hq,bd,gastown"
20+
SKIP_GIT=false
21+
SKIP_DOLT_PUSH=false
22+
23+
# --- Argument parsing --------------------------------------------------------
24+
25+
while [[ $# -gt 0 ]]; do
26+
case "$1" in
27+
--databases) DEFAULT_DBS="$2"; shift 2 ;;
28+
--skip-git) SKIP_GIT=true; shift ;;
29+
--skip-dolt-push) SKIP_DOLT_PUSH=true; shift ;;
30+
--help|-h)
31+
echo "Usage: $0 [--databases db1,db2,...] [--skip-git] [--skip-dolt-push]"
32+
exit 0
33+
;;
34+
*) echo "Unknown option: $1"; exit 1 ;;
35+
esac
36+
done
37+
38+
# --- Helpers -----------------------------------------------------------------
39+
40+
log() {
41+
echo "[dolt-archive] $*"
42+
}
43+
44+
LOGFILE=$(mktemp /tmp/dolt-archive-stderr.XXXXXX)
45+
trap 'rm -f "$LOGFILE"' EXIT
46+
47+
dolt_query() {
48+
local db="$1"
49+
local query="$2"
50+
local args=(dolt --host "$DOLT_HOST" --port "$DOLT_PORT" --no-tls -u "$DOLT_USER" -p "")
51+
if [[ -n "$db" ]]; then
52+
args+=(--use-db "$db")
53+
fi
54+
args+=(sql -q "$query" --result-format csv)
55+
"${args[@]}" 2>>"$LOGFILE" | tail -n +2 | tr -d '\r'
56+
}
57+
58+
dolt_query_json() {
59+
local db="$1"
60+
local query="$2"
61+
dolt --host "$DOLT_HOST" --port "$DOLT_PORT" --no-tls -u "$DOLT_USER" -p "" \
62+
--use-db "$db" sql -q "$query" --result-format json 2>>"$LOGFILE"
63+
}
64+
65+
# --- Step 1: JSONL export ----------------------------------------------------
66+
67+
IFS=',' read -ra PROD_DBS <<< "$DEFAULT_DBS"
68+
69+
log "Starting archive cycle (databases: ${PROD_DBS[*]})"
70+
mkdir -p "$JSONL_EXPORT_DIR"
71+
72+
EXPORTED=0
73+
EXPORT_FAILED=0
74+
EXPORT_ERRORS=""
75+
76+
for DB in "${PROD_DBS[@]}"; do
77+
EXPORT_FILE="$JSONL_EXPORT_DIR/${DB}-$(date +%Y%m%d-%H%M).jsonl"
78+
LATEST_LINK="$JSONL_EXPORT_DIR/${DB}-latest.jsonl"
79+
80+
log "Exporting $DB..."
81+
82+
# Try bd export first (native beads export)
83+
if bd export --db "$DB" --format jsonl > "$EXPORT_FILE" 2>/dev/null; then
84+
LINE_COUNT=$(wc -l < "$EXPORT_FILE" | tr -d ' ')
85+
FILE_SIZE=$(du -h "$EXPORT_FILE" | cut -f1)
86+
log " $DB: $LINE_COUNT issues exported ($FILE_SIZE) [bd export]"
87+
ln -sf "$(basename "$EXPORT_FILE")" "$LATEST_LINK"
88+
EXPORTED=$((EXPORTED + 1))
89+
else
90+
# Fallback: query Dolt directly for issues table
91+
if dolt_query_json "$DB" "SELECT * FROM issues ORDER BY id" > "$EXPORT_FILE" 2>/dev/null && [[ -s "$EXPORT_FILE" ]]; then
92+
LINE_COUNT=$(wc -l < "$EXPORT_FILE" | tr -d ' ')
93+
log " $DB: exported via SQL ($LINE_COUNT lines)"
94+
ln -sf "$(basename "$EXPORT_FILE")" "$LATEST_LINK"
95+
EXPORTED=$((EXPORTED + 1))
96+
else
97+
log " WARN: $DB export failed"
98+
rm -f "$EXPORT_FILE"
99+
EXPORT_FAILED=$((EXPORT_FAILED + 1))
100+
EXPORT_ERRORS="${EXPORT_ERRORS}${DB} "
101+
fi
102+
fi
103+
done
104+
105+
# Prune old exports (keep last 24 snapshots per DB)
106+
for DB in "${PROD_DBS[@]}"; do
107+
SNAPSHOTS=$(ls -t "$JSONL_EXPORT_DIR/${DB}-2"*.jsonl 2>/dev/null | tail -n +25)
108+
if [[ -n "$SNAPSHOTS" ]]; then
109+
echo "$SNAPSHOTS" | xargs rm -f
110+
log "Pruned old $DB snapshots"
111+
fi
112+
done
113+
114+
log "JSONL export: $EXPORTED succeeded, $EXPORT_FAILED failed"
115+
116+
# --- Step 2: Git commit and push ---------------------------------------------
117+
118+
GIT_PUSHED=false
119+
120+
if ! $SKIP_GIT && [[ -d "$BACKUP_REPO/.git" ]]; then
121+
log ""
122+
log "=== Git Push ==="
123+
124+
# Copy latest JSONL files to git repo
125+
for DB in "${PROD_DBS[@]}"; do
126+
LATEST="$JSONL_EXPORT_DIR/${DB}-latest.jsonl"
127+
if [[ -L "$LATEST" ]]; then
128+
REAL_FILE="$JSONL_EXPORT_DIR/$(readlink "$LATEST")"
129+
if [[ -f "$REAL_FILE" ]]; then
130+
cp "$REAL_FILE" "$BACKUP_REPO/${DB}.jsonl"
131+
fi
132+
elif [[ -f "$LATEST" ]]; then
133+
cp "$LATEST" "$BACKUP_REPO/${DB}.jsonl"
134+
fi
135+
done
136+
137+
cd "$BACKUP_REPO"
138+
139+
if git diff --quiet && git diff --staged --quiet; then
140+
log "No changes to commit"
141+
else
142+
git add *.jsonl 2>/dev/null || true
143+
git commit -m "Archive snapshot $(date +%Y-%m-%d-%H%M)" \
144+
--author="Gas Town Archive <archive@gastown.local>" 2>/dev/null || true
145+
146+
if git remote get-url origin > /dev/null 2>&1; then
147+
if git push origin main 2>/dev/null; then
148+
GIT_PUSHED=true
149+
log "Pushed to GitHub"
150+
else
151+
log "WARN: Git push to remote failed"
152+
fi
153+
else
154+
log "WARN: No git remote configured for backup repo"
155+
fi
156+
fi
157+
elif ! $SKIP_GIT; then
158+
log "No git backup repo at $BACKUP_REPO — skipping git push"
159+
fi
160+
161+
# --- Step 3: Dolt native push ------------------------------------------------
162+
163+
DOLT_PUSHED=0
164+
DOLT_PUSH_FAILED=0
165+
166+
if ! $SKIP_DOLT_PUSH; then
167+
log ""
168+
log "=== Dolt Push ==="
169+
170+
for DB in "${PROD_DBS[@]}"; do
171+
DB_DIR="$DOLT_DATA_DIR/$DB"
172+
173+
if [[ ! -d "$DB_DIR/.dolt" ]]; then
174+
log " $DB: no .dolt directory, skipping"
175+
continue
176+
fi
177+
178+
REMOTES=$(cd "$DB_DIR" && dolt remote -v 2>/dev/null | grep -v "^$" | head -5)
179+
if [[ -z "$REMOTES" ]]; then
180+
log " $DB: no remotes configured, skipping"
181+
continue
182+
fi
183+
184+
log " $DB: pushing to remotes..."
185+
cd "$DB_DIR"
186+
187+
for REMOTE_NAME in $(dolt remote -v 2>/dev/null | awk '{print $1}' | sort -u); do
188+
if timeout 120 dolt push "$REMOTE_NAME" main 2>/dev/null; then
189+
log " $REMOTE_NAME: pushed"
190+
DOLT_PUSHED=$((DOLT_PUSHED + 1))
191+
else
192+
log " $REMOTE_NAME: FAILED"
193+
DOLT_PUSH_FAILED=$((DOLT_PUSH_FAILED + 1))
194+
fi
195+
done
196+
done
197+
198+
log "Dolt push: $DOLT_PUSHED succeeded, $DOLT_PUSH_FAILED failed"
199+
fi
200+
201+
# --- Step 4: Report results --------------------------------------------------
202+
203+
log ""
204+
log "=== Archive Cycle Complete ==="
205+
206+
SUMMARY="Archive: jsonl=$EXPORTED/$((EXPORTED + EXPORT_FAILED)), git=${GIT_PUSHED}, dolt_push=$DOLT_PUSHED/$((DOLT_PUSHED + DOLT_PUSH_FAILED))"
207+
log "$SUMMARY"
208+
209+
RESULT="success"
210+
if [[ "$EXPORT_FAILED" -gt 0 ]] || [[ "$DOLT_PUSH_FAILED" -gt 0 ]]; then
211+
RESULT="warning"
212+
fi
213+
214+
bd create "$SUMMARY" -t chore --ephemeral \
215+
-l type:plugin-run,plugin:dolt-archive,result:$RESULT \
216+
-d "$SUMMARY" --silent 2>/dev/null || true
217+
218+
if [[ "$EXPORT_FAILED" -gt 0 ]]; then
219+
gt escalate "dolt-archive: JSONL export failed for $EXPORT_FAILED databases ($EXPORT_ERRORS)" \
220+
-s critical \
221+
--reason "JSONL is our last-resort recovery layer. Failed databases: $EXPORT_ERRORS" 2>/dev/null || true
222+
fi
223+
224+
log "Done."

0 commit comments

Comments
 (0)