Skip to content

Commit a611cde

Browse files
committed
feat: add reset-repo ops scripts with dry-run and storage permission fixes
- Add ensure-storage.sh and reset-repo.sh for new/existing repos (prepare, inspect, dry-run) - prepare updates existing indexes incrementally; soft/full-reset force rebuild - Run ensure-storage on deploy and hetzner setup to keep /var/codewalk writable (uid 999) - mkdir cloud index artifact dirs before Chroma/DuckDB open - Document coverage matrix and workflows in SERVER_OPS.md
1 parent fd44e30 commit a611cde

9 files changed

Lines changed: 676 additions & 8 deletions

File tree

FULL_SETUP_GUIDE.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,11 +574,25 @@ curl -s -X POST "$API/admin/register" \
574574

575575
## 13. Delete / re-index
576576

577-
### Index files only
577+
> **Preferred (server):** [deploy/reset-repo.sh](deploy/reset-repo.sh) — new + existing repos, `prepare`/`soft-reset`/`full-reset`, `--dry-run`, auto `chown 999:999`. See [SERVER_OPS.md §11](deploy/SERVER_OPS.md#11-re-index-reset--fix-stuck-indexing).
578+
579+
```bash
580+
cd /opt/codewalk-src
581+
chmod +x deploy/reset-repo.sh deploy/ensure-storage.sh
582+
583+
./deploy/reset-repo.sh inspect gupta29470/codewalk
584+
./deploy/reset-repo.sh prepare gupta29470/codewalk --index --dry-run # preview
585+
./deploy/reset-repo.sh prepare gupta29470/codewalk --index # update or create
586+
./deploy/reset-repo.sh soft-reset gupta29470/codewalk --index # wipe index, rebuild
587+
./deploy/reset-repo.sh delete-repo gupta29470/codewalk
588+
```
589+
590+
### Index files only (manual)
578591

579592
```bash
580593
REPO="gupta29470/codewalk"
581594
rm -rf /var/codewalk/indexes/${REPO}
595+
/opt/codewalk-src/deploy/ensure-storage.sh "${REPO}" # required after rm as root
582596

583597
docker compose exec postgres psql -U codewalk -d codewalk -c \
584598
"UPDATE repos SET last_indexed_sha=NULL, index_status='pending', index_version=0 WHERE full_name='${REPO}';"

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,7 +1438,7 @@ Local MCP → GET /indexes/{owner}/{repo} → query locally
14381438
6. Verify: `POST /admin/repos` with `X-Admin-Key``index_status: ready`
14391439
7. Get `repo_token` from DB → set `CODEWALK_REPO_TOKEN` in MCP config
14401440

1441-
**Day-to-day server ops:** [deploy/SERVER_OPS.md](deploy/SERVER_OPS.md) — health checks, indexing status, jobs/webhooks SQL, logs, reset/re-index.
1441+
**Day-to-day server ops:** [deploy/SERVER_OPS.md](deploy/SERVER_OPS.md) — health, SQL, logs, [reset-repo.sh](deploy/reset-repo.sh) (prepare/reset/delete, `--dry-run`).
14421442

14431443
### Indexing a repo (checklist)
14441444

@@ -1475,7 +1475,7 @@ Cloud reads this on every index. Pushes to other branches are ignored. See [FULL
14751475
|------|-----|
14761476
| [FULL_SETUP_GUIDE.md](FULL_SETUP_GUIDE.md) | Complete A→Z setup |
14771477
| [deploy/DEPLOY.md](deploy/DEPLOY.md) | Deployment guide |
1478-
| [deploy/SERVER_OPS.md](deploy/SERVER_OPS.md) | Server ops cheat sheet — health, indexing, SQL, logs |
1478+
| [deploy/SERVER_OPS.md](deploy/SERVER_OPS.md) | Server ops — health, indexing, SQL, [reset-repo.sh](deploy/reset-repo.sh) |
14791479
| [env.server.example.txt](env.server.example.txt) | Hetzner `/opt/codewalk/.env` |
14801480
| [env.local.example.txt](env.local.example.txt) | Local dev `.env` |
14811481
| [mcp.json.example](mcp.json.example) | MCP config → `.vscode/mcp.json` |

deploy/DEPLOY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
6. [Future Deploys (CI/CD)](#future-deploys-cicd)
1717
7. [Rollback Procedure](#rollback-procedure)
1818
8. [Troubleshooting](#troubleshooting)
19-
9. [Server Ops Command Reference](./SERVER_OPS.md) — health, indexing, SQL, logs (cheat sheet)
19+
9. [Server Ops Command Reference](./SERVER_OPS.md) — health, indexing, SQL, `reset-repo.sh` / `ensure-storage.sh`
2020

2121
---
2222

deploy/SERVER_OPS.md

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,12 +396,63 @@ cd /opt/codewalk && docker compose up -d --force-recreate codewalk-api
396396

397397
## 11. Re-index, reset & fix stuck indexing
398398

399+
> **Preferred:** use `deploy/reset-repo.sh` — works for **new** and **existing** repos. Detects on-disk index: **update** (incremental) vs **create** (full embed). Every mode supports **`--dry-run`**.
400+
401+
```bash
402+
cd /opt/codewalk-src
403+
chmod +x deploy/reset-repo.sh deploy/ensure-storage.sh
404+
405+
# Inspect state + plans (no changes)
406+
./deploy/reset-repo.sh inspect gupta29470/codewalk
407+
408+
# Dry-run any action first
409+
./deploy/reset-repo.sh prepare gupta29470/codewalk --index --dry-run
410+
./deploy/reset-repo.sh soft-reset gupta29470/codewalk --index --dry-run
411+
412+
# Smart prepare: existing index → UPDATE (incremental); new → CREATE + register
413+
./deploy/reset-repo.sh prepare gupta29470/codewalk --index
414+
415+
# Fix permissions — one repo or all
416+
./deploy/reset-repo.sh fix-perms
417+
./deploy/reset-repo.sh fix-perms gupta29470/codewalk --dry-run
418+
419+
# Force wipe index (keep clone) + full re-create
420+
./deploy/reset-repo.sh soft-reset gupta29470/codewalk --index
421+
422+
# Force wipe index + clone
423+
./deploy/reset-repo.sh full-reset gupta29470/codewalk --index
424+
425+
# Delete from DB + disk
426+
./deploy/reset-repo.sh delete-repo gupta29470/codewalk --dry-run
427+
```
428+
429+
**Coverage matrix** (see `inspect` for your repo):
430+
431+
| Situation | Command |
432+
|-----------|---------|
433+
| New repo (no DB, no index) | `prepare owner/repo --index` → register + CREATE |
434+
| Existing index OK | `prepare owner/repo --index` → UPDATE (incremental) |
435+
| Permissions broken (code 14) | `fix-perms owner/repo` then `prepare --index` |
436+
| `index_status=failed` / stuck job | `prepare owner/repo --index` (unsticks + retries) |
437+
| Corrupt index (chroma, no manifest) | `soft-reset owner/repo --index` |
438+
| Force full rebuild | `soft-reset` (keep clone) or `full-reset` (wipe clone) |
439+
| Remove repo entirely | `delete-repo` → then `prepare --index` or git push |
440+
| All repos perms | `fix-perms` (no slug) |
441+
| Preview any of the above | add `--dry-run` or `inspect owner/repo` |
442+
443+
**Not in script** (manual): private-repo clone auth (GitHub App PEM), DuckDB `.wal` lock delete, webhook branch allowlist (`codewalk.yaml`), laptop `rm -rf .codewalk` + MCP pull.
444+
445+
`deploy-server.sh` and `hetzner-setup.sh` call `ensure-storage.sh` so deploys keep `/var/codewalk` writable.
446+
447+
**Manual equivalents** (if script unavailable):
448+
399449
```bash
400450
cd /opt/codewalk
401451
export REPO="gupta29470/codewalk"
402452

403453
# ── Soft re-index (keep clone, wipe index artifacts) ─────────────────
404454
rm -rf /var/codewalk/indexes/${REPO}
455+
/opt/codewalk-src/deploy/ensure-storage.sh "${REPO}"
405456

406457
docker compose exec postgres psql -U codewalk -d codewalk -c \
407458
"UPDATE repos SET last_indexed_sha=NULL, index_status='pending', index_version=0
@@ -414,6 +465,7 @@ curl -s -X POST https://api.codewalk.xyz/admin/index \
414465

415466
# ── Full reset (index + clone) ─────────────────────────────────────
416467
rm -rf /var/codewalk/indexes/${REPO} /var/codewalk/repos/${REPO}
468+
/opt/codewalk-src/deploy/ensure-storage.sh "${REPO}"
417469
# then UPDATE repos + admin/index as above
418470

419471
# ── Unstick zombie row (indexing for hours, job still queued) ───────
@@ -440,7 +492,12 @@ docker compose exec postgres psql -U codewalk -d codewalk -c \
440492
## 12. Permissions & common fixes
441493

442494
```bash
443-
# Permission denied on /var/codewalk/repos
495+
# Permission denied on /var/codewalk/repos (Chroma code 14: unable to open database file)
496+
/opt/codewalk-src/deploy/ensure-storage.sh
497+
# or per-repo:
498+
/opt/codewalk-src/deploy/ensure-storage.sh gupta29470/codewalk
499+
500+
# Manual equivalent:
444501
chown -R 999:999 /var/codewalk
445502
chmod 600 /var/codewalk/secrets/*.pem
446503

deploy/deploy-server.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ echo "Restarting containers..."
114114
cd "$COMPOSE_DIR"
115115
docker compose up -d --force-recreate --remove-orphans
116116

117+
# ── Index storage permissions (root rm/chown breaks Chroma writes) ─
118+
if [ -f "$SRC_DIR/deploy/ensure-storage.sh" ]; then
119+
bash "$SRC_DIR/deploy/ensure-storage.sh" || echo "⚠️ ensure-storage.sh failed (non-fatal)"
120+
fi
121+
117122
# ── Health check ────────────────────────────────────────────────────
118123
echo ""
119124
echo "Running health check..."

deploy/ensure-storage.sh

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#!/bin/bash
2+
# Ensure /var/codewalk layout exists and is writable by the API container (uid 999).
3+
# Safe to run anytime — idempotent. Works for one repo, all known repos, or base only.
4+
#
5+
# Usage:
6+
# ./ensure-storage.sh [--dry-run] # base + all repos
7+
# ./ensure-storage.sh [--dry-run] --all
8+
# ./ensure-storage.sh [--dry-run] owner/repo
9+
set -euo pipefail
10+
11+
STORAGE_ROOT="${INDEX_STORAGE_PATH:-/var/codewalk}"
12+
API_UID="${CODEWALK_API_UID:-999}"
13+
API_GID="${CODEWALK_API_GID:-999}"
14+
COMPOSE_DIR="${COMPOSE_DIR:-/opt/codewalk}"
15+
16+
REPO_SLUG=""
17+
FIX_ALL=false
18+
DRY_RUN=false
19+
20+
for arg in "$@"; do
21+
case "${arg}" in
22+
--all) FIX_ALL=true ;;
23+
--dry-run) DRY_RUN=true ;;
24+
*/*) REPO_SLUG="${arg}" ;;
25+
esac
26+
done
27+
28+
if [ $# -eq 0 ] || { [ $# -eq 1 ] && [ "${1:-}" = "--dry-run" ]; }; then
29+
FIX_ALL=true
30+
fi
31+
32+
run_or_dry() {
33+
local desc="$1"
34+
shift
35+
if ${DRY_RUN}; then
36+
echo " [dry-run] ${desc}"
37+
else
38+
"$@"
39+
fi
40+
}
41+
42+
ensure_repo_paths() {
43+
local slug="$1"
44+
[ -n "${slug}" ] || return 0
45+
run_or_dry "mkdir -p ${STORAGE_ROOT}/indexes/${slug}/chroma" \
46+
mkdir -p "${STORAGE_ROOT}/indexes/${slug}/chroma"
47+
run_or_dry "mkdir -p ${STORAGE_ROOT}/repos/${slug}" \
48+
mkdir -p "${STORAGE_ROOT}/repos/${slug}"
49+
echo " repo paths: indexes/${slug}, repos/${slug}"
50+
}
51+
52+
discover_repos() {
53+
local -a found=()
54+
local slug=""
55+
56+
if [ -f "${COMPOSE_DIR}/docker-compose.yml" ]; then
57+
while IFS= read -r slug; do
58+
[ -n "${slug}" ] && found+=("${slug}")
59+
done < <(docker compose -f "${COMPOSE_DIR}/docker-compose.yml" exec -T postgres \
60+
psql -U codewalk -d codewalk -t -A -c "SELECT full_name FROM repos ORDER BY full_name;" \
61+
2>/dev/null || true)
62+
fi
63+
64+
if [ -d "${STORAGE_ROOT}/indexes" ]; then
65+
while IFS= read -r slug; do
66+
[ -n "${slug}" ] && found+=("${slug}")
67+
done < <(find "${STORAGE_ROOT}/indexes" -mindepth 2 -maxdepth 2 -type d 2>/dev/null \
68+
| sed "s|^${STORAGE_ROOT}/indexes/||" || true)
69+
fi
70+
71+
if [ -d "${STORAGE_ROOT}/repos" ]; then
72+
while IFS= read -r slug; do
73+
[ -n "${slug}" ] && found+=("${slug}")
74+
done < <(find "${STORAGE_ROOT}/repos" -mindepth 2 -maxdepth 2 -type d 2>/dev/null \
75+
| sed "s|^${STORAGE_ROOT}/repos/||" || true)
76+
fi
77+
78+
printf '%s\n' "${found[@]}" 2>/dev/null | sort -u
79+
}
80+
81+
prefix="==>"
82+
${DRY_RUN} && prefix="[dry-run] ==>"
83+
84+
echo "${prefix} Ensuring storage at ${STORAGE_ROOT} (owner ${API_UID}:${API_GID})"
85+
86+
run_or_dry "mkdir base dirs under ${STORAGE_ROOT}" \
87+
mkdir -p "${STORAGE_ROOT}/repos" "${STORAGE_ROOT}/indexes" "${STORAGE_ROOT}/secrets"
88+
run_or_dry "mkdir ${STORAGE_ROOT}/hf-cache" \
89+
mkdir -p "${STORAGE_ROOT}/hf-cache" 2>/dev/null || true
90+
91+
if [ -n "${REPO_SLUG}" ]; then
92+
ensure_repo_paths "${REPO_SLUG}"
93+
elif ${FIX_ALL}; then
94+
echo "${prefix} Ensuring paths for all registered / on-disk repos"
95+
while IFS= read -r slug; do
96+
[ -n "${slug}" ] && ensure_repo_paths "${slug}"
97+
done < <(discover_repos)
98+
fi
99+
100+
run_or_dry "chown -R ${API_UID}:${API_GID} ${STORAGE_ROOT}" \
101+
chown -R "${API_UID}:${API_GID}" "${STORAGE_ROOT}"
102+
run_or_dry "chmod 755 ${STORAGE_ROOT}" chmod 755 "${STORAGE_ROOT}"
103+
run_or_dry "chmod 755 repos/ indexes/" \
104+
chmod 755 "${STORAGE_ROOT}/repos" "${STORAGE_ROOT}/indexes" 2>/dev/null || true
105+
106+
if [ -d "${STORAGE_ROOT}/secrets" ]; then
107+
run_or_dry "chmod 700 secrets/" chmod 700 "${STORAGE_ROOT}/secrets"
108+
if ! ${DRY_RUN}; then
109+
find "${STORAGE_ROOT}/secrets" -type f -name '*.pem' -exec chmod 600 {} + 2>/dev/null || true
110+
else
111+
echo " [dry-run] chmod 600 ${STORAGE_ROOT}/secrets/*.pem"
112+
fi
113+
fi
114+
115+
echo "${prefix} Storage OK"

deploy/hetzner-setup.sh

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,13 @@ ${DOMAIN} {
143143
EOF
144144

145145
# ── 7. Index storage (API container runs as uid 999) ─────────────────
146-
mkdir -p /var/codewalk/repos /var/codewalk/indexes /var/codewalk/secrets
147-
chown -R 999:999 /var/codewalk
148-
chmod 755 /var/codewalk
146+
if [ -f "$(dirname "$0")/ensure-storage.sh" ]; then
147+
bash "$(dirname "$0")/ensure-storage.sh"
148+
else
149+
mkdir -p /var/codewalk/repos /var/codewalk/indexes /var/codewalk/secrets
150+
chown -R 999:999 /var/codewalk
151+
chmod 755 /var/codewalk
152+
fi
149153

150154
echo "=== Setup complete ==="
151155
echo ""

0 commit comments

Comments
 (0)