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) nginx/ # Nginx Proxy Manager (reverse proxy + TLS)
timothykim.net/ # timothykim.net static site timothykim.net/ # timothykim.net static site
scripts/ scripts/
deploy-dispatch.sh # SSH command dispatcher (routes to per-app deploy scripts) deploy-dispatch.sh # SSH command dispatcher (validates and routes deploy commands)
deploy-nginx.sh # Deploy script for nginx deploy.sh # Generic deploy script (git pull + docker compose up)
deploy-timothykim.sh # Deploy script for timothykim.net
new-app.sh # Scaffolding script to add a new app new-app.sh # Scaffolding script to add a new app
setup.sh # One-time server provisioning script setup.sh # One-time server provisioning script
.gitea/workflows/ # Per-app deploy workflows triggered by path changes .gitea/workflows/ # Per-app deploy workflows triggered by path changes
@@ -43,16 +42,15 @@ base64 /path/to/git-crypt-key
## How deploys work ## How deploys work
Each app has three components: Each app has two components:
1. **Deploy script** (`scripts/deploy-<app>.sh`) -- pulls the latest repo 1. **Gitea Actions workflow** (`.gitea/workflows/deploy-<app>.yml`) -- triggers
changes, then runs `docker compose pull && up -d` for that app.
2. **Gitea Actions workflow** (`.gitea/workflows/deploy-<app>.yml`) -- triggers
on pushes to `main` when files under `docker/<app>/` change. SSHes into the on pushes to `main` when files under `docker/<app>/` change. SSHes into the
server as the `deploy` user to trigger the deploy. 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 `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: This means:
@@ -72,9 +70,7 @@ This creates:
- `docker/<app-name>/compose.yml` -- a starter compose file on the `shared` - `docker/<app-name>/compose.yml` -- a starter compose file on the `shared`
network network
- `scripts/deploy-<app-name>.sh` -- the deploy script
- `.gitea/workflows/deploy-<app-name>.yml` -- the Gitea Actions workflow - `.gitea/workflows/deploy-<app-name>.yml` -- the Gitea Actions workflow
- A new case in `scripts/deploy-dispatch.sh`
After running the script: After running the script:
+4 -7
View File
@@ -1,12 +1,9 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
case "$SSH_ORIGINAL_COMMAND" in if [[ "$SSH_ORIGINAL_COMMAND" =~ ^deploy-[a-zA-Z0-9._-]+$ ]]; then
deploy-nginx) sudo /opt/hantim/scripts/deploy-nginx.sh ;; sudo /opt/hantim/scripts/deploy.sh "$SSH_ORIGINAL_COMMAND"
deploy-timothykim) sudo /opt/hantim/scripts/deploy-timothykim.sh ;; else
*)
echo "Unknown command: $SSH_ORIGINAL_COMMAND" echo "Unknown command: $SSH_ORIGINAL_COMMAND"
echo "Available: deploy-nginx, deploy-timothykim"
exit 1 exit 1
;; fi
esac
-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 fi
APP="$1" 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)" REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
if [ -d "$REPO_ROOT/docker/$APP" ]; then if [ -d "$REPO_ROOT/docker/$APP" ]; then
@@ -30,17 +36,6 @@ networks:
external: true external: true
EOF 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..." echo "Creating .gitea/workflows/deploy-$APP.yml..."
mkdir -p "$REPO_ROOT/.gitea/workflows" mkdir -p "$REPO_ROOT/.gitea/workflows"
cat > "$REPO_ROOT/.gitea/workflows/deploy-$APP.yml" <<'OUTER' 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 ""
echo "Done! Files created:" echo "Done! Files created:"
echo " - docker/$APP/compose.yml" echo " - docker/$APP/compose.yml"
echo " - scripts/deploy-$APP.sh"
echo " - .gitea/workflows/deploy-$APP.yml" echo " - .gitea/workflows/deploy-$APP.yml"
echo " - Updated scripts/deploy-dispatch.sh"
echo "" echo ""
echo "Next steps:" echo "Next steps:"
echo " 1. Edit docker/$APP/compose.yml to fit your app" 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..." echo "==> Installing dependencies..."
dnf install -y git-crypt dnf install -y git-crypt
dnf module reset -y nodejs
dnf module install -y nodejs:20/common dnf module install -y nodejs:20/common
BW_CLI="$(npm config get prefix)/bin/bw" BW_CLI="$(npm config get prefix)/bin/bw"
@@ -44,7 +45,8 @@ fi
# --- Unlock git-crypt via Bitwarden --- # --- Unlock git-crypt via Bitwarden ---
echo "==> Restoring git-tracked files..." echo "==> Restoring git-tracked files..."
cd "$REPO_DIR" && git checkout -- . cd "$REPO_DIR"
git checkout -- .
echo "==> Unlocking git-crypt via Bitwarden..." echo "==> Unlocking git-crypt via Bitwarden..."
echo " Log in to Bitwarden when prompted." echo " Log in to Bitwarden when prompted."
@@ -89,7 +91,7 @@ chown -R deploy:deploy /home/deploy/.ssh
echo "==> Configuring sudo for deploy user..." echo "==> Configuring sudo for deploy user..."
cat > /etc/sudoers.d/deploy <<SUDOERS 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 SUDOERS
chmod 440 /etc/sudoers.d/deploy chmod 440 /etc/sudoers.d/deploy
@@ -102,8 +104,10 @@ echo "==> Logging into Gitea registry..."
echo "Run manually: docker login git.timothykim.net" echo "Run manually: docker login git.timothykim.net"
echo "==> Starting services..." echo "==> Starting services..."
cd "$REPO_DIR/docker/nginx" && docker compose up -d cd "$REPO_DIR/docker/nginx"
cd "$REPO_DIR/docker/timothykim.net" && docker compose up -d docker compose up -d
cd "$REPO_DIR/docker/timothykim.net"
docker compose up -d
echo "==> Done. Don't forget to:" echo "==> Done. Don't forget to:"
echo " 1. Add the deploy SSH public key to /home/deploy/.ssh/authorized_keys" echo " 1. Add the deploy SSH public key to /home/deploy/.ssh/authorized_keys"