Rustelo/scripts/docs/deploy-docs.sh

546 lines
13 KiB
Bash
Raw Normal View History

2025-07-07 23:53:50 +01:00
#!/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}"