replace per-app deploy scripts with generic deploy.sh

Dispatch now validates the command format and routes to a single
deploy.sh that handles any app. This fixes first-deploy failures
for new apps (dispatch no longer needs a static case list) and
simplifies new-app.sh (no deploy script or dispatch update needed).

Also adds input validation to new-app.sh, set -euo pipefail to
deploy scripts, and dnf module reset before nodejs install.
This commit is contained in:
2026-03-13 17:00:59 -04:00
parent 587b671e61
commit 7d5f0920b7
7 changed files with 35 additions and 41 deletions
+7 -11
View File
@@ -11,9 +11,8 @@ docker/ # One directory per app, each with a compose.yml
nginx/ # Nginx Proxy Manager (reverse proxy + TLS)
timothykim.net/ # timothykim.net static site
scripts/
deploy-dispatch.sh # SSH command dispatcher (routes to per-app deploy scripts)
deploy-nginx.sh # Deploy script for nginx
deploy-timothykim.sh # Deploy script for timothykim.net
deploy-dispatch.sh # SSH command dispatcher (validates and routes deploy commands)
deploy.sh # Generic deploy script (git pull + docker compose up)
new-app.sh # Scaffolding script to add a new app
setup.sh # One-time server provisioning script
.gitea/workflows/ # Per-app deploy workflows triggered by path changes
@@ -43,16 +42,15 @@ base64 /path/to/git-crypt-key
## How deploys work
Each app has three components:
Each app has two components:
1. **Deploy script** (`scripts/deploy-<app>.sh`) -- pulls the latest repo
changes, then runs `docker compose pull && up -d` for that app.
2. **Gitea Actions workflow** (`.gitea/workflows/deploy-<app>.yml`) -- triggers
1. **Gitea Actions workflow** (`.gitea/workflows/deploy-<app>.yml`) -- triggers
on pushes to `main` when files under `docker/<app>/` change. SSHes into the
server as the `deploy` user to trigger the deploy.
3. **SSH dispatcher** (`scripts/deploy-dispatch.sh`) -- the `deploy` user's
2. **SSH dispatcher** (`scripts/deploy-dispatch.sh`) -- the `deploy` user's
`authorized_keys` is locked to this script via a `command=` directive. It
reads `SSH_ORIGINAL_COMMAND` to route to the correct per-app deploy script.
validates the command and runs `scripts/deploy.sh`, which pulls the latest
repo changes and runs `docker compose pull && up -d` for that app.
This means:
@@ -72,9 +70,7 @@ This creates:
- `docker/<app-name>/compose.yml` -- a starter compose file on the `shared`
network
- `scripts/deploy-<app-name>.sh` -- the deploy script
- `.gitea/workflows/deploy-<app-name>.yml` -- the Gitea Actions workflow
- A new case in `scripts/deploy-dispatch.sh`
After running the script:
+6 -9
View File
@@ -1,12 +1,9 @@
#!/bin/bash
set -euo pipefail
case "$SSH_ORIGINAL_COMMAND" in
deploy-nginx) sudo /opt/hantim/scripts/deploy-nginx.sh ;;
deploy-timothykim) sudo /opt/hantim/scripts/deploy-timothykim.sh ;;
*)
echo "Unknown command: $SSH_ORIGINAL_COMMAND"
echo "Available: deploy-nginx, deploy-timothykim"
exit 1
;;
esac
if [[ "$SSH_ORIGINAL_COMMAND" =~ ^deploy-[a-zA-Z0-9._-]+$ ]]; then
sudo /opt/hantim/scripts/deploy.sh "$SSH_ORIGINAL_COMMAND"
else
echo "Unknown command: $SSH_ORIGINAL_COMMAND"
exit 1
fi
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
cd /opt/hantim && git pull && cd docker/nginx && docker compose pull && docker compose up -d
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
cd /opt/hantim && git pull && cd docker/timothykim.net && docker compose pull && docker compose up -d
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
set -euo pipefail
APP="${1#deploy-}"
cd /opt/hantim
git pull
cd "docker/$APP"
docker compose pull
docker compose up -d
+6 -13
View File
@@ -8,6 +8,12 @@ if [ -z "${1:-}" ]; then
fi
APP="$1"
if ! [[ "$APP" =~ ^[a-zA-Z0-9][a-zA-Z0-9._-]*$ ]]; then
echo "Error: app name must be alphanumeric (hyphens, dots, underscores allowed)."
exit 1
fi
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
if [ -d "$REPO_ROOT/docker/$APP" ]; then
@@ -30,17 +36,6 @@ networks:
external: true
EOF
echo "Creating scripts/deploy-$APP.sh..."
cat > "$REPO_ROOT/scripts/deploy-$APP.sh" <<EOF
#!/bin/bash
cd /opt/hantim && git pull && cd docker/$APP && docker compose pull && docker compose up -d
EOF
chmod +x "$REPO_ROOT/scripts/deploy-$APP.sh"
echo "Adding $APP to deploy-dispatch.sh..."
sed -i "/^ \*)$/i\\ deploy-$APP) sudo /opt/hantim/scripts/deploy-$APP.sh ;;" \
"$REPO_ROOT/scripts/deploy-dispatch.sh"
echo "Creating .gitea/workflows/deploy-$APP.yml..."
mkdir -p "$REPO_ROOT/.gitea/workflows"
cat > "$REPO_ROOT/.gitea/workflows/deploy-$APP.yml" <<'OUTER'
@@ -69,9 +64,7 @@ sed -i "s/APP_PLACEHOLDER/$APP/g" "$REPO_ROOT/.gitea/workflows/deploy-$APP.yml"
echo ""
echo "Done! Files created:"
echo " - docker/$APP/compose.yml"
echo " - scripts/deploy-$APP.sh"
echo " - .gitea/workflows/deploy-$APP.yml"
echo " - Updated scripts/deploy-dispatch.sh"
echo ""
echo "Next steps:"
echo " 1. Edit docker/$APP/compose.yml to fit your app"
+8 -4
View File
@@ -33,6 +33,7 @@ dnf upgrade -y
echo "==> Installing dependencies..."
dnf install -y git-crypt
dnf module reset -y nodejs
dnf module install -y nodejs:20/common
BW_CLI="$(npm config get prefix)/bin/bw"
@@ -44,7 +45,8 @@ fi
# --- Unlock git-crypt via Bitwarden ---
echo "==> Restoring git-tracked files..."
cd "$REPO_DIR" && git checkout -- .
cd "$REPO_DIR"
git checkout -- .
echo "==> Unlocking git-crypt via Bitwarden..."
echo " Log in to Bitwarden when prompted."
@@ -89,7 +91,7 @@ chown -R deploy:deploy /home/deploy/.ssh
echo "==> Configuring sudo for deploy user..."
cat > /etc/sudoers.d/deploy <<SUDOERS
deploy ALL=(root) NOPASSWD: /opt/hantim/scripts/deploy-*.sh
deploy ALL=(root) NOPASSWD: /opt/hantim/scripts/deploy.sh
SUDOERS
chmod 440 /etc/sudoers.d/deploy
@@ -102,8 +104,10 @@ echo "==> Logging into Gitea registry..."
echo "Run manually: docker login git.timothykim.net"
echo "==> Starting services..."
cd "$REPO_DIR/docker/nginx" && docker compose up -d
cd "$REPO_DIR/docker/timothykim.net" && docker compose up -d
cd "$REPO_DIR/docker/nginx"
docker compose up -d
cd "$REPO_DIR/docker/timothykim.net"
docker compose up -d
echo "==> Done. Don't forget to:"
echo " 1. Add the deploy SSH public key to /home/deploy/.ssh/authorized_keys"