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
@@ -1,18 +1,21 @@
# Change Proposal: add-ftps-deployment
## Why
Currently there is no automated way to deploy the built Hugo site to the production server. Manual file transfers are error-prone and time-consuming. This change adds a deployment script to automate FTPS uploads to www.markusgraf.ch.
Currently there is no automated way to deploy the built Hugo site to the production server. Manual file transfers are error-prone and time-consuming. This change adds a deployment script to automate uploads via rsync over SSH, properly mirroring content (including deletions).
## What Changes
- Add deployment script `scripts/deploy.sh` that uploads `public/` directory via FTPS
- Use environment variable `FTPS_PASSWORD` for secure credential management
- Implement prerequisite checks (public/ exists, lftp installed, credentials set)
- Add deployment script `scripts/deploy.sh` that syncs `public/` directory via rsync
- Use environment variables `SSH_USER`, `SSH_HOST`, `SSH_PORT`, and `REMOTE_ROOT` for configuration
- Implement mirror behavior with `--delete` flag to remove remote files not present locally
- Implement prerequisite checks (public/ exists, rsync installed, credentials set)
- Add error handling and progress reporting
- Update documentation with deployment instructions
## Impact
- Affected specs: New `deployment` capability
- Affected code: New file `scripts/deploy.sh`
- Dependencies: Requires `lftp` tool to be installed
- Configuration: User must set `FTPS_PASSWORD` environment variable
- Dependencies: Requires `rsync` to be installed
- Configuration: User must set `SSH_USER` and `SSH_HOST` environment variables
- `SSH_PORT` defaults to 22
- `REMOTE_ROOT` defaults to /httpsdocs
- No changes to existing Hugo templates, content, or build process
@@ -2,29 +2,30 @@
## ADDED Requirements
### Requirement: FTPS Deployment Script SHALL be provided
The system SHALL provide an automated deployment script that uploads the built Hugo site to the production server via FTPS.
### Requirement: Rsync Deployment Script SHALL be provided
The system SHALL provide an automated deployment script that syncs the built Hugo site to the production server via rsync over SSH, properly mirroring content.
#### Scenario: User deploys site after building
**Given** the Hugo site has been built successfully (public/ directory exists)
**And** the user has set the FTPS_PASSWORD environment variable
**And** the user has set the SSH_USER and SSH_HOST environment variables
**When** the user runs the deployment script
**Then** the script connects to www.markusgraf.ch via FTPS using the username "gurix"
**And** uploads all files from the public/ directory to the httpsdocs/ directory on the server
**And** displays upload progress and completion status
**Then** the script connects to the specified SSH_HOST via rsync over SSH
**And** syncs all files from the public/ directory to the REMOTE_ROOT directory on the server
**And** deletes remote files that don't exist locally (mirror behavior)
**And** displays sync progress and completion status
**And** exits with status code 0 on success
#### Scenario: Script fails when credentials are missing
**Given** the Hugo site has been built
**And** the FTPS_PASSWORD environment variable is not set
**And** the SSH_USER or SSH_HOST environment variable is not set
**When** the user runs the deployment script
**Then** the script displays an error message explaining the missing credential
**Then** the script displays an error message explaining the missing configuration
**And** exits with a non-zero status code
**And** does not attempt to connect to the server
#### Scenario: Script handles connection failures gracefully
**Given** the FTPS_PASSWORD is set correctly
**And** the network connection to www.markusgraf.ch is unavailable or fails
**Given** the SSH_USER and SSH_HOST are set correctly
**And** the network connection to the server is unavailable or SSH authentication fails
**When** the user runs the deployment script
**Then** the script displays a clear error message about the connection failure
**And** exits with a non-zero status code
@@ -33,18 +34,19 @@ The system SHALL provide an automated deployment script that uploads the built H
### Requirement: Secure Credential Management SHALL be enforced
The deployment process SHALL handle credentials securely without exposing them in version control or script output.
#### Scenario: Credentials stored as environment variables
#### Scenario: Configuration stored as environment variables
**Given** the user needs to deploy the site
**When** the user reviews the deployment documentation
**Then** the documentation instructs them to set FTPS_PASSWORD as an environment variable
**And** the deployment script reads credentials only from environment variables
**Then** the documentation instructs them to set SSH_USER, SSH_HOST, SSH_PORT, and REMOTE_ROOT as environment variables
**And** the deployment script reads configuration only from environment variables
**And** credentials are never hardcoded in scripts or configuration files
**And** SSH key-based authentication is used for secure, password-less access
#### Scenario: Script does not expose credentials in output
**Given** the deployment script is running
**When** the script displays status messages or logs
**Then** the password is never displayed in plain text
**And** connection strings mask the password portion
**Then** SSH keys or passwords are never displayed in plain text
**And** connection strings show only the host and user information
**And** error messages do not reveal credential values
### Requirement: Deployment Status Feedback SHALL be provided
@@ -84,6 +86,13 @@ The deployment script SHALL verify that prerequisites are met before attempting
#### Scenario: Script checks for required tools
**Given** the deployment script starts
**When** it performs prerequisite checks
**Then** it verifies that lftp is installed and available
**And** displays an installation message if lftp is missing
**Then** it verifies that rsync is installed and available
**And** displays an installation message if rsync is missing
**And** exits with an error if required tools are unavailable
#### Scenario: Script supports configurable ports and remote directories
**Given** the user needs to deploy to a non-standard SSH port or directory
**When** the user sets SSH_PORT and REMOTE_ROOT environment variables
**Then** the script uses the specified port instead of the default (22)
**And** the script uploads to the specified remote directory instead of the default (/httpsdocs)
**And** if these variables are not set, the script uses sensible defaults
+25 -23
View File
@@ -10,30 +10,31 @@
### 2. Implement prerequisite checks
- [x] Check for existence and non-empty state of `public/` directory
- [x] Verify `lftp` is installed and available in PATH
- [x] Check that `FTPS_PASSWORD` environment variable is set
- [x] Verify `rsync` is installed and available in PATH
- [x] Check that `SSH_USER` and `SSH_HOST` environment variables are set
- [x] Display clear error messages for any missing prerequisites
- **Validates**: Script exits early with helpful errors when prerequisites are missing
### 3. Implement FTPS connection logic
- [x] Configure lftp connection to www.markusgraf.ch with username "gurix"
- [x] Use `FTPS_PASSWORD` environment variable for authentication
- [x] Set FTPS-specific lftp settings (SSL/TLS requirements)
- [x] Implement connection timeout and retry logic
- **Validates**: Script can establish FTPS connection with correct credentials
### 3. Implement SSH/rsync connection logic
- [x] Configure rsync to connect using SSH_USER and SSH_HOST
- [x] Support configurable SSH_PORT (defaults to 22)
- [x] Support configurable REMOTE_ROOT (defaults to /httpsdocs)
- [x] Use SSH key-based authentication for secure, password-less access
- **Validates**: Script can establish SSH connection with correct credentials
### 4. Implement file upload functionality
- [x] Use lftp mirror command to upload `public/` contents to `httpsdocs/`
- [x] Configure upload to preserve file permissions and timestamps
- [x] Enable parallel transfers for improved performance
- [x] Handle special files (symlinks, hidden files) appropriately
- **Validates**: All files from public/ are correctly uploaded to httpsdocs/
### 4. Implement file sync functionality
- [x] Use rsync command to sync `public/` contents to remote directory
- [x] Configure sync to preserve file permissions and timestamps (-a flag)
- [x] Enable compression during transfer (-z flag)
- [x] Implement mirror behavior with --delete flag (removes remote files not present locally)
- [x] Handle all file types appropriately
- **Validates**: All files from public/ are correctly synced to the remote directory and old files are removed
### 5. Add progress and status reporting
- [x] Display connection status messages
- [x] Show upload progress (file counts, current file being uploaded)
- [x] Report upload completion with summary statistics
- [x] Ensure password is never displayed in output
- [x] Show sync progress through rsync verbose output
- [x] Report sync completion with summary
- [x] Ensure SSH keys or passwords are never displayed in output
- **Validates**: User receives clear feedback during deployment process
### 6. Implement error handling
@@ -45,9 +46,9 @@
### 7. Add script documentation
- [x] Add header comments explaining script purpose and usage
- [x] Document required environment variables
- [x] Document required environment variables (SSH_USER, SSH_HOST, SSH_PORT, REMOTE_ROOT)
- [x] Include example usage in comments
- [x] Add inline comments for complex lftp commands
- [x] Add inline comments for rsync command options
- **Validates**: Script is self-documenting for future maintenance
### 8. Update project documentation
@@ -58,10 +59,11 @@
- **Validates**: User documentation exists and covers deployment process
### 9. Test deployment script
- [x] Test with missing prerequisites (no public/, no lftp, no password)
- [x] Test with incorrect credentials
- [x] Test successful deployment with valid credentials
- [x] Verify uploaded files match local public/ directory
- [x] Test with missing prerequisites (no public/, no rsync, no SSH_USER/SSH_HOST)
- [x] Test with SSH authentication issues
- [x] Test successful deployment with valid credentials and SSH keys
- [x] Verify synced files match local public/ directory
- [x] Verify remote files are deleted when removed locally (mirror behavior)
- **Validates**: Script behaves correctly in success and failure scenarios
### 10. Create .gitignore entry for environment files