1+ # .github/actions/build-docc/action.yml
2+ name : ' Build DocC Documentation'
3+ description : ' Builds Swift DocC documentation for multiple products and platforms'
4+
5+ inputs :
6+ products :
7+ description : ' Comma-separated list of product names (e.g., "ValidatorCore,ValidatorUI")'
8+ required : true
9+ docc-paths :
10+ description : ' Comma-separated list of .docc paths (e.g., "Sources/ValidatorCore/Validator.docc,Sources/ValidatorUI/ValidatorUI.docc")'
11+ required : false
12+ default : ' '
13+ bundle-identifier-prefix :
14+ description : ' Bundle identifier prefix (e.g., "dev.validator")'
15+ required : false
16+ default : ' dev.package'
17+ bundle-version :
18+ description : ' Bundle version for documentation'
19+ required : false
20+ default : ' 1.0.0'
21+ platforms :
22+ description : ' Comma-separated list of platforms to build for (e.g., "iOS,macOS,watchOS,tvOS,visionOS")'
23+ required : false
24+ default : ' iOS,macOS,watchOS,tvOS,visionOS'
25+ output-path :
26+ description : ' Output directory for generated documentation'
27+ required : false
28+ default : ' ./docs'
29+ hosting-base-path :
30+ description : ' Base path for static hosting (leave empty for root)'
31+ required : false
32+ default : ' '
33+
34+ runs :
35+ using : ' composite'
36+ steps :
37+ - name : Verify Schemes
38+ shell : bash
39+ run : |
40+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
41+ echo "Verifying available schemes"
42+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
43+ xcodebuild -list
44+ echo ""
45+
46+ IFS=',' read -ra PRODUCTS <<< "${{ inputs.products }}"
47+ for PRODUCT in "${PRODUCTS[@]}"; do
48+ if xcodebuild -list | grep -q "^\s*${PRODUCT}\s*$"; then
49+ echo "✓ Scheme '${PRODUCT}' found"
50+ else
51+ echo "✗ Scheme '${PRODUCT}' NOT FOUND"
52+ echo " Available schemes:"
53+ xcodebuild -list | grep -A 100 "Schemes:" | grep "^\s" | head -20
54+ exit 1
55+ fi
56+ done
57+
58+ - name : Setup Build Directories
59+ shell : bash
60+ run : |
61+ mkdir -p .build/symbol-graphs
62+ mkdir -p ${{ inputs.output-path }}
63+
64+ - name : Build Symbol Graphs
65+ shell : bash
66+ run : |
67+ IFS=',' read -ra PRODUCTS <<< "${{ inputs.products }}"
68+ IFS=',' read -ra PLATFORMS <<< "${{ inputs.platforms }}"
69+
70+ for PRODUCT in "${PRODUCTS[@]}"; do
71+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
72+ echo "Building symbol graphs for ${PRODUCT}"
73+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
74+
75+ for PLATFORM in "${PLATFORMS[@]}"; do
76+ echo "→ Platform: ${PLATFORM}"
77+ SYMBOL_DIR=".build/symbol-graphs/${PRODUCT}/${PLATFORM}"
78+ mkdir -p "${SYMBOL_DIR}"
79+
80+ xcodebuild build \
81+ -scheme "${PRODUCT}" \
82+ -destination "generic/platform=${PLATFORM}" \
83+ -derivedDataPath .deriveddata \
84+ DOCC_EXTRACT_EXTENSION_SYMBOLS=YES \
85+ OTHER_SWIFT_FLAGS="-Xfrontend -emit-symbol-graph -Xfrontend -emit-symbol-graph-dir -Xfrontend ${SYMBOL_DIR} -Xfrontend -emit-extension-block-symbols"
86+
87+ BUILD_STATUS=$?
88+ if [ $BUILD_STATUS -eq 0 ]; then
89+ echo "✓ ${PRODUCT} built successfully for ${PLATFORM}"
90+ echo " Symbol graphs in: ${SYMBOL_DIR}"
91+ ls -la "${SYMBOL_DIR}" || echo " (empty)"
92+ else
93+ echo "✗ ${PRODUCT} build failed for ${PLATFORM} (exit code: ${BUILD_STATUS})"
94+ fi
95+ done
96+ done
97+
98+ - name : Generate Documentation
99+ shell : bash
100+ run : |
101+ IFS=',' read -ra PRODUCTS <<< "${{ inputs.products }}"
102+ IFS=',' read -ra DOCC_PATHS <<< "${{ inputs.docc-paths }}"
103+
104+ for i in "${!PRODUCTS[@]}"; do
105+ PRODUCT="${PRODUCTS[$i]}"
106+ DOCC_PATH=""
107+
108+ if [ ${#DOCC_PATHS[@]} -gt $i ]; then
109+ DOCC_PATH="${DOCC_PATHS[$i]}"
110+ fi
111+
112+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
113+ echo "Generating documentation for ${PRODUCT}"
114+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
115+
116+ BUNDLE_ID="${{ inputs.bundle-identifier-prefix }}.${PRODUCT}"
117+ SYMBOL_GRAPHS_DIR=".build/symbol-graphs/${PRODUCT}"
118+
119+ # Check if symbol graphs exist
120+ if [ -d "${SYMBOL_GRAPHS_DIR}" ]; then
121+ SYMBOL_COUNT=$(find "${SYMBOL_GRAPHS_DIR}" -name "*.symbols.json" | wc -l)
122+ echo "Found ${SYMBOL_COUNT} symbol graph files"
123+ if [ $SYMBOL_COUNT -eq 0 ]; then
124+ echo "⚠ Warning: No symbol graphs found for ${PRODUCT}"
125+ echo " This usually means the build failed or there's no public API"
126+ fi
127+ else
128+ echo "⚠ Warning: Symbol graphs directory not found: ${SYMBOL_GRAPHS_DIR}"
129+ fi
130+
131+ if [ -n "${DOCC_PATH}" ] && [ -d "${DOCC_PATH}" ]; then
132+ echo "Using .docc catalog: ${DOCC_PATH}"
133+ $(xcrun --find docc) convert "${DOCC_PATH}" \
134+ --fallback-display-name "${PRODUCT}" \
135+ --fallback-bundle-identifier "${BUNDLE_ID}" \
136+ --fallback-bundle-version "${{ inputs.bundle-version }}" \
137+ --output-dir "${PRODUCT}.doccarchive" \
138+ --additional-symbol-graph-dir "${SYMBOL_GRAPHS_DIR}"
139+ else
140+ echo "⚠ .docc catalog not found at: ${DOCC_PATH}"
141+ echo "Generating from symbol graphs only"
142+
143+ if [ ! -d "${SYMBOL_GRAPHS_DIR}" ] || [ $(find "${SYMBOL_GRAPHS_DIR}" -name "*.symbols.json" | wc -l) -eq 0 ]; then
144+ echo "✗ Error: Cannot generate documentation without .docc catalog or symbol graphs"
145+ continue
146+ fi
147+
148+ $(xcrun --find docc) convert \
149+ --fallback-display-name "${PRODUCT}" \
150+ --fallback-bundle-identifier "${BUNDLE_ID}" \
151+ --fallback-bundle-version "${{ inputs.bundle-version }}" \
152+ --output-dir "${PRODUCT}.doccarchive" \
153+ --additional-symbol-graph-dir "${SYMBOL_GRAPHS_DIR}"
154+ fi
155+
156+ DOCC_STATUS=$?
157+ if [ $DOCC_STATUS -ne 0 ]; then
158+ echo "✗ Failed to generate documentation for ${PRODUCT}"
159+ continue
160+ fi
161+
162+ # Check if .doccarchive was created
163+ if [ ! -d "${PRODUCT}.doccarchive" ]; then
164+ echo "✗ Error: ${PRODUCT}.doccarchive was not created"
165+ continue
166+ fi
167+
168+ echo "✓ Documentation archive created"
169+
170+ # Transform for static hosting
171+ if [ -n "${{ inputs.hosting-base-path }}" ]; then
172+ BASE_PATH="${{ inputs.hosting-base-path }}/${PRODUCT}"
173+ else
174+ BASE_PATH="${PRODUCT}"
175+ fi
176+
177+ echo "Transforming for static hosting with base path: ${BASE_PATH}"
178+
179+ $(xcrun --find docc) process-archive transform-for-static-hosting \
180+ "${PRODUCT}.doccarchive" \
181+ --output-path "${{ inputs.output-path }}/${PRODUCT}" \
182+ --hosting-base-path "${BASE_PATH}"
183+
184+ TRANSFORM_STATUS=$?
185+ if [ $TRANSFORM_STATUS -eq 0 ]; then
186+ echo "✓ ${PRODUCT} documentation generated successfully"
187+ echo " Output: ${{ inputs.output-path }}/${PRODUCT}"
188+ ls -la "${{ inputs.output-path }}/${PRODUCT}" | head -10
189+ else
190+ echo "✗ Failed to transform ${PRODUCT} for static hosting"
191+ fi
192+ done
193+
194+ - name : Verify Generated Documentation
195+ shell : bash
196+ run : |
197+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
198+ echo "Verifying generated documentation structure"
199+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
200+
201+ IFS=',' read -ra PRODUCTS <<< "${{ inputs.products }}"
202+
203+ for PRODUCT in "${PRODUCTS[@]}"; do
204+ DOCS_DIR="${{ inputs.output-path }}/${PRODUCT}"
205+
206+ if [ ! -d "${DOCS_DIR}" ]; then
207+ echo "✗ Documentation directory not found: ${DOCS_DIR}"
208+ continue
209+ fi
210+
211+ echo "Checking ${PRODUCT}:"
212+
213+ # Check for essential files
214+ if [ -f "${DOCS_DIR}/index.html" ]; then
215+ echo " ✓ index.html exists"
216+ else
217+ echo " ✗ index.html MISSING"
218+ fi
219+
220+ if [ -d "${DOCS_DIR}/documentation" ]; then
221+ echo " ✓ documentation/ directory exists"
222+ DOC_COUNT=$(find "${DOCS_DIR}/documentation" -name "*.html" | wc -l)
223+ echo " Found ${DOC_COUNT} HTML documentation pages"
224+ else
225+ echo " ✗ documentation/ directory MISSING"
226+ fi
227+
228+ if [ -d "${DOCS_DIR}/data" ]; then
229+ echo " ✓ data/ directory exists"
230+ JSON_COUNT=$(find "${DOCS_DIR}/data" -name "*.json" | wc -l)
231+ echo " Found ${JSON_COUNT} JSON data files"
232+ else
233+ echo " ✗ data/ directory MISSING"
234+ fi
235+
236+ echo ""
237+ done
238+
239+ - name : Cleanup Build Artifacts
240+ shell : bash
241+ if : always()
242+ run : |
243+ rm -rf .deriveddata
244+ rm -rf .build
245+ rm -rf *.doccarchive
0 commit comments