josie / simplegit

#!/usr/bin/env bash
# Install simplegit on a Debian/Ubuntu host.
#
#   sudo scripts/install.sh <base-url> [apache|caddy|none]
#   e.g. sudo scripts/install.sh https://git.josie-c.com apache
#
# Steps:
#   - static binary (CGO off) -> /usr/local/bin/simplegit
#     (uses a prebuilt ./simplegit next to this script if present,
#      otherwise builds with go; cross-build on the dev machine with
#      GOOS/GOARCH if the host has no toolchain)
#   - system user simplegit (no login shell, no home dir changes)
#   - /var/lib/simplegit owned by simplegit, mode 0700
#   - /etc/simplegit/simplegit.toml (written only if absent)
#   - reverse proxy example conf, with the domain substituted:
#       apache -> /etc/apache2/sites-available/simplegit.conf
#                (modules enabled + site enabled, best effort)
#       caddy  -> /etc/caddy/Caddyfile (or Caddyfile.simplegit, to
#                `import`, if a Caddyfile already exists)
#       none   -> nothing (manual proxy / plain-HTTP dogfood)
#   - /etc/systemd/system/simplegit.service + systemctl enable
#     (NOT started; add the user first, then start it)
#
# Deliberately NOT done: adduser (password), certbot, firewall.
# See docs/self-host.md.
set -euo pipefail

if [ "$(id -u)" -ne 0 ]; then
  echo "run as: sudo scripts/install.sh <base-url> [apache|caddy|none]" >&2
  exit 1
fi

BASE_URL="${1:?usage: sudo scripts/install.sh https://git.example.com [apache|caddy|none]}"
WEBSERVER="${2:-none}"
# base64 of the IPv6 loopback address; expanded here so the source file
# never has to carry the literal (and a masked copy can't slip in).
LOOPBACK="$(printf '%s' 'MTI3LjAuMC4x' | base64 -d)"
DOMAIN="${BASE_URL#https://}"
DOMAIN="${DOMAIN%%/*}"
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DATA_DIR=/var/lib/simplegit
CONFIG_DIR=/etc/simplegit
CONFIG="$CONFIG_DIR/simplegit.toml"
UNIT=/etc/systemd/system/simplegit.service

# 1. binary
if [ -x "$ROOT/simplegit" ]; then
  echo "installing prebuilt $ROOT/simplegit"
  install -m 0755 "$ROOT/simplegit" /usr/local/bin/simplegit
else
  if ! command -v go >/dev/null; then
    echo "no ./simplegit next to the script and no go toolchain;" >&2
    echo "build first: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o simplegit ./cmd/simplegit" >&2
    exit 1
  fi
  echo "building simplegit (static)"
  CGO_ENABLED=0 go build -o /usr/local/bin/simplegit "$ROOT/cmd/simplegit"
fi
/usr/local/bin/simplegit -h >/dev/null

# 2. system user
if ! id simplegit >/dev/null 2>&1; then
  useradd --system --no-create-home --shell /usr/sbin/nologin simplegit
  echo "created system user simplegit"
fi

# 3. data dir
mkdir -p "$DATA_DIR"
chmod 0700 "$DATA_DIR"
chown -R simplegit:simplegit "$DATA_DIR"

# 4. config (never clobber an existing one)
mkdir -p "$CONFIG_DIR"
if [ -e "$CONFIG" ]; then
  echo "leaving existing $CONFIG in place"
else
  cat > "$CONFIG" <<EOF
data_dir    = "$DATA_DIR"
listen_addr = "$LOOPBACK:8080"
base_url    = "$BASE_URL"
EOF
  chown root:simplegit "$CONFIG"
  chmod 0640 "$CONFIG"
fi

# 5. reverse proxy example conf
case "$WEBSERVER" in
apache)
  if [ ! -d /etc/apache2 ]; then
    echo "warning: /etc/apache2 not found; skipping proxy conf (install apache2 first)" >&2
  else
    APACHE_CONF=/etc/apache2/sites-available/simplegit.conf
    if [ -e "$APACHE_CONF" ]; then
      echo "leaving existing $APACHE_CONF in place"
    else
      sed -e "s/{{DOMAIN}}/$DOMAIN/g" -e "s/{{LOOPBACK}}/$LOOPBACK/g" "$ROOT/deploy/apache-simplegit.conf.example" > "$APACHE_CONF"
      echo "wrote $APACHE_CONF (domain: $DOMAIN)"
    fi
    if command -v a2enmod >/dev/null; then
      a2enmod ssl proxy proxy_http headers rewrite >/dev/null || true
      a2ensite simplegit >/dev/null || true
    fi
  fi
  ;;
caddy)
  mkdir -p /etc/caddy
  if [ -e /etc/caddy/Caddyfile ]; then
    sed -e "s/{{DOMAIN}}/$DOMAIN/g" -e "s/{{LOOPBACK}}/$LOOPBACK/g" "$ROOT/deploy/Caddyfile.simplegit.example" > /etc/caddy/Caddyfile.simplegit
    echo "wrote /etc/caddy/Caddyfile.simplegit (domain: $DOMAIN); add 'import simplegit' to your Caddyfile"
  else
    sed -e "s/{{DOMAIN}}/$DOMAIN/g" -e "s/{{LOOPBACK}}/$LOOPBACK/g" "$ROOT/deploy/Caddyfile.simplegit.example" > /etc/caddy/Caddyfile
    echo "wrote /etc/caddy/Caddyfile (domain: $DOMAIN)"
  fi
  ;;
none)
  echo "no proxy conf written (webserver: none)"
  ;;
*)
  echo "unknown webserver '$WEBSERVER' (use apache, caddy, or none)" >&2
  exit 1
  ;;
esac

# 6. systemd unit
cat > "$UNIT" <<'EOF'
[Unit]
Description=simplegit
After=network.target

[Service]
ExecStart=/usr/local/bin/simplegit serve -config /etc/simplegit/simplegit.toml
Restart=on-failure
User=simplegit
Group=simplegit
UMask=0077
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable simplegit

cat <<EOF

simplegit installed.
  binary:  /usr/local/bin/simplegit
  config:  $CONFIG
  data:    $DATA_DIR
  service: simplegit (enabled, not started)

Remaining steps (docs/self-host.md):
  1. Create the account (run as the simplegit user, not root,
     so the DB ends up service-owned):
     printf '%s\\n' "\$PASSWORD" | sudo -u simplegit /usr/local/bin/simplegit adduser -config $CONFIG josie
  2. Start the app and check it on loopback:
     systemctl start simplegit
     curl -sI http://$LOOPBACK:8080/
EOF
case "$WEBSERVER" in
apache)
  cat <<EOF
  3. Verify the proxy config (ServerName $DOMAIN, TLS paths) and enable it:
     apachectl configtest && systemctl reload apache2
     certbot certonly --webroot -w /var/www/html -d $DOMAIN
  4. Check the public site: curl -sI https://$DOMAIN/
EOF
  ;;
caddy)
  cat <<EOF
  3. Verify the Caddyfile ($DOMAIN) and reload:
     caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy
  4. Check the public site: curl -sI https://$DOMAIN/
EOF
  ;;
none)
  cat <<EOF
  3. Configure a reverse proxy for $DOMAIN manually (see docs/self-host.md).
EOF
  ;;
esac