website-htmx-rustelo-code/scripts/sh/content/generate-content.sh

580 lines
14 KiB
Bash
Raw Permalink Normal View History

2026-07-10 03:44:13 +01:00
#!/bin/bash
# Content generation script for localized blog and recipe content
# Generates new content files from templates with proper localization
set -e
# Configuration
CONTENT_DIR="content"
TEMPLATES_DIR="scripts/content/templates"
SUPPORTED_LANGUAGES=("en" "es")
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Helper functions
log_error() {
echo -e "${RED}❌ ERROR: $1${NC}"
}
log_warning() {
echo -e "${YELLOW}⚠️ WARNING: $1${NC}"
}
log_success() {
echo -e "${GREEN}$1${NC}"
}
log_info() {
echo -e "${BLUE} $1${NC}"
}
log_step() {
echo -e "${CYAN}🔧 $1${NC}"
}
# Display usage information
show_usage() {
echo -e "${BLUE}📝 Content Generation Tool${NC}"
echo "================================"
echo ""
echo "Usage: $0 [COMMAND] [OPTIONS]"
echo ""
echo "Commands:"
echo " blog-post Generate a new blog post"
echo " recipe Generate a new recipe"
echo " templates Initialize template files"
echo " index Update content indices"
echo " help Show this help message"
echo ""
echo "Blog Post Options:"
echo " --title Post title (required)"
echo " --author Author name (default: 'Jesús Pérez')"
echo " --category Post category"
echo " --tags Comma-separated tags"
echo " --featured Mark as featured (true/false)"
echo " --language Target language (en/es/all)"
echo ""
echo "Recipe Options:"
echo " --title Recipe title (required)"
echo " --category Recipe category (required)"
echo " --difficulty Difficulty level (Beginner/Intermediate/Advanced/Expert)"
echo " --prep-time Preparation time (e.g., '30 min', '2 hours')"
echo " --tags Comma-separated tags"
echo " --featured Mark as featured (true/false)"
echo " --language Target language (en/es/all)"
echo ""
echo "Examples:"
echo " $0 blog-post --title 'Advanced Rust Patterns' --category 'Programming' --tags 'rust,patterns'"
echo " $0 recipe --title 'Docker Security Setup' --category 'Security' --difficulty 'Advanced'"
echo " $0 templates"
echo ""
}
# Generate unique ID from title
generate_id() {
local title="$1"
echo "$title" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g'
}
# Generate current date
get_current_date() {
date +"%Y-%m-%d"
}
# Create templates directory and files
create_templates() {
log_step "Creating template files..."
mkdir -p "$TEMPLATES_DIR"
# Blog post template
cat > "$TEMPLATES_DIR/blog-post.md" << 'EOF'
---
id: "{{ID}}"
title: "{{TITLE}}"
subtitle: "{{SUBTITLE}}"
excerpt: "{{EXCERPT}}"
author: "{{AUTHOR}}"
date: "{{DATE}}"
read_time: "{{READ_TIME}}"
tags: [{{TAGS}}]
featured: {{FEATURED}}
published: true
language: "{{LANGUAGE}}"
translations: [{{TRANSLATIONS}}]
---
# {{TITLE}}
{{SUBTITLE}}
## Introduction
{{EXCERPT}}
## Content
Write your blog post content here...
## Conclusion
Summarize your key points here...
---
*Published on {{DATE}} by {{AUTHOR}}*
EOF
# Recipe template
cat > "$TEMPLATES_DIR/recipe.md" << 'EOF'
---
id: "{{ID}}"
title: "{{TITLE}}"
subtitle: "{{SUBTITLE}}"
excerpt: "{{EXCERPT}}"
category: "{{CATEGORY}}"
difficulty: "{{DIFFICULTY}}"
prep_time: "{{PREP_TIME}}"
tags: [{{TAGS}}]
featured: {{FEATURED}}
language: "{{LANGUAGE}}"
translations: [{{TRANSLATIONS}}]
---
# {{TITLE}}
{{SUBTITLE}}
## Overview
{{EXCERPT}}
## Prerequisites
- List prerequisites here
- Required tools and knowledge
- System requirements
## Difficulty Level: {{DIFFICULTY}}
**Estimated Time:** {{PREP_TIME}}
## Steps
### Step 1: Initial Setup
Describe the first step...
### Step 2: Configuration
Describe configuration steps...
### Step 3: Implementation
Describe implementation details...
## Testing
How to test the implementation...
## Troubleshooting
Common issues and solutions...
## Additional Resources
- Link to relevant documentation
- Related recipes or articles
- Community resources
---
*Category: {{CATEGORY}} | Difficulty: {{DIFFICULTY}} | Time: {{PREP_TIME}}*
EOF
# JSON templates
cat > "$TEMPLATES_DIR/blog-post.json" << 'EOF'
{
"id": "{{ID}}",
"title": "{{TITLE}}",
"subtitle": "{{SUBTITLE}}",
"excerpt": "{{EXCERPT}}",
"author": "{{AUTHOR}}",
"date": "{{DATE}}",
"read_time": "{{READ_TIME}}",
"tags": [{{TAGS_JSON}}],
"featured": {{FEATURED}},
"published": true,
"language": "{{LANGUAGE}}",
"source_file": "content/blog/{{LANGUAGE}}/{{ID}}.md",
"html_file": "/blog/{{LANGUAGE}}/{{ID}}.html",
"translations": [{{TRANSLATIONS_JSON}}]
}
EOF
cat > "$TEMPLATES_DIR/recipe.json" << 'EOF'
{
"id": "{{ID}}",
"title": "{{TITLE}}",
"subtitle": "{{SUBTITLE}}",
"excerpt": "{{EXCERPT}}",
"category": "{{CATEGORY}}",
"difficulty": "{{DIFFICULTY}}",
"prep_time": "{{PREP_TIME}}",
"tags": [{{TAGS_JSON}}],
"featured": {{FEATURED}},
"language": "{{LANGUAGE}}",
"source_file": "content/recipes/{{LANGUAGE}}/{{ID}}.md",
"html_file": "/recipes/{{LANGUAGE}}/{{ID}}.html",
"translations": [{{TRANSLATIONS_JSON}}]
}
EOF
log_success "Template files created in $TEMPLATES_DIR"
}
# Parse command line arguments
parse_args() {
TITLE=""
AUTHOR="Jesús Pérez"
CATEGORY=""
DIFFICULTY=""
PREP_TIME=""
TAGS=""
FEATURED="false"
LANGUAGE="all"
SUBTITLE=""
EXCERPT=""
while [[ $# -gt 0 ]]; do
case $1 in
--title)
TITLE="$2"
shift 2
;;
--author)
AUTHOR="$2"
shift 2
;;
--category)
CATEGORY="$2"
shift 2
;;
--difficulty)
DIFFICULTY="$2"
shift 2
;;
--prep-time)
PREP_TIME="$2"
shift 2
;;
--tags)
TAGS="$2"
shift 2
;;
--featured)
FEATURED="$2"
shift 2
;;
--language)
LANGUAGE="$2"
shift 2
;;
--subtitle)
SUBTITLE="$2"
shift 2
;;
--excerpt)
EXCERPT="$2"
shift 2
;;
*)
log_error "Unknown option: $1"
show_usage
exit 1
;;
esac
done
}
# Convert tags to JSON array format
tags_to_json() {
local tags="$1"
if [[ -z "$tags" ]]; then
echo ""
return
fi
echo "$tags" | sed 's/,/", "/g' | sed 's/^/"/' | sed 's/$/"/'
}
# Get translation languages
get_translations() {
local current_lang="$1"
local translations=""
for lang in "${SUPPORTED_LANGUAGES[@]}"; do
if [[ "$lang" != "$current_lang" ]]; then
if [[ -z "$translations" ]]; then
translations="\"$lang\""
else
translations="$translations, \"$lang\""
fi
fi
done
echo "$translations"
}
# Replace template variables
replace_template_vars() {
local template_file="$1"
local output_file="$2"
local lang="$3"
local id=$(generate_id "$TITLE")
local date=$(get_current_date)
local tags_json=$(tags_to_json "$TAGS")
local translations_json=$(get_translations "$lang")
# Set defaults if not provided
if [[ -z "$SUBTITLE" ]]; then
SUBTITLE="A comprehensive guide"
fi
if [[ -z "$EXCERPT" ]]; then
EXCERPT="This content provides detailed information and practical examples."
fi
# Calculate read time (rough estimate: 200 words per minute)
local read_time="5 min read"
cp "$template_file" "$output_file"
# Replace all template variables
sed -i "s|{{ID}}|$id|g" "$output_file"
sed -i "s|{{TITLE}}|$TITLE|g" "$output_file"
sed -i "s|{{SUBTITLE}}|$SUBTITLE|g" "$output_file"
sed -i "s|{{EXCERPT}}|$EXCERPT|g" "$output_file"
sed -i "s|{{AUTHOR}}|$AUTHOR|g" "$output_file"
sed -i "s|{{DATE}}|$date|g" "$output_file"
sed -i "s|{{READ_TIME}}|$read_time|g" "$output_file"
sed -i "s|{{CATEGORY}}|$CATEGORY|g" "$output_file"
sed -i "s|{{DIFFICULTY}}|$DIFFICULTY|g" "$output_file"
sed -i "s|{{PREP_TIME}}|$PREP_TIME|g" "$output_file"
sed -i "s|{{TAGS}}|$TAGS|g" "$output_file"
sed -i "s|{{TAGS_JSON}}|$tags_json|g" "$output_file"
sed -i "s|{{FEATURED}}|$FEATURED|g" "$output_file"
sed -i "s|{{LANGUAGE}}|$lang|g" "$output_file"
sed -i "s|{{TRANSLATIONS}}|$(get_translations "$lang" | sed 's/"//g')|g" "$output_file"
sed -i "s|{{TRANSLATIONS_JSON}}|$translations_json|g" "$output_file"
}
# Generate blog post
generate_blog_post() {
if [[ -z "$TITLE" ]]; then
log_error "Title is required for blog posts"
show_usage
exit 1
fi
local id=$(generate_id "$TITLE")
local languages=("${SUPPORTED_LANGUAGES[@]}")
if [[ "$LANGUAGE" != "all" ]]; then
languages=("$LANGUAGE")
fi
log_step "Generating blog post: $TITLE"
for lang in "${languages[@]}"; do
local blog_dir="${CONTENT_DIR}/blog/${lang}"
mkdir -p "$blog_dir"
local md_file="${blog_dir}/${id}.md"
local json_file="/tmp/${id}.json"
# Generate markdown file
replace_template_vars "$TEMPLATES_DIR/blog-post.md" "$md_file" "$lang"
log_success "Created markdown file: $md_file"
# Generate JSON entry
replace_template_vars "$TEMPLATES_DIR/blog-post.json" "$json_file" "$lang"
# Update index file
local index_file="${blog_dir}/index.json"
if [[ -f "$index_file" ]]; then
# Add to existing index
local temp_file="/tmp/blog_index_temp.json"
jq --argjson new_post "$(cat "$json_file")" '.blog += [$new_post]' "$index_file" > "$temp_file"
mv "$temp_file" "$index_file"
else
# Create new index
cat > "$index_file" << EOF
{
"blog": [$(cat "$json_file")],
"language": "$lang"
}
EOF
fi
log_success "Updated blog index for language: $lang"
rm "$json_file"
done
log_success "Blog post '$TITLE' generated successfully!"
}
# Generate recipe
generate_recipe() {
if [[ -z "$TITLE" ]]; then
log_error "Title is required for recipes"
show_usage
exit 1
fi
if [[ -z "$CATEGORY" ]]; then
log_error "Category is required for recipes"
show_usage
exit 1
fi
# Set defaults
if [[ -z "$DIFFICULTY" ]]; then
DIFFICULTY="Intermediate"
fi
if [[ -z "$PREP_TIME" ]]; then
PREP_TIME="30 min"
fi
local id=$(generate_id "$TITLE")
local languages=("${SUPPORTED_LANGUAGES[@]}")
if [[ "$LANGUAGE" != "all" ]]; then
languages=("$LANGUAGE")
fi
log_step "Generating recipe: $TITLE"
for lang in "${languages[@]}"; do
local recipe_dir="${CONTENT_DIR}/recipes/${lang}"
mkdir -p "$recipe_dir"
local md_file="${recipe_dir}/${id}.md"
local json_file="/tmp/${id}.json"
# Generate markdown file
replace_template_vars "$TEMPLATES_DIR/recipe.md" "$md_file" "$lang"
log_success "Created markdown file: $md_file"
# Generate JSON entry
replace_template_vars "$TEMPLATES_DIR/recipe.json" "$json_file" "$lang"
# Update index file
local index_file="${recipe_dir}/index.json"
if [[ -f "$index_file" ]]; then
# Add to existing index
local temp_file="/tmp/recipe_index_temp.json"
jq --argjson new_recipe "$(cat "$json_file")" '.recipes += [$new_recipe]' "$index_file" > "$temp_file"
mv "$temp_file" "$index_file"
else
# Create new index
cat > "$index_file" << EOF
{
"recipes": [$(cat "$json_file")],
"language": "$lang"
}
EOF
fi
log_success "Updated recipe index for language: $lang"
rm "$json_file"
done
log_success "Recipe '$TITLE' generated successfully!"
}
# Update all indices
update_indices() {
log_step "Updating all content indices..."
for lang in "${SUPPORTED_LANGUAGES[@]}"; do
# Update blog index
local blog_dir="${CONTENT_DIR}/blog/${lang}"
local blog_index="${blog_dir}/index.json"
if [[ -d "$blog_dir" ]] && [[ ! -f "$blog_index" ]]; then
log_info "Creating missing blog index for language: $lang"
cat > "$blog_index" << EOF
{
"blog": [],
"language": "$lang"
}
EOF
fi
# Update recipe index
local recipe_dir="${CONTENT_DIR}/recipes/${lang}"
local recipe_index="${recipe_dir}/index.json"
if [[ -d "$recipe_dir" ]] && [[ ! -f "$recipe_index" ]]; then
log_info "Creating missing recipe index for language: $lang"
cat > "$recipe_index" << EOF
{
"recipes": [],
"language": "$lang"
}
EOF
fi
done
log_success "Indices updated successfully!"
}
# Main function
main() {
local command="$1"
shift
case "$command" in
"blog-post")
parse_args "$@"
generate_blog_post
;;
"recipe")
parse_args "$@"
generate_recipe
;;
"templates")
create_templates
;;
"index")
update_indices
;;
"help"|"")
show_usage
;;
*)
log_error "Unknown command: $command"
show_usage
exit 1
;;
esac
}
# Check if templates exist, create if not
if [[ ! -d "$TEMPLATES_DIR" ]] && [[ "$1" != "templates" ]] && [[ "$1" != "help" ]] && [[ "$1" != "" ]]; then
log_info "Templates not found, creating them first..."
create_templates
fi
# Run main function
main "$@"