#!/usr/bin/env bash set -Eeuo pipefail DEPLOY_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" cd "$DEPLOY_ROOT" fail() { printf 'ERROR: %s\n' "$*" >&2 exit 1 } prompt_default() { local prompt="$1" local fallback="$2" local value printf '%s [%s]: ' "$prompt" "$fallback" >&2 read -r value printf '%s' "${value:-$fallback}" } valid_hostname() { [[ "$1" =~ ^[A-Za-z0-9]([A-Za-z0-9.-]*[A-Za-z0-9])?$ && "$1" != *..* ]] } valid_port() { [[ "$1" =~ ^[0-9]+$ ]] && (( 10#$1 >= 1 && 10#$1 <= 65535 )) } [[ $# -eq 0 ]] || fail "usage: ./install.sh" [[ ! -e .env ]] || fail ".env already exists; use ./manage.sh instead of overwriting this deployment" for required_file in \ compose.yaml \ compose.http.yaml \ compose.tls.yaml \ deploy/nginx/templates/http.conf.template \ deploy/nginx/templates/tls.conf.template; do [[ -f "$required_file" ]] || fail "repository file is missing or not a regular file: ${required_file}" done for command_name in docker openssl; do command -v "$command_name" >/dev/null 2>&1 || fail "$command_name is required" done docker compose version >/dev/null 2>&1 || fail "Docker Compose v2 is required" docker info >/dev/null 2>&1 || fail "the Docker daemon is unavailable or this user cannot access it" printf '\nTAPM Deployment Access installer\n' printf 'This creates the initial environment and starts Gitea behind Nginx.\n' printf 'The broker starts only after its Gitea OAuth and package credentials are configured.\n\n' broker_domain="$(prompt_default 'Broker hostname' 'tapm.example.com')" gitea_domain="$(prompt_default 'Gitea hostname' 'git.example.com')" valid_hostname "$broker_domain" || fail "invalid broker hostname" valid_hostname "$gitea_domain" || fail "invalid Gitea hostname" [[ "$broker_domain" != "$gitea_domain" ]] || fail "broker and Gitea hostnames must be different" http_port="$(prompt_default 'VM port mapped to Nginx HTTP port 80' '8680')" https_port="$(prompt_default 'VM port mapped to Nginx HTTPS port 443' '8643')" valid_port "$http_port" || fail "invalid HTTP port" valid_port "$https_port" || fail "invalid HTTPS port" [[ "$http_port" != "$https_port" ]] || fail "HTTP and HTTPS ports must differ" ssl_mode="$(prompt_default 'SSL mode (none, letsencrypt, or custom)' 'none')" case "$ssl_mode" in none) public_scheme=http public_port_suffix=":${http_port}" letsencrypt_email='' certificate_dir="./config/letsencrypt/live/${broker_domain}" ;; letsencrypt) public_scheme=https public_port_suffix='' printf 'Let'\''s Encrypt account email: ' read -r letsencrypt_email [[ "$letsencrypt_email" == *@* ]] || fail "a valid email is required" certificate_dir="./config/letsencrypt/live/${broker_domain}" ;; custom) public_scheme=https public_port_suffix='' letsencrypt_email='' certificate_dir='./config/ssl' ;; *) fail "SSL mode must be none, letsencrypt, or custom" ;; esac cookie_secret="$(openssl rand -base64 48)" umask 077 { printf 'BROKER_DOMAIN=%s\n' "$broker_domain" printf 'GITEA_DOMAIN=%s\n' "$gitea_domain" printf 'GITEA_PUBLIC_URL=%s://%s%s/\n' "$public_scheme" "$gitea_domain" "$public_port_suffix" printf 'LETSENCRYPT_EMAIL=%s\n' "$letsencrypt_email" printf 'HTTP_PORT=%s\n' "$http_port" printf 'HTTPS_PORT=%s\n\n' "$https_port" printf 'BROKER_DIRECT_PORT=8080\n' printf 'GITEA_DIRECT_PORT=3000\n' printf 'GITEA_DIRECT_ROOT_URL=http://localhost:3000/\n\n' printf 'SSL_MODE=%s\n' "$ssl_mode" printf 'SSL_CERTIFICATE_DIR=%s\n' "$certificate_dir" printf 'SSL_CERTIFICATE_FILE=fullchain.pem\n' printf 'SSL_CERTIFICATE_KEY_FILE=privkey.pem\n\n' printf 'TAPM_DATABASE_DSN=file:/data/tapm.db?_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)\n' printf 'TAPM_PUBLIC_URL=%s://%s%s\n' "$public_scheme" "$broker_domain" "$public_port_suffix" printf 'TAPM_GITEA_URL=%s://%s%s\n' "$public_scheme" "$gitea_domain" "$public_port_suffix" printf 'TAPM_GITEA_CLIENT_ID=\n' printf 'TAPM_GITEA_CLIENT_SECRET=\n' printf 'TAPM_GITEA_PACKAGE_OWNER=TAI\n' printf 'TAPM_GITEA_PACKAGE_USERNAME=\n' printf 'TAPM_GITEA_PACKAGE_TOKEN=\n' printf 'TAPM_GITEA_PACKAGE_WRITE_USERNAME=\n' printf 'TAPM_GITEA_PACKAGE_WRITE_TOKEN=\n' printf 'TAPM_ALLOWED_GITEA_USERS=taiadmin\n' printf 'TAPM_COOKIE_SECRET=%s\n' "$cookie_secret" printf 'TAPM_DEFAULT_DURATION=3h\n' printf 'TAPM_DEFAULT_HOST_LIMIT=3\n' printf 'TAPM_MAX_HOST_LIMIT=25\n' printf 'TAPM_MAX_UPLOAD_BYTES=1073741824\n' printf 'TAPM_TRUST_PROXY_HEADERS=true\n' printf 'TAPM_DISPLAY_TIME_ZONE=America/Chicago\n' } >.env chmod 600 .env printf '\nInitial configuration saved to %s/.env.\n' "$DEPLOY_ROOT" if [[ "$ssl_mode" == "custom" ]]; then printf 'Place the certificate and key in config/ssl, then run ./manage.sh bootstrap.\n' printf 'Gitea has not been started yet.\n' exit 0 fi "${DEPLOY_ROOT}/manage.sh" bootstrap printf '\nInitial Gitea deployment is running.\n' printf 'Gitea URL: %s://%s%s\n' "$public_scheme" "$gitea_domain" "$public_port_suffix" if [[ "$ssl_mode" == "none" ]]; then printf 'This is HTTP-only testing mode. Do not expose it publicly.\n' printf 'Make both hostnames resolve to this VM or its test-forwarding address.\n' fi printf '\nNext steps:\n' printf ' 1. Run ./manage.sh create-admin\n' printf ' 2. Sign in to Gitea and create the organization, package users/tokens,\n' printf ' and OAuth application documented in docs/deployment.md.\n' printf ' 3. Run ./manage.sh configure-broker\n' printf ' 4. Run ./manage.sh start\n' if [[ "$ssl_mode" == "none" ]]; then printf ' 5. Later, after DNS and public 80/443 forwarding are ready, run:\n' printf ' ./manage.sh set-ssl letsencrypt\n' printf ' ./manage.sh bootstrap\n' printf ' ./manage.sh start\n' fi