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

451 lines
No EOL
15 KiB
Text
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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"
}