#!/bin/bash # Unified Content Management Script # Replaces: generate-index.sh, validate-id-consistency.sh, validate-content.sh # Usage: ./content-manager.sh [command] [options] set -e # Get script directory and load shared library SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/lib.sh" # ============================================================================= # COMMAND FUNCTIONS # ============================================================================= # Generate JSON indices from markdown frontmatter cmd_generate_indices() { log_step "Generating content indices..." local content_types=($(get_enabled_content_types)) local languages=($(get_supported_languages)) local total_errors=0 for content_type in "${content_types[@]}"; do for language in "${languages[@]}"; do if ! content_dir_exists "$content_type" "$language"; then log_warning "Content directory not found: ${CONTENT_DIR}/${content_type}/${language}" continue fi log_step "Generating $content_type index for language: $language" local content_dir="${CONTENT_DIR}/${content_type}/${language}" local index_file="$content_dir/index.json" local temp_entries="/tmp/index_entries_$$" local entry_count=0 # Clear temp file > "$temp_entries" # Process each markdown file for md_file in $(get_markdown_files "$content_type" "$language"); do if process_markdown_file "$md_file" "$content_type" "$language" > /tmp/json_entry_$$; then # Add comma if not first entry if [ $entry_count -gt 0 ]; then echo "," >> "$temp_entries" fi cat /tmp/json_entry_$$ >> "$temp_entries" entry_count=$((entry_count + 1)) rm -f /tmp/json_entry_$$ else log_warning "Failed to process: $md_file" fi done # Determine array name (keep legacy naming for recipes) local array_name="${content_type}_posts" if [ "$content_type" = "recipes" ]; then array_name="prescriptions" # Keep legacy array name for recipes fi # Build final index.json cat > "$index_file" << EOF { "$array_name": [ $(cat "$temp_entries") ], "language": "$language" } EOF # Validate JSON if validate_json "$index_file"; then log_success "Generated valid $content_type index ($language): $entry_count entries" else log_error "Generated invalid JSON for $content_type ($language)" total_errors=$((total_errors + 1)) fi # Cleanup rm -f "$temp_entries" done done if [ $total_errors -eq 0 ]; then log_success "🎉 Index generation completed successfully!" else log_error "Index generation completed with $total_errors errors" exit 1 fi } # Validate ID consistency across languages cmd_validate_ids() { log_step "Validating ID consistency across languages..." local content_types=($(get_enabled_content_types)) local languages=($(get_supported_languages)) local total_errors=0 local primary_lang="en" for content_type in "${content_types[@]}"; do log_step "Validating $content_type ID consistency..." local primary_dir="${CONTENT_DIR}/${content_type}/${primary_lang}" if [ ! -d "$primary_dir" ]; then log_error "Primary language directory not found: $primary_dir" total_errors=$((total_errors + 1)) continue fi local temp_ids="/tmp/id_validation_${content_type}_$$" > "$temp_ids" # Collect all IDs from primary language for primary_file in $(get_markdown_files "$content_type" "$primary_lang"); do local filename=$(basename "$primary_file" .md) local primary_id=$(extract_id "$primary_file") if [ -z "$primary_id" ]; then log_warning "No ID found in primary file: $primary_file" continue fi echo "$filename:$primary_id" >> "$temp_ids" # Check corresponding files in other languages for lang in "${languages[@]}"; do if [ "$lang" = "$primary_lang" ]; then continue fi local lang_file="${CONTENT_DIR}/${content_type}/${lang}/${filename}.md" if [ ! -f "$lang_file" ]; then log_warning "Missing $lang translation for $filename" continue fi local lang_id=$(extract_id "$lang_file") if [ -z "$lang_id" ]; then log_error "No ID found in $lang file: $lang_file" total_errors=$((total_errors + 1)) continue fi if [ "$primary_id" != "$lang_id" ]; then log_error "ID mismatch for $filename: $primary_lang='$primary_id' vs $lang='$lang_id'" total_errors=$((total_errors + 1)) fi done done # Check for duplicate IDs within each language for lang in "${languages[@]}"; do if ! content_dir_exists "$content_type" "$lang"; then continue fi local temp_lang_ids="/tmp/lang_ids_${content_type}_${lang}_$$" > "$temp_lang_ids" for md_file in $(get_markdown_files "$content_type" "$lang"); do local file_id=$(extract_id "$md_file") if [ -n "$file_id" ]; then echo "$file_id" >> "$temp_lang_ids" fi done # Check for duplicates local duplicates=$(sort "$temp_lang_ids" | uniq -d) if [ -n "$duplicates" ]; then echo "$duplicates" | while read -r duplicate_id; do log_error "Duplicate ID '$duplicate_id' found in $content_type ($lang)" total_errors=$((total_errors + 1)) done fi rm -f "$temp_lang_ids" done rm -f "$temp_ids" done if [ $total_errors -eq 0 ]; then log_success "🎉 All content IDs are consistent across languages!" else log_error "❌ Content validation failed with $total_errors issue(s)" exit 1 fi } # Validate content structure and quality cmd_validate_content() { log_step "Validating content structure and quality..." local content_types=($(get_enabled_content_types)) local languages=($(get_supported_languages)) local total_issues=0 for content_type in "${content_types[@]}"; do for language in "${languages[@]}"; do if ! content_dir_exists "$content_type" "$language"; then log_warning "Content directory not found: ${CONTENT_DIR}/${content_type}/${language}" continue fi log_step "Validating $content_type content for language: $language" local issues_found=0 # Check for index.json local index_file="${CONTENT_DIR}/${content_type}/${language}/index.json" if [ -f "$index_file" ]; then if validate_json "$index_file"; then log_success "Valid index.json for $content_type ($language)" else log_error "Invalid index.json for $content_type ($language)" issues_found=$((issues_found + 1)) fi else log_warning "No index.json found for $content_type ($language)" issues_found=$((issues_found + 1)) fi # Validate markdown files local md_files=($(get_markdown_files "$content_type" "$language")) local valid_files=0 for md_file in "${md_files[@]}"; do if has_valid_frontmatter "$md_file"; then valid_files=$((valid_files + 1)) else log_error "Invalid or missing frontmatter: $md_file" issues_found=$((issues_found + 1)) fi done log_info "$content_type ($language): $valid_files/${#md_files[@]} files with valid frontmatter" total_issues=$((total_issues + issues_found)) done done if [ $total_issues -eq 0 ]; then log_success "🎉 All content validation passed!" else log_error "❌ Content validation found $total_issues issue(s)" exit 1 fi } # Show content statistics cmd_stats() { log_step "Generating content statistics..." local content_types=($(get_enabled_content_types)) local languages=($(get_supported_languages)) echo "" echo "📊 Content Statistics:" echo "======================" for content_type in "${content_types[@]}"; do echo "" echo "📝 Content Type: $content_type" echo "--------------------------------" local total_files=0 for language in "${languages[@]}"; do if content_dir_exists "$content_type" "$language"; then local md_files=($(get_markdown_files "$content_type" "$language")) local count=${#md_files[@]} echo " $language: $count files" total_files=$((total_files + count)) else echo " $language: 0 files (directory not found)" fi done echo " Total: $total_files files" done echo "" echo "🌍 Languages: ${languages[*]}" echo "📂 Content Types: ${content_types[*]}" echo "📍 Content Directory: $CONTENT_DIR" } # ============================================================================= # MAIN COMMAND DISPATCHER # ============================================================================= show_help() { cat << EOF Unified Content Management Script Usage: $0 [command] [options] Commands: generate-indices Generate index.json files from markdown frontmatter validate-ids Validate ID consistency across languages validate-content Validate content structure and quality stats Show content statistics all Run generate-indices, validate-ids, and validate-content help Show this help message Options: --content-dir DIR Content directory (default: content) --verbose Enable verbose output Examples: $0 generate-indices $0 validate-ids $0 validate-content $0 all $0 stats This script replaces the previous separate scripts: - generate-index.sh - validate-id-consistency.sh - validate-content.sh The script automatically reads content types from content-kinds.toml and supports all enabled content types and languages. EOF } # Parse command line arguments VERBOSE=false CONTENT_DIR_OVERRIDE="" while [[ $# -gt 0 ]]; do case $1 in --content-dir) CONTENT_DIR_OVERRIDE="$2" shift 2 ;; --verbose) VERBOSE=true shift ;; --help|-h|help) show_help exit 0 ;; *) COMMAND="$1" shift ;; esac done # Override content directory if specified if [ -n "$CONTENT_DIR_OVERRIDE" ]; then CONTENT_DIR="$CONTENT_DIR_OVERRIDE" CONTENT_KINDS_FILE="${CONTENT_DIR}/content-kinds.toml" fi # Initialize library init_content_lib "content-manager" # Execute command case "${COMMAND:-help}" in generate-indices|indices|index) cmd_generate_indices ;; validate-ids|validate-id|ids) cmd_validate_ids ;; validate-content|validate|content) cmd_validate_content ;; stats|statistics|info) cmd_stats ;; all|full) cmd_generate_indices echo "" cmd_validate_ids echo "" cmd_validate_content ;; help|--help|-h) show_help ;; *) log_error "Unknown command: $COMMAND" echo "" show_help exit 1 ;; esac