feat: Dockerized deployment and home cleanup
- Replace rsync deployment with Docker-based deployment - Container runs nginx on port 3000, proxied by host nginx - Add architecture documentation - Add home directory cleanup script - No more direct /var/www writes Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
36eae69a00
commit
adc6c76688
50
.github/workflows/deploy-site.yml
vendored
50
.github/workflows/deploy-site.yml
vendored
@ -43,28 +43,52 @@ jobs:
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
deploy-server:
|
||||
name: Deploy to hatch.surf server
|
||||
deploy-docker:
|
||||
name: Deploy via Docker to hatch.surf
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Deploy via rsync
|
||||
uses: burnett01/rsync-deployments@7.0.1
|
||||
with:
|
||||
switches: -avz --delete
|
||||
path: site/
|
||||
remote_path: /var/www/hatch.surf/
|
||||
remote_host: ${{ secrets.DEPLOY_HOST }}
|
||||
remote_user: ${{ secrets.DEPLOY_USER }}
|
||||
remote_key: ${{ secrets.DEPLOY_KEY }}
|
||||
- name: Build Docker image
|
||||
run: docker build -t hatch-homepage:latest ./site
|
||||
|
||||
- name: Reload nginx
|
||||
- name: Save Docker image
|
||||
run: docker save hatch-homepage:latest | gzip > hatch-homepage.tar.gz
|
||||
|
||||
- name: Deploy to server
|
||||
uses: appleboy/scp-action@v0.1.7
|
||||
with:
|
||||
host: ${{ secrets.DEPLOY_HOST }}
|
||||
username: ${{ secrets.DEPLOY_USER }}
|
||||
key: ${{ secrets.DEPLOY_KEY }}
|
||||
source: "hatch-homepage.tar.gz"
|
||||
target: "/tmp/hatch-homepage.tar.gz"
|
||||
|
||||
- name: Load and restart container
|
||||
uses: appleboy/ssh-action@v1
|
||||
with:
|
||||
host: ${{ secrets.DEPLOY_HOST }}
|
||||
username: ${{ secrets.DEPLOY_USER }}
|
||||
key: ${{ secrets.DEPLOY_KEY }}
|
||||
script: |
|
||||
nginx -t && systemctl reload nginx
|
||||
# Load the image
|
||||
docker load < /tmp/hatch-homepage.tar.gz
|
||||
|
||||
# Stop and remove old container if exists
|
||||
docker stop hatch-homepage 2>/dev/null || true
|
||||
docker rm hatch-homepage 2>/dev/null || true
|
||||
|
||||
# Run new container
|
||||
docker run -d \
|
||||
--name hatch-homepage \
|
||||
--restart unless-stopped \
|
||||
-p 127.0.0.1:3000:80 \
|
||||
-v /var/www/hatch.surf:/usr/share/nginx/html:ro \
|
||||
hatch-homepage:latest
|
||||
|
||||
# Clean up
|
||||
rm -f /tmp/hatch-homepage.tar.gz
|
||||
|
||||
# Reload nginx if it's managing the reverse proxy
|
||||
nginx -t && systemctl reload nginx 2>/dev/null || true
|
||||
|
||||
173
docs/ARCHITECTURE.md
Normal file
173
docs/ARCHITECTURE.md
Normal file
@ -0,0 +1,173 @@
|
||||
# Hatch.surf Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
Hatch.surf is the landing page and brand site for El Foundation. It consists of:
|
||||
- **Static frontend** (site/) — HTML/CSS/JS served via nginx
|
||||
- **Go backend** (cmd/hatch) — API server for future dynamic features
|
||||
|
||||
## Deployment Architecture
|
||||
|
||||
### Current State (Deprecated)
|
||||
|
||||
```
|
||||
GitHub Push → GitHub Actions → rsync → /var/www/hatch.surf → nginx (host)
|
||||
```
|
||||
|
||||
**Problems:**
|
||||
- Direct filesystem deployment — no isolation
|
||||
- Host-dependent — can't reproduce locally
|
||||
- Secrets (SSH keys) required for rsync
|
||||
|
||||
### New Architecture
|
||||
|
||||
```
|
||||
GitHub Push → GitHub Actions → Build Docker Image → SCP to Server → Docker Run → nginx reverse proxy
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Containerized — isolated from host
|
||||
- Reproducible — same image in dev/prod
|
||||
- No host filesystem dependency
|
||||
- Easy rollback — just change image tag
|
||||
|
||||
## Docker Setup
|
||||
|
||||
### Frontend (site/)
|
||||
|
||||
**Dockerfile:**
|
||||
```dockerfile
|
||||
FROM nginx:alpine
|
||||
RUN rm -rf /usr/share/nginx/html/*
|
||||
COPY . /usr/share/nginx/html/
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
```
|
||||
|
||||
**docker-compose.yml:**
|
||||
```yaml
|
||||
services:
|
||||
hatch-homepage:
|
||||
build: .
|
||||
ports:
|
||||
- "127.0.0.1:3000:80"
|
||||
volumes:
|
||||
- .:/usr/share/nginx/html:ro
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Backend (cmd/hatch)
|
||||
|
||||
**Dockerfile:**
|
||||
```dockerfile
|
||||
FROM golang:1.25-alpine AS build
|
||||
WORKDIR /src
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . ./
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /bin/hatch ./cmd/hatch
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /bin/hatch /bin/hatch
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["/bin/hatch"]
|
||||
```
|
||||
|
||||
**docker-compose.yml:**
|
||||
```yaml
|
||||
services:
|
||||
hatch:
|
||||
build: .
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:8080:8080"
|
||||
volumes:
|
||||
- hatch-data:/data
|
||||
environment:
|
||||
- HATCH_PORT=8080
|
||||
- HATCH_DB_PATH=/data/hatch.db
|
||||
- HATCH_BASE_URL=${HATCH_BASE_URL:-http://localhost}
|
||||
|
||||
caddy:
|
||||
image: caddy:2-alpine
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- caddy-data:/data
|
||||
- caddy-config:/config
|
||||
environment:
|
||||
- HATCH_HOSTNAME=${HATCH_HOSTNAME:-localhost}
|
||||
profiles:
|
||||
- with-caddy
|
||||
|
||||
volumes:
|
||||
hatch-data:
|
||||
caddy-data:
|
||||
caddy-config:
|
||||
```
|
||||
|
||||
## Deployment Flow
|
||||
|
||||
### GitHub Actions (deploy-site.yml)
|
||||
|
||||
1. **Build** — `docker build -t hatch-homepage:latest ./site`
|
||||
2. **Save** — `docker save hatch-homepage:latest | gzip > hatch-homepage.tar.gz`
|
||||
3. **SCP** — Copy tarball to server
|
||||
4. **Load** — `docker load < /tmp/hatch-homepage.tar.gz`
|
||||
5. **Run** — `docker run -d --name hatch-homepage ...`
|
||||
|
||||
### Server Configuration
|
||||
|
||||
**Nginx reverse proxy** (host nginx):
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name hatch.surf;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name hatch.surf;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/hatch.surf/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/hatch.surf/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Secrets Management
|
||||
|
||||
- **DEPLOY_HOST** — Server IP/hostname
|
||||
- **DEPLOY_USER** — SSH user (nara)
|
||||
- **DEPLOY_KEY** — SSH private key (stored in GitHub Secrets)
|
||||
|
||||
No secrets in repo. All deployment credentials in GitHub Secrets.
|
||||
|
||||
## Rollback Procedure
|
||||
|
||||
If deployment fails:
|
||||
```bash
|
||||
# On server
|
||||
docker stop hatch-homepage
|
||||
docker rm hatch-homepage
|
||||
docker run -d --name hatch-homepage --restart unless-stopped -p 127.0.0.1:3000:80 hatch-homepage:previous-tag
|
||||
```
|
||||
|
||||
## Future Improvements
|
||||
|
||||
1. **Container registry** — Push images to GitHub Container Registry (ghcr.io)
|
||||
2. **Health checks** — Add HEALTHCHECK to Dockerfile
|
||||
3. **Monitoring** — Add Prometheus metrics endpoint
|
||||
4. **CDN** — Serve static assets via Cloudflare/CloudFront
|
||||
5. **SSL automation** — Certbot in container or Caddy auto-SSL
|
||||
95
scripts/cleanup-home.sh
Executable file
95
scripts/cleanup-home.sh
Executable file
@ -0,0 +1,95 @@
|
||||
#!/bin/bash
|
||||
# cleanup-home.sh — Clean up home directory clutter
|
||||
# Run as: bash scripts/cleanup-home.sh [--dry-run]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DRY_RUN=false
|
||||
if [[ "${1:-}" == "--dry-run" ]]; then
|
||||
DRY_RUN=true
|
||||
echo "DRY RUN MODE — nothing will be deleted"
|
||||
fi
|
||||
|
||||
HOME_DIR="/home/nara"
|
||||
|
||||
echo "=== Cleaning up $HOME_DIR ==="
|
||||
|
||||
# Function to remove items
|
||||
remove_item() {
|
||||
local item="$1"
|
||||
local full_path="$HOME_DIR/$item"
|
||||
|
||||
if [[ -e "$full_path" ]]; then
|
||||
if $DRY_RUN; then
|
||||
echo " [DRY RUN] Would remove: $item"
|
||||
else
|
||||
echo " Removing: $item"
|
||||
rm -rf "$full_path"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to move items
|
||||
move_item() {
|
||||
local item="$1"
|
||||
local dest="$2"
|
||||
local full_path="$HOME_DIR/$item"
|
||||
local dest_path="$HOME_DIR/$dest"
|
||||
|
||||
if [[ -e "$full_path" ]]; then
|
||||
if $DRY_RUN; then
|
||||
echo " [DRY RUN] Would move: $item → $dest/"
|
||||
else
|
||||
echo " Moving: $item → $dest/"
|
||||
mkdir -p "$dest_path"
|
||||
mv "$full_path" "$dest_path/"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "1. Removing stale Go installations..."
|
||||
remove_item "go"
|
||||
remove_item "go-1.25"
|
||||
remove_item "go-local"
|
||||
remove_item "gopath"
|
||||
remove_item "go-tools"
|
||||
|
||||
echo ""
|
||||
echo "2. Removing Node.js artifacts..."
|
||||
remove_item "node_modules"
|
||||
remove_item "package.json"
|
||||
remove_item "pnpm-lock.yaml"
|
||||
remove_item "pnpm-workspace.yaml"
|
||||
|
||||
echo ""
|
||||
echo "3. Removing temp bundles..."
|
||||
remove_item "vibecode-bundle"
|
||||
remove_item "vibecode-bundle-20260622.tar.gz"
|
||||
|
||||
echo ""
|
||||
echo "4. Removing stray scripts..."
|
||||
remove_item "add-bridge-nginx.sh"
|
||||
remove_item "setup-nginx-tls.sh"
|
||||
|
||||
echo ""
|
||||
echo "5. Removing log files..."
|
||||
remove_item "paperclip-onboard.log"
|
||||
remove_item "paperclip-run.log"
|
||||
|
||||
echo ""
|
||||
echo "6. Moving project files..."
|
||||
move_item "ELF-6-completion-report.md" "Documents"
|
||||
move_item "README.md" "Documents"
|
||||
|
||||
echo ""
|
||||
echo "7. Verifying clean state..."
|
||||
echo "Current home directory:"
|
||||
ls -la "$HOME_DIR" | grep -v "^\." | head -20
|
||||
|
||||
echo ""
|
||||
if $DRY_RUN; then
|
||||
echo "=== Dry run complete. Run without --dry-run to apply changes. ==="
|
||||
else
|
||||
echo "=== Cleanup complete! ==="
|
||||
fi
|
||||
Loading…
Reference in New Issue
Block a user