v0.7.30-beta - installatiescript voor Linux
curl -fsSL https://git.ramonbesselink.nl/bes-r/server-up/raw/branch/main/install.sh | sh install.sh controleert het systeem, biedt aan Docker te installeren als dat ontbreekt, kloont de repo (of pakt het tar-archief uit als git ontbreekt), maakt .env aan, bouwt en start, wacht op /healthz en toont waar je terecht kunt met de waarschuwing meteen een account aan te maken. - Opties: --dir, --port, --bind, --branch, --token, --update, --uninstall, --yes, --dry-run. - --update haalt nieuwe code op en herstart; .env, stacks en gegevens blijven. - --uninstall stopt de container maar laat gegevens staan, en vertelt hoe je die alsnog opruimt. - Gebruikt alleen sudo waar nodig: kun je zelf in de doelmap schrijven en docker aanroepen, dan blijft alles onder je eigen account. Tijdens het testen gevonden en verholpen: - `docker compose --project-directory` zoekt zonder -f het compose-bestand in de huidige map; bij `curl | sh` is dat je thuismap. Nu altijd -f erbij. - De wachtlus gaf anderhalve minuut geen teken van leven; nu een punt per poging en 90 in plaats van 120 seconden. - Bij --dry-run braken ontbrekende Docker-onderdelen de voorvertoning af; nu waarschuwingen zodat je de hele gang van zaken ziet. - De versie-uitlezing uit /healthz tolereert nu ook json met spaties. Documentatie: docs/installeren.md met alle opties, bijwerken, verwijderen en een probleemoplostabel; README verwijst ernaar. Tests: syntaxis onder sh/dash/bash, geen bashismen, --help noemt alle opties, ongeldige invoer stopt, een proefdraai maakt niets aan, en de installatieregel in README en docs is identiek. 958 tests groen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C7oLCRYzY5ixJ5Sv8Y8EFb
This commit is contained in:
parent
2ba15418ab
commit
2862f7844c
7 changed files with 693 additions and 5 deletions
38
CHANGELOG.md
38
CHANGELOG.md
|
|
@ -1,3 +1,41 @@
|
|||
# v0.7.30-beta — Installeren met één regel
|
||||
|
||||
```bash
|
||||
curl -fsSL https://git.ramonbesselink.nl/bes-r/server-up/raw/branch/main/install.sh | sh
|
||||
```
|
||||
|
||||
Het script controleert je systeem, biedt aan Docker te installeren als dat
|
||||
ontbreekt, haalt de code op, maakt `.env` aan, bouwt het image, start de
|
||||
container en wacht tot `/healthz` antwoordt. Daarna toont het waar je terecht
|
||||
kunt — met de waarschuwing dat je meteen een beheerdersaccount moet aanmaken.
|
||||
|
||||
Opties: `--dir`, `--port`, `--bind`, `--branch`, `--token`, `--update`,
|
||||
`--uninstall`, `--yes` en `--dry-run`.
|
||||
|
||||
**`--dry-run`** toont de hele gang van zaken zonder iets te wijzigen. Handig om
|
||||
te zien wat er gaat gebeuren voordat je een script met root-rechten loslaat op
|
||||
je server; `docs/installeren.md` zet die stap dan ook vooraan.
|
||||
|
||||
**Bijwerken** gaat met `sh /opt/server-up/install.sh --update`: nieuwe code
|
||||
ophalen, opnieuw bouwen, herstarten. Je `.env`, stacks en gegevens blijven staan.
|
||||
**Verwijderen** stopt de container maar laat je gegevens met rust, en vertelt
|
||||
daarna hoe je die alsnog opruimt.
|
||||
|
||||
Het script gebruikt alleen `sudo` waar dat nodig is: kun je zelf al in de
|
||||
doelmap schrijven én docker aanroepen, dan blijft alles onder je eigen account.
|
||||
|
||||
Nieuw: `README.md` verwijst er nu naar, plus `docs/installeren.md` met alle
|
||||
opties en een probleemoplostabel.
|
||||
|
||||
Getest onder `sh`, `dash` en `bash`, met een testsuite die controleert dat er
|
||||
geen bashismen in sluipen, dat een typefout in de opties netjes stopt, en dat
|
||||
een proefdraai echt niets aanmaakt. Twee dingen die daarbij naar boven kwamen:
|
||||
de wachtlus gaf anderhalve minuut lang geen enkel teken van leven (nu puntjes),
|
||||
en `docker compose --project-directory` zoekt zonder `-f` het compose-bestand
|
||||
alsnog in de huidige map — wat bij `curl | sh` je thuismap is.
|
||||
|
||||
---
|
||||
|
||||
# v0.7.20-beta — Instellingen wijzigen, koppelen achteraf, en een defect uit v0.7.00
|
||||
|
||||
## 🔴 Gedeeld netwerk botste met `network_mode`
|
||||
|
|
|
|||
41
README.md
41
README.md
|
|
@ -33,13 +33,35 @@ IP-adres in je netwerk geven.
|
|||
## Installeren
|
||||
|
||||
```bash
|
||||
git clone https://github.com/bes-r/server-up.git
|
||||
cd server-up
|
||||
cp .env.example .env # instellingen; zie hieronder
|
||||
curl -fsSL https://git.ramonbesselink.nl/bes-r/server-up/raw/branch/main/install.sh | sh
|
||||
```
|
||||
|
||||
Dat controleert je systeem, installeert Docker als het ontbreekt, zet Server Up
|
||||
in `/opt/server-up` en start het op `http://localhost:5000`.
|
||||
|
||||
Liever eerst lezen wat je uitvoert — verstandig, want het script gebruikt
|
||||
root-rechten:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://git.ramonbesselink.nl/bes-r/server-up/raw/branch/main/install.sh -o install.sh
|
||||
less install.sh
|
||||
sh install.sh --dry-run # toont wat het zou doen, zonder iets te wijzigen
|
||||
sh install.sh
|
||||
```
|
||||
|
||||
Handmatig kan ook:
|
||||
|
||||
```bash
|
||||
git clone https://git.ramonbesselink.nl/bes-r/server-up.git /opt/server-up
|
||||
cd /opt/server-up
|
||||
cp .env.example .env
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
Open daarna `http://localhost:5000` en **maak meteen een beheerdersaccount aan.**
|
||||
Alle opties staan in [docs/installeren.md](docs/installeren.md):
|
||||
`--dir`, `--port`, `--bind`, `--branch`, `--token`, `--update`, `--uninstall`.
|
||||
|
||||
Open daarna de interface en **maak meteen een beheerdersaccount aan.**
|
||||
Zolang dat niet gebeurd is, kan iedereen die de pagina bereikt het account
|
||||
claimen — de container waarschuwt daar bij het opstarten ook voor.
|
||||
|
||||
|
|
@ -60,6 +82,7 @@ proxy met TLS voor staat.
|
|||
|
||||
| Document | Waarover |
|
||||
|---|---|
|
||||
| [docs/installeren.md](docs/installeren.md) | Het installatiescript, opties, bijwerken en verwijderen |
|
||||
| [docs/beveiliging.md](docs/beveiliging.md) | Accounts, rollen, SSO, reverse proxy, wat wel en niet is afgedekt |
|
||||
| [docs/apps-maken.md](docs/apps-maken.md) | Eigen apps toevoegen: het volledige templateformaat |
|
||||
| [docs/netwerken.md](docs/netwerken.md) | Stacks een eigen IP geven met macvlan of ipvlan |
|
||||
|
|
@ -82,6 +105,16 @@ apps/mijn-app/
|
|||
|
||||
Het volledige formaat staat in [docs/apps-maken.md](docs/apps-maken.md).
|
||||
|
||||
## Bijwerken en verwijderen
|
||||
|
||||
```bash
|
||||
sh /opt/server-up/install.sh --update # nieuwste versie ophalen en herstarten
|
||||
sh /opt/server-up/install.sh --uninstall # stoppen; je gegevens blijven staan
|
||||
```
|
||||
|
||||
Draait Server Up vanaf een registry-image, dan kan bijwerken ook met een knop in
|
||||
de interface — zie [docs/updates.md](docs/updates.md).
|
||||
|
||||
## Ontwikkelen
|
||||
|
||||
```bash
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.7.20-beta
|
||||
0.7.30-beta
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
| Document | Waarover, en wanneer je het nodig hebt |
|
||||
|---|---|
|
||||
| [installeren.md](installeren.md) | Installeren met één regel, alle opties van het script, bijwerken en verwijderen. |
|
||||
| [beveiliging.md](beveiliging.md) | Accounts, rollen, reverse-proxy-SSO en TLS. **Lees dit voordat je Server Up van buiten je netwerk bereikbaar maakt** — toegang tot de interface staat gelijk aan root op de host. |
|
||||
| [apps-maken.md](apps-maken.md) | Eigen apps toevoegen: het volledige `template.json`-formaat, de compose-syntaxis, apps aan elkaar koppelen en onderdelen optioneel maken. |
|
||||
| [netwerken.md](netwerken.md) | Stacks een eigen IP-adres geven in je LAN met macvlan of ipvlan. Inclusief de valkuil dat de host zijn eigen macvlan-containers niet bereikt. |
|
||||
|
|
|
|||
149
docs/installeren.md
Normal file
149
docs/installeren.md
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
# Installeren
|
||||
|
||||
## In één regel
|
||||
|
||||
```bash
|
||||
curl -fsSL https://git.ramonbesselink.nl/bes-r/server-up/raw/branch/main/install.sh | sh
|
||||
```
|
||||
|
||||
Dat installeert Server Up in `/opt/server-up`, bereikbaar op
|
||||
`http://localhost:5000`.
|
||||
|
||||
**Liever eerst zien wat je uitvoert?** Verstandig — je haalt een script binnen
|
||||
dat root-rechten gebruikt:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://git.ramonbesselink.nl/bes-r/server-up/raw/branch/main/install.sh -o install.sh
|
||||
less install.sh
|
||||
sh install.sh
|
||||
```
|
||||
|
||||
Wil je alleen weten wát het zou doen, zonder dat er iets verandert:
|
||||
|
||||
```bash
|
||||
sh install.sh --dry-run
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Wat het script doet
|
||||
|
||||
1. Controleert of je op Linux zit en of `curl` en `tar` aanwezig zijn
|
||||
2. Kijkt of Docker draait — zo niet, biedt het aan die te installeren via het
|
||||
officiële script van docker.com (het vraagt eerst)
|
||||
3. Kloont de repo naar `/opt/server-up` (of pakt het tar-archief uit als git
|
||||
ontbreekt)
|
||||
4. Maakt `.env` aan op basis van `.env.example`, met jouw poort en bind-adres
|
||||
5. Bouwt het image en start de container
|
||||
6. Wacht tot `/healthz` antwoordt en toont waar je terecht kunt
|
||||
|
||||
Het gebruikt alleen `sudo` waar dat nodig is. Kun je zelf al in de doelmap
|
||||
schrijven én `docker` aanroepen, dan blijft alles onder je eigen account.
|
||||
|
||||
---
|
||||
|
||||
## Opties
|
||||
|
||||
```
|
||||
--dir PAD Installatiemap (standaard: /opt/server-up)
|
||||
--port POORT Poort voor de webinterface (standaard: 5000)
|
||||
--bind ADRES Waarop de poort luistert (standaard: 127.0.0.1)
|
||||
--branch NAAM Branch om te installeren (standaard: main)
|
||||
--token TOKEN Toegangstoken, als je repo niet openbaar is
|
||||
--update Bijwerken naar de nieuwste versie
|
||||
--uninstall Stoppen en verwijderen (gegevens blijven staan)
|
||||
--yes Niets vragen
|
||||
--dry-run Alleen tonen wat er zou gebeuren
|
||||
```
|
||||
|
||||
### Voorbeelden
|
||||
|
||||
```bash
|
||||
# Standaard, alleen bereikbaar vanaf de server zelf
|
||||
sh install.sh
|
||||
|
||||
# Bereikbaar op je netwerk, andere poort
|
||||
sh install.sh --bind 0.0.0.0 --port 8080
|
||||
|
||||
# Ergens anders neerzetten
|
||||
sh install.sh --dir /srv/server-up
|
||||
|
||||
# De beta-branch, zonder vragen
|
||||
sh install.sh --branch dev --yes
|
||||
|
||||
# Uit een repo die niet openbaar is
|
||||
sh install.sh --token jouw-forgejo-token
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Meteen na het installeren
|
||||
|
||||
**Maak een beheerdersaccount aan.** Server Up beheert de Docker-daemon, en wie
|
||||
containers kan starten kan willekeurige mappen van de host mounten. Zolang er
|
||||
nog geen account is, kan iedereen die de pagina bereikt het claimen.
|
||||
|
||||
De poort staat standaard op `127.0.0.1`, dus alleen bereikbaar vanaf de server
|
||||
zelf. Wil je er van buitenaf bij, zet dan eerst een reverse proxy met TLS ervoor
|
||||
en pas daarna `BIND` aan:
|
||||
|
||||
```bash
|
||||
sed -i 's/^BIND=.*/BIND=0.0.0.0/' /opt/server-up/.env
|
||||
docker compose -f /opt/server-up/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
Zie [beveiliging.md](beveiliging.md) voor de reverse proxy, rollen en SSO.
|
||||
|
||||
---
|
||||
|
||||
## Bijwerken
|
||||
|
||||
```bash
|
||||
sh /opt/server-up/install.sh --update
|
||||
```
|
||||
|
||||
Haalt de nieuwste versie op, bouwt opnieuw en herstart. Je `.env` blijft staan,
|
||||
net als je stacks en gegevens.
|
||||
|
||||
Draait Server Up vanaf een registry-image, dan kun je ook bijwerken vanuit de
|
||||
interface zelf — zie [updates.md](updates.md).
|
||||
|
||||
---
|
||||
|
||||
## Verwijderen
|
||||
|
||||
```bash
|
||||
sh /opt/server-up/install.sh --uninstall
|
||||
```
|
||||
|
||||
Stopt en verwijdert de container. **Je gegevens blijven staan**: het
|
||||
docker-volume `su-data` (instellingen, accounts, auditlog) en je stackmappen
|
||||
onder `/opt/serverup`. Het script vertelt daarna hoe je die alsnog opruimt.
|
||||
|
||||
---
|
||||
|
||||
## Handmatig, zonder script
|
||||
|
||||
```bash
|
||||
git clone https://git.ramonbesselink.nl/bes-r/server-up.git /opt/server-up
|
||||
cd /opt/server-up
|
||||
cp .env.example .env
|
||||
$EDITOR .env
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Als er iets misgaat
|
||||
|
||||
| Melding | Wat er aan de hand is |
|
||||
|---|---|
|
||||
| `Geen root en geen sudo` | Draai als root, of kies met `--dir` een map waar je zelf in mag schrijven |
|
||||
| `Docker is geïnstalleerd maar de daemon reageert niet` | `systemctl start docker` |
|
||||
| `De docker-compose-plugin ontbreekt` | Installeer `docker-compose-plugin` via je pakketbeheerder |
|
||||
| `Ophalen mislukt. Is de repo openbaar?` | Gebruik `--token` met een Forgejo-token dat de repo mag lezen |
|
||||
| Reageert niet binnen anderhalve minuut | `docker compose -f /opt/server-up/docker-compose.yml logs --tail=50` |
|
||||
|
||||
Laadt de interface zonder opmaak, dan is het downloaden van de front-end-
|
||||
bestanden tijdens de build misgegaan. Server Up meldt dat bij het opstarten in
|
||||
zijn eigen log; opnieuw bouwen lost het meestal op.
|
||||
356
install.sh
Executable file
356
install.sh
Executable file
|
|
@ -0,0 +1,356 @@
|
|||
#!/bin/sh
|
||||
# Server Up — installeren, bijwerken of verwijderen op een Linux-server.
|
||||
#
|
||||
# curl -fsSL https://git.ramonbesselink.nl/bes-r/server-up/raw/branch/main/install.sh | sh
|
||||
#
|
||||
# Liever eerst lezen wat je uitvoert (aan te raden):
|
||||
#
|
||||
# curl -fsSL https://git.ramonbesselink.nl/bes-r/server-up/raw/branch/main/install.sh -o install.sh
|
||||
# less install.sh
|
||||
# sh install.sh
|
||||
#
|
||||
# POSIX sh, dus het werkt onder dash, ash en bash.
|
||||
|
||||
set -eu
|
||||
|
||||
SCRIPT_VERSIE="1.0"
|
||||
GIT_HOST="git.ramonbesselink.nl"
|
||||
GIT_PAD="bes-r/server-up"
|
||||
BRANCH="main"
|
||||
DOEL="/opt/server-up"
|
||||
POORT="5000"
|
||||
BIND="127.0.0.1"
|
||||
TOKEN=""
|
||||
JA=0
|
||||
DROOG=0
|
||||
ACTIE="install"
|
||||
|
||||
# ── Uitvoer ──────────────────────────────────────────────────────────────────
|
||||
# Kleuren alleen als we naar een terminal schrijven; in een pipe of logbestand
|
||||
# leveren escape-codes onleesbare rommel op.
|
||||
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
|
||||
C_OK=$(printf '\033[32m'); C_WAARSCHUW=$(printf '\033[33m')
|
||||
C_FOUT=$(printf '\033[31m'); C_KOP=$(printf '\033[1m'); C_UIT=$(printf '\033[0m')
|
||||
else
|
||||
C_OK=''; C_WAARSCHUW=''; C_FOUT=''; C_KOP=''; C_UIT=''
|
||||
fi
|
||||
|
||||
log() { printf '%s\n' " $*"; }
|
||||
stap() { printf '\n%s%s%s\n' "$C_KOP" "$*" "$C_UIT"; }
|
||||
goed() { printf ' %s✔%s %s\n' "$C_OK" "$C_UIT" "$*"; }
|
||||
waarschuw() { printf ' %s!%s %s\n' "$C_WAARSCHUW" "$C_UIT" "$*"; }
|
||||
fout() { printf '\n %s✖ %s%s\n\n' "$C_FOUT" "$*" "$C_UIT" >&2; exit 1; }
|
||||
# Bij --dry-run alleen waarschuwen: je wilt de hele voorvertoning zien, ook op
|
||||
# een machine waar Docker (nog) niet staat.
|
||||
fout_tenzij_droog() {
|
||||
if [ "$DROOG" -eq 1 ]; then waarschuw "$*"; else fout "$*"; fi
|
||||
}
|
||||
|
||||
heeft() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
# Voert uit, of toont alleen wat er zou gebeuren bij --dry-run.
|
||||
doe() {
|
||||
if [ "$DROOG" -eq 1 ]; then
|
||||
printf ' [droog] %s\n' "$*"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
vraag() {
|
||||
# vraag "Vraag?" -> 0 bij ja. Met --yes of zonder terminal: altijd ja.
|
||||
[ "$JA" -eq 1 ] && return 0
|
||||
[ -t 0 ] || return 0
|
||||
printf ' %s [J/n] ' "$1"
|
||||
read -r antwoord </dev/tty || return 0
|
||||
case "$antwoord" in [nN]*) return 1 ;; *) return 0 ;; esac
|
||||
}
|
||||
|
||||
gebruik() {
|
||||
cat <<UITLEG
|
||||
Server Up installatiescript ${SCRIPT_VERSIE}
|
||||
|
||||
Gebruik: install.sh [opties]
|
||||
|
||||
--dir PAD Installatiemap (standaard: ${DOEL})
|
||||
--port POORT Poort voor de webinterface (standaard: ${POORT})
|
||||
--bind ADRES Waarop de poort luistert (standaard: ${BIND})
|
||||
Gebruik 0.0.0.0 om er van buiten de server bij te kunnen,
|
||||
maar zet er dan wel een reverse proxy met TLS voor.
|
||||
--branch NAAM Branch om te installeren (standaard: ${BRANCH})
|
||||
--token TOKEN Toegangstoken, als je git-repo niet openbaar is
|
||||
--update Bijwerken naar de nieuwste versie
|
||||
--uninstall Stoppen en verwijderen (je gegevens blijven staan)
|
||||
--yes Niets vragen, overal ja op antwoorden
|
||||
--dry-run Alleen tonen wat er zou gebeuren
|
||||
--help Deze uitleg
|
||||
|
||||
Voorbeelden:
|
||||
sh install.sh
|
||||
sh install.sh --bind 0.0.0.0 --port 8080
|
||||
sh install.sh --update
|
||||
UITLEG
|
||||
}
|
||||
|
||||
# ── Argumenten ───────────────────────────────────────────────────────────────
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--dir) DOEL="${2:?--dir vereist een pad}"; shift 2 ;;
|
||||
--port) POORT="${2:?--port vereist een nummer}"; shift 2 ;;
|
||||
--bind) BIND="${2:?--bind vereist een adres}"; shift 2 ;;
|
||||
--branch) BRANCH="${2:?--branch vereist een naam}"; shift 2 ;;
|
||||
--token) TOKEN="${2:?--token vereist een waarde}"; shift 2 ;;
|
||||
--update) ACTIE="update"; shift ;;
|
||||
--uninstall) ACTIE="uninstall"; shift ;;
|
||||
--yes|-y) JA=1; shift ;;
|
||||
--dry-run) DROOG=1; shift ;;
|
||||
--help|-h) gebruik; exit 0 ;;
|
||||
*) fout "Onbekende optie: $1 (gebruik --help)" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$POORT" in ''|*[!0-9]*) fout "Ongeldige poort: $POORT" ;; esac
|
||||
|
||||
# ── Rechten ──────────────────────────────────────────────────────────────────
|
||||
# Alleen sudo gebruiken waar het echt nodig is: kan de gebruiker zelf al in de
|
||||
# doelmap schrijven en docker aanroepen, dan blijft alles onder zijn eigen account.
|
||||
SUDO=""
|
||||
bepaal_rechten() {
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
return 0
|
||||
fi
|
||||
bovenliggend="$(dirname "$DOEL")"
|
||||
if [ -w "$bovenliggend" ] || { [ -d "$DOEL" ] && [ -w "$DOEL" ]; }; then
|
||||
if docker info >/dev/null 2>&1; then
|
||||
goed "Draait onder je eigen account; sudo is niet nodig"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
if heeft sudo; then
|
||||
SUDO="sudo"
|
||||
log "sudo wordt gebruikt voor ${DOEL} en docker"
|
||||
else
|
||||
fout "Geen root en geen sudo. Draai als root, of kies met --dir een map waar je zelf in mag schrijven."
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Vereisten ────────────────────────────────────────────────────────────────
|
||||
controleer_systeem() {
|
||||
stap "Systeem controleren"
|
||||
bepaal_rechten
|
||||
[ "$(uname -s)" = "Linux" ] || waarschuw "Dit script is voor Linux; op $(uname -s) is het onbeproefd."
|
||||
for hulp in curl tar; do
|
||||
heeft "$hulp" || fout "'$hulp' ontbreekt. Installeer het en probeer opnieuw."
|
||||
done
|
||||
goed "$(uname -s) $(uname -m)"
|
||||
}
|
||||
|
||||
controleer_docker() {
|
||||
if heeft docker; then
|
||||
if $SUDO docker info >/dev/null 2>&1; then
|
||||
goed "Docker draait ($($SUDO docker version --format '{{.Server.Version}}' 2>/dev/null || echo '?'))"
|
||||
else
|
||||
fout_tenzij_droog "Docker is geïnstalleerd maar de daemon reageert niet. Start hem met: systemctl start docker"
|
||||
fi
|
||||
else
|
||||
waarschuw "Docker is niet geïnstalleerd."
|
||||
if vraag "Nu installeren via het officiële script van docker.com?"; then
|
||||
stap "Docker installeren"
|
||||
doe sh -c "curl -fsSL https://get.docker.com | $SUDO sh"
|
||||
doe $SUDO systemctl enable --now docker || true
|
||||
heeft docker || [ "$DROOG" -eq 1 ] || fout "Installeren van Docker is niet gelukt."
|
||||
goed "Docker geïnstalleerd"
|
||||
else
|
||||
fout_tenzij_droog "Docker is vereist. Zie https://docs.docker.com/engine/install/"
|
||||
fi
|
||||
fi
|
||||
|
||||
if $SUDO docker compose version >/dev/null 2>&1; then
|
||||
goed "Compose-plugin aanwezig"
|
||||
else
|
||||
fout_tenzij_droog "De docker-compose-plugin ontbreekt. Installeer 'docker-compose-plugin' via je pakketbeheerder."
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Broncode ophalen ─────────────────────────────────────────────────────────
|
||||
bron_url() {
|
||||
if [ -n "$TOKEN" ]; then
|
||||
printf 'https://%s@%s/%s.git' "$TOKEN" "$GIT_HOST" "$GIT_PAD"
|
||||
else
|
||||
printf 'https://%s/%s.git' "$GIT_HOST" "$GIT_PAD"
|
||||
fi
|
||||
}
|
||||
|
||||
haal_broncode() {
|
||||
stap "Broncode ophalen"
|
||||
if [ -d "$DOEL/.git" ]; then
|
||||
log "Bestaande installatie bijwerken in $DOEL"
|
||||
doe $SUDO git -C "$DOEL" fetch --depth 1 origin "$BRANCH"
|
||||
doe $SUDO git -C "$DOEL" reset --hard "origin/$BRANCH"
|
||||
goed "Bijgewerkt naar de nieuwste $BRANCH"
|
||||
return 0
|
||||
fi
|
||||
|
||||
doe $SUDO mkdir -p "$DOEL"
|
||||
if heeft git; then
|
||||
# Zonder .git kunnen we later niet netjes bijwerken, dus git heeft de
|
||||
# voorkeur boven een los archief.
|
||||
if doe $SUDO git clone --depth 1 --branch "$BRANCH" "$(bron_url)" "$DOEL"; then
|
||||
goed "Gekloond naar $DOEL"
|
||||
return 0
|
||||
fi
|
||||
waarschuw "Klonen mislukte; ik probeer het archief."
|
||||
fi
|
||||
|
||||
archief="https://${GIT_HOST}/${GIT_PAD}/archive/${BRANCH}.tar.gz"
|
||||
log "Downloaden: $archief"
|
||||
if [ "$DROOG" -eq 0 ]; then
|
||||
tijdelijk="$(mktemp -d)"
|
||||
curl -fsSL ${TOKEN:+-H "Authorization: token $TOKEN"} "$archief" \
|
||||
| tar xz -C "$tijdelijk" --strip-components=1 \
|
||||
|| fout "Ophalen mislukt. Is de repo openbaar? Zo niet, gebruik --token."
|
||||
$SUDO cp -r "$tijdelijk/." "$DOEL/"
|
||||
rm -rf "$tijdelijk"
|
||||
fi
|
||||
goed "Uitgepakt naar $DOEL"
|
||||
}
|
||||
|
||||
# ── Instellingen ─────────────────────────────────────────────────────────────
|
||||
schrijf_env() {
|
||||
stap "Instellingen"
|
||||
env_bestand="$DOEL/.env"
|
||||
if [ -f "$env_bestand" ]; then
|
||||
goed "Bestaande .env blijft ongemoeid"
|
||||
# BIND en PORT wel bijwerken als ze expliciet zijn meegegeven.
|
||||
return 0
|
||||
fi
|
||||
if [ "$DROOG" -eq 1 ]; then
|
||||
printf ' [droog] .env aanmaken met BIND=%s PORT=%s\n' "$BIND" "$POORT"
|
||||
return 0
|
||||
fi
|
||||
if [ -f "$DOEL/.env.example" ]; then
|
||||
$SUDO cp "$DOEL/.env.example" "$env_bestand"
|
||||
else
|
||||
printf '' | $SUDO tee "$env_bestand" >/dev/null
|
||||
fi
|
||||
$SUDO sh -c "sed -i '/^BIND=/d;/^PORT=/d' '$env_bestand'"
|
||||
$SUDO sh -c "printf 'BIND=%s\nPORT=%s\n' '$BIND' '$POORT' >> '$env_bestand'"
|
||||
goed "Aangemaakt: $env_bestand (BIND=$BIND, PORT=$POORT)"
|
||||
}
|
||||
|
||||
# ── Starten ──────────────────────────────────────────────────────────────────
|
||||
start() {
|
||||
stap "Bouwen en starten"
|
||||
if [ "$DROOG" -eq 0 ] && [ ! -f "$DOEL/docker-compose.yml" ]; then
|
||||
fout "Geen docker-compose.yml in $DOEL — het ophalen van de broncode is misgegaan."
|
||||
fi
|
||||
log "Dit duurt de eerste keer enkele minuten."
|
||||
doe $SUDO docker compose -f "$DOEL/docker-compose.yml" --project-directory "$DOEL" \
|
||||
up -d --build --remove-orphans
|
||||
goed "Container gestart"
|
||||
}
|
||||
|
||||
wacht_op_gereed() {
|
||||
[ "$DROOG" -eq 1 ] && return 0
|
||||
stap "Wachten tot Server Up reageert"
|
||||
printf ' '
|
||||
i=0
|
||||
while [ "$i" -lt 45 ]; do
|
||||
if curl -fsS "http://127.0.0.1:${POORT}/healthz" >/dev/null 2>&1; then
|
||||
# Spatie na de dubbele punt toestaan: of json compact is,
|
||||
# hangt af van de Flask-versie.
|
||||
versie="$(curl -fsS "http://127.0.0.1:${POORT}/healthz" 2>/dev/null \
|
||||
| sed -n 's/.*"version" *: *"\([^"]*\)".*/\1/p')"
|
||||
printf '\n'
|
||||
goed "Server Up${versie:+ }${versie:-} draait"
|
||||
return 0
|
||||
fi
|
||||
# Een puntje per poging, zodat je ziet dat er nog iets gebeurt.
|
||||
printf '.'
|
||||
i=$((i + 1))
|
||||
sleep 2
|
||||
done
|
||||
printf '\n'
|
||||
waarschuw "Server Up reageerde niet binnen anderhalve minuut."
|
||||
log "Bekijk de logs met: $SUDO docker compose -f $DOEL/docker-compose.yml logs --tail=50"
|
||||
return 1
|
||||
}
|
||||
|
||||
afsluiting() {
|
||||
if [ "$BIND" = "0.0.0.0" ]; then
|
||||
ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
||||
adres="http://${ip:-<server-ip>}:${POORT}"
|
||||
else
|
||||
adres="http://localhost:${POORT}"
|
||||
fi
|
||||
|
||||
printf '\n%s────────────────────────────────────────────────────────%s\n' "$C_KOP" "$C_UIT"
|
||||
printf '%s Server Up draait op %s%s\n' "$C_OK" "$adres" "$C_UIT"
|
||||
printf '%s────────────────────────────────────────────────────────%s\n\n' "$C_KOP" "$C_UIT"
|
||||
|
||||
printf ' %sMaak nu meteen een beheerdersaccount aan.%s\n' "$C_WAARSCHUW" "$C_UIT"
|
||||
printf ' Zolang dat niet gebeurd is, kan iedereen die deze pagina\n'
|
||||
printf ' bereikt het account claimen.\n\n'
|
||||
|
||||
if [ "$BIND" = "127.0.0.1" ]; then
|
||||
printf ' Alleen bereikbaar vanaf deze server zelf. Van buitenaf erbij?\n'
|
||||
printf ' 1. Zet een reverse proxy met TLS ervoor\n'
|
||||
printf ' 2. sed -i "s/^BIND=.*/BIND=0.0.0.0/" %s/.env\n' "$DOEL"
|
||||
printf ' 3. %s docker compose -f %s/docker-compose.yml up -d\n\n' "${SUDO:-}" "$DOEL"
|
||||
else
|
||||
printf ' %sDe interface staat open op je netwerk. Server Up beheert%s\n' "$C_WAARSCHUW" "$C_UIT"
|
||||
printf ' Docker als root, dus zet er een reverse proxy met TLS voor.\n\n'
|
||||
fi
|
||||
|
||||
printf ' Bijwerken: sh %s/install.sh --update\n' "$DOEL"
|
||||
printf ' Logs: %s docker compose -f %s/docker-compose.yml logs -f\n' "${SUDO:-}" "$DOEL"
|
||||
printf ' Documentatie: %s/README.md\n\n' "$DOEL"
|
||||
}
|
||||
|
||||
verwijderen() {
|
||||
stap "Server Up verwijderen"
|
||||
[ -d "$DOEL" ] || fout "Niets gevonden in $DOEL"
|
||||
if ! vraag "Container stoppen en verwijderen? Je gegevens blijven staan."; then
|
||||
log "Afgebroken."
|
||||
exit 0
|
||||
fi
|
||||
doe $SUDO docker compose -f "$DOEL/docker-compose.yml" --project-directory "$DOEL" \
|
||||
down --remove-orphans
|
||||
goed "Container gestopt en verwijderd"
|
||||
printf '\n'
|
||||
log "Je gegevens staan nog in het docker-volume 'su-data' en in je stackmappen."
|
||||
log "Alles definitief weg? Dan:"
|
||||
log " $SUDO docker volume rm su-data"
|
||||
log " $SUDO rm -rf $DOEL"
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
# ── Hoofdprogramma ───────────────────────────────────────────────────────────
|
||||
printf '\n%sServer Up — installatiescript %s%s\n' "$C_KOP" "$SCRIPT_VERSIE" "$C_UIT"
|
||||
[ "$DROOG" -eq 1 ] && waarschuw "Proefdraai: er wordt niets gewijzigd."
|
||||
|
||||
case "$ACTIE" in
|
||||
uninstall)
|
||||
bepaal_rechten
|
||||
verwijderen
|
||||
;;
|
||||
update)
|
||||
[ -d "$DOEL" ] || fout "Geen installatie gevonden in $DOEL. Installeer eerst zonder --update."
|
||||
controleer_systeem
|
||||
controleer_docker
|
||||
haal_broncode
|
||||
start
|
||||
wacht_op_gereed || true
|
||||
printf '\n'
|
||||
goed "Bijgewerkt."
|
||||
;;
|
||||
install)
|
||||
controleer_systeem
|
||||
controleer_docker
|
||||
haal_broncode
|
||||
schrijf_env
|
||||
start
|
||||
wacht_op_gereed || true
|
||||
afsluiting
|
||||
;;
|
||||
esac
|
||||
111
tests/test_install_script.py
Normal file
111
tests/test_install_script.py
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
"""Het installatiescript: syntaxis, opties en de dingen die stuk mogen gaan.
|
||||
|
||||
Een installer die je met `curl | sh` draait moet in elk geval syntactisch
|
||||
kloppen onder de shells die distributies als /bin/sh gebruiken, en niet
|
||||
stilzwijgend het verkeerde doen bij een typefout in de opties.
|
||||
"""
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
WORTEL = Path(__file__).resolve().parent.parent
|
||||
SCRIPT = WORTEL / "install.sh"
|
||||
|
||||
|
||||
def _draai(*args, **kw):
|
||||
return subprocess.run(["sh", str(SCRIPT), *args], capture_output=True,
|
||||
text=True, timeout=60, **kw)
|
||||
|
||||
|
||||
def test_script_bestaat_en_is_uitvoerbaar():
|
||||
assert SCRIPT.is_file()
|
||||
assert SCRIPT.stat().st_mode & 0o111, "niet uitvoerbaar"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("shell", ["sh", "dash", "bash"])
|
||||
def test_syntaxis_klopt_onder(shell):
|
||||
"""Debian en Ubuntu gebruiken dash als /bin/sh; bashismen breken daar."""
|
||||
if not shutil.which(shell):
|
||||
pytest.skip(f"{shell} niet aanwezig")
|
||||
r = subprocess.run([shell, "-n", str(SCRIPT)], capture_output=True, text=True)
|
||||
assert r.returncode == 0, r.stderr
|
||||
|
||||
|
||||
def test_geen_bashismen():
|
||||
tekst = SCRIPT.read_text(encoding="utf-8")
|
||||
for patroon, uitleg in [
|
||||
(r"\[\[", "[[ ]] is bash-only"),
|
||||
(r"^\s*declare ", "declare is bash-only"),
|
||||
(r"\$\{[A-Za-z_]+\[", "arrays zijn bash-only"),
|
||||
(r"==", "== in test is bash-only"),
|
||||
]:
|
||||
treffers = [l for l in tekst.splitlines()
|
||||
if re.search(patroon, l) and not l.strip().startswith("#")]
|
||||
assert not treffers, f"{uitleg}: {treffers[:2]}"
|
||||
|
||||
|
||||
def test_help_werkt_en_noemt_alle_opties():
|
||||
r = _draai("--help")
|
||||
assert r.returncode == 0
|
||||
for optie in ("--dir", "--port", "--bind", "--branch", "--token",
|
||||
"--update", "--uninstall", "--yes", "--dry-run"):
|
||||
assert optie in r.stdout, f"{optie} niet gedocumenteerd in --help"
|
||||
|
||||
|
||||
def test_onbekende_optie_stopt():
|
||||
r = _draai("--bestaatniet")
|
||||
assert r.returncode != 0
|
||||
assert "Onbekende optie" in r.stderr
|
||||
|
||||
|
||||
@pytest.mark.parametrize("poort", ["abc", "", "80a", "-1"])
|
||||
def test_ongeldige_poort_stopt(poort):
|
||||
r = _draai("--port", poort)
|
||||
assert r.returncode != 0
|
||||
# Een lege waarde valt al af bij het uitlezen van de optie, de rest bij de
|
||||
# controle daarna; beide meldingen gaan over de poort.
|
||||
assert "poort" in r.stderr.lower() or "port" in r.stderr.lower()
|
||||
|
||||
|
||||
def test_update_zonder_installatie_zegt_dat(tmp_path):
|
||||
r = _draai("--update", "--dir", str(tmp_path / "bestaatniet"))
|
||||
assert r.returncode != 0
|
||||
assert "Geen installatie gevonden" in r.stderr
|
||||
|
||||
|
||||
def test_dry_run_wijzigt_niets(tmp_path):
|
||||
doel = tmp_path / "server-up"
|
||||
r = _draai("--dry-run", "--dir", str(doel), "--yes")
|
||||
assert not doel.exists(), "proefdraai heeft toch iets aangemaakt"
|
||||
assert "[droog]" in r.stdout
|
||||
|
||||
|
||||
def test_dry_run_toont_de_hele_gang_van_zaken(tmp_path):
|
||||
"""Ook op een machine zonder Docker moet je de voorvertoning kunnen zien."""
|
||||
r = _draai("--dry-run", "--dir", str(tmp_path / "su"), "--yes")
|
||||
for kop in ("Systeem controleren", "Broncode ophalen", "Instellingen",
|
||||
"Bouwen en starten"):
|
||||
assert kop in r.stdout, f"stap ontbreekt: {kop}"
|
||||
|
||||
|
||||
def test_verwijst_naar_het_juiste_git_domein():
|
||||
tekst = SCRIPT.read_text(encoding="utf-8")
|
||||
assert 'GIT_HOST="git.ramonbesselink.nl"' in tekst
|
||||
|
||||
|
||||
def test_compose_wordt_met_expliciet_bestand_aangeroepen():
|
||||
"""`--project-directory` alleen laat compose het bestand nog in de huidige
|
||||
map zoeken; met `curl | sh` is dat je thuismap."""
|
||||
for regel in SCRIPT.read_text(encoding="utf-8").splitlines():
|
||||
if "docker compose" in regel and ("up -d" in regel or "down " in regel):
|
||||
assert "-f " in regel, f"geen expliciet compose-bestand: {regel.strip()}"
|
||||
|
||||
|
||||
def test_readme_en_docs_noemen_dezelfde_installatieregel():
|
||||
regel = ("curl -fsSL https://git.ramonbesselink.nl/bes-r/server-up/"
|
||||
"raw/branch/main/install.sh")
|
||||
assert regel in (WORTEL / "README.md").read_text(encoding="utf-8")
|
||||
assert regel in (WORTEL / "docs" / "installeren.md").read_text(encoding="utf-8")
|
||||
Loading…
Reference in a new issue