#!/usr/bin/env bash set -euo pipefail ############################################################################### # Hugo Archive Generator # # This script scans all posts in `content/posts`, determines which months fall # outside of the rolling window, and generates archive pages in: # # content/archive/YYYY/_index.md # content/archive/YYYY/MM/_index.md # # Additionally: # - Generates a separate articles archive in: # content/archive/articles/YYYY/MM # - Only includes posts with: # layout: post # AND without "review" tag # # Behavior: # - Extracts `date:` from front matter (supports ISO timestamps) # - Groups posts by year/month # - Skips months within the rolling window (recent content stays on homepage) # - Reads rolling window from config.toml ([params].archive_rolling_window) # - Allows CLI override # # Usage: # ./generate-archive.sh [ROLLING_WINDOW] # # Priority: # 1. CLI argument # 2. config.toml # 3. default = 20 # # Compatibility: # - Works with macOS default bash (3.2) by avoiding associative arrays ############################################################################### POSTS_DIR="content/posts" ARCHIVE_DIR="content/archive" ARTICLES_ARCHIVE_DIR="content/archive/articles" CONFIG_FILE="config.toml" # Default fallback DEFAULT_WINDOW=20 # Read from config.toml if present CONFIG_WINDOW="" if [[ -f "$CONFIG_FILE" ]]; then CONFIG_WINDOW=$(grep -E 'archive_rolling_window' "$CONFIG_FILE" \ | sed -E 's/.*=[[:space:]]*([0-9]+).*/\1/' \ | head -n1 || true) fi # Final rolling window resolution ROLLING_WINDOW="${1:-${CONFIG_WINDOW:-$DEFAULT_WINDOW}}" echo "Using rolling window: $ROLLING_WINDOW months" # Current year/month CURRENT_YEAR=$(date +%Y) CURRENT_MONTH=$(date +%m) current_abs=$((10#$CURRENT_YEAR * 12 + 10#$CURRENT_MONTH)) ############################################################################### # Collect all post months (general archive) ############################################################################### months_list=$(while IFS= read -r file; do raw_date=$(grep -E '^date:' "$file" | head -n1 | sed -E 's/^date:[[:space:]]*"?([^"]*)"?/\1/') if [[ -z "${raw_date:-}" ]]; then continue fi year=${raw_date:0:4} month=${raw_date:5:2} if [[ "$year" =~ ^[0-9]{4}$ && "$month" =~ ^[0-9]{2}$ ]]; then echo "$year/$month" fi done < <(find "$POSTS_DIR" -type f -name "*.md") | sort -u) ############################################################################### # Collect article months (filtered) ############################################################################### article_months_list=$(while IFS= read -r file; do # Must have layout: post if ! grep -qE '^layout:[[:space:]]*post' "$file"; then continue fi # Must NOT contain "review" tag if grep -qE '^tags:.*review' "$file"; then continue fi raw_date=$(grep -E '^date:' "$file" | head -n1 | sed -E 's/^date:[[:space:]]*"?([^"]*)"?/\1/') if [[ -z "${raw_date:-}" ]]; then continue fi year=${raw_date:0:4} month=${raw_date:5:2} if [[ "$year" =~ ^[0-9]{4}$ && "$month" =~ ^[0-9]{2}$ ]]; then echo "$year/$month" fi done < <(find "$POSTS_DIR" -type f -name "*.md") | sort -u) ############################################################################### # Function to process archive generation ############################################################################### generate_archive() { local list="$1" local base_dir="$2" local is_articles="$3" while IFS= read -r key; do year="${key%%/*}" month="${key##*/}" abs=$((10#$year * 12 + 10#$month)) diff=$((current_abs - abs)) if (( diff < ROLLING_WINDOW )); then continue fi year_dir="$base_dir/$year" month_dir="$year_dir/$month" mkdir -p "$month_dir" # Year index if [[ ! -f "$year_dir/_index.md" ]]; then if [[ "$is_articles" == "true" ]]; then cat > "$year_dir/_index.md" < "$year_dir/_index.md" < "$month_dir/_index.md" < "$month_dir/_index.md" <