provisioning-outreach/presentations/rust-laspalmas-250926/auroraframe/scripts/prod.nu

451 lines
15 KiB
Text
Raw Normal View History

#!/usr/bin/env nu
# Production workflow script
use build.nu build_page
use utils.nu *
use optimize-html.nu *
use optimize-css.nu *
use optimize-svg.nu *
# Production build with full optimization
def main [
--analyze(-a) # Show detailed analysis
--compress(-c) # Generate compressed versions
--verbose(-v) # Verbose output
--output(-o): string # Custom output directory
] {
let config = if ($output | is-not-empty) {
(load_config | update dist_dir $output)
} else {
(load_config)
}
print "🚀 Starting production build..."
print $"📁 Output directory: ($config.dist_dir)"
# Validate configuration
if not (validate_config $config) {
print "❌ Configuration validation failed"
return
}
# Full production build
let build_result = (measure_time "Production build" {
build_page --clean --verbose --analyze
})
# Generate compressed versions if requested
if $compress {
generate_compressed_versions $config $verbose
}
# Performance audit
if $analyze {
performance_audit $config $verbose
}
# Generate deployment files
generate_deployment_files $config $verbose
print "✅ Production build completed successfully!"
# Show final summary
show_build_summary $config
}
# Generate compressed versions (gzip, brotli)
def generate_compressed_versions [config: record, verbose: bool] {
print "📦 Generating compressed versions..."
let files_to_compress = [
$"($config.dist_dir)/($config.html_output)",
$"($config.css_dist)/($config.css_output)",
$"($config.svg_dist)/sprites.svg"
]
let compressed_files = (compress_files $files_to_compress)
if $verbose {
print $"📦 Generated ($compressed_files | length) compressed files"
}
}
# Comprehensive performance audit
def performance_audit [config: record, verbose: bool] {
print "\n🔍 Performance Audit"
print "===================="
# HTML analysis
let html_file = $"($config.dist_dir)/($config.html_output)"
if ($html_file | path exists) {
let html_content = (open $html_file)
let html_analysis = (analyze_html $html_content)
let performance_hints = (generate_performance_hints $html_content)
print $"📄 HTML Analysis:"
print $" Size: (format_file_size $html_analysis.size_bytes)"
print $" Lines: ($html_analysis.lines)"
print $" External resources: CSS($html_analysis.external_css), JS($html_analysis.external_js), Images($html_analysis.images)"
if ($performance_hints | length) > 0 {
print $" 💡 Performance hints:"
$performance_hints | each { |hint| print $" - ($hint)" }
}
}
# CSS analysis
let css_file = $"($config.css_dist)/($config.css_output)"
if ($css_file | path exists) {
let css_content = (open $css_file)
let css_analysis = (analyze_css $css_content)
let css_metrics = (css_performance_metrics $css_content)
print $"\n🎨 CSS Analysis:"
print $" Size: (format_file_size $css_analysis.size_bytes)"
print $" Rules: ($css_analysis.rule_lines)"
print $" Selectors: ($css_metrics.total_selectors)"
print $" Properties: ($css_metrics.total_properties)"
print $" Avg properties per rule: ($css_metrics.avg_properties_per_rule)"
print $" Complex selectors: ($css_metrics.complex_selectors)"
}
# SVG analysis
let svg_files = (glob $"($config.svg_dist)/*.svg")
if ($svg_files | length) > 0 {
print $"\n🖼 SVG Analysis:"
let svg_report = (svg_performance_report $svg_files)
$svg_report | each { |svg|
print $" ($svg.file | path basename): (format_file_size $svg.size)"
print $" Elements: ~($svg.elements), Paths: ~($svg.paths)"
print $" Animations: ($svg.animations), Valid: ($svg.valid)"
print $" Optimization potential: (format_file_size $svg.optimization_potential)"
}
}
# Overall performance score
calculate_performance_score $config
}
# Calculate overall performance score
def calculate_performance_score [config: record] {
let html_file = $"($config.dist_dir)/($config.html_output)"
let css_file = $"($config.css_dist)/($config.css_output)"
let total_size = ([$html_file, $css_file]
| where ($it | path exists)
| each { |file|
let size_raw = (ls $file | get size | first)
($size_raw | into int)
}
| math sum
)
let score = if $total_size < 50000 {
"A+ (Excellent)"
} else if $total_size < 100000 {
"A (Good)"
} else if $total_size < 200000 {
"B (Average)"
} else {
"C (Needs optimization)"
}
print $"\n⭐ Performance Score: ($score)"
print $" Total size: (format_file_size $total_size)"
# Recommendations based on size
if $total_size > 100000 {
print "💡 Recommendations:"
print " - Consider splitting CSS into critical/non-critical"
print " - Optimize SVG files further"
print " - Enable server compression (gzip/brotli)"
}
}
# Generate deployment files
def generate_deployment_files [config: record, verbose: bool] {
print "📋 Generating deployment files..."
# Ensure the output directory exists
if not ($config.dist_dir | path exists) {
mkdir $config.dist_dir
}
# Generate .htaccess for Apache
generate_htaccess $config
# Generate nginx.conf snippet
generate_nginx_config $config
# Generate deploy script
generate_deploy_script $config
# Generate robots.txt
generate_robots_txt $config
if $verbose {
print "📋 Generated deployment configuration files"
}
}
# Generate Apache .htaccess
def generate_htaccess [config: record] {
let htaccess_content = '# Page Build System - Apache Configuration
# Enable compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE image/svg+xml
</IfModule>
# Cache static assets
<IfModule mod_expires.c>
ExpiresActive on
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType text/html "access plus 1 hour"
</IfModule>
# Security headers
<IfModule mod_headers.c>
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
Header always set X-XSS-Protection "1; mode=block"
Header always set Content-Security-Policy "default-src '\''self'\''; style-src '\''self'\'' '\''unsafe-inline'\'' fonts.googleapis.com; font-src fonts.gstatic.com; img-src '\''self'\'' data:; script-src '\''none'\'';"
</IfModule>
# MIME types
AddType image/svg+xml .svg
AddType text/css .css'
$htaccess_content | save --force $"($config.dist_dir)/.htaccess"
}
# Generate Nginx configuration snippet
def generate_nginx_config [config: record] {
let nginx_content = '# Page Build System - Nginx Configuration
# Add this to your nginx server block
location / {
try_files $uri $uri/ =404;
# Cache static assets
location ~* \.(css|js|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML files - shorter cache
location ~* \.html$ {
expires 1h;
add_header Cache-Control "public";
}
}
# 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
image/svg+xml;
# Security headers
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
add_header Content-Security-Policy "default-src '\''self'\''; style-src '\''self'\'' '\''unsafe-inline'\'' fonts.googleapis.com; font-src fonts.gstatic.com; img-src '\''self'\'' data:; script-src '\''none'\'';";'
$nginx_content | save --force $"($config.dist_dir)/nginx.conf"
}
# Generate deployment script
def generate_deploy_script [config: record] {
let deploy_script = '#!/bin/bash
# Deploy script for optimized web page
set -e
echo "🚀 Deploying optimized web page..."
# Configuration
DIST_DIR="dist"
REMOTE_HOST="${DEPLOY_HOST:-your-server.com}"
REMOTE_PATH="${DEPLOY_PATH:-/var/www/optimized-page}"
REMOTE_USER="${DEPLOY_USER:-deploy}"
# Check if dist directory exists
if [ ! -d "$DIST_DIR" ]; then
echo "❌ Distribution directory not found. Run build first."
exit 1
fi
# Deploy via rsync
echo "📦 Uploading files..."
rsync -avz --delete \
--exclude="*.map" \
--exclude="build-manifest.json" \
"$DIST_DIR/" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/"
echo "✅ Deployment completed!"
echo "🌐 Site should be available at: https://$REMOTE_HOST"'
$deploy_script | save --force $"($config.dist_dir)/deploy.sh"
# Make executable
chmod +x $"($config.dist_dir)/deploy.sh"
}
# Generate robots.txt
def generate_robots_txt [config: record] {
let robots_content = 'User-agent: *
Allow: /
# Sitemap
Sitemap: https://your-domain.com/sitemap.xml'
$robots_content | save --force $"($config.dist_dir)/robots.txt"
}
# Show build summary
def show_build_summary [config: record] {
print "\n📊 Build Summary"
print "================"
let files = [
{ name: "index.html", path: $"($config.dist_dir)/($config.html_output)" },
{ name: "main.min.css", path: $"($config.css_dist)/($config.css_output)" },
{ name: "sprites.svg", path: $"($config.svg_dist)/sprites.svg" },
{ name: "symbols.svg", path: $"($config.svg_dist)/symbols.svg" }
]
$files | each { |file|
if ($file.path | path exists) {
let size_raw = (ls $file.path | get size | first)
let size = ($size_raw | into int)
let size_formatted = (format_file_size $size)
# Check for compressed version
let compressed_path = $"($file.path).gz"
let compressed_info = if ($compressed_path | path exists) {
let compressed_size_raw = (ls $compressed_path | get size | first)
let compressed_size = ($compressed_size_raw | into int)
let savings = (($size - $compressed_size) / $size * 100 | math round -p 1)
$" \(gzipped: (format_file_size $compressed_size), -($savings)%\)"
} else {
""
}
print $" 📄 ($file.name): ($size_formatted)($compressed_info)"
}
}
# Total uncompressed size
let total_size = ($files
| each { |file|
if ($file.path | path exists) {
let size_raw = (ls $file.path | get size | first)
($size_raw | into int)
} else {
0
}
}
| math sum
)
print $"\n📦 Total size: (format_file_size $total_size)"
# Build info
print $"\n📋 Build completed at: (date now | format date '%Y-%m-%d %H:%M:%S')"
print $"📁 Output directory: ($config.dist_dir)"
print $"🌐 Ready for deployment!"
}
# Quick production build
def "main quick" [
--analyze(-a) # Show detailed analysis
--compress(-c) # Generate compressed versions
--verbose(-v) # Verbose output
--output(-o): string # Custom output directory
] {
# Quick build defaults to verbose, user can override other flags
main --analyze=$analyze --compress=$compress --verbose=true --output=$output
}
# Production build with full analysis
def "main full" [
--analyze(-a) # Show detailed analysis
--compress(-c) # Generate compressed versions
--verbose(-v) # Verbose output
--output(-o): string # Custom output directory
] {
# Full build defaults to analyze+compress+verbose, user can override
let final_analyze = if $analyze != null { $analyze } else { true }
let final_compress = if $compress != null { $compress } else { true }
let final_verbose = if $verbose != null { $verbose } else { true }
main --analyze=$final_analyze --compress=$final_compress --verbose=$final_verbose --output=$output
}
# Deploy to staging
def "main staging" [
--analyze(-a) # Show detailed analysis
--compress(-c) # Generate compressed versions
--verbose(-v) # Verbose output
--output(-o): string # Custom output directory
] {
print "🚀 Building for staging..."
# Staging defaults to output="staging" and verbose=true, user can override
let final_output = if ($output | is-not-empty) { $output } else { "staging" }
let final_verbose = if $verbose != null { $verbose } else { true }
main --analyze=$analyze --compress=$compress --verbose=$final_verbose --output=$final_output
print $"📦 Staging build ready in ./($final_output)/"
}
# Production help
def "main help" [] {
print "🏭 Production Commands:"
print "======================="
print " nu prod.nu - Full production build (all flags)"
print " nu prod.nu quick - Quick production build"
print " nu prod.nu full - Full build with analysis and compression"
print " nu prod.nu staging - Build for staging environment"
print " nu prod.nu help - Show this help"
print ""
print "Main Command Flags:"
print " --analyze, -a - Show detailed performance analysis"
print " --compress, -c - Generate compressed versions (gzip)"
print " --verbose, -v - Verbose output"
print " --output, -o <DIR> - Custom output directory"
print ""
print "All subcommands support all flags:"
print " quick: defaults to --verbose"
print " full: defaults to --analyze --compress --verbose"
print " staging: defaults to --verbose --output staging"
print ""
print "Examples:"
print " nu prod.nu --analyze --compress # Main with all flags"
print " nu prod.nu --output staging # Main with custom output"
print " nu prod.nu quick -v -o test # Quick build to test dir"
print " nu prod.nu staging -a # Staging with analysis"
print " nu prod.nu full -o production # Full build to production dir"
}