#!/bin/bash # Content Migration Script - Category-Based Structure # Migrates existing flat content structure to category-based organization set -e # Configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" CONTENT_DIR="${PROJECT_ROOT}/content" BACKUP_DIR="${PROJECT_ROOT}/content-backup-$(date +%Y%m%d_%H%M%S)" # Colors for output GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m' echo -e "${BLUE}🔄 Content Migration to Category Structure${NC}" echo -e "${BLUE}=========================================${NC}" # Create backup echo -e "${YELLOW}📋 Creating backup at: $BACKUP_DIR${NC}" cp -r "$CONTENT_DIR" "$BACKUP_DIR" echo -e "${GREEN}✅ Backup created${NC}" # Get available content types from content-kinds.toml get_content_types() { local config_file="$CONTENT_DIR/content-kinds.toml" if [ -f "$config_file" ]; then # Extract enabled content types grep -A 5 '\[\[content_kinds\]\]' "$config_file" | grep -E '^(name|enabled)' | \ awk '/name =/ {name=$3; gsub(/["]/, "", name)} /enabled = true/ {print name}' | \ grep -v '^$' else echo "blog recipes" # Fallback fi } # Get available languages by checking locales directory get_languages() { if [ -d "$CONTENT_DIR/locales" ]; then ls "$CONTENT_DIR/locales" | grep -E '^[a-z]{2}$' | tr '\n' ' ' else echo "en es" # Fallback fi } # Extract frontmatter from markdown file extract_frontmatter() { local file="$1" if head -1 "$file" | grep -q "^---$"; then awk '/^---$/{flag++; next} flag==1 && /^---$/{flag++} flag==1{print}' "$file" fi } # Get category from frontmatter get_category() { local file="$1" local frontmatter=$(extract_frontmatter "$file") echo "$frontmatter" | grep -E '^category:' | sed 's/category:[[:space:]]*//' | tr -d '"' | xargs } # Get post ID from frontmatter get_post_id() { local file="$1" local frontmatter=$(extract_frontmatter "$file") local id=$(echo "$frontmatter" | grep -E '^id:' | sed 's/id:[[:space:]]*//' | tr -d '"' | xargs) # If no ID found, use filename without extension if [ -z "$id" ]; then basename "$file" .md else echo "$id" fi } # Get emoji for category from root meta.toml get_category_emoji() { local content_type="$1" local lang="$2" local category="$3" local root_meta="$CONTENT_DIR/$content_type/$lang/meta.toml" if [ -f "$root_meta" ]; then # Extract emoji from [categories_emojis] section local emoji=$(awk -v cat="$category" ' /^\[categories_emojis\]/ { in_section = 1; next } /^\[/ && !/^\[categories_emojis\]/ { in_section = 0; next } in_section && $0 ~ "^" cat " *= *\".*\"$" { gsub(/^[^"]*"/, "", $0); gsub(/".*$/, "", $0); print $0; exit } ' "$root_meta") # Return emoji or default if [ -n "$emoji" ]; then echo "$emoji" else echo "📝" # Default fallback fi else echo "📝" # Default fallback fi } # Create category meta.toml if it doesn't exist create_category_meta() { local content_type="$1" local lang="$2" local category="$3" local category_dir="$CONTENT_DIR/$content_type/$lang/$category" local meta_file="$category_dir/meta.toml" if [ ! -f "$meta_file" ]; then mkdir -p "$category_dir" # Generate title from category slug local title=$(echo "$category" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) substr($i,2)}1') # Get emoji from root meta.toml local emoji=$(get_category_emoji "$content_type" "$lang" "$category") cat > "$meta_file" << EOF slug = "$category" title = "$title" emoji = "$emoji" published = true sort_order = 1 description = "$title content" css_class = "category-$category" css_style = "" [translations] # Add translations here when available EOF echo -e "${GREEN}📝 Created meta.toml for category: $category ($lang) with emoji: $emoji${NC}" fi } # Migrate posts for a content type and language migrate_content_type() { local content_type="$1" local lang="$2" local source_dir="$CONTENT_DIR/$content_type/$lang" echo -e "${BLUE}🔄 Migrating $content_type ($lang)...${NC}" if [ ! -d "$source_dir" ]; then echo -e "${YELLOW}⚠️ Directory not found: $source_dir${NC}" return fi # Track categories and post counts local categories="" local posts_migrated=0 local posts_uncategorized=0 # Process all markdown files in the source directory for file in "$source_dir"/*.md; do [ ! -f "$file" ] && continue local filename=$(basename "$file") local post_id=$(get_post_id "$file") local category=$(get_category "$file") # Use 'uncategorized' if no category found if [ -z "$category" ]; then category="uncategorized" ((posts_uncategorized++)) fi # Add category to list if not already present if ! echo "$categories" | grep -q "$category"; then categories="$categories $category" fi # Create category directory and meta.toml create_category_meta "$content_type" "$lang" "$category" # Determine target file path local target_dir="$CONTENT_DIR/$content_type/$lang/$category" local target_file="$target_dir/${post_id}.md" # Move file if it's not already in the right place if [ "$file" != "$target_file" ]; then mv "$file" "$target_file" echo " 📄 Moved: $filename → $category/${post_id}.md" ((posts_migrated++)) else echo " ✅ Already in place: $category/${post_id}.md" fi done # Summary for this content type/language if [ $posts_migrated -gt 0 ]; then echo -e "${GREEN}✅ Migrated $posts_migrated posts for $content_type ($lang)${NC}" echo -e " 📁 Categories:$categories" [ $posts_uncategorized -gt 0 ] && echo -e "${YELLOW} ⚠️ $posts_uncategorized posts moved to 'uncategorized'${NC}" fi } # Main migration process main() { echo -e "${BLUE}📋 Scanning content structure...${NC}" # Get content types and languages CONTENT_TYPES=($(get_content_types)) LANGUAGES=($(get_languages)) echo -e "${BLUE}Content types: ${CONTENT_TYPES[*]}${NC}" echo -e "${BLUE}Languages: ${LANGUAGES[*]}${NC}" echo "" # Migrate each content type for each language for content_type in "${CONTENT_TYPES[@]}"; do for lang in "${LANGUAGES[@]}"; do migrate_content_type "$content_type" "$lang" done echo "" done echo -e "${GREEN}🎉 Migration completed!${NC}" echo -e "${BLUE}📁 Backup saved at: $BACKUP_DIR${NC}" echo -e "${BLUE}💡 Next steps:${NC}" echo " 1. Run 'just content-build' to rebuild with new structure" echo " 2. Test content discovery and routing" echo " 3. Update any hardcoded content paths in code" echo " 4. Verify translation links work correctly" } # Check if running in correct directory if [ ! -f "$CONTENT_DIR/content-kinds.toml" ]; then echo -e "${RED}❌ Error: Not in project root or content-kinds.toml not found${NC}" echo " Run this script from the project root directory" exit 1 fi # Run migration main echo -e "${BLUE}🔍 Migration summary saved to info/summaries/${NC}" mkdir -p info/summaries cat > info/summaries/content-migration-$(date +%Y%m%d_%H%M%S).md << EOF # Content Migration Summary **Date:** $(date) **Script:** migrate-to-category-structure.sh **Backup:** $BACKUP_DIR ## Changes Made - Reorganized content into category-based directories - Created category meta.toml files for styling and metadata - Ensured filenames match frontmatter ID fields - Moved uncategorized posts to 'uncategorized' directories ## Content Types Processed $(printf "- %s\\n" "${CONTENT_TYPES[@]}") ## Languages Processed $(printf "- %s\\n" "${LANGUAGES[@]}") ## Next Steps 1. Run \`just content-build\` to rebuild content indexes 2. Test routing with new URL patterns 3. Verify translation discovery works 4. Update any hardcoded content references EOF