546 lines
13 KiB
Bash
546 lines
13 KiB
Bash
![]() |
#!/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}"
|