Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/scripts/preview_slug.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
DOMAIN = "tfminecraft.net"
MAX_LENGTH = 40
# Hostnames with their own DNS records or tunnel rules; a preview there could never be reached.
RESERVED = {"www", "dev", "api", "mail", "map", "admin", "staff", "play", "mc", "status", "preview", "t3"}
RESERVED = {"www", "dev", "api", "mail", "map", "admin", "staff", "play", "mc", "status", "preview", "t3", "portainer"}
VALID = re.compile(r"[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?")


Expand Down
3 changes: 3 additions & 0 deletions docker-compose.staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: tfmc-staging

services:
backend:
# Reaps the renderer's Chromium processes.
init: true
build:
context: .
dockerfile: backend/Dockerfile
Expand Down Expand Up @@ -37,6 +39,7 @@ services:
restart: unless-stopped

frontend:
init: true
build:
context: .
dockerfile: frontend/Dockerfile
Expand Down
3 changes: 3 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
services:
backend:
# Reaps the renderer's Chromium processes.
init: true
build:
context: .
dockerfile: backend/Dockerfile
Expand All @@ -17,6 +19,7 @@ services:
restart: always

frontend:
init: true
build:
context: .
dockerfile: frontend/Dockerfile
Expand Down
52 changes: 52 additions & 0 deletions scripts/ps-compose.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/bin/bash
# Limited docker compose for the ProvinceSystem main and dev sites. Installed as root's
# /usr/local/sbin/ps-compose and run as tfmc (docker group) through sudo, so people
# without docker access can manage the two sites but not mount the host.
set -euo pipefail

usage() {
echo "usage: ps-compose <main|dev> <ps|logs|start|stop|restart|up|build|pull|exec|top> [args...]" >&2
exit 1
}

[[ $# -ge 2 ]] || usage
target="$1"
shift
cmd="$1"
shift

case "$target" in
main)
dir=/home/tfmc/ProvinceSystem
files=(-f "$dir/docker-compose.yml" -f "$dir/docker-compose.override.yml")
;;
dev)
dir=/home/tfmc/tfmc-staging
files=(-f "$dir/docker-compose.staging.yml" -f "$dir/docker-compose.override.yml")
;;
*)
usage
;;
esac

case "$cmd" in
ps|logs|start|stop|restart|up|build|pull|exec|top) ;;
*)
echo "subcommand not allowed: $cmd" >&2
usage
;;
esac

for arg in "$@"; do
# After `logs`, -f is --follow; elsewhere it could select a compose file.
[[ $cmd == logs && $arg == -f ]] && continue
case "$arg" in
-f|--file|--file=*|-H|--host|--host=*|--context|--context=*|--project-directory|--project-directory=*|--project-name|--project-name=*|--env-file|--env-file=*|--privileged|--privileged=*|-v|--volume|--volume=*|--mount|--mount=*)
echo "argument not allowed: $arg" >&2
exit 1
;;
esac
done

cd "$dir"
exec docker compose --project-directory "$dir" "${files[@]}" "$cmd" "$@"
10 changes: 10 additions & 0 deletions scripts/ps-image-prune.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Unit]
Description=Remove untagged ProvinceSystem website builds and week-old build cache
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
Nice=10
IOSchedulingClass=idle
ExecStart=/usr/local/sbin/ps-image-prune
8 changes: 8 additions & 0 deletions scripts/ps-image-prune.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
# Installed as root's /usr/local/sbin/ps-image-prune; run weekly by ps-image-prune.timer.
# Untagged local builds of the main and dev sites, plus build cache unused for a week.
# Tagged images used by the running containers are left alone. Preview images are handled by ps-preview.
set -euo pipefail
docker image prune -f --filter "label=com.docker.compose.project=provincesystem"
docker image prune -f --filter "label=com.docker.compose.project=tfmc-staging"
docker builder prune -f --filter "until=168h"
10 changes: 10 additions & 0 deletions scripts/ps-image-prune.timer
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Unit]
Description=Weekly clean-up of old ProvinceSystem website builds

[Timer]
OnCalendar=Mon *-*-* 05:00:00
AccuracySec=1min
Persistent=true

[Install]
WantedBy=timers.target
13 changes: 13 additions & 0 deletions scripts/ps-preview-reap.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Unit]
Description=Stop expired ProvinceSystem website previews
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
User=tfmc
Group=tfmc
SupplementaryGroups=docker
Nice=10
IOSchedulingClass=idle
ExecStart=/usr/local/sbin/ps-preview reap
10 changes: 10 additions & 0 deletions scripts/ps-preview-reap.timer
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Unit]
Description=Stop expired ProvinceSystem website previews every minute

[Timer]
OnCalendar=*-*-* *:*:00
AccuracySec=5s
Persistent=true

[Install]
WantedBy=timers.target
26 changes: 26 additions & 0 deletions scripts/ps-preview-router.Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
admin off
}

:80 {
@preview header_regexp host Host ^([a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?)\.tfminecraft\.net$
handle @preview {
header X-Robots-Tag "noindex, nofollow"
handle_path /api/* {
reverse_proxy pv-{re.host.1}-backend:8000 {
header_up X-Forwarded-Proto https
}
}
handle {
reverse_proxy pv-{re.host.1}-frontend:3000 {
header_up X-Forwarded-Proto https
}
}
}
handle {
respond "Not found" 404
}
handle_errors 502 503 504 {
respond "This preview isn't running. It has probably expired; redeploy it from GitHub Actions." 503
}
}
11 changes: 11 additions & 0 deletions scripts/ps-preview-ssh.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
# Forced command for the Preview workflow's key on psdeploy. Installed as root's
# /usr/local/sbin/ps-preview-ssh; sudoers lets psdeploy run ps-preview as tfmc:
# restrict,command="/usr/local/sbin/ps-preview-ssh" ssh-ed25519 ... github-preview-deploy
set -euo pipefail
read -r -a args <<< "${SSH_ORIGINAL_COMMAND:-}"
case "${args[0]:-}" in
up|down|list) ;;
*) echo "usage: up <slug> <sha> | down <slug> | list" >&2; exit 2 ;;
esac
exec sudo -n -u tfmc /usr/local/sbin/ps-preview "${args[@]}"
Loading
Loading