website-htmx-rustelo-code/scripts/sh/content/migrate-to-category-structure.sh

272 lines
8.4 KiB
Bash
Raw Permalink Normal View History

2026-07-10 03:44:13 +01:00
#!/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