refactor: switch from FTPS/lftp to rsync for deployment

Replace the FTPS-based deployment approach with rsync over SSH for more
reliable and efficient mirroring of the Hugo site. This change addresses
issues with the previous approach and provides true mirror behavior.

Key changes:
- Replace lftp with rsync for file synchronization
- Add --delete flag for true mirror behavior (removes stale remote files)
- Simplify authentication to use SSH keys only (no password needed)
- Update all documentation and OpenSpec artifacts
- Add .envrc to gitignore for direnv users

Environment variables:
- SSH_USER: SSH username (required)
- SSH_HOST: server hostname (required)
- SSH_PORT: SSH port (optional, defaults to 22)
- REMOTE_ROOT: remote directory path (optional, defaults to /httpsdocs)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-28 16:02:35 +01:00
co-authored by Claude
parent acdce1d15e
commit fcb82acc6f
6 changed files with 193 additions and 150 deletions
+86 -53
View File
@@ -1,28 +1,28 @@
# Deployment Guide
This guide explains how to deploy the Hugo-built static site to the production server at www.markusgraf.ch.
This guide explains how to deploy the Hugo-built static site to the production server via rsync over SSH, properly mirroring the content.
## Prerequisites
Before deploying, ensure you have:
1. **Built the site**: Run `hugo build` to generate the `public/` directory
2. **lftp installed**: The deployment script requires the `lftp` command-line tool
3. **FTPS credentials**: You need the FTPS username and password for the server
2. **SSH access configured**: You need SSH access to your server with key-based authentication
3. **Rsync available**: The deployment script requires the `rsync` command-line tool
### Installing lftp
### Installing Rsync
If `lftp` is not installed on your system:
Rsync is typically pre-installed on most systems, but if needed:
```bash
# Ubuntu/Debian
sudo apt-get install lftp
sudo apt-get install rsync
# macOS
brew install lftp
# macOS (usually pre-installed)
# No action needed, or: brew install rsync
# Fedora
sudo dnf install lftp
sudo dnf install rsync
```
## Deployment Process
@@ -39,14 +39,16 @@ This generates the static files in the `public/` directory.
### 2. Set Environment Variables
Set the FTPS credentials as environment variables:
Set the SSH connection details as environment variables:
```bash
export FTPS_USER='gurix'
export FTPS_PASSWORD='your-password-here'
export SSH_USER='your-username'
export SSH_HOST='your-server.com'
export SSH_PORT='22' # Optional, defaults to 22
export REMOTE_ROOT='/httpsdocs' # Optional, defaults to /httpsdocs
```
**Important**: Never commit passwords to version control. Credentials should only be stored as environment variables.
**Important**: Ensure SSH key-based authentication is set up for password-less access.
### 3. Run the Deployment Script
@@ -58,8 +60,9 @@ Execute the deployment script:
The script will:
- Check that all prerequisites are met
- Connect to www.markusgraf.ch via FTPS
- Upload all files from `public/` to `httpsdocs/` on the server
- Connect to your server via rsync over SSH
- Sync all files from `public/` to the specified remote directory
- Delete remote files that don't exist locally (true mirror behavior)
- Display progress and completion status
### Complete Example
@@ -70,9 +73,11 @@ Here's the full workflow:
# Build the site
hugo build
# Set credentials (only needed once per session)
export FTPS_USER='gurix'
export FTPS_PASSWORD='your-password'
# Set connection details (only needed once per session)
export SSH_USER='your-username'
export SSH_HOST='your-server.com'
export SSH_PORT='22' # Optional
export REMOTE_ROOT='/httpsdocs' # Optional
# Deploy
./scripts/deploy.sh
@@ -82,20 +87,22 @@ export FTPS_PASSWORD='your-password'
The deployment script uses the following configuration:
- **Server**: www.markusgraf.ch
- **Protocol**: FTPS (FTP over SSL/TLS)
- **Username**: gurix
- **Target Directory**: httpsdocs/
- **Protocol**: rsync over SSH
- **Default Port**: 22 (configurable via SSH_PORT)
- **Default Target Directory**: /httpsdocs (configurable via REMOTE_ROOT)
- **Source Directory**: public/
- **Sync Behavior**: Mirror mode with `--delete` flag (removes remote files not present locally)
## Security Notes
### Credential Management
### SSH Key-Based Authentication
- **Never** hardcode passwords in scripts or configuration files
- **Never** commit passwords to version control
- Use environment variables to pass credentials securely
- The deployment script never displays passwords in its output
The deployment script uses SSH/SCP, which supports key-based authentication:
- **Set up SSH keys**: Generate an SSH key pair and add your public key to the server's `~/.ssh/authorized_keys`
- **Test SSH access**: Verify you can connect without a password: `ssh -p 22 user@server`
- **No password required**: SCP will use your SSH keys automatically
- **Never** commit private keys to version control
### Optional: Using .env Files
@@ -103,7 +110,10 @@ For convenience, you can create a `.env` file (which is ignored by git):
```bash
# .env
FTPS_PASSWORD=your-password
export SSH_USER='your-username'
export SSH_HOST='your-server.com'
export SSH_PORT='22'
export REMOTE_ROOT='/httpsdocs'
```
Then source it before deployment:
@@ -121,58 +131,81 @@ source .env
**Solution**: Run `hugo build` before deploying.
### Error: "lftp is not installed"
### Error: "rsync is not installed"
**Cause**: The `lftp` tool is not available on your system.
**Cause**: The `rsync` tool is not available on your system.
**Solution**: Install lftp using the instructions above.
**Solution**: Install rsync using the instructions above.
### Error: "FTPS_PASSWORD environment variable is not set"
### Error: "SSH_USER environment variable is not set"
**Cause**: The password environment variable hasn't been set.
**Cause**: Required environment variables haven't been set.
**Solution**: Run `export FTPS_PASSWORD='your-password'` before deploying.
**Solution**: Set the required variables:
```bash
export SSH_USER='your-username'
export SSH_HOST='your-server.com'
```
### Connection Failures
### SSH Connection Failures
**Cause**: Network issues or incorrect credentials.
**Cause**: SSH authentication issues or network problems.
**Solutions**:
- Verify your internet connection
- Check that the password is correct
- Ensure the server (www.markusgraf.ch) is accessible
- Verify firewall settings aren't blocking FTPS (port 21)
- Verify you can connect manually: `ssh -p 22 user@server`
- Check that your SSH key is added to the server's `~/.ssh/authorized_keys`
- Ensure your internet connection is working
- Verify the server is accessible
- Check firewall settings aren't blocking SSH (default port 22)
### Partial Upload Failures
### Permission Denied on Remote Directory
**Cause**: Network interruption during upload.
**Cause**: User doesn't have write permissions to the target directory.
**Solution**: Simply run the deployment script again. The `mirror` command will resume and complete the upload.
**Solution**:
- Verify the REMOTE_ROOT directory exists on the server
- Ensure your SSH user has write permissions to that directory
- Contact your server administrator if needed
## Advanced Usage
### Testing Without Deploying
### Testing SSH Connection
To test the script without actually uploading files, you can modify the `deploy()` function temporarily to use the `--dry-run` flag:
To test your SSH connection before deploying:
```bash
mirror --reverse --delete --verbose --parallel=3 --dry-run $SOURCE_DIR $TARGET_DIR;
# Test basic SSH connection
ssh -p "${SSH_PORT:-22}" "$SSH_USER@$SSH_HOST"
# Test that you can write to the target directory
ssh -p "${SSH_PORT:-22}" "$SSH_USER@$SSH_HOST" "touch ${REMOTE_ROOT:-/httpsdocs}/test.txt && rm ${REMOTE_ROOT:-/httpsdocs}/test.txt"
```
### Deployment from CI/CD
For automated deployments from CI/CD pipelines:
1. Store `FTPS_PASSWORD` as a secret in your CI/CD system
2. Ensure the CI/CD environment has `lftp` installed
3. Run the deployment script after successful builds
1. Set up SSH key-based authentication for your CI/CD runner
2. Store `SSH_USER` and `SSH_HOST` as environment variables or secrets
3. Ensure the CI/CD environment has `rsync` installed
4. Run the deployment script after successful builds
Example GitHub Actions workflow snippet:
```yaml
- name: Setup SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
- name: Deploy to server
env:
FTPS_PASSWORD: ${{ secrets.FTPS_PASSWORD }}
SSH_USER: ${{ secrets.SSH_USER }}
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_PORT: ${{ secrets.SSH_PORT }}
REMOTE_ROOT: ${{ secrets.REMOTE_ROOT }}
run: ./scripts/deploy.sh
```
@@ -180,7 +213,7 @@ Example GitHub Actions workflow snippet:
If you encounter issues not covered in this guide:
1. Check the server logs
2. Verify network connectivity to www.markusgraf.ch
3. Ensure the `httpsdocs/` directory exists on the server
1. Test SSH connectivity manually: `ssh user@server`
2. Check server logs for authentication or permission issues
3. Verify the target directory exists and has correct permissions
4. Contact your hosting provider for server-side issues