#!/usr/bin/env bash # update-examples-references.sh # # Recursively finds README.md files under examples/ that have frontmatter # (name, description, workflow) and rebuilds: # 1. The Examples Reference table in README.md (between EXAMPLES_REFERENCE markers) # 2. The detailed Examples section in examples/README.md (between EXAMPLES_DETAIL markers) # # Examples can be nested at any depth (e.g., examples/eks-upgrades/in-place/README.md). # Only README.md files with valid frontmatter (name field) are included. # # Usage: # ./misc/update-examples-references.sh # run from repo root # ./misc/update-examples-references.sh --dry-run # preview without writing set -euo pipefail REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" README="$REPO_ROOT/README.md" EXAMPLES_README="$REPO_ROOT/examples/README.md" EXAMPLES_DIR="$REPO_ROOT/examples" DRY_RUN=false if [[ "${1:-}" == "--dry-run" ]]; then DRY_RUN=true fi # --- Parse frontmatter from a markdown file --- parse_frontmatter() { local file="$1" local key="$2" awk -v key="$key" ' /^---$/ { block++; next } block == 1 && $0 ~ "^" key ":" { sub("^" key ":[ ]*", "") print exit } block >= 2 { exit } ' "$file" } # --- Convert filename to human-readable label --- humanize_filename() { local filename="$1" local base="${filename%.*}" base="${base//-/ }" echo "${base^}" } # --- Find all example README.md files with frontmatter --- # Returns paths sorted alphabetically find_example_readmes() { while IFS= read -r -d '' readme; do # Only include if it has frontmatter with a name field local name name="$(parse_frontmatter "$readme" "name")" if [[ -n "$name" ]]; then echo "$readme" fi done < <(find "$EXAMPLES_DIR" -name 'README.md' -print0 | sort -z) } # --- Build the main README table --- build_table() { echo '## Examples' echo '' echo '> _This table is auto-generated by [`misc/update-examples-references.sh`](misc/update-examples-references.sh). Do not edit manually._' echo '' echo '| Example | Description | Workflow |' echo '|---------|-------------|----------|' local readmes readmes="$(find_example_readmes)" if [[ -z "$readmes" ]]; then return fi while IFS= read -r readme; do local name description workflow rel_dir name="$(parse_frontmatter "$readme" "name")" description="$(parse_frontmatter "$readme" "description")" workflow="$(parse_frontmatter "$readme" "workflow")" if [[ -z "$description" ]]; then description="_(no description in frontmatter)_" fi # Build relative path to the example directory (not the README itself) rel_dir="$(dirname "${readme#$REPO_ROOT/}")" # Build workflow link if present local workflow_cell if [[ -n "$workflow" ]]; then local wf_name wf_name="$(basename "$workflow" .md)" workflow_cell="[$wf_name]($workflow)" else workflow_cell="--" fi echo "| **[$name]($rel_dir/)** | $description | $workflow_cell |" done <<< "$readmes" } # --- Build the detailed examples/README.md section --- build_examples_detail() { local readmes first=true readmes="$(find_example_readmes)" if [[ -z "$readmes" ]]; then return fi while IFS= read -r readme; do local name description workflow rel_dir example_dir name="$(parse_frontmatter "$readme" "name")" description="$(parse_frontmatter "$readme" "description")" workflow="$(parse_frontmatter "$readme" "workflow")" example_dir="$(dirname "$readme")" # Relative from examples/ directory rel_dir="${example_dir#$EXAMPLES_DIR/}" if [[ -z "$description" ]]; then description="_(no description in frontmatter)_" fi if $first; then first=false else echo "" echo "---" echo "" fi echo "### [$name](./$rel_dir/)" echo "" echo "$description" # Workflow link if [[ -n "$workflow" ]]; then local wf_name wf_name="$(basename "$workflow" .md)" echo "" echo "**Workflow:** [$wf_name](../$workflow)" fi # --- Manifests --- local manifests_dir="$example_dir/manifests" if [[ -d "$manifests_dir" ]]; then local manifest_files=() while IFS= read -r -d '' f; do manifest_files+=("$f") done < <(find "$manifests_dir" -maxdepth 1 -type f -print0 | sort -z) if [[ ${#manifest_files[@]} -gt 0 ]]; then echo "" echo "**Manifests:**" echo "" echo "| File | Description |" echo "|------|-------------|" for f in "${manifest_files[@]}"; do local base base="$(basename "$f")" local label label="$(humanize_filename "$base")" echo "| [$base](./$rel_dir/manifests/$base) | $label |" done fi fi # --- Scripts --- local scripts_dir="$example_dir/scripts" if [[ -d "$scripts_dir" ]]; then local script_files=() while IFS= read -r -d '' f; do script_files+=("$f") done < <(find "$scripts_dir" -maxdepth 1 -type f -print0 | sort -z) if [[ ${#script_files[@]} -gt 0 ]]; then echo "" echo "**Scripts:**" echo "" echo "| Script | Description |" echo "|--------|-------------|" for f in "${script_files[@]}"; do local base base="$(basename "$f")" local label label="$(humanize_filename "$base")" echo "| [$base](./$rel_dir/scripts/$base) | $label |" done fi fi # --- Tests --- local tests_dir="$example_dir/tests" if [[ -d "$tests_dir" ]]; then local test_files=() while IFS= read -r -d '' f; do test_files+=("$f") done < <(find "$tests_dir" -maxdepth 1 -type f -name '*.md' -print0 | sort -z) if [[ ${#test_files[@]} -gt 0 ]]; then echo "" echo "**Test Results:**" echo "" echo "| Test | Description |" echo "|------|-------------|" for f in "${test_files[@]}"; do local base base="$(basename "$f")" local label label="$(humanize_filename "$base")" echo "| [$base](./$rel_dir/tests/$base) | $label |" done fi fi done <<< "$readmes" } # --- Replace a section between markers in a file --- replace_section() { local file="$1" local start_marker="$2" local end_marker="$3" local new_content="$4" if ! grep -q "$start_marker" "$file"; then echo "ERROR: Could not find $start_marker in $file" >&2 return 1 fi if ! grep -q "$end_marker" "$file"; then echo "ERROR: Could not find $end_marker in $file" >&2 return 1 fi awk -v start="$start_marker" -v end="$end_marker" -v new_section="$new_content" ' $0 == start { print start print new_section skip = 1 next } $0 == end { print end skip = 0 next } !skip { print } ' "$file" > "$file.tmp" mv "$file.tmp" "$file" } # ============================================= # 1. Update main README.md — Examples Reference # ============================================= README_START='' README_END='' if ! grep -q "$README_START" "$README"; then echo "ERROR: Could not find $README_START in $README" >&2 echo "Add the markers around the Examples section first." >&2 exit 1 fi NEW_README_SECTION="$(build_table)" if $DRY_RUN; then echo "=== DRY RUN — README.md Examples Reference ===" echo "" echo "$README_START" echo "$NEW_README_SECTION" echo "$README_END" echo "" fi # ============================================= # 2. Update examples/README.md — Detailed Examples # ============================================= EXAMPLES_START='' EXAMPLES_END='' NEW_EXAMPLES_SECTION="" if [[ -f "$EXAMPLES_README" ]] && grep -q "$EXAMPLES_START" "$EXAMPLES_README" 2>/dev/null; then NEW_EXAMPLES_SECTION="$(build_examples_detail)" if $DRY_RUN; then echo "=== DRY RUN — examples/README.md Detailed Examples ===" echo "" echo "$EXAMPLES_START" echo "$NEW_EXAMPLES_SECTION" echo "$EXAMPLES_END" echo "" fi fi # ============================================= # Apply changes (unless dry run) # ============================================= if $DRY_RUN; then echo "=== No changes written ===" exit 0 fi # Update main README.md replace_section "$README" "$README_START" "$README_END" "$NEW_README_SECTION" echo "Updated Examples Reference table in README.md" # Update examples/README.md (if markers exist) if [[ -n "$NEW_EXAMPLES_SECTION" ]]; then replace_section "$EXAMPLES_README" "$EXAMPLES_START" "$EXAMPLES_END" "$NEW_EXAMPLES_SECTION" echo "Updated detailed examples section in examples/README.md" fi echo " Examples found:" find_example_readmes | while IFS= read -r readme; do local_name="$(parse_frontmatter "$readme" "name")" rel_path="${readme#$REPO_ROOT/}" echo " - $local_name ($(dirname "$rel_path")/)" done