11#! /bin/bash
22
33# Script to generate HTML documentation from all API specs using Redocly
4- # Preserves folder structure under docs/api-docs and generates index.html with correct links
4+ # Preserves folder structure and generates a card-based index page
55
66set -ex
77set -o pipefail
88
9- # Colors for output
9+ # Colors
1010RED=' \033[0;31m'
1111GREEN=' \033[0;32m'
1212YELLOW=' \033[1;33m'
1313BLUE=' \033[0;34m'
14- NC=' \033[0m' # No Color
14+ NC=' \033[0m'
1515
1616# Directories
1717SPECS_DIR=" specs"
@@ -21,61 +21,49 @@ ERROR_LOG="$OUTPUT_DIR/errors.log"
2121
2222echo -e " ${BLUE} 🚀 Starting API documentation generation...${NC} "
2323
24- # === CLEAN OUTPUT DIRECTORY ===
25- echo -e " ${YELLOW} 🧹 Cleaning output folder...${NC} "
24+ # Clean output folder
2625rm -rf " $OUTPUT_DIR "
2726mkdir -p " $OUTPUT_DIR "
2827
29- # Check if redocly is installed
28+ # Check Redocly
3029if ! command -v redocly & > /dev/null; then
31- echo -e " ${RED} ❌ Redocly CLI not found. Install it with :${NC} "
30+ echo -e " ${RED} ❌ Redocly CLI not found. Install it:${NC} "
3231 echo " npm install -g @redocly/cli"
3332 exit 1
3433fi
3534echo -e " ${GREEN} ✅ Redocly found: $( redocly --version) ${NC} "
3635
37- # Counters
3836success_count=0
3937error_count=0
40-
41- # Clear error log
4238> " $ERROR_LOG "
4339
44- # Function to convert a spec file to HTML
4540convert_spec_to_html () {
4641 local spec_file=" $1 "
4742 local relative_path=" ${spec_file# $SPECS_DIR / } "
4843 local output_file=" $OUTPUT_DIR /${relative_path% .* } .html"
49-
50- # Create output directory if it doesn't exist
5144 mkdir -p " $( dirname " $output_file " ) "
5245
5346 echo -e " ${BLUE} 📄 Converting: $spec_file ${NC} "
54-
5547 if redocly build-docs " $spec_file " -o " $output_file " > /dev/null 2>&1 ; then
5648 echo -e " ${GREEN} ✅ Success: $output_file ${NC} "
5749 (( success_count++ ))
58- return 0
5950 else
6051 echo -e " ${RED} ❌ Failed: $spec_file ${NC} "
6152 echo " $spec_file " >> " $ERROR_LOG "
6253 (( error_count++ ))
63- return 1
6454 fi
6555}
6656
67- # === FIND AND CONVERT SPEC FILES ===
68- echo -e " ${YELLOW} 🔍 Finding all spec files...${NC} "
57+ # Find spec files
6958mapfile -t spec_files < <( find " $SPECS_DIR " -type f \( -name " *.yaml" -o -name " *.yml" \) | sort)
70- echo -e " ${BLUE} 📊 Found ${# spec_files[@]} spec files ${NC} "
59+ echo -e " ${BLUE} 📊 Found ${# spec_files[@]} specs ${NC} "
7160
61+ # Convert specs
7262for spec_file in " ${spec_files[@]} " ; do
7363 convert_spec_to_html " $spec_file " || true
7464done
7565
76- # === GENERATE INDEX.HTML ===
77- echo -e " ${YELLOW} 📝 Generating index page...${NC} "
78-
66+ # Generate index.html
7967cat > " $INDEX_FILE " << 'EOF '
8068<!DOCTYPE html>
8169<html lang="en">
@@ -85,31 +73,26 @@ cat > "$INDEX_FILE" << 'EOF'
8573<title>Devtron API Documentation</title>
8674<style>
8775body { font-family: Arial, sans-serif; margin: 20px; background: #f8f9fa; color: #333; }
88- h1 { color: #2c3e50; }
89- h3 { margin-top: 20px; border-bottom: 1px solid #ccc; padding-bottom: 5px; color: #34495e; }
90- ul { list-style: none; padding-left: 0; }
91- li { margin: 5px 0; }
92- a { text-decoration: none; color: #1a73e8; }
93- a:hover { text-decoration: underline; }
94- .container { max-width: 900px; margin: auto; }
95- .description { margin-bottom: 20px; font-size: 1rem; color: #555; }
96- .category { margin-bottom: 20px; padding: 10px; background: #fff; border-radius: 6px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
97- .api-list li { padding: 3px 0; }
98- .footer { margin-top: 40px; font-size: 0.9rem; color: #666; }
76+ h1 { text-align: center; color: #2c3e50; }
77+ h2 { text-align: center; margin-top: 40px; color: #34495e; }
78+ .container { max-width: 1200px; margin: auto; }
79+ .grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 20px; margin-top: 20px; }
80+ .card { background: #fff; border-radius: 8px; padding: 15px; width: calc(25% - 20px); box-shadow: 0 2px 6px rgba(0,0,0,0.1); text-align: center; }
81+ .card a { text-decoration: none; color: #1a73e8; font-weight: bold; }
82+ .card a:hover { text-decoration: underline; }
83+ .footer { margin-top: 40px; font-size: 0.9rem; color: #666; text-align: center; }
9984.footer a { color: #1a73e8; text-decoration: none; }
10085.footer a:hover { text-decoration: underline; }
10186.timestamp { font-style: italic; }
87+ @media(max-width: 1024px){ .card { width: calc(33.33% - 20px); } }
88+ @media(max-width: 768px){ .card { width: calc(50% - 20px); } }
89+ @media(max-width: 480px){ .card { width: 100%; } }
10290</style>
10391</head>
10492<body>
10593<div class="container">
10694<h1>🚀 Devtron API Documentation</h1>
107- <div class="description">
108- Comprehensive API documentation for Devtron - Kubernetes-native software delivery platform
109- </div>
110-
111- <div class="categories" id="categories"></div>
112-
95+ <div id="categories"></div>
11396<div class="footer">
11497<p><a href="https://devtron.ai/" target="_blank">Devtron</a></p>
11598<p class="timestamp">Last updated: <span id="timestamp"></span></p>
@@ -126,18 +109,14 @@ for spec_file in "${spec_files[@]}"; do
126109 category=$( dirname " $relative_path " )
127110 [[ " $category " == " ." ]] && category=" Root"
128111
129- # Capitalize words and split camelCase
130112 display_category=$( echo " $category " | sed ' s/[-_]/ /g' | sed ' s/\([a-z]\)\([A-Z]\)/\1 \2/g' | sed ' s/\b\w/\U&/g' )
131-
132- # Get title or fallback
133113 title=$( grep -m 1 ' ^[[:space:]]*title:' " $spec_file " | sed ' s/^[[:space:]]*title:[[:space:]]*//' | tr -d ' "' || echo " ${relative_path% .* } " )
134114
135115 if [[ -f " $OUTPUT_DIR /$html_file " ]]; then
136116 echo " \" ${category} _$( basename " ${relative_path% .* } " ) \" : {\" category\" : \" ${display_category} \" , \" title\" : \" ${title} \" , \" filename\" : \" ${html_file} \" }," >> " $INDEX_FILE "
137117 fi
138118done
139119
140- # Remove trailing comma
141120sed -i ' $ s/,$//' " $INDEX_FILE "
142121
143122cat >> " $INDEX_FILE " << 'EOF '
@@ -153,24 +132,26 @@ function populatePage() {
153132 });
154133
155134 Object.keys(categories).sort().forEach(cat => {
156- const section = document.createElement('div');
157- section.className = "category";
158- const h3 = document.createElement('h3');
159- h3.textContent = cat;
160- section.appendChild(h3);
135+ const heading = document.createElement('h2');
136+ heading.textContent = cat;
137+ container.appendChild(heading);
138+
139+ const grid = document.createElement('div');
140+ grid.className = "grid";
161141
162- const ul = document.createElement('ul');
163142 categories[cat].sort((a,b)=>a.title.localeCompare(b.title)).forEach(api => {
164- const li = document.createElement('li');
143+ const card = document.createElement('div');
144+ card.className = "card";
145+
165146 const a = document.createElement('a');
166- a.href = api.filename; // links preserve folder structure
147+ a.href = api.filename;
167148 a.textContent = api.title;
168- li.appendChild(a);
169- ul.appendChild(li);
149+
150+ card.appendChild(a);
151+ grid.appendChild(card);
170152 });
171153
172- section.appendChild(ul);
173- container.appendChild(section);
154+ container.appendChild(grid);
174155 });
175156
176157 document.getElementById('timestamp').textContent = new Date().toLocaleString();
@@ -182,13 +163,13 @@ document.addEventListener('DOMContentLoaded', populatePage);
182163</html>
183164EOF
184165
185- echo -e " ${GREEN} ✅ Index page generated: $INDEX_FILE ${NC} "
166+ echo -e " ${GREEN} ✅ Card-based index page generated: $INDEX_FILE ${NC} "
186167
187- # === FINAL SUMMARY ===
168+ # === SUMMARY ===
188169echo -e " ${BLUE} 📊 Final Summary:${NC} "
189170echo -e " ${GREEN} ✅ Successfully converted: $success_count specs${NC} "
190171if (( error_count > 0 )) ; then
191- echo -e " ${RED} ❌ Failed to convert : $error_count (see $ERROR_LOG )${NC} "
172+ echo -e " ${RED} ❌ Failed: $error_count (see $ERROR_LOG )${NC} "
192173fi
193174echo -e " ${BLUE} 📁 Output directory: $OUTPUT_DIR ${NC} "
194175echo -e " ${BLUE} 🌐 Main index: $INDEX_FILE ${NC} "
0 commit comments