forgejo/terraform/user_data.sh

253 lines
8.7 KiB
Bash
Raw Normal View History

#!/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)"
# The root volume is 20 GB. A dump expands well past that, so unpack on the data volume.
RESTORE_DIR=/var/lib/forgejo/.restore
rm -rf "$RESTORE_DIR"
mkdir -p "$RESTORE_DIR"
aws s3 cp "s3://${BACKUP_BUCKET}/${S3_PREFIX}/${LATEST}/${FILE}" - --region "${AWS_REGION}" --no-progress | tar -xz -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
# Older dumps keep sqlite at the archive root. Current dumps already have data/forgejo.db.
if [ -f "$RESTORE_DIR/gitea-db.sqlite3" ]; then
cp "$RESTORE_DIR/gitea-db.sqlite3" /var/lib/forgejo/data/forgejo.db
fi
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