71
scripts/install.sh
Executable file
71
scripts/install.sh
Executable file
@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
WITH_TLS=false
|
||||
OPEN_PUBLIC_WEB=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--with-tls) WITH_TLS=true ;;
|
||||
--open-public-web) OPEN_PUBLIC_WEB=true ;;
|
||||
*) die "Unknown argument: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
log "Starting Gitea install"
|
||||
require_cmd docker
|
||||
require_cmd curl
|
||||
|
||||
load_env
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
: "${TLS_EMAIL:?TLS_EMAIL is required in .env when using --with-tls}"
|
||||
fi
|
||||
|
||||
mkdir -p "${GITEA_BASE_PATH}" "${GITEA_BASE_PATH}/gitea-data" "${GITEA_BASE_PATH}/postgres" "${GITEA_BASE_PATH}/backups" "${GITEA_BASE_PATH}/caddy-data" "${GITEA_BASE_PATH}/caddy-config"
|
||||
|
||||
log "Pulling images"
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
compose --profile tls pull
|
||||
else
|
||||
compose pull
|
||||
fi
|
||||
|
||||
log "Starting containers"
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
compose --profile tls up -d
|
||||
else
|
||||
compose up -d
|
||||
fi
|
||||
|
||||
wait_for_gitea_health
|
||||
apply_firewall_rules "${OPEN_PUBLIC_WEB}"
|
||||
|
||||
cat <<MSG
|
||||
Install complete.
|
||||
|
||||
Local access:
|
||||
- Web UI: http://localhost:${GITEA_HTTP_PORT}
|
||||
- SSH for Git: ssh://git@localhost:${GITEA_SSH_PORT}
|
||||
|
||||
Expected repository root on host:
|
||||
${GITEA_BASE_PATH}/gitea-data/git/repositories
|
||||
MSG
|
||||
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
cat <<TLSMSG
|
||||
TLS profile enabled (Caddy).
|
||||
- HTTPS endpoint (after DNS + router config): https://${GITEA_DOMAIN}
|
||||
- ACME contact email: ${TLS_EMAIL}
|
||||
- Optional firewall for 80/443 was $( [[ "${OPEN_PUBLIC_WEB}" == "true" ]] && printf 'enabled' || printf 'not changed' )
|
||||
TLSMSG
|
||||
fi
|
||||
|
||||
cat <<NEXT
|
||||
|
||||
Next:
|
||||
1) Run scripts/test.sh$( [[ "${WITH_TLS}" == "true" ]] && printf ' --with-tls' || true )
|
||||
2) Complete DNS/router/Cloudflare steps documented in README.md
|
||||
NEXT
|
||||
103
scripts/lib.sh
Executable file
103
scripts/lib.sh
Executable file
@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
ENV_FILE="${REPO_ROOT}/.env"
|
||||
ENV_EXAMPLE_FILE="${REPO_ROOT}/.env.example"
|
||||
COMPOSE_FILE="${REPO_ROOT}/docker-compose.yml"
|
||||
|
||||
log() {
|
||||
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
|
||||
}
|
||||
|
||||
die() {
|
||||
printf 'ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1"
|
||||
}
|
||||
|
||||
create_env_if_missing() {
|
||||
if [[ ! -f "${ENV_FILE}" ]]; then
|
||||
log "No .env found; creating .env from template"
|
||||
cp "${ENV_EXAMPLE_FILE}" "${ENV_FILE}"
|
||||
fi
|
||||
}
|
||||
|
||||
load_env() {
|
||||
[[ -f "${ENV_EXAMPLE_FILE}" ]] || die "Missing template: ${ENV_EXAMPLE_FILE}"
|
||||
create_env_if_missing
|
||||
|
||||
set -a
|
||||
source "${ENV_FILE}"
|
||||
set +a
|
||||
|
||||
: "${GITEA_BASE_PATH:?GITEA_BASE_PATH is required in .env}"
|
||||
: "${GITEA_HTTP_PORT:?GITEA_HTTP_PORT is required in .env}"
|
||||
: "${GITEA_SSH_PORT:?GITEA_SSH_PORT is required in .env}"
|
||||
: "${GITEA_DOMAIN:?GITEA_DOMAIN is required in .env}"
|
||||
}
|
||||
|
||||
compose() {
|
||||
docker compose --env-file "${ENV_FILE}" -f "${COMPOSE_FILE}" "$@"
|
||||
}
|
||||
|
||||
wait_for_gitea_health() {
|
||||
local retries=45
|
||||
local delay=4
|
||||
local url="http://localhost:${GITEA_HTTP_PORT}/api/healthz"
|
||||
|
||||
log "Waiting for Gitea health endpoint: ${url}"
|
||||
for ((i=1; i<=retries; i++)); do
|
||||
if curl -fsS "${url}" >/dev/null 2>&1; then
|
||||
log "Gitea health check passed"
|
||||
return 0
|
||||
fi
|
||||
sleep "${delay}"
|
||||
done
|
||||
|
||||
die "Gitea did not become healthy in time"
|
||||
}
|
||||
|
||||
ufw_active() {
|
||||
command -v ufw >/dev/null 2>&1 || return 1
|
||||
sudo ufw status | grep -q '^Status: active'
|
||||
}
|
||||
|
||||
apply_firewall_rules() {
|
||||
local open_public_web="${1:-false}"
|
||||
|
||||
if ufw_active; then
|
||||
log "Applying UFW rules for Gitea ports"
|
||||
sudo ufw allow "${GITEA_HTTP_PORT}/tcp" comment 'Gitea HTTP'
|
||||
sudo ufw allow "${GITEA_SSH_PORT}/tcp" comment 'Gitea SSH'
|
||||
|
||||
if [[ "${open_public_web}" == "true" ]]; then
|
||||
log "Applying UFW rules for public reverse proxy ports"
|
||||
sudo ufw allow 80/tcp comment 'Gitea TLS HTTP-01'
|
||||
sudo ufw allow 443/tcp comment 'Gitea TLS HTTPS'
|
||||
fi
|
||||
else
|
||||
log "UFW not active; skipping firewall rule setup"
|
||||
fi
|
||||
}
|
||||
|
||||
remove_firewall_rules() {
|
||||
local close_public_web="${1:-false}"
|
||||
|
||||
if ufw_active; then
|
||||
log "Removing UFW rules for Gitea ports"
|
||||
sudo ufw --force delete allow "${GITEA_HTTP_PORT}/tcp" || true
|
||||
sudo ufw --force delete allow "${GITEA_SSH_PORT}/tcp" || true
|
||||
|
||||
if [[ "${close_public_web}" == "true" ]]; then
|
||||
log "Removing UFW rules for public reverse proxy ports"
|
||||
sudo ufw --force delete allow 80/tcp || true
|
||||
sudo ufw --force delete allow 443/tcp || true
|
||||
fi
|
||||
else
|
||||
log "UFW not active; skipping firewall rule removal"
|
||||
fi
|
||||
}
|
||||
72
scripts/test.sh
Executable file
72
scripts/test.sh
Executable file
@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
WITH_TLS=false
|
||||
ALLOW_PENDING_EXTERNAL=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--with-tls) WITH_TLS=true ;;
|
||||
--allow-pending-external) ALLOW_PENDING_EXTERNAL=true ;;
|
||||
*) die "Unknown argument: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
load_env
|
||||
require_cmd curl
|
||||
|
||||
log "Checking docker compose status"
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
compose --profile tls ps
|
||||
else
|
||||
compose ps
|
||||
fi
|
||||
|
||||
log "Checking local HTTP health"
|
||||
curl -fsS "http://localhost:${GITEA_HTTP_PORT}/api/healthz" && printf '\n'
|
||||
|
||||
log "Checking local web root"
|
||||
curl -I -sS "http://localhost:${GITEA_HTTP_PORT}" | grep -E 'HTTP/[0-9.]+ 200|HTTP/[0-9.]+ 302'
|
||||
|
||||
if command -v getent >/dev/null 2>&1; then
|
||||
log "Checking DNS resolution for ${GITEA_DOMAIN}"
|
||||
getent hosts "${GITEA_DOMAIN}" || log "DNS resolution not yet set for ${GITEA_DOMAIN}"
|
||||
fi
|
||||
|
||||
if command -v nc >/dev/null 2>&1; then
|
||||
log "Checking SSH TCP port on localhost"
|
||||
nc -z localhost "${GITEA_SSH_PORT}"
|
||||
fi
|
||||
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
log "Checking Caddy container status"
|
||||
compose --profile tls ps caddy | grep -E 'gitea-caddy|caddy'
|
||||
|
||||
if command -v nc >/dev/null 2>&1; then
|
||||
log "Checking reverse proxy listener ports"
|
||||
nc -z localhost 80
|
||||
nc -z localhost 443
|
||||
fi
|
||||
|
||||
if getent hosts "${GITEA_DOMAIN}" >/dev/null 2>&1; then
|
||||
log "Checking HTTPS response by domain"
|
||||
if [[ "${ALLOW_PENDING_EXTERNAL}" == "true" ]]; then
|
||||
if ! curl -k -I -sS "https://${GITEA_DOMAIN}" | grep -E 'HTTP/[0-9.]+ 200|HTTP/[0-9.]+ 302|HTTP/[0-9.]+ 308'; then
|
||||
log "HTTPS domain check did not pass yet (pending DNS/routing/certificate propagation)"
|
||||
fi
|
||||
else
|
||||
curl -I -sS "https://${GITEA_DOMAIN}" | grep -E 'HTTP/[0-9.]+ 200|HTTP/[0-9.]+ 302|HTTP/[0-9.]+ 308'
|
||||
fi
|
||||
else
|
||||
if [[ "${ALLOW_PENDING_EXTERNAL}" == "true" ]]; then
|
||||
log "Skipping strict HTTPS domain check until DNS is configured"
|
||||
else
|
||||
die "DNS not configured for ${GITEA_DOMAIN}; rerun with --allow-pending-external if still propagating"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
log "All tests passed"
|
||||
50
scripts/uninstall.sh
Executable file
50
scripts/uninstall.sh
Executable file
@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
PURGE_DATA=false
|
||||
PURGE_IMAGES=false
|
||||
ASSUME_YES=false
|
||||
WITH_TLS=false
|
||||
CLOSE_PUBLIC_WEB=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--purge-data) PURGE_DATA=true ;;
|
||||
--purge-images) PURGE_IMAGES=true ;;
|
||||
--yes) ASSUME_YES=true ;;
|
||||
--with-tls) WITH_TLS=true ;;
|
||||
--close-public-web) CLOSE_PUBLIC_WEB=true ;;
|
||||
*) die "Unknown argument: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
load_env
|
||||
|
||||
if [[ "${PURGE_DATA}" == "true" && "${ASSUME_YES}" != "true" ]]; then
|
||||
read -r -p "This will delete ${GITEA_BASE_PATH}/gitea-data and ${GITEA_BASE_PATH}/postgres. Continue? [y/N]: " answer
|
||||
[[ "${answer}" == "y" || "${answer}" == "Y" ]] || die "Aborted by user"
|
||||
fi
|
||||
|
||||
log "Stopping and removing containers"
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
compose --profile tls down --remove-orphans
|
||||
else
|
||||
compose down --remove-orphans
|
||||
fi
|
||||
|
||||
if [[ "${PURGE_IMAGES}" == "true" ]]; then
|
||||
log "Removing docker images"
|
||||
docker image rm gitea/gitea:1.24.2 postgres:16-alpine caddy:2.10-alpine || true
|
||||
fi
|
||||
|
||||
if [[ "${PURGE_DATA}" == "true" ]]; then
|
||||
log "Purging persistent data under ${GITEA_BASE_PATH}"
|
||||
rm -rf "${GITEA_BASE_PATH}/gitea-data" "${GITEA_BASE_PATH}/postgres" "${GITEA_BASE_PATH}/caddy-data" "${GITEA_BASE_PATH}/caddy-config"
|
||||
fi
|
||||
|
||||
remove_firewall_rules "${CLOSE_PUBLIC_WEB}"
|
||||
|
||||
log "Uninstall complete"
|
||||
Reference in New Issue
Block a user