392 lines
No EOL
13 KiB
Bash
Executable file
392 lines
No EOL
13 KiB
Bash
Executable file
#!/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 |