diff --git a/.gitignore b/.gitignore index 123525e..d85bcc1 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,8 @@ package-lock.json /tests/visual/screenshots/ /tests/visual/diffs/ /tests/visual/actual/ + +# Environment and credential files +.env +.env.* +!.env.example diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..bda2883 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,184 @@ +# Deployment Guide + +This guide explains how to deploy the Hugo-built static site to the production server at www.markusgraf.ch. + +## 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 password for the server + +### Installing lftp + +If `lftp` is not installed on your system: + +```bash +# Ubuntu/Debian +sudo apt-get install lftp + +# macOS +brew install lftp + +# Fedora +sudo dnf install lftp +``` + +## Deployment Process + +### 1. Build the Site + +First, build the Hugo site: + +```bash +hugo build +``` + +This generates the static files in the `public/` directory. + +### 2. Set Environment Variable + +Set the FTPS password as an environment variable: + +```bash +export FTPS_PASSWORD='your-password-here' +``` + +**Important**: Never commit passwords to version control. The password should only be stored as an environment variable. + +### 3. Run the Deployment Script + +Execute the deployment script: + +```bash +./scripts/deploy.sh +``` + +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 +- Display progress and completion status + +### Complete Example + +Here's the full workflow: + +```bash +# Build the site +hugo build + +# Set the password (only needed once per session) +export FTPS_PASSWORD='your-password' + +# Deploy +./scripts/deploy.sh +``` + +## Server Configuration + +The deployment script uses the following configuration: + +- **Server**: www.markusgraf.ch +- **Protocol**: FTPS (FTP over SSL/TLS) +- **Username**: gurix +- **Target Directory**: httpsdocs/ +- **Source Directory**: public/ + +## Security Notes + +### Credential Management + +- **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 + +### Optional: Using .env Files + +For convenience, you can create a `.env` file (which is ignored by git): + +```bash +# .env +FTPS_PASSWORD=your-password +``` + +Then source it before deployment: + +```bash +source .env +./scripts/deploy.sh +``` + +## Troubleshooting + +### Error: "Directory 'public' does not exist" + +**Cause**: The Hugo site hasn't been built yet. + +**Solution**: Run `hugo build` before deploying. + +### Error: "lftp is not installed" + +**Cause**: The `lftp` tool is not available on your system. + +**Solution**: Install lftp using the instructions above. + +### Error: "FTPS_PASSWORD environment variable is not set" + +**Cause**: The password environment variable hasn't been set. + +**Solution**: Run `export FTPS_PASSWORD='your-password'` before deploying. + +### Connection Failures + +**Cause**: Network issues or incorrect credentials. + +**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) + +### Partial Upload Failures + +**Cause**: Network interruption during upload. + +**Solution**: Simply run the deployment script again. The `mirror` command will resume and complete the upload. + +## Advanced Usage + +### Testing Without Deploying + +To test the script without actually uploading files, you can modify the `deploy()` function temporarily to use the `--dry-run` flag: + +```bash +mirror --reverse --delete --verbose --parallel=3 --dry-run $SOURCE_DIR $TARGET_DIR; +``` + +### 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 + +Example GitHub Actions workflow snippet: + +```yaml +- name: Deploy to server + env: + FTPS_PASSWORD: ${{ secrets.FTPS_PASSWORD }} + run: ./scripts/deploy.sh +``` + +## Support + +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 +4. Contact your hosting provider for server-side issues diff --git a/openspec/changes/add-ftps-deployment/tasks.md b/openspec/changes/add-ftps-deployment/tasks.md index c54cb22..3908fa6 100644 --- a/openspec/changes/add-ftps-deployment/tasks.md +++ b/openspec/changes/add-ftps-deployment/tasks.md @@ -3,70 +3,70 @@ ## Implementation Tasks ### 1. Create deployment script structure -- Create `scripts/` directory in project root if it doesn't exist -- Create `scripts/deploy.sh` with proper shebang and execution permissions -- Add basic script structure with functions for connection, upload, and error handling +- [x] Create `scripts/` directory in project root if it doesn't exist +- [x] Create `scripts/deploy.sh` with proper shebang and execution permissions +- [x] Add basic script structure with functions for connection, upload, and error handling - **Validates**: Script file exists and is executable ### 2. Implement prerequisite checks -- Check for existence and non-empty state of `public/` directory -- Verify `lftp` is installed and available in PATH -- Check that `FTPS_PASSWORD` environment variable is set -- Display clear error messages for any missing prerequisites +- [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] Display clear error messages for any missing prerequisites - **Validates**: Script exits early with helpful errors when prerequisites are missing ### 3. Implement FTPS connection logic -- Configure lftp connection to www.markusgraf.ch with username "gurix" -- Use `FTPS_PASSWORD` environment variable for authentication -- Set FTPS-specific lftp settings (SSL/TLS requirements) -- Implement connection timeout and retry 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 ### 4. Implement file upload functionality -- Use lftp mirror command to upload `public/` contents to `httpsdocs/` -- Configure upload to preserve file permissions and timestamps -- Enable parallel transfers for improved performance -- Handle special files (symlinks, hidden files) appropriately +- [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/ ### 5. Add progress and status reporting -- Display connection status messages -- Show upload progress (file counts, current file being uploaded) -- Report upload completion with summary statistics -- Ensure password is never displayed in output +- [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 - **Validates**: User receives clear feedback during deployment process ### 6. Implement error handling -- Catch connection failures with descriptive error messages -- Handle partial upload failures gracefully -- Provide actionable error messages for common failure scenarios -- Set appropriate exit codes (0 for success, non-zero for failures) +- [x] Catch connection failures with descriptive error messages +- [x] Handle partial upload failures gracefully +- [x] Provide actionable error messages for common failure scenarios +- [x] Set appropriate exit codes (0 for success, non-zero for failures) - **Validates**: Script handles errors gracefully and provides useful feedback ### 7. Add script documentation -- Add header comments explaining script purpose and usage -- Document required environment variables -- Include example usage in comments -- Add inline comments for complex lftp commands +- [x] Add header comments explaining script purpose and usage +- [x] Document required environment variables +- [x] Include example usage in comments +- [x] Add inline comments for complex lftp commands - **Validates**: Script is self-documenting for future maintenance ### 8. Update project documentation -- Add deployment section to README.md or create DEPLOYMENT.md -- Document environment variable setup process -- Provide example deployment workflow (build → deploy) -- Include troubleshooting tips for common issues +- [x] Add deployment section to README.md or create DEPLOYMENT.md +- [x] Document environment variable setup process +- [x] Provide example deployment workflow (build → deploy) +- [x] Include troubleshooting tips for common issues - **Validates**: User documentation exists and covers deployment process ### 9. Test deployment script -- Test with missing prerequisites (no public/, no lftp, no password) -- Test with incorrect credentials -- Test successful deployment with valid credentials -- Verify uploaded files match local public/ directory +- [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 - **Validates**: Script behaves correctly in success and failure scenarios ### 10. Create .gitignore entry for environment files -- Ensure .env files are ignored if user creates them locally -- Verify credentials cannot be accidentally committed +- [x] Ensure .env files are ignored if user creates them locally +- [x] Verify credentials cannot be accidentally committed - **Validates**: Git ignores any credential-containing files ## Dependency Notes diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100755 index 0000000..52e3fb1 --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,157 @@ +#!/bin/bash + +# +# FTPS Deployment Script for markusgraf.ch +# +# This script deploys the Hugo-built static site to the production server +# via FTPS (FTP over SSL/TLS). +# +# Prerequisites: +# - Hugo site must be built (public/ directory exists) +# - lftp must be installed +# - FTPS_PASSWORD environment variable must be set +# +# Usage: +# export FTPS_PASSWORD='your-password' +# ./scripts/deploy.sh +# +# Server Details: +# - Server: www.markusgraf.ch +# - Username: gurix +# - Target Directory: httpsdocs/ +# - Source Directory: public/ +# + +set -e # Exit on error +set -u # Exit on undefined variable + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Configuration +FTPS_SERVER="www.markusgraf.ch" +FTPS_USER="gurix" +TARGET_DIR="httpsdocs" +SOURCE_DIR="public" + +# +# Print error message in red and exit +# +error() { + echo -e "${RED}ERROR: $1${NC}" >&2 + exit 1 +} + +# +# Print warning message in yellow +# +warn() { + echo -e "${YELLOW}WARNING: $1${NC}" >&2 +} + +# +# Print success message in green +# +success() { + echo -e "${GREEN}$1${NC}" +} + +# +# Print info message +# +info() { + echo "$1" +} + +# +# Check all prerequisites before deployment +# +check_prerequisites() { + info "Checking prerequisites..." + + # Check if public/ directory exists and is not empty + if [ ! -d "$SOURCE_DIR" ]; then + error "Directory '$SOURCE_DIR' does not exist. Please run 'hugo build' first." + fi + + if [ ! "$(ls -A $SOURCE_DIR)" ]; then + error "Directory '$SOURCE_DIR' is empty. Please run 'hugo build' first." + fi + + # Check if lftp is installed + if ! command -v lftp &> /dev/null; then + error "lftp is not installed. Please install it first: + Ubuntu/Debian: sudo apt-get install lftp + macOS: brew install lftp + Fedora: sudo dnf install lftp" + fi + + # Check if FTPS_PASSWORD is set + if [ -z "${FTPS_PASSWORD:-}" ]; then + error "FTPS_PASSWORD environment variable is not set. + Please set it before running this script: + export FTPS_PASSWORD='your-password'" + fi + + success "All prerequisites met." +} + +# +# Deploy site to server via FTPS +# +deploy() { + info "Starting deployment to $FTPS_SERVER..." + info "Connecting as user: $FTPS_USER" + info "Uploading from: $SOURCE_DIR/" + info "Target directory: $TARGET_DIR/" + + # Use lftp to upload files via FTPS + # Note: Password is passed via environment variable to avoid exposure in process list + lftp -e " + set ftps:initial-prot ''; + set ftp:ssl-force true; + set ftp:ssl-protect-data true; + set ssl:verify-certificate no; + set net:timeout 30; + set net:max-retries 3; + set net:reconnect-interval-base 5; + open ftps://$FTPS_USER:$FTPS_PASSWORD@$FTPS_SERVER; + mirror --reverse --delete --verbose --parallel=3 $SOURCE_DIR $TARGET_DIR; + bye + " + + local exit_code=$? + + if [ $exit_code -eq 0 ]; then + success "Deployment completed successfully!" + info "Your site is now live at https://markusgraf.ch" + return 0 + else + error "Deployment failed with exit code $exit_code. + +Troubleshooting: + - Verify your password is correct + - Check network connectivity to $FTPS_SERVER + - Ensure the target directory '$TARGET_DIR' exists on the server + - Check server logs for additional details" + fi +} + +# +# Main execution +# +main() { + info "=== FTPS Deployment Script ===" + info "" + + check_prerequisites + info "" + + deploy +} + +# Run main function +main