initial commit
Signed-off-by: Jason Hall <imjasonh@gmail.com>
This commit is contained in:
commit
4dc1b58f2f
20 changed files with 1398 additions and 0 deletions
16
scripts/backup.sh
Executable file
16
scripts/backup.sh
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash
|
||||
# Run on the VM via the forgejo-backup.timer systemd unit.
|
||||
# Snapshots the SQLite DB, tars the data dir, and uploads to GCS.
|
||||
# Note: the canonical copy of this script is embedded in cloud-init/user-data.yaml.tpl
|
||||
# at /var/lib/forgejo/backup.sh. This file is kept for readability and ad-hoc reuse.
|
||||
set -euo pipefail
|
||||
|
||||
: "${GCS_BACKUP_BUCKET:?GCS_BACKUP_BUCKET must be set}"
|
||||
|
||||
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
|
||||
docker exec forgejo sqlite3 /data/gitea/gitea.db ".backup '/data/gitea/snapshot.db'"
|
||||
tar czf "/tmp/forgejo-${STAMP}.tar.gz" -C /mnt/disks/forgejo-data forgejo
|
||||
docker run --rm -v /tmp:/tmp google/cloud-sdk:slim \
|
||||
gsutil cp "/tmp/forgejo-${STAMP}.tar.gz" "gs://${GCS_BACKUP_BUCKET}/"
|
||||
rm "/tmp/forgejo-${STAMP}.tar.gz"
|
||||
docker exec forgejo rm -f /data/gitea/snapshot.db
|
||||
25
scripts/bootstrap-secrets.sh
Executable file
25
scripts/bootstrap-secrets.sh
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
#!/bin/bash
|
||||
# Generate and upload Forgejo secrets to Google Secret Manager.
|
||||
# Run once per project before the first `terraform apply`.
|
||||
# Idempotent: skips secrets that already exist.
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -z "$(gcloud config get-value project 2>/dev/null)" ]]; then
|
||||
echo "ERROR: no active gcloud project. Run 'gcloud config set project YOUR_PROJECT' first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for SECRET in forgejo-secret-key forgejo-internal-token; do
|
||||
if gcloud secrets describe "$SECRET" >/dev/null 2>&1; then
|
||||
echo "$SECRET already exists, skipping"
|
||||
else
|
||||
openssl rand -hex 32 \
|
||||
| gcloud secrets create "$SECRET" --replication-policy=automatic --data-file=-
|
||||
echo "Created $SECRET"
|
||||
fi
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Tip: also save these values in your password manager for cross-project recovery."
|
||||
echo " gcloud secrets versions access latest --secret=forgejo-secret-key"
|
||||
echo " gcloud secrets versions access latest --secret=forgejo-internal-token"
|
||||
30
scripts/restore.sh
Executable file
30
scripts/restore.sh
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/bash
|
||||
# Restore a Forgejo backup tarball from GCS over the live data dir.
|
||||
# Run on the VM via SSH. Stops the stack, restores, restarts.
|
||||
#
|
||||
# Usage: sudo ./restore.sh forgejo-20260507T033000Z.tar.gz
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo "Usage: $0 <backup-filename>" >&2
|
||||
echo " e.g. $0 forgejo-20260507T033000Z.tar.gz" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BACKUP=$1
|
||||
|
||||
BUCKET=$(curl -sf -H "Metadata-Flavor: Google" \
|
||||
"http://metadata.google.internal/computeMetadata/v1/project/project-id")-forgejo-backups
|
||||
|
||||
read -r -p "About to restore '$BACKUP' from gs://$BUCKET/ over /mnt/disks/forgejo-data/forgejo. Continue? [y/N] " ans
|
||||
[[ "$ans" == "y" || "$ans" == "Y" ]] || exit 1
|
||||
|
||||
systemctl stop forgejo-stack.service
|
||||
docker run --rm -v /tmp:/tmp google/cloud-sdk:slim \
|
||||
gsutil cp "gs://${BUCKET}/${BACKUP}" /tmp/
|
||||
rm -rf /mnt/disks/forgejo-data/forgejo
|
||||
tar xzf "/tmp/${BACKUP}" -C /mnt/disks/forgejo-data/
|
||||
rm "/tmp/${BACKUP}"
|
||||
systemctl start forgejo-stack.service
|
||||
|
||||
echo "Restore complete. Check 'docker logs forgejo' for migration output."
|
||||
63
scripts/test-restore.sh
Executable file
63
scripts/test-restore.sh
Executable file
|
|
@ -0,0 +1,63 @@
|
|||
#!/bin/bash
|
||||
# Verify the latest GCS backup is restorable in a throwaway local Docker setup.
|
||||
# Run from a workstation with gcloud + docker. Does not touch the production VM.
|
||||
#
|
||||
# Usage: ./test-restore.sh [project-id]
|
||||
# project-id defaults to the active gcloud project
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT=${1:-$(gcloud config get-value project 2>/dev/null)}
|
||||
if [[ -z "$PROJECT" ]]; then
|
||||
echo "ERROR: no project specified and no active gcloud project." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUCKET="${PROJECT}-forgejo-backups"
|
||||
WORKDIR=$(mktemp -d -t forgejo-test-restore.XXXXXX)
|
||||
trap 'docker rm -f forgejo-test >/dev/null 2>&1 || true; rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
echo "Workdir: $WORKDIR"
|
||||
|
||||
LATEST=$(gsutil ls "gs://${BUCKET}/" | grep '\.tar\.gz$' | sort | tail -1)
|
||||
if [[ -z "$LATEST" ]]; then
|
||||
echo "ERROR: no backups found in gs://${BUCKET}/" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Latest backup: $LATEST"
|
||||
|
||||
gsutil cp "$LATEST" "$WORKDIR/backup.tar.gz"
|
||||
tar xzf "$WORKDIR/backup.tar.gz" -C "$WORKDIR"
|
||||
|
||||
if [[ ! -d "$WORKDIR/forgejo" ]]; then
|
||||
echo "ERROR: tarball does not contain a 'forgejo' directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$WORKDIR/forgejo/gitea/gitea.db" ]]; then
|
||||
echo "ERROR: SQLite DB missing from backup" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Boot Forgejo against the restored data and probe it.
|
||||
docker run -d --rm --name forgejo-test \
|
||||
-p 13000:3000 \
|
||||
-v "$WORKDIR/forgejo:/data" \
|
||||
-e FORGEJO__server__DISABLE_SSH=true \
|
||||
-e FORGEJO__database__DB_TYPE=sqlite3 \
|
||||
codeberg.org/forgejo/forgejo:11 >/dev/null
|
||||
|
||||
echo "Waiting for Forgejo to start..."
|
||||
for i in $(seq 1 30); do
|
||||
if curl -sf http://localhost:13000/api/v1/version >/dev/null; then
|
||||
echo "OK: Forgejo responded with version: $(curl -s http://localhost:13000/api/v1/version)"
|
||||
REPOS=$(curl -s http://localhost:13000/api/v1/repos/search?limit=1 | python3 -c 'import sys,json; d=json.load(sys.stdin); print(len(d.get("data", [])))')
|
||||
echo "OK: API repos endpoint returned $REPOS result(s)"
|
||||
echo "PASS: backup is restorable"
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo "FAIL: Forgejo did not respond within 60s" >&2
|
||||
docker logs forgejo-test >&2 || true
|
||||
exit 1
|
||||
Loading…
Add table
Add a link
Reference in a new issue