#!/usr/bin/env bash # Author : Allan Christensen # First Created : 15-09-2022 (DD-MM-YYYY) # Description : Installs multiple isolated Gitea instances on Ubuntu 24.04 # License : MIT License # # Are we root # if [[ $(id -u) -ne 0 ]]; then echo "" && echo "Must be root or use sudo" && echo "" ; exit ; fi # # Detect database engine (MariaDB or MySQL) # if systemctl list-unit-files | grep -q '^mariadb\.service'; then db_engine="mariadb" db_service="mariadb" elif systemctl list-unit-files | grep -q '^mysql\.service'; then db_engine="mysql" db_service="mysql" else printf "\nNo supported database server found.\n\n" exit 1 fi # # Check required services # phpfpm=$(systemctl list-unit-files --type=service | awk '/php[0-9]+\.[0-9]+-fpm\.service/ {sub(".service","",$1); print $1; exit}') for svc in nginx "$db_service"; do systemctl is-active --quiet "$svc" || { printf "\n%s is not running, cannot continue...\n\n" "${svc^}" ; exit 1 ; }; done # # Detect socket authentication (local root access) # if mysql -u root -e ";" 2>/dev/null; then socketauth="yes" else socketauth="no" fi # # Variables # fallbackversion=$(/dev/null | awk -v port=":${installer_port}\\b" '$0 ~ port && /LISTEN/ {print $7}' | sed 's/users:(("//; s/",.*//') if [[ -n "$listener" ]]; then printf "Process using port %s : %s\n" "$installer_port" "$listener" fi printf "\nThis port is required temporarily by the Gitea web installer.\n" printf "Please free port %s before running this installer.\n\n" "$installer_port" exit 1 fi # # Usage # usage() { printf -- "\ngiteainstall multi instance\n\n" printf -- "Database engine detected: %s\n\n" "$db_engine" if [[ "$socketauth" == "yes" ]]; then printf -- "LOCAL ROOT ACCESS DETECTED — no need for -a or -m\n\n" printf -- "Installs multiple Gitea instances using local socket authentication.\n\n" printf -- "Usage:\n" printf -- " sudo ./giteainstall -n -p \n\n" printf -- "Example:\n" printf -- " sudo ./giteainstall -n git1.example.com -p gitea1dbpwd\n\n" else printf -- "NO LOCAL ROOT ACCESS — you must use -a and -m\n\n" printf -- "Installs multiple Gitea instances using database admin credentials.\n\n" printf -- "Usage:\n" printf -- " sudo ./giteainstall -n -p -a -m \n\n" printf -- "Examples:\n" printf -- " sudo ./giteainstall -n git2.example.com -p gitea2dbpwd -a root -m rootpwd\n" printf -- " sudo ./giteainstall -n git3.example.com -p gitea3dbpwd -a admin -m adminpwd\n\n" fi printf -- "Options:\n" printf -- " -h | -help | --help Show this help screen\n\n" } # # Let's go # clear # # Help traps # firstarg="${1:-}" if [[ "$firstarg" == "-help" || "$firstarg" == "--help" ]]; then usage ; exit 0 ; fi if [[ $# -eq 0 || ! "$firstarg" =~ ^- ]]; then usage ; exit 1 ; fi # # Configure command line options # while getopts "n:p:a:m:h" option; do case "$option" in n) hostname=$(echo "$OPTARG" | tr '[:upper:]' '[:lower:]');; p) dbpass="$OPTARG";; a) db_admin_user="$OPTARG";; m) db_admin_pwd="$OPTARG";; h) usage ; exit 0 ;; \?) printf "Type sudo %s -h for help\n" "$0" ; exit 1 ;; esac done # # Validate input # if [[ -z "${hostname:-}" || -z "${dbpass:-}" ]]; then usage printf "\nERROR: Both -n (domain) and -p (database password) are required.\n\n" exit 1 fi # # Normalise DB admin defaults # db_admin_user="${db_admin_user:-root}" # # If no socket auth, require admin password # if [[ "$socketauth" == "no" && -z "${db_admin_pwd:-}" ]]; then usage printf "\nERROR: No local root access detected — you must provide -a and -m.\n\n" exit 1 fi # # Ensure required tools # for tool in curl git; do dpkg -s "$tool" &>/dev/null || apt install -y -qq "$tool" ; done # # Check for the latest Gitea version # version=$(curl -fsSL https://dl.gitea.com/gitea/version.json | grep -oP '"version"\s*:\s*"\K[^"]+') if [[ -z "$version" ]]; then printf "Could not determine latest version. Falling back to version %s\n\n" "$fallbackversion" version="$fallbackversion" fi printf "\nUsing Gitea version: %s\n" "$version" # # Download gitea binary if not already present (non-interactive) # if [[ ! -x "$gitealocation" ]]; then curl -fL "https://dl.gitea.com/gitea/${version}/gitea-${version}-linux-amd64" -o "$gitealocation" \ || { printf "\nDownload failed.\n\n" ; exit 1 ; } chmod 755 "$gitealocation" fi # # Clone nginx-snippets; if it exists then pull latest # if [[ -d "$nginxsnippets/.git" ]]; then git -C "$nginxsnippets" pull --quiet else git clone --quiet "$nginxrepo" "$nginxsnippets" fi # # Auto-increment instance number (gitea1, gitea2, ...) # mkdir -p /var/lib/gitea counter_file="/var/lib/gitea/giteausercount" [[ ! -f "$counter_file" ]] && printf "0\n" > "$counter_file" current=$(cat "$counter_file") next=$((current + 1)) printf "%s\n" "$next" > "$counter_file" giteauser="gitea${next}" gitea_db="${giteauser}${giteadb_suffix}" final_port=$((3000 + next)) # # Basic collision protection # if id "$giteauser" &>/dev/null; then printf "\nERROR: User %s already exists — refusing to continue.\n\n" "$giteauser" exit 1 fi if [[ -d "/var/lib/$giteauser" || -d "/etc/$giteauser" ]]; then printf "\nERROR: Instance paths already exist for %s — refusing to continue.\n\n" "$giteauser" exit 1 fi if [[ -f "/etc/nginx/conf.d/${hostname}.conf" ]]; then printf "\nERROR: A configuration file already exists for %s.\n" "$hostname" printf "Refusing to overwrite existing site.\n\n" exit 1 fi # # Create Gitea system user # adduser --system --group --disabled-password --shell /bin/bash --home /home/$giteauser --gecos 'Git Version Control' $giteauser mkdir -p /home/$giteauser/.ssh chown -R $giteauser:$giteauser /home/$giteauser chmod 700 /home/$giteauser/.ssh # # Create Gitea data structure # mkdir -p /var/lib/$giteauser/{custom,data,indexers,public,log} chown "$giteauser":"$giteauser" /var/lib/$giteauser/{data,indexers,log} chmod 750 /var/lib/$giteauser/{data,indexers,log} mkdir -p /var/lib/$giteauser/custom/{templates,public/assets/img} # # Create /etc/$giteauser and its custom directory # mkdir -p /etc/$giteauser/custom chown root:"$giteauser" /etc/$giteauser chmod 770 /etc/$giteauser chown -R "$giteauser":"$giteauser" /etc/$giteauser/custom chmod -R 750 /etc/$giteauser/custom # # Create database and user # printf "\nCreating database: %s\n" "$gitea_db" if [[ "$socketauth" == "yes" ]]; then mysql -u root < /etc/systemd/system/$giteauser.service < final port and locks down settings) # postscript="/etc/$giteauser/gitea-postinstall" cat > "$postscript" <> /etc/\$giteauser/app.ini <<'INNER_EOF' [ui.admin] USER_PAGING_NUM = 50 REPO_PAGING_NUM = 50 NOTICE_PAGING_NUM = 25 ORG_PAGING_NUM = 25 [ui.user] USER_PAGING_NUM = 50 REPO_PAGING_NUM = 50 NOTICE_PAGING_NUM = 25 ORG_PAGING_NUM = 25 [ui] THEMES = gitea-auto, gitea-light, gitea-dark EXPLORE_PAGING_DEFAULT_SORT = alphabetically [other] SHOW_FOOTER_POWERED_BY = false SHOW_FOOTER_VERSION = false SHOW_FOOTER_TEMPLATE_LOAD_TIME = false ENABLE_FEED = false INNER_EOF # Update Nginx proxy to final assigned port sed -i "s/127\\.0\\.0\\.1:3000/127.0.0.1:\$final_port/" /etc/nginx/conf.d/\$hostname.conf systemctl reload nginx # Restart Gitea instance systemctl restart \$giteauser # Remove postinstall script once it has run rm -f "$postscript" EOF chmod 755 "$postscript" # # Web installer + postinstall notice # dbhost="127.0.0.1" [[ "$socketauth" == "yes" ]] && dbhost="localhost" clear cat <