mirror of
https://github.com/Sea-Haven-Industries/forgejo.git
synced 2026-09-30 07:43:11 +00:00
249 lines
8.5 KiB
Bash
249 lines
8.5 KiB
Bash
|
|
#!/bin/bash
|
||
|
|
set -euxo pipefail
|
||
|
|
|
||
|
|
FORGEJO_VERSION="__FORGEJO_VERSION__"
|
||
|
|
BACKUP_BUCKET="__BACKUP_BUCKET__"
|
||
|
|
AWS_REGION="__AWS_REGION__"
|
||
|
|
|
||
|
|
dnf install -y git cronie python3
|
||
|
|
systemctl enable --now crond
|
||
|
|
|
||
|
|
useradd --system --shell /bin/bash --home-dir /home/forgejo --create-home forgejo
|
||
|
|
|
||
|
|
# Persistent data volume. Wait until the attachment shows up, and never format a disk that already has a filesystem.
|
||
|
|
root_disk() {
|
||
|
|
lsblk -no PKNAME "$(findmnt -n -o SOURCE /)" 2>/dev/null || echo xvda
|
||
|
|
}
|
||
|
|
data_disk() {
|
||
|
|
local root name
|
||
|
|
root="$(root_disk)"
|
||
|
|
while read -r name; do
|
||
|
|
if [ -n "$name" ] && [ "$name" != "$root" ]; then
|
||
|
|
printf '%s\n' "$name"
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
done < <(lsblk -dno NAME)
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
until data_disk >/dev/null; do
|
||
|
|
echo "Waiting for data volume..."
|
||
|
|
sleep 5
|
||
|
|
done
|
||
|
|
DATA_DEVICE="/dev/$(data_disk)"
|
||
|
|
if ! blkid "$DATA_DEVICE"; then
|
||
|
|
mkfs.ext4 -L forgejo-data "$DATA_DEVICE"
|
||
|
|
fi
|
||
|
|
mkdir -p /var/lib/forgejo
|
||
|
|
echo "LABEL=forgejo-data /var/lib/forgejo ext4 defaults,nofail 0 2" >> /etc/fstab
|
||
|
|
mount -a
|
||
|
|
|
||
|
|
mkdir -p /var/lib/forgejo/{data,log}
|
||
|
|
chown -R forgejo:forgejo /var/lib/forgejo
|
||
|
|
chmod 750 /var/lib/forgejo
|
||
|
|
|
||
|
|
curl -Lo /usr/local/bin/forgejo "https://codeberg.org/forgejo/forgejo/releases/download/v${FORGEJO_VERSION}/forgejo-${FORGEJO_VERSION}-linux-arm64"
|
||
|
|
chmod +x /usr/local/bin/forgejo
|
||
|
|
|
||
|
|
mkdir -p /etc/forgejo
|
||
|
|
chown root:forgejo /etc/forgejo
|
||
|
|
chmod 770 /etc/forgejo
|
||
|
|
|
||
|
|
cat > /etc/forgejo/app.ini << 'INIEOF'
|
||
|
|
APP_NAME = Sea Haven Git
|
||
|
|
|
||
|
|
[server]
|
||
|
|
DOMAIN = forgejo.seahaven.com
|
||
|
|
ROOT_URL = https://forgejo.seahaven.com/
|
||
|
|
HTTP_PORT = 3000
|
||
|
|
START_SSH_SERVER = true
|
||
|
|
SSH_PORT = 2222
|
||
|
|
SSH_LISTEN_PORT = 2222
|
||
|
|
LFS_START_SERVER = true
|
||
|
|
|
||
|
|
[database]
|
||
|
|
DB_TYPE = sqlite3
|
||
|
|
PATH = /var/lib/forgejo/data/forgejo.db
|
||
|
|
|
||
|
|
[repository]
|
||
|
|
ROOT = /var/lib/forgejo/data/repositories
|
||
|
|
|
||
|
|
[log]
|
||
|
|
ROOT_PATH = /var/lib/forgejo/log
|
||
|
|
|
||
|
|
[security]
|
||
|
|
INSTALL_LOCK = true
|
||
|
|
|
||
|
|
[service]
|
||
|
|
DISABLE_REGISTRATION = true
|
||
|
|
|
||
|
|
[mirror]
|
||
|
|
DEFAULT_INTERVAL = 1h
|
||
|
|
INIEOF
|
||
|
|
|
||
|
|
chown root:forgejo /etc/forgejo/app.ini
|
||
|
|
chmod 660 /etc/forgejo/app.ini
|
||
|
|
|
||
|
|
cat > /etc/systemd/system/forgejo.service << 'SVCEOF'
|
||
|
|
[Unit]
|
||
|
|
Description=Forgejo
|
||
|
|
After=network.target
|
||
|
|
|
||
|
|
[Service]
|
||
|
|
Type=simple
|
||
|
|
User=forgejo
|
||
|
|
Group=forgejo
|
||
|
|
WorkingDirectory=/var/lib/forgejo
|
||
|
|
ExecStart=/usr/local/bin/forgejo web --config /etc/forgejo/app.ini
|
||
|
|
Restart=always
|
||
|
|
RestartSec=5
|
||
|
|
Environment=USER=forgejo HOME=/home/forgejo FORGEJO_WORK_DIR=/var/lib/forgejo
|
||
|
|
|
||
|
|
[Install]
|
||
|
|
WantedBy=multi-user.target
|
||
|
|
SVCEOF
|
||
|
|
|
||
|
|
systemctl daemon-reload
|
||
|
|
|
||
|
|
# Restore from the latest S3 dump when the data volume has no database.
|
||
|
|
if [ ! -f /var/lib/forgejo/data/forgejo.db ]; then
|
||
|
|
echo "No database on data volume - restoring latest backup from S3"
|
||
|
|
S3_PREFIX="$(aws ssm get-parameter --name /forgejo/backup-s3-prefix --query Parameter.Value --output text --region "${AWS_REGION}" || echo archive)"
|
||
|
|
LATEST="$(aws s3 ls "s3://${BACKUP_BUCKET}/${S3_PREFIX}/" --region "${AWS_REGION}" | awk '{print $2}' | sort | tail -1 | tr -d '/')"
|
||
|
|
if [ -n "$LATEST" ]; then
|
||
|
|
FILE="$(aws s3 ls "s3://${BACKUP_BUCKET}/${S3_PREFIX}/${LATEST}/" --region "${AWS_REGION}" | awk '{print $4}' | tail -1)"
|
||
|
|
RESTORE_DIR="$(mktemp -d)"
|
||
|
|
aws s3 cp "s3://${BACKUP_BUCKET}/${S3_PREFIX}/${LATEST}/${FILE}" "$RESTORE_DIR/dump.tar.gz" --region "${AWS_REGION}"
|
||
|
|
tar xzf "$RESTORE_DIR/dump.tar.gz" -C "$RESTORE_DIR"
|
||
|
|
mkdir -p /var/lib/forgejo/data /var/lib/forgejo/custom
|
||
|
|
if [ -d "$RESTORE_DIR/data" ]; then
|
||
|
|
cp -a "$RESTORE_DIR"/data/. /var/lib/forgejo/data/
|
||
|
|
fi
|
||
|
|
rm -rf /var/lib/forgejo/data/repositories
|
||
|
|
mkdir -p /var/lib/forgejo/data/repositories
|
||
|
|
if [ -d "$RESTORE_DIR/repos" ]; then
|
||
|
|
cp -a "$RESTORE_DIR"/repos/. /var/lib/forgejo/data/repositories/
|
||
|
|
fi
|
||
|
|
# Dump sqlite lives at the archive root, not under data/. Copy it last.
|
||
|
|
cp "$RESTORE_DIR/gitea-db.sqlite3" /var/lib/forgejo/data/forgejo.db
|
||
|
|
if [ -d "$RESTORE_DIR/lfs" ]; then
|
||
|
|
mkdir -p /var/lib/forgejo/data/lfs
|
||
|
|
cp -a "$RESTORE_DIR"/lfs/. /var/lib/forgejo/data/lfs/
|
||
|
|
fi
|
||
|
|
if [ -d "$RESTORE_DIR/custom" ]; then
|
||
|
|
cp -a "$RESTORE_DIR"/custom/. /var/lib/forgejo/custom/
|
||
|
|
fi
|
||
|
|
chown -R forgejo:forgejo /var/lib/forgejo
|
||
|
|
rm -rf "$RESTORE_DIR"
|
||
|
|
if [ ! -s /var/lib/forgejo/data/forgejo.db ]; then
|
||
|
|
echo "Restore did not produce /var/lib/forgejo/data/forgejo.db" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
echo "No backup found in S3 - starting fresh"
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
systemctl enable --now forgejo
|
||
|
|
|
||
|
|
cat > /usr/local/bin/forgejo-backup.sh << 'BAKEOF'
|
||
|
|
#!/bin/bash
|
||
|
|
set -euo pipefail
|
||
|
|
TIMESTAMP="$(date +%Y-%m-%d)"
|
||
|
|
S3_PREFIX="$(aws ssm get-parameter --name /forgejo/backup-s3-prefix --query Parameter.Value --output text --region __AWS_REGION__ || echo archive)"
|
||
|
|
DUMP_DIR="$(mktemp -d)"
|
||
|
|
chown forgejo:forgejo "$DUMP_DIR"
|
||
|
|
cd "$DUMP_DIR"
|
||
|
|
sudo -u forgejo /usr/local/bin/forgejo dump --config /etc/forgejo/app.ini --type tar.gz --file "$DUMP_DIR/forgejo-${TIMESTAMP}.tar.gz"
|
||
|
|
aws s3 cp "$DUMP_DIR/forgejo-${TIMESTAMP}.tar.gz" "s3://__BACKUP_BUCKET__/${S3_PREFIX}/${TIMESTAMP}/forgejo-${TIMESTAMP}.tar.gz" --region __AWS_REGION__
|
||
|
|
rm -rf "$DUMP_DIR"
|
||
|
|
BAKEOF
|
||
|
|
chmod +x /usr/local/bin/forgejo-backup.sh
|
||
|
|
|
||
|
|
echo "0 5 * * * root /usr/local/bin/forgejo-backup.sh >> /var/log/forgejo-backup.log 2>&1" > /etc/cron.d/forgejo-backup
|
||
|
|
chmod 644 /etc/cron.d/forgejo-backup
|
||
|
|
|
||
|
|
cat > /usr/local/bin/forgejo-autodiscover.sh << 'ADEOF'
|
||
|
|
#!/bin/bash
|
||
|
|
set -euo pipefail
|
||
|
|
GH_PAT="$(aws secretsmanager get-secret-value --secret-id forgejo/github-pat --query SecretString --output text --region __AWS_REGION__)"
|
||
|
|
FORGEJO_TOKEN="$(aws secretsmanager get-secret-value --secret-id forgejo/api-token --query SecretString --output text --region __AWS_REGION__)"
|
||
|
|
FORGEJO_URL="https://forgejo.seahaven.com/api/v1"
|
||
|
|
GH_ORG="Sea-Haven-Industries"
|
||
|
|
|
||
|
|
gh_repos="$(curl -sf -H "Authorization: token ${GH_PAT}" "https://api.github.com/orgs/${GH_ORG}/repos?per_page=100&type=all" | python3 -c '
|
||
|
|
import json, sys
|
||
|
|
for r in json.load(sys.stdin):
|
||
|
|
print("%s\t%s" % (r["name"], r["archived"]))
|
||
|
|
')"
|
||
|
|
|
||
|
|
forgejo_repos="$(curl -sf -H "Authorization: token ${FORGEJO_TOKEN}" "${FORGEJO_URL}/repos/search?limit=100" | python3 -c '
|
||
|
|
import json, sys
|
||
|
|
data = json.load(sys.stdin)
|
||
|
|
repos = data.get("data", data) if isinstance(data, dict) else data
|
||
|
|
for r in repos:
|
||
|
|
print(r["name"])
|
||
|
|
')"
|
||
|
|
|
||
|
|
while IFS=$'\t' read -r name archived; do
|
||
|
|
if ! echo "$forgejo_repos" | grep -qx "$name"; then
|
||
|
|
mirror=true
|
||
|
|
[ "$archived" = "True" ] && mirror=false
|
||
|
|
echo "$(date -Is) Discovering: $name (mirror=$mirror)"
|
||
|
|
curl -sf -X POST "${FORGEJO_URL}/repos/migrate" \
|
||
|
|
-H "Authorization: token ${FORGEJO_TOKEN}" \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d "{
|
||
|
|
\"clone_addr\": \"https://github.com/${GH_ORG}/${name}.git\",
|
||
|
|
\"auth_token\": \"${GH_PAT}\",
|
||
|
|
\"repo_name\": \"${name}\",
|
||
|
|
\"repo_owner\": \"adam\",
|
||
|
|
\"service\": \"github\",
|
||
|
|
\"mirror\": ${mirror},
|
||
|
|
\"issues\": true,
|
||
|
|
\"labels\": true,
|
||
|
|
\"milestones\": true,
|
||
|
|
\"pull_requests\": true,
|
||
|
|
\"releases\": true,
|
||
|
|
\"wiki\": true
|
||
|
|
}" > /dev/null
|
||
|
|
fi
|
||
|
|
done <<< "$gh_repos"
|
||
|
|
ADEOF
|
||
|
|
chmod +x /usr/local/bin/forgejo-autodiscover.sh
|
||
|
|
|
||
|
|
cat > /usr/local/bin/forgejo-refresh-tokens.sh << 'RTEOF'
|
||
|
|
#!/bin/bash
|
||
|
|
set -euo pipefail
|
||
|
|
GH_PAT="$(aws secretsmanager get-secret-value --secret-id forgejo/github-pat --query SecretString --output text --region __AWS_REGION__)"
|
||
|
|
FORGEJO_TOKEN="$(aws secretsmanager get-secret-value --secret-id forgejo/api-token --query SecretString --output text --region __AWS_REGION__)"
|
||
|
|
FORGEJO_URL="https://forgejo.seahaven.com/api/v1"
|
||
|
|
REPO_ROOT="/var/lib/forgejo/data/repositories/adam"
|
||
|
|
|
||
|
|
export GIT_CONFIG_COUNT=1
|
||
|
|
export GIT_CONFIG_KEY_0=safe.directory
|
||
|
|
export GIT_CONFIG_VALUE_0='*'
|
||
|
|
|
||
|
|
mirrors="$(curl -sf -H "Authorization: token ${FORGEJO_TOKEN}" "${FORGEJO_URL}/repos/search?limit=100" | python3 -c '
|
||
|
|
import json, sys
|
||
|
|
data = json.load(sys.stdin)
|
||
|
|
repos = data.get("data", data) if isinstance(data, dict) else data
|
||
|
|
for r in repos:
|
||
|
|
if r.get("mirror", False):
|
||
|
|
print(r["name"])
|
||
|
|
')"
|
||
|
|
|
||
|
|
while read -r repo_name; do
|
||
|
|
[ -z "$repo_name" ] && continue
|
||
|
|
repo_dir="${REPO_ROOT}/${repo_name}.git"
|
||
|
|
if [ -d "$repo_dir" ]; then
|
||
|
|
new_url="https://${GH_PAT}@github.com/Sea-Haven-Industries/${repo_name}.git"
|
||
|
|
git -C "$repo_dir" remote set-url origin "$new_url" 2>/dev/null && echo "$(date -Is) Refreshed: ${repo_name}"
|
||
|
|
fi
|
||
|
|
done <<< "$mirrors"
|
||
|
|
RTEOF
|
||
|
|
chmod +x /usr/local/bin/forgejo-refresh-tokens.sh
|
||
|
|
|
||
|
|
printf '%s\n' '0 * * * * root /usr/local/bin/forgejo-autodiscover.sh >> /var/log/forgejo-autodiscover.log 2>&1' > /etc/cron.d/forgejo-autodiscover
|
||
|
|
printf '%s\n' '30 4 * * * root /usr/local/bin/forgejo-refresh-tokens.sh >> /var/log/forgejo-refresh-tokens.log 2>&1' > /etc/cron.d/forgejo-refresh-tokens
|
||
|
|
chmod 644 /etc/cron.d/forgejo-autodiscover /etc/cron.d/forgejo-refresh-tokens
|