Initial commit

Co-Authored-By: Oz <oz-agent@warp.dev>
This commit is contained in:
Raghav
2026-04-16 09:04:22 +05:30
commit bb68b6b9f2
11 changed files with 617 additions and 0 deletions

71
scripts/install.sh Executable file
View 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
View 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
View 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
View 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"