91 lines
3.1 KiB
Markdown
91 lines
3.1 KiB
Markdown
---
|
|
title: "Shell Deploy Script Patterns for Static Frontends"
|
|
aliases: [deploy-script, cp-recursive, frontend-deploy, apache-reload]
|
|
tags: [shell, bash, deploy, apache, nginx, static-files]
|
|
sources:
|
|
- "daily/2026-04-15.md"
|
|
created: 2026-04-15
|
|
updated: 2026-04-15
|
|
---
|
|
|
|
# Shell Deploy Script Patterns for Static Frontends
|
|
|
|
Bash deploy scripts for static frontend projects have two common failure modes: incomplete file copying and serving stale files after deploy. Both are silent — the script exits 0 but the deployment is wrong.
|
|
|
|
## Key Points
|
|
|
|
- `cp frontend/*` glob does **not** recurse into subdirectories — use `cp -r frontend/. "$DEST/"` instead
|
|
- `cp -r frontend/.` (with dot) copies all contents including hidden files and nested dirs; `cp -r frontend/` copies the directory itself into the dest
|
|
- Always add `mkdir -p "$DEST"` before the copy as a safety net
|
|
- After copying static files, reload the web server: `systemctl reload apache2` (or `nginx -s reload`) — otherwise the old cached version is served
|
|
- Distinguish `setup.sh` (first-time init) from `deploy.sh` (all subsequent updates) — only `deploy.sh` runs on updates
|
|
|
|
## Details
|
|
|
|
### The cp Glob Pitfall
|
|
|
|
```bash
|
|
# WRONG — skips subdirectories silently
|
|
cp frontend/* /var/www/html/
|
|
|
|
# CORRECT — copies all contents recursively, including hidden files
|
|
cp -r frontend/. /var/www/html/
|
|
```
|
|
|
|
The shell glob `frontend/*` expands to all non-hidden entries in `frontend/` but `cp` without `-r` will skip any entries that are directories (it prints a warning but exits 0). Using `frontend/.` instead of `frontend/` as the source avoids creating a nested directory inside the destination.
|
|
|
|
### Apache / Nginx Reload Requirement
|
|
|
|
Web servers cache configuration and sometimes static file handles. After overwriting files in the web root:
|
|
|
|
```bash
|
|
# Apache
|
|
systemctl reload apache2
|
|
|
|
# Nginx
|
|
nginx -s reload
|
|
# or
|
|
systemctl reload nginx
|
|
```
|
|
|
|
`reload` is preferred over `restart` — it re-reads config and gracefully hands off connections without dropping active requests.
|
|
|
|
### Recommended deploy.sh Structure
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
set -e
|
|
|
|
FRONTEND_DIR="/var/www/html/myapp"
|
|
BACKEND_DIR="/opt/myapp"
|
|
|
|
# 1. Copy frontend files
|
|
mkdir -p "$FRONTEND_DIR"
|
|
cp -r frontend/. "$FRONTEND_DIR/"
|
|
|
|
# 2. Rebuild and restart backend
|
|
cd "$BACKEND_DIR"
|
|
docker compose build
|
|
docker compose up -d
|
|
|
|
# 3. Reload web server
|
|
systemctl reload apache2
|
|
|
|
echo "Deploy complete"
|
|
```
|
|
|
|
### setup.sh vs deploy.sh Distinction
|
|
|
|
- `setup.sh` — runs once at first install: creates directories, sets permissions, initializes the database, sets up systemd services
|
|
- `deploy.sh` — runs on every update: copies code, rebuilds containers, reloads servers
|
|
|
|
Never run `setup.sh` after initial setup — it may overwrite config files or re-initialize databases.
|
|
|
|
## Related Concepts
|
|
|
|
- [[wiki/concepts/msal-vanilla-js-pkce]] — deploy pattern for social-reporting-tool where this was encountered
|
|
- [[wiki/architecture/docker-compose-patterns]] — container restart patterns that work alongside deploy scripts
|
|
|
|
## Sources
|
|
|
|
- [[daily/2026-04-15.md]] — Fixed broken deploy scripts in social listening project (commit `7a70283`)
|