| 1 | #!/bin/sh |
| 2 | # Install or upgrade Ferrenium from its GitHub releases. |
| 3 | # |
| 4 | # curl -fsSL https://github.com/krolyxon/ferrenium/releases/latest/download/install.sh | sudo sh |
| 5 | # |
| 6 | # Options, as environment variables (put them after `sudo`): |
| 7 | # FERRENIUM_DOMAIN=git.example.com fill in base_url and clone URLs (fresh config only) |
| 8 | # FERRENIUM_USER=git user that owns the repositories (created if missing) |
| 9 | # FERRENIUM_VERSION=v0.1.0 a specific release instead of the latest |
| 10 | # |
| 11 | # `sh -s -- --uninstall` removes the binary and the unit, and keeps |
| 12 | # /etc/ferrenium, the user and the repositories. |
| 13 | # |
| 14 | # Upgrades replace the binary and the unit and restart the service; the |
| 15 | # config is never overwritten. Customize the unit with `systemctl edit |
| 16 | # ferrenium`, not by editing the file. |
| 17 | |
| 18 | set -eu |
| 19 | |
| 20 | REPO=krolyxon/ferrenium |
| 21 | BIN=/usr/local/bin/ferrenium |
| 22 | ETC=/etc/ferrenium |
| 23 | CONFIG=$ETC/ferrenium.toml |
| 24 | UNIT=/etc/systemd/system/ferrenium.service |
| 25 | |
| 26 | say() { printf '%s\n' "$*"; } |
| 27 | die() { printf 'ferrenium install: %s\n' "$*" >&2; exit 1; } |
| 28 | has() { command -v "$1" >/dev/null 2>&1; } |
| 29 | |
| 30 | fetch() { |
| 31 | if has curl; then |
| 32 | curl -fsSL -o "$2" "$1" |
| 33 | elif has wget; then |
| 34 | wget -qO "$2" "$1" |
| 35 | else |
| 36 | die "curl or wget is required" |
| 37 | fi |
| 38 | } |
| 39 | |
| 40 | uninstall() { |
| 41 | if [ -d /run/systemd/system ]; then |
| 42 | systemctl disable --now ferrenium 2>/dev/null || true |
| 43 | fi |
| 44 | rm -f "$UNIT" "$BIN" |
| 45 | if [ -d /run/systemd/system ]; then |
| 46 | systemctl daemon-reload |
| 47 | fi |
| 48 | say "Removed $BIN and $UNIT. Kept $ETC and the repositories." |
| 49 | } |
| 50 | |
| 51 | main() { |
| 52 | [ "$(id -u)" -eq 0 ] || die "run as root (… | sudo sh)" |
| 53 | if [ "${1:-}" = "--uninstall" ]; then |
| 54 | uninstall |
| 55 | return |
| 56 | fi |
| 57 | |
| 58 | [ "$(uname -s)" = Linux ] || die "only Linux is supported" |
| 59 | case "$(uname -m)" in |
| 60 | x86_64 | amd64) arch=x86_64 ;; |
| 61 | aarch64 | arm64) arch=aarch64 ;; |
| 62 | *) die "no release build for $(uname -m); build from source instead" ;; |
| 63 | esac |
| 64 | has sha256sum || die "sha256sum is required" |
| 65 | has git || say "warning: git is not installed; HTTPS clone needs it (apt install git)" |
| 66 | |
| 67 | version=${FERRENIUM_VERSION:-latest} |
| 68 | if [ -n "${FERRENIUM_BASE_URL:-}" ]; then |
| 69 | base=$FERRENIUM_BASE_URL |
| 70 | elif [ "$version" = latest ]; then |
| 71 | base=https://github.com/$REPO/releases/latest/download |
| 72 | else |
| 73 | base=https://github.com/$REPO/releases/download/$version |
| 74 | fi |
| 75 | asset=ferrenium-$arch-linux.tar.gz |
| 76 | |
| 77 | tmp=$(mktemp -d) |
| 78 | trap 'rm -rf "$tmp"' EXIT |
| 79 | say "Downloading $asset ($version)" |
| 80 | fetch "$base/$asset" "$tmp/$asset" || die "download failed: $base/$asset" |
| 81 | fetch "$base/SHA256SUMS" "$tmp/SHA256SUMS" || die "download failed: $base/SHA256SUMS" |
| 82 | (cd "$tmp" && grep " $asset\$" SHA256SUMS | sha256sum -c - >/dev/null) || |
| 83 | die "checksum mismatch for $asset" |
| 84 | tar -xzf "$tmp/$asset" -C "$tmp" ferrenium |
| 85 | "$tmp/ferrenium" --version >/dev/null 2>&1 || |
| 86 | die "the downloaded binary does not run here (needs glibc 2.28 or newer)" |
| 87 | |
| 88 | # The service runs as the user that owns the repositories. |
| 89 | user=${FERRENIUM_USER:-git} |
| 90 | if ! id "$user" >/dev/null 2>&1; then |
| 91 | shell=$(command -v git-shell || echo /usr/sbin/nologin) |
| 92 | useradd --system --create-home --home-dir "/home/$user" --shell "$shell" "$user" |
| 93 | say "Created user $user (home /home/$user, shell $shell)" |
| 94 | fi |
| 95 | group=$(id -gn "$user") |
| 96 | home=$(getent passwd "$user" | cut -d: -f6) |
| 97 | |
| 98 | install -m 755 "$tmp/ferrenium" "$BIN.new" |
| 99 | mv -f "$BIN.new" "$BIN" |
| 100 | say "Installed $("$BIN" --version) to $BIN" |
| 101 | |
| 102 | if [ -e "$CONFIG" ]; then |
| 103 | say "Kept $CONFIG" |
| 104 | else |
| 105 | mkdir -p "$ETC" |
| 106 | "$BIN" example config | sed "s|^scan_path = .*|scan_path = \"$home\"|" >"$tmp/config" |
| 107 | if [ -n "${FERRENIUM_DOMAIN:-}" ]; then |
| 108 | d=$FERRENIUM_DOMAIN |
| 109 | sed -i \ |
| 110 | -e "s|^# base_url = .*|base_url = \"https://$d\"|" \ |
| 111 | -e "s|^# clone_urls = .*|clone_urls = [\"https://$d/{name}\", \"$user@$d:{name}\"]|" \ |
| 112 | "$tmp/config" |
| 113 | fi |
| 114 | install -m 644 "$tmp/config" "$CONFIG" |
| 115 | say "Wrote $CONFIG" |
| 116 | fi |
| 117 | |
| 118 | fresh=yes |
| 119 | [ -e "$UNIT" ] && fresh=no |
| 120 | "$BIN" example systemd | |
| 121 | sed -e "s/^User=.*/User=$user/" -e "s/^Group=.*/Group=$group/" >"$tmp/unit" |
| 122 | install -m 644 "$tmp/unit" "$UNIT" |
| 123 | |
| 124 | if [ ! -d /run/systemd/system ]; then |
| 125 | say "systemd is not running; start it yourself:" |
| 126 | say " $BIN serve --config $CONFIG" |
| 127 | return |
| 128 | fi |
| 129 | systemctl daemon-reload |
| 130 | "$BIN" check --config "$CONFIG" >/dev/null || die "$CONFIG is invalid; fix it, then run: systemctl enable --now ferrenium" |
| 131 | if [ "$fresh" = yes ]; then |
| 132 | systemctl enable --now ferrenium |
| 133 | say "Started ferrenium.service" |
| 134 | elif systemctl is-active --quiet ferrenium; then |
| 135 | systemctl restart ferrenium |
| 136 | say "Restarted ferrenium.service" |
| 137 | fi |
| 138 | |
| 139 | say "" |
| 140 | say "Repositories: bare repos under $home" |
| 141 | say "Config: $CONFIG (then: systemctl reload ferrenium)" |
| 142 | say "Logs: journalctl -u ferrenium" |
| 143 | say "Proxy: ferrenium example caddy" |
| 144 | } |
| 145 | |
| 146 | main "$@" |