init
This commit is contained in:
commit
27c4191be8
17 changed files with 730 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
fly.toml
|
||||
17
Caddyfile
Normal file
17
Caddyfile
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
auto_https off
|
||||
admin off
|
||||
}
|
||||
|
||||
:8080 {
|
||||
handle_path /healthz {
|
||||
respond "ok" 200
|
||||
}
|
||||
|
||||
import /etc/caddy/auth.caddy
|
||||
|
||||
reverse_proxy 127.0.0.1:3000 {
|
||||
lb_try_duration 30s
|
||||
lb_try_interval 250ms
|
||||
}
|
||||
}
|
||||
61
Dockerfile
Normal file
61
Dockerfile
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
FROM golang:1.25-alpine AS tools
|
||||
|
||||
RUN apk add --no-cache git
|
||||
RUN GOBIN=/usr/local/bin go install github.com/caddyserver/caddy/v2/cmd/caddy@v2.10.2
|
||||
RUN GOBIN=/usr/local/bin go install github.com/DarthSim/hivemind@latest
|
||||
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG TARGETARCH
|
||||
ARG FORGEJO_VERSION=11.0.14
|
||||
|
||||
ENV LANG=C.UTF-8
|
||||
ENV GITEA_WORK_DIR=/data/forgejo
|
||||
ENV GITEA_CUSTOM=/data/forgejo/custom
|
||||
ENV GITEA_TEMP=/tmp/gitea
|
||||
ENV GITEA_APP_INI=/data/forgejo/custom/conf/app.ini
|
||||
ENV FORGEJO_HOME=/data/forgejo/git
|
||||
|
||||
RUN apt-get -qq update && \
|
||||
apt-get -qq install --no-install-recommends -y \
|
||||
bash \
|
||||
ca-certificates \
|
||||
crudini \
|
||||
curl \
|
||||
gettext-base \
|
||||
git \
|
||||
locales \
|
||||
openssh-client \
|
||||
restic \
|
||||
sqlite3 \
|
||||
tzdata \
|
||||
xz-utils && \
|
||||
apt-get -qq clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
RUN printf 'C.UTF-8 UTF-8\n' > /etc/locale.gen && \
|
||||
locale-gen && \
|
||||
groupadd -g 1001 forgejo && \
|
||||
useradd -d /data/forgejo/git -M -u 1001 -g forgejo -s /bin/sh forgejo
|
||||
|
||||
RUN arch="${TARGETARCH:-$(dpkg --print-architecture)}" && \
|
||||
case "$arch" in \
|
||||
amd64|arm64) forgejo_arch="$arch" ;; \
|
||||
*) echo "Unsupported Forgejo architecture: $arch" >&2; exit 1 ;; \
|
||||
esac && \
|
||||
curl -fLo /usr/local/bin/forgejo "https://codeberg.org/forgejo/forgejo/releases/download/v${FORGEJO_VERSION}/forgejo-${FORGEJO_VERSION}-linux-${forgejo_arch}" && \
|
||||
chmod 755 /usr/local/bin/forgejo && \
|
||||
ln -sf /usr/local/bin/forgejo /usr/local/bin/gitea
|
||||
|
||||
RUN mkdir -p /etc/caddy /etc/templates /etc/auth /tmp/gitea /data /data/forgejo
|
||||
|
||||
COPY --from=tools /usr/local/bin/caddy /usr/local/bin/hivemind /usr/local/bin/
|
||||
COPY Caddyfile /etc/caddy/Caddyfile
|
||||
COPY Procfile /etc/Procfile
|
||||
COPY forgejo-app.ini.tmpl /etc/templates/forgejo-app.ini.tmpl
|
||||
COPY bin/ /usr/local/bin/
|
||||
|
||||
RUN chmod 755 /usr/local/bin/* && \
|
||||
chown -R forgejo:forgejo /data/forgejo /tmp/gitea
|
||||
|
||||
CMD ["/usr/local/bin/start-services.sh"]
|
||||
3
Procfile
Normal file
3
Procfile
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
caddy: /usr/local/bin/caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||
forgejo: /usr/local/bin/run-forgejo.sh
|
||||
backup: /usr/local/bin/run-restic-backups.sh
|
||||
76
README.md
Normal file
76
README.md
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# Forgejo on Fly.io
|
||||
|
||||
Standalone Forgejo deployment for Fly.io with one initial admin, optional Caddy HTTP basic auth, and optional restic backups.
|
||||
|
||||
## Setup
|
||||
|
||||
Install and authenticate the [Fly CLI](https://fly.io/docs/flyctl/install/), then run:
|
||||
|
||||
```bash
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
The script prompts for:
|
||||
|
||||
- A globally unique Fly app name
|
||||
- A Fly region
|
||||
- The initial Forgejo admin username and email
|
||||
- Whether to enable Caddy HTTP basic auth and, if enabled, its single username and password
|
||||
|
||||
It creates the Fly app, writes the generated `fly.toml`, stores credentials as Fly secrets, and deploys Forgejo with a 10 GB persistent volume. The Forgejo admin password is generated securely and printed before and after deployment. Save it when displayed.
|
||||
|
||||
The app is available at `https://<appname>.fly.dev/`. Registration is disabled, so the generated admin is the only account on a new installation. Add any later users through Forgejo's admin interface.
|
||||
|
||||
## Access Control
|
||||
|
||||
Two independent settings restrict access by default:
|
||||
|
||||
- Forgejo's `GITEA__service__REQUIRE_SIGNIN_VIEW` is set to `true`, so anonymous visitors cannot view Forgejo content.
|
||||
- Caddy basic auth is enabled when both `CADDY_BASIC_AUTH_USERNAME` and `CADDY_BASIC_AUTH_PASSWORD` are set. The setup script can configure them for you. Git HTTP endpoints and the package API remain outside Caddy authentication so Git and package clients can authenticate directly with Forgejo.
|
||||
|
||||
To make the instance publicly viewable, both controls must be disabled: leave the Caddy credentials unset and change `GITEA__service__REQUIRE_SIGNIN_VIEW` to `false` in the generated `fly.toml`, then run `fly deploy`.
|
||||
|
||||
To enable or change Caddy authentication later:
|
||||
|
||||
```bash
|
||||
fly secrets set CADDY_BASIC_AUTH_USERNAME=forge CADDY_BASIC_AUTH_PASSWORD='your-password'
|
||||
```
|
||||
|
||||
To disable it, unset both secrets:
|
||||
|
||||
```bash
|
||||
fly secrets unset CADDY_BASIC_AUTH_USERNAME CADDY_BASIC_AUTH_PASSWORD
|
||||
```
|
||||
|
||||
## Backups
|
||||
|
||||
Set all restic configuration as Fly secrets. Backups stay disabled until every value is present.
|
||||
|
||||
```bash
|
||||
fly secrets set \
|
||||
AWS_ACCESS_KEY_ID='...' \
|
||||
AWS_SECRET_ACCESS_KEY='...' \
|
||||
RESTIC_REPOSITORY='s3:https://.../forgejo' \
|
||||
RESTIC_PASSWORD='...'
|
||||
```
|
||||
|
||||
Initialize the repository once:
|
||||
|
||||
```bash
|
||||
fly ssh console --command "/usr/local/bin/backup-init.sh"
|
||||
```
|
||||
|
||||
The backup process snapshots `/data` hourly when all required variables are available. To restore a snapshot from inside a stopped or debug machine:
|
||||
|
||||
```bash
|
||||
backup-restore.sh latest
|
||||
```
|
||||
|
||||
## Debug Startup
|
||||
|
||||
Set `DEBUGDEPLOY` to keep a machine alive without starting services, or use:
|
||||
|
||||
```bash
|
||||
./bin/debug-deploy.sh start
|
||||
./bin/debug-deploy.sh stop
|
||||
```
|
||||
16
bin/backup-init.sh
Executable file
16
bin/backup-init.sh
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
: "${AWS_ACCESS_KEY_ID:?AWS_ACCESS_KEY_ID must be set}"
|
||||
: "${AWS_SECRET_ACCESS_KEY:?AWS_SECRET_ACCESS_KEY must be set}"
|
||||
: "${RESTIC_REPOSITORY:?RESTIC_REPOSITORY must be set}"
|
||||
: "${RESTIC_PASSWORD:?RESTIC_PASSWORD must be set}"
|
||||
|
||||
if restic snapshots >/dev/null 2>&1; then
|
||||
echo "Restic repository is already initialized."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Initializing restic repository at $RESTIC_REPOSITORY..."
|
||||
restic init
|
||||
echo "Restic repository initialized successfully."
|
||||
54
bin/backup-restore.sh
Executable file
54
bin/backup-restore.sh
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat >&2 <<'EOF'
|
||||
Usage:
|
||||
backup-restore.sh [snapshot]
|
||||
|
||||
Environment:
|
||||
AWS_ACCESS_KEY_ID
|
||||
AWS_SECRET_ACCESS_KEY
|
||||
RESTIC_REPOSITORY
|
||||
RESTIC_PASSWORD
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# -gt 1 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
: "${AWS_ACCESS_KEY_ID:?AWS_ACCESS_KEY_ID must be set}"
|
||||
: "${AWS_SECRET_ACCESS_KEY:?AWS_SECRET_ACCESS_KEY must be set}"
|
||||
: "${RESTIC_REPOSITORY:?RESTIC_REPOSITORY must be set}"
|
||||
: "${RESTIC_PASSWORD:?RESTIC_PASSWORD must be set}"
|
||||
|
||||
snapshot="${1:-latest}"
|
||||
restore_path='/data/forgejo'
|
||||
|
||||
clear_directory() {
|
||||
local dir=$1
|
||||
|
||||
mkdir -p "$dir"
|
||||
find "$dir" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
|
||||
}
|
||||
|
||||
staging_dir="$(mktemp -d)"
|
||||
cleanup() {
|
||||
rm -rf "$staging_dir"
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
echo "Restoring snapshot '$snapshot' from $RESTIC_REPOSITORY into $restore_path ..."
|
||||
restic restore "$snapshot" --target "$staging_dir" --include "$restore_path"
|
||||
|
||||
restored_path="$staging_dir$restore_path"
|
||||
if [ ! -e "$restored_path" ]; then
|
||||
echo "Snapshot '$snapshot' did not contain $restore_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
clear_directory "$restore_path"
|
||||
cp -a "$restored_path"/. "$restore_path"/
|
||||
echo "Restore completed at $restore_path."
|
||||
27
bin/debug-deploy.sh
Executable file
27
bin/debug-deploy.sh
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
usage() {
|
||||
printf 'Usage: %s <start|stop>\n' "$0" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
script_dir="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
|
||||
repo_root="$(dirname "$script_dir")"
|
||||
config_path="$repo_root/fly.toml"
|
||||
|
||||
[ $# -eq 1 ] || usage
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
printf 'Enabling DEBUGDEPLOY using %s\n' "$config_path"
|
||||
exec fly secrets set -c "$config_path" DEBUGDEPLOY=1
|
||||
;;
|
||||
stop)
|
||||
printf 'Disabling DEBUGDEPLOY using %s\n' "$config_path"
|
||||
exec fly secrets unset -c "$config_path" DEBUGDEPLOY
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
44
bin/initialize-forgejo.sh
Executable file
44
bin/initialize-forgejo.sh
Executable file
|
|
@ -0,0 +1,44 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
: "${FORGEJO_ADMIN_USERNAME:?FORGEJO_ADMIN_USERNAME must be set}"
|
||||
: "${FORGEJO_ADMIN_EMAIL:?FORGEJO_ADMIN_EMAIL must be set}"
|
||||
: "${FORGEJO_ADMIN_PASSWORD:?FORGEJO_ADMIN_PASSWORD must be set}"
|
||||
|
||||
shell_quote() {
|
||||
printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"
|
||||
}
|
||||
|
||||
run_forgejo() {
|
||||
command="exec /usr/local/bin/forgejo"
|
||||
for argument in "$@"; do
|
||||
command="$command $(shell_quote "$argument")"
|
||||
done
|
||||
su forgejo -s /bin/sh -c "$command"
|
||||
}
|
||||
|
||||
if ! run_forgejo -c "$GITEA_APP_INI" migrate >/tmp/forgejo-migrate.log 2>&1; then
|
||||
cat /tmp/forgejo-migrate.log >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if run_forgejo -c "$GITEA_APP_INI" admin user create \
|
||||
--username "$FORGEJO_ADMIN_USERNAME" \
|
||||
--password "$FORGEJO_ADMIN_PASSWORD" \
|
||||
--email "$FORGEJO_ADMIN_EMAIL" \
|
||||
--admin \
|
||||
--must-change-password=false >/tmp/forgejo-admin-create.log 2>&1; then
|
||||
printf 'Created Forgejo admin user %s.\n' "$FORGEJO_ADMIN_USERNAME"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
run_forgejo -c "$GITEA_APP_INI" admin user list >/tmp/forgejo-admin-list.log 2>/dev/null || true
|
||||
while read -r _id username _rest; do
|
||||
if [ "$username" = "$FORGEJO_ADMIN_USERNAME" ]; then
|
||||
printf 'Forgejo admin user %s already exists; leaving it unchanged.\n' "$FORGEJO_ADMIN_USERNAME"
|
||||
exit 0
|
||||
fi
|
||||
done < /tmp/forgejo-admin-list.log
|
||||
|
||||
cat /tmp/forgejo-admin-create.log >&2
|
||||
exit 1
|
||||
35
bin/prepare-caddy.sh
Executable file
35
bin/prepare-caddy.sh
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
auth_file=/etc/caddy/auth.caddy
|
||||
username="${CADDY_BASIC_AUTH_USERNAME:-}"
|
||||
password="${CADDY_BASIC_AUTH_PASSWORD:-}"
|
||||
|
||||
if [ -z "$username" ] && [ -z "$password" ]; then
|
||||
: > "$auth_file"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "$username" ] || [ -z "$password" ]; then
|
||||
printf 'CADDY_BASIC_AUTH_USERNAME and CADDY_BASIC_AUTH_PASSWORD must both be set.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$username" in
|
||||
*[!A-Za-z0-9._@-]*)
|
||||
printf 'CADDY_BASIC_AUTH_USERNAME contains unsupported characters.\n' >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
password_hash="$(/usr/local/bin/caddy hash-password --plaintext "$password")"
|
||||
cat > "$auth_file" <<EOF
|
||||
@protected {
|
||||
not path /healthz
|
||||
not path_regexp public ^(/[^/]+/[^/]+\.git/.*|/api/packages(/.*)?)$
|
||||
}
|
||||
|
||||
basic_auth @protected {
|
||||
$username $password_hash
|
||||
}
|
||||
EOF
|
||||
85
bin/prepare-forgejo.sh
Executable file
85
bin/prepare-forgejo.sh
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
: "${GITEA_WORK_DIR:=/data/forgejo}"
|
||||
: "${GITEA_CUSTOM:=$GITEA_WORK_DIR/custom}"
|
||||
: "${GITEA_TEMP:=/tmp/gitea}"
|
||||
: "${GITEA_APP_INI:=$GITEA_CUSTOM/conf/app.ini}"
|
||||
: "${FORGEJO_HOME:=/data/forgejo/git}"
|
||||
|
||||
sed_escape() {
|
||||
printf '%s' "$1" | sed 's/[\\/&]/\\&/g'
|
||||
}
|
||||
|
||||
set_top_level_key() {
|
||||
key="$1"
|
||||
value="$2"
|
||||
escaped_value="$(sed_escape "$value")"
|
||||
|
||||
if grep -q "^$key[[:space:]]*=" "$GITEA_APP_INI"; then
|
||||
sed -i "s/^$key[[:space:]]*=.*/$key = $escaped_value/" "$GITEA_APP_INI"
|
||||
return
|
||||
fi
|
||||
|
||||
tmp_file="$(mktemp)"
|
||||
{
|
||||
printf '%s = %s\n' "$key" "$value"
|
||||
cat "$GITEA_APP_INI"
|
||||
} > "$tmp_file"
|
||||
mv "$tmp_file" "$GITEA_APP_INI"
|
||||
}
|
||||
|
||||
mkdir -p \
|
||||
"$FORGEJO_HOME" \
|
||||
"$GITEA_CUSTOM" \
|
||||
"$GITEA_TEMP" \
|
||||
"$GITEA_WORK_DIR/data" \
|
||||
"$GITEA_WORK_DIR/git/repositories" \
|
||||
"$GITEA_WORK_DIR/git/lfs" \
|
||||
"$(dirname "$GITEA_APP_INI")"
|
||||
|
||||
chown -R forgejo:forgejo "$GITEA_WORK_DIR" "$GITEA_TEMP"
|
||||
chmod 700 "$FORGEJO_HOME" "$GITEA_CUSTOM" "$GITEA_TEMP" "$(dirname "$GITEA_APP_INI")"
|
||||
|
||||
APP_NAME="${APP_NAME:-Forgejo: Beyond coding. We forge.}"
|
||||
RUN_MODE="${RUN_MODE:-prod}"
|
||||
RUN_USER="${RUN_USER:-forgejo}"
|
||||
|
||||
if [ ! -f "$GITEA_APP_INI" ]; then
|
||||
SSH_DOMAIN="${SSH_DOMAIN:-localhost}"
|
||||
HTTP_PORT="${HTTP_PORT:-3000}"
|
||||
ROOT_URL="${ROOT_URL:-${GITEA__server__ROOT_URL:-}}"
|
||||
DISABLE_SSH="${DISABLE_SSH:-false}"
|
||||
SSH_PORT="${SSH_PORT:-2222}"
|
||||
SSH_LISTEN_PORT="${SSH_LISTEN_PORT:-$SSH_PORT}"
|
||||
LFS_START_SERVER="${LFS_START_SERVER:-true}"
|
||||
DB_TYPE="${DB_TYPE:-sqlite3}"
|
||||
DB_HOST="${DB_HOST:-localhost:3306}"
|
||||
DB_NAME="${DB_NAME:-gitea}"
|
||||
DB_USER="${DB_USER:-root}"
|
||||
DB_PASSWD="${DB_PASSWD:-}"
|
||||
INSTALL_LOCK="${INSTALL_LOCK:-false}"
|
||||
DISABLE_REGISTRATION="${DISABLE_REGISTRATION:-false}"
|
||||
REQUIRE_SIGNIN_VIEW="${REQUIRE_SIGNIN_VIEW:-false}"
|
||||
SECRET_KEY="${SECRET_KEY:-}"
|
||||
export APP_NAME RUN_MODE RUN_USER SSH_DOMAIN HTTP_PORT ROOT_URL DISABLE_SSH SSH_PORT SSH_LISTEN_PORT LFS_START_SERVER DB_TYPE DB_HOST DB_NAME DB_USER DB_PASSWD INSTALL_LOCK DISABLE_REGISTRATION REQUIRE_SIGNIN_VIEW SECRET_KEY
|
||||
envsubst < /etc/templates/forgejo-app.ini.tmpl > "$GITEA_APP_INI"
|
||||
fi
|
||||
|
||||
set_top_level_key APP_NAME "$APP_NAME"
|
||||
set_top_level_key RUN_USER "$RUN_USER"
|
||||
set_top_level_key RUN_MODE "$RUN_MODE"
|
||||
crudini --set "$GITEA_APP_INI" server BUILTIN_SSH_SERVER_USER "$RUN_USER"
|
||||
|
||||
printenv | while IFS='=' read -r name value; do
|
||||
case "$name" in
|
||||
GITEA__*__*)
|
||||
rest="${name#GITEA__}"
|
||||
section="${rest%%__*}"
|
||||
key="${rest#*__}"
|
||||
crudini --set "$GITEA_APP_INI" "$section" "$key" "$value"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
chown forgejo:forgejo "$GITEA_APP_INI"
|
||||
5
bin/run-forgejo.sh
Executable file
5
bin/run-forgejo.sh
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
/usr/local/bin/prepare-forgejo.sh
|
||||
exec su forgejo -s /bin/sh -c "exec /usr/local/bin/forgejo -c \"$GITEA_APP_INI\" web"
|
||||
57
bin/run-restic-backups.sh
Executable file
57
bin/run-restic-backups.sh
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
#!/bin/bash
|
||||
set -uo pipefail
|
||||
|
||||
required_vars=(
|
||||
AWS_ACCESS_KEY_ID
|
||||
AWS_SECRET_ACCESS_KEY
|
||||
RESTIC_REPOSITORY
|
||||
RESTIC_PASSWORD
|
||||
)
|
||||
|
||||
missing_vars=()
|
||||
for var_name in "${required_vars[@]}"; do
|
||||
if [ -z "${!var_name:-}" ]; then
|
||||
missing_vars+=("$var_name")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${#missing_vars[@]}" -gt 0 ]; then
|
||||
echo "Restic backups disabled; missing ${missing_vars[*]}." >&2
|
||||
exec sleep infinity
|
||||
fi
|
||||
|
||||
BACKUP_INTERVAL_SECONDS="${RESTIC_BACKUP_INTERVAL_SECONDS:-3600}"
|
||||
BACKUP_PATHS=("/data")
|
||||
|
||||
KEEP_HOURLY=24
|
||||
KEEP_DAILY=7
|
||||
KEEP_WEEKLY=4
|
||||
KEEP_MONTHLY=6
|
||||
|
||||
run_backup() {
|
||||
echo "Starting restic backup..."
|
||||
restic backup "${BACKUP_PATHS[@]}" || return $?
|
||||
|
||||
echo "Pruning old backups..."
|
||||
restic forget \
|
||||
--keep-hourly "$KEEP_HOURLY" \
|
||||
--keep-daily "$KEEP_DAILY" \
|
||||
--keep-weekly "$KEEP_WEEKLY" \
|
||||
--keep-monthly "$KEEP_MONTHLY" \
|
||||
--prune || return $?
|
||||
|
||||
echo "Checking repository..."
|
||||
restic check || return $?
|
||||
echo "Backup completed successfully."
|
||||
}
|
||||
|
||||
while true; do
|
||||
if run_backup; then
|
||||
:
|
||||
else
|
||||
status=$?
|
||||
echo "Restic backup failed with status $status." >&2
|
||||
fi
|
||||
|
||||
sleep "$BACKUP_INTERVAL_SECONDS"
|
||||
done
|
||||
13
bin/start-services.sh
Executable file
13
bin/start-services.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
if [ -n "${DEBUGDEPLOY:-}" ]; then
|
||||
printf 'DEBUGDEPLOY is set; skipping startup and waiting indefinitely.\n' >&2
|
||||
exec sleep infinity
|
||||
fi
|
||||
|
||||
/usr/local/bin/prepare-caddy.sh
|
||||
/usr/local/bin/prepare-forgejo.sh
|
||||
/usr/local/bin/initialize-forgejo.sh
|
||||
|
||||
exec /usr/local/bin/hivemind /etc/Procfile
|
||||
49
fly.toml.tmpl
Normal file
49
fly.toml.tmpl
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
app = '__APP_NAME__'
|
||||
primary_region = '__PRIMARY_REGION__'
|
||||
swap_size_mb = 1024
|
||||
|
||||
[build]
|
||||
dockerfile = 'Dockerfile'
|
||||
|
||||
[env]
|
||||
GITEA__default__APP_NAME = 'Forgejo'
|
||||
GITEA__default__APP_SLOGAN = 'Self-hosted Git service'
|
||||
GITEA__log__REQUEST_ID_HEADERS = 'Fly-Client-IP'
|
||||
GITEA__other__SHOW_FOOTER_VERSION = 'false'
|
||||
GITEA__packages__ENABLED = 'true'
|
||||
GITEA__security__COOKIE_REMEMBER_NAME = 'forgejo_remember'
|
||||
GITEA__security__INSTALL_LOCK = 'true'
|
||||
GITEA__security__LOGIN_REMEMBER_DAYS = '10'
|
||||
GITEA__security__REVERSE_PROXY_LIMIT = '2'
|
||||
GITEA__server__DISABLE_SSH = 'true'
|
||||
GITEA__server__HTTP_ADDR = '127.0.0.1'
|
||||
GITEA__server__LANDING_PAGE = 'explore'
|
||||
GITEA__server__OFFLINE_MODE = 'false'
|
||||
GITEA__server__ROOT_URL = 'https://__APP_NAME__.fly.dev/'
|
||||
GITEA__server__START_SSH_SERVER = 'false'
|
||||
GITEA__service__DISABLE_REGISTRATION = 'true'
|
||||
GITEA__service__REQUIRE_SIGNIN_VIEW = 'true'
|
||||
GITEA__session__COOKIE_NAME = 'forgejo_session'
|
||||
GITEA__session__SAME_SITE = 'strict'
|
||||
|
||||
[[mounts]]
|
||||
source = 'data'
|
||||
destination = '/data'
|
||||
initial_size = '10gb'
|
||||
snapshot_retention = 20
|
||||
auto_extend_size_threshold = 80
|
||||
auto_extend_size_increment = '5gb'
|
||||
auto_extend_size_limit = '50gb'
|
||||
|
||||
[http_service]
|
||||
internal_port = 8080
|
||||
force_https = true
|
||||
auto_stop_machines = false
|
||||
auto_start_machines = true
|
||||
min_machines_running = 1
|
||||
processes = ['app']
|
||||
|
||||
[[vm]]
|
||||
memory = '2048mb'
|
||||
cpu_kind = 'shared'
|
||||
cpus = 4
|
||||
58
forgejo-app.ini.tmpl
Normal file
58
forgejo-app.ini.tmpl
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
APP_NAME = $APP_NAME
|
||||
RUN_USER = $RUN_USER
|
||||
RUN_MODE = $RUN_MODE
|
||||
|
||||
[repository]
|
||||
ROOT = $GITEA_WORK_DIR/git/repositories
|
||||
|
||||
[repository.local]
|
||||
LOCAL_COPY_PATH = $GITEA_TEMP/local-repo
|
||||
|
||||
[repository.upload]
|
||||
TEMP_PATH = $GITEA_TEMP/uploads
|
||||
|
||||
[server]
|
||||
APP_DATA_PATH = $GITEA_WORK_DIR
|
||||
SSH_DOMAIN = $SSH_DOMAIN
|
||||
HTTP_PORT = $HTTP_PORT
|
||||
ROOT_URL = $ROOT_URL
|
||||
DISABLE_SSH = $DISABLE_SSH
|
||||
START_SSH_SERVER = true
|
||||
SSH_PORT = $SSH_PORT
|
||||
SSH_LISTEN_PORT = $SSH_LISTEN_PORT
|
||||
BUILTIN_SSH_SERVER_USER = $RUN_USER
|
||||
LFS_START_SERVER = $LFS_START_SERVER
|
||||
|
||||
[database]
|
||||
PATH = $GITEA_WORK_DIR/data/gitea.db
|
||||
DB_TYPE = $DB_TYPE
|
||||
HOST = $DB_HOST
|
||||
NAME = $DB_NAME
|
||||
USER = $DB_USER
|
||||
PASSWD = $DB_PASSWD
|
||||
|
||||
[session]
|
||||
PROVIDER_CONFIG = $GITEA_WORK_DIR/data/sessions
|
||||
|
||||
[picture]
|
||||
AVATAR_UPLOAD_PATH = $GITEA_WORK_DIR/data/avatars
|
||||
REPOSITORY_AVATAR_UPLOAD_PATH = $GITEA_WORK_DIR/data/repo-avatars
|
||||
|
||||
[attachment]
|
||||
PATH = $GITEA_WORK_DIR/data/attachments
|
||||
|
||||
[log]
|
||||
ROOT_PATH = $GITEA_WORK_DIR/data/log
|
||||
|
||||
[security]
|
||||
INSTALL_LOCK = $INSTALL_LOCK
|
||||
SECRET_KEY = $SECRET_KEY
|
||||
REVERSE_PROXY_LIMIT = 1
|
||||
REVERSE_PROXY_TRUSTED_PROXIES = *
|
||||
|
||||
[service]
|
||||
DISABLE_REGISTRATION = $DISABLE_REGISTRATION
|
||||
REQUIRE_SIGNIN_VIEW = $REQUIRE_SIGNIN_VIEW
|
||||
|
||||
[lfs]
|
||||
PATH = $GITEA_WORK_DIR/git/lfs
|
||||
129
setup.sh
Executable file
129
setup.sh
Executable file
|
|
@ -0,0 +1,129 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
|
||||
template="$repo_root/fly.toml.tmpl"
|
||||
config="$repo_root/fly.toml"
|
||||
cd "$repo_root"
|
||||
|
||||
for command_name in fly openssl sed tr; do
|
||||
if ! command -v "$command_name" >/dev/null 2>&1; then
|
||||
printf 'Required command not found: %s\n' "$command_name" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
prompt() {
|
||||
local variable_name=$1
|
||||
local message=$2
|
||||
local default_value=${3:-}
|
||||
local value
|
||||
|
||||
if [ -n "$default_value" ]; then
|
||||
read -r -p "$message [$default_value]: " value
|
||||
value=${value:-$default_value}
|
||||
else
|
||||
while [ -z "${value:-}" ]; do
|
||||
read -r -p "$message: " value
|
||||
done
|
||||
fi
|
||||
printf -v "$variable_name" '%s' "$value"
|
||||
}
|
||||
|
||||
prompt app_name 'Fly app name'
|
||||
case "$app_name" in
|
||||
-*|*-|*[!a-z0-9-]*)
|
||||
printf 'App names may only contain lowercase letters, numbers, and internal hyphens.\n' >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
prompt primary_region 'Fly primary region' 'nrt'
|
||||
case "$primary_region" in
|
||||
*[!a-z0-9]*)
|
||||
printf 'Invalid Fly region: %s\n' "$primary_region" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
prompt admin_username 'Forgejo admin username' 'admin'
|
||||
case "$admin_username" in
|
||||
-*|*-|*[!A-Za-z0-9._-]*)
|
||||
printf 'The Forgejo username may only contain letters, numbers, ., _, and internal hyphens.\n' >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
prompt admin_email 'Forgejo admin email' "admin@$app_name.fly.dev"
|
||||
if [[ "$admin_email" != *@* ]]; then
|
||||
printf 'Invalid email address: %s\n' "$admin_email" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
caddy_username=
|
||||
caddy_password=
|
||||
read -r -p 'Enable Caddy HTTP basic auth? [y/N]: ' enable_caddy_auth
|
||||
case "$enable_caddy_auth" in
|
||||
y|Y|yes|YES)
|
||||
prompt caddy_username 'Caddy basic auth username' "$admin_username"
|
||||
case "$caddy_username" in
|
||||
*[!A-Za-z0-9._@-]*)
|
||||
printf 'The Caddy username may only contain letters, numbers, ., _, @, and -.\n' >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
while [ -z "$caddy_password" ]; do
|
||||
read -r -s -p 'Caddy basic auth password: ' caddy_password
|
||||
printf '\n'
|
||||
done
|
||||
read -r -s -p 'Confirm Caddy basic auth password: ' caddy_password_confirmation
|
||||
printf '\n'
|
||||
if [ "$caddy_password" != "$caddy_password_confirmation" ]; then
|
||||
printf 'Caddy passwords do not match.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
''|n|N|no|NO)
|
||||
;;
|
||||
*)
|
||||
printf 'Please answer yes or no.\n' >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
admin_password="$(openssl rand -base64 24 | tr -d '\n')"
|
||||
|
||||
sed \
|
||||
-e "s/__APP_NAME__/$app_name/g" \
|
||||
-e "s/__PRIMARY_REGION__/$primary_region/g" \
|
||||
"$template" > "$config"
|
||||
|
||||
printf '\nGenerated Forgejo admin credentials:\n'
|
||||
printf ' Username: %s\n' "$admin_username"
|
||||
printf ' Password: %s\n' "$admin_password"
|
||||
|
||||
printf '\nCreating Fly app %s...\n' "$app_name"
|
||||
fly apps create "$app_name"
|
||||
|
||||
secrets=(
|
||||
"FORGEJO_ADMIN_USERNAME=$admin_username"
|
||||
"FORGEJO_ADMIN_EMAIL=$admin_email"
|
||||
"FORGEJO_ADMIN_PASSWORD=$admin_password"
|
||||
)
|
||||
if [ -n "$caddy_username" ]; then
|
||||
secrets+=(
|
||||
"CADDY_BASIC_AUTH_USERNAME=$caddy_username"
|
||||
"CADDY_BASIC_AUTH_PASSWORD=$caddy_password"
|
||||
)
|
||||
fi
|
||||
fly secrets set --stage --app "$app_name" "${secrets[@]}"
|
||||
|
||||
printf '\nDeploying Forgejo...\n'
|
||||
fly deploy --config "$config"
|
||||
|
||||
printf '\nForgejo is available at https://%s.fly.dev/\n' "$app_name"
|
||||
printf 'Admin username: %s\n' "$admin_username"
|
||||
printf 'Admin password: %s\n' "$admin_password"
|
||||
if [ -n "$caddy_username" ]; then
|
||||
printf 'Caddy basic auth username: %s\n' "$caddy_username"
|
||||
fi
|
||||
Loading…
Add table
Add a link
Reference in a new issue