#!/bin/bash # Rustelo Documentation Deployment Script # This script deploys the documentation to various platforms set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Script directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" echo -e "${BLUE}🚀 Rustelo Documentation Deployment Script${NC}" echo "===========================================" # Function to show usage show_usage() { echo "Usage: $0 [PLATFORM] [OPTIONS]" echo "" echo "Platforms:" echo " github-pages Deploy to GitHub Pages" echo " netlify Deploy to Netlify" echo " vercel Deploy to Vercel" echo " aws-s3 Deploy to AWS S3" echo " docker Build Docker image" echo " local Serve locally (development)" echo "" echo "Options:" echo " --dry-run Show what would be deployed without actually deploying" echo " --force Force deployment even if no changes detected" echo " --branch NAME Deploy from specific branch (default: main)" echo " --help Show this help message" echo "" echo "Examples:" echo " $0 github-pages" echo " $0 netlify --dry-run" echo " $0 local --force" echo " $0 docker" } # Parse command line arguments PLATFORM="" DRY_RUN=false FORCE=false BRANCH="main" while [[ $# -gt 0 ]]; do case $1 in github-pages|netlify|vercel|aws-s3|docker|local) PLATFORM="$1" shift ;; --dry-run) DRY_RUN=true shift ;; --force) FORCE=true shift ;; --branch) BRANCH="$2" shift 2 ;; --help) show_usage exit 0 ;; *) echo -e "${RED}❌ Unknown option: $1${NC}" show_usage exit 1 ;; esac done if [ -z "$PLATFORM" ]; then echo -e "${RED}❌ Please specify a platform${NC}" show_usage exit 1 fi # Check dependencies check_dependencies() { echo -e "${BLUE}🔍 Checking dependencies...${NC}" if ! command -v mdbook &> /dev/null; then echo -e "${RED}❌ mdbook is not installed${NC}" echo "Please install mdbook: cargo install mdbook" exit 1 fi if ! command -v git &> /dev/null; then echo -e "${RED}❌ git is not installed${NC}" exit 1 fi echo -e "${GREEN}✅ Dependencies check passed${NC}" } # Build documentation build_docs() { echo -e "${BLUE}🔨 Building documentation...${NC}" cd "$PROJECT_ROOT" # Clean previous build rm -rf book-output # Build with mdbook if mdbook build; then echo -e "${GREEN}✅ Documentation built successfully${NC}" else echo -e "${RED}❌ Documentation build failed${NC}" exit 1 fi } # Deploy to GitHub Pages deploy_github_pages() { echo -e "${BLUE}🐙 Deploying to GitHub Pages...${NC}" # Check if we're in a git repository if [ ! -d ".git" ]; then echo -e "${RED}❌ Not in a git repository${NC}" exit 1 fi # Check if gh-pages branch exists if ! git rev-parse --verify gh-pages >/dev/null 2>&1; then echo -e "${YELLOW}📝 Creating gh-pages branch...${NC}" git checkout --orphan gh-pages git rm -rf . git commit --allow-empty -m "Initial gh-pages commit" git checkout "$BRANCH" fi if [ "$DRY_RUN" = true ]; then echo -e "${YELLOW}🔍 DRY RUN: Would deploy to GitHub Pages${NC}" return 0 fi # Deploy to gh-pages branch echo -e "${BLUE}📤 Pushing to gh-pages branch...${NC}" # Create temporary directory TEMP_DIR=$(mktemp -d) cp -r book-output/html/* "$TEMP_DIR/" # Add .nojekyll file to prevent Jekyll processing touch "$TEMP_DIR/.nojekyll" # Add CNAME file if it exists if [ -f "CNAME" ]; then cp CNAME "$TEMP_DIR/" fi # Switch to gh-pages branch git checkout gh-pages # Remove old files git rm -rf . || true # Copy new files cp -r "$TEMP_DIR/"* . cp "$TEMP_DIR/.nojekyll" . # Add and commit git add . git commit -m "Deploy documentation - $(date '+%Y-%m-%d %H:%M:%S')" # Push to GitHub git push origin gh-pages # Switch back to original branch git checkout "$BRANCH" # Clean up rm -rf "$TEMP_DIR" echo -e "${GREEN}✅ Deployed to GitHub Pages${NC}" echo "Documentation will be available at: https://yourusername.github.io/rustelo" } # Deploy to Netlify deploy_netlify() { echo -e "${BLUE}🌐 Deploying to Netlify...${NC}" # Check if netlify CLI is installed if ! command -v netlify &> /dev/null; then echo -e "${RED}❌ Netlify CLI is not installed${NC}" echo "Please install: npm install -g netlify-cli" exit 1 fi # Create netlify.toml if it doesn't exist if [ ! -f "netlify.toml" ]; then echo -e "${YELLOW}📝 Creating netlify.toml...${NC}" cat > netlify.toml << 'EOF' [build] publish = "book-output/html" command = "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && source ~/.cargo/env && cargo install mdbook && mdbook build" [build.environment] RUST_VERSION = "1.75" [[redirects]] from = "/docs/*" to = "/:splat" status = 200 [[headers]] for = "/*" [headers.values] X-Frame-Options = "DENY" X-XSS-Protection = "1; mode=block" X-Content-Type-Options = "nosniff" Referrer-Policy = "strict-origin-when-cross-origin" Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;" EOF fi if [ "$DRY_RUN" = true ]; then echo -e "${YELLOW}🔍 DRY RUN: Would deploy to Netlify${NC}" return 0 fi # Deploy to Netlify netlify deploy --prod --dir=book-output/html echo -e "${GREEN}✅ Deployed to Netlify${NC}" } # Deploy to Vercel deploy_vercel() { echo -e "${BLUE}▲ Deploying to Vercel...${NC}" # Check if vercel CLI is installed if ! command -v vercel &> /dev/null; then echo -e "${RED}❌ Vercel CLI is not installed${NC}" echo "Please install: npm install -g vercel" exit 1 fi # Create vercel.json if it doesn't exist if [ ! -f "vercel.json" ]; then echo -e "${YELLOW}📝 Creating vercel.json...${NC}" cat > vercel.json << 'EOF' { "version": 2, "builds": [ { "src": "book.toml", "use": "@vercel/static-build", "config": { "distDir": "book-output/html" } } ], "routes": [ { "src": "/docs/(.*)", "dest": "/$1" } ], "headers": [ { "source": "/(.*)", "headers": [ { "key": "X-Frame-Options", "value": "DENY" }, { "key": "X-Content-Type-Options", "value": "nosniff" }, { "key": "X-XSS-Protection", "value": "1; mode=block" } ] } ] } EOF fi # Create package.json for build script if [ ! -f "package.json" ]; then echo -e "${YELLOW}📝 Creating package.json...${NC}" cat > package.json << 'EOF' { "name": "rustelo-docs", "version": "1.0.0", "scripts": { "build": "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && source ~/.cargo/env && cargo install mdbook && mdbook build" } } EOF fi if [ "$DRY_RUN" = true ]; then echo -e "${YELLOW}🔍 DRY RUN: Would deploy to Vercel${NC}" return 0 fi # Deploy to Vercel vercel --prod echo -e "${GREEN}✅ Deployed to Vercel${NC}" } # Deploy to AWS S3 deploy_aws_s3() { echo -e "${BLUE}☁️ Deploying to AWS S3...${NC}" # Check if AWS CLI is installed if ! command -v aws &> /dev/null; then echo -e "${RED}❌ AWS CLI is not installed${NC}" echo "Please install AWS CLI and configure credentials" exit 1 fi # Check for required environment variables if [ -z "$AWS_S3_BUCKET" ]; then echo -e "${RED}❌ AWS_S3_BUCKET environment variable is not set${NC}" exit 1 fi if [ "$DRY_RUN" = true ]; then echo -e "${YELLOW}🔍 DRY RUN: Would deploy to AWS S3 bucket: $AWS_S3_BUCKET${NC}" return 0 fi # Sync to S3 echo -e "${BLUE}📤 Syncing to S3...${NC}" aws s3 sync book-output/html/ "s3://$AWS_S3_BUCKET/" --delete # Set up CloudFront invalidation if configured if [ -n "$AWS_CLOUDFRONT_DISTRIBUTION_ID" ]; then echo -e "${BLUE}🔄 Creating CloudFront invalidation...${NC}" aws cloudfront create-invalidation \ --distribution-id "$AWS_CLOUDFRONT_DISTRIBUTION_ID" \ --paths "/*" fi echo -e "${GREEN}✅ Deployed to AWS S3${NC}" echo "Documentation available at: https://$AWS_S3_BUCKET.s3-website-us-east-1.amazonaws.com" } # Build Docker image build_docker() { echo -e "${BLUE}🐳 Building Docker image...${NC}" # Create Dockerfile if it doesn't exist if [ ! -f "Dockerfile.docs" ]; then echo -e "${YELLOW}📝 Creating Dockerfile.docs...${NC}" cat > Dockerfile.docs << 'EOF' # Multi-stage Docker build for Rustelo documentation FROM rust:1.75-alpine AS builder # Install dependencies RUN apk add --no-cache musl-dev # Install mdbook RUN cargo install mdbook # Set working directory WORKDIR /app # Copy book configuration and source COPY book.toml . COPY book/ ./book/ # Build documentation RUN mdbook build # Production stage FROM nginx:alpine # Copy built documentation COPY --from=builder /app/book-output/html /usr/share/nginx/html # Copy nginx configuration COPY nginx.conf /etc/nginx/nginx.conf # Add labels LABEL org.opencontainers.image.title="Rustelo Documentation" LABEL org.opencontainers.image.description="Rustelo web application template documentation" LABEL org.opencontainers.image.source="https://github.com/yourusername/rustelo" # Expose port EXPOSE 80 # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost/ || exit 1 # Start nginx CMD ["nginx", "-g", "daemon off;"] EOF fi # Create nginx configuration if [ ! -f "nginx.conf" ]; then echo -e "${YELLOW}📝 Creating nginx.conf...${NC}" cat > nginx.conf << 'EOF' events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; # Gzip compression gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json; server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; # Security headers add_header X-Frame-Options "DENY" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Cache static assets location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; } # Main location block location / { try_files $uri $uri/ $uri.html =404; } # Redirect /docs to root location /docs { return 301 /; } # Error pages error_page 404 /404.html; error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } } EOF fi if [ "$DRY_RUN" = true ]; then echo -e "${YELLOW}🔍 DRY RUN: Would build Docker image${NC}" return 0 fi # Build Docker image docker build -f Dockerfile.docs -t rustelo-docs:latest . echo -e "${GREEN}✅ Docker image built successfully${NC}" echo "To run the documentation server:" echo " docker run -p 8080:80 rustelo-docs:latest" } # Serve locally serve_local() { echo -e "${BLUE}🌐 Serving documentation locally...${NC}" if [ "$DRY_RUN" = true ]; then echo -e "${YELLOW}🔍 DRY RUN: Would serve locally${NC}" return 0 fi cd "$PROJECT_ROOT" echo "Documentation will be available at: http://localhost:3000" echo "Press Ctrl+C to stop the server" mdbook serve --open } # Main deployment logic main() { check_dependencies # Build documentation unless serving locally if [ "$PLATFORM" != "local" ]; then build_docs fi case $PLATFORM in github-pages) deploy_github_pages ;; netlify) deploy_netlify ;; vercel) deploy_vercel ;; aws-s3) deploy_aws_s3 ;; docker) build_docker ;; local) serve_local ;; *) echo -e "${RED}❌ Unknown platform: $PLATFORM${NC}" show_usage exit 1 ;; esac } # Run main function main echo "" echo -e "${GREEN}🎉 Deployment complete!${NC}"