33const { execSync, spawnSync } = require ( 'child_process' ) ;
44const { Command } = require ( 'commander' ) ;
55const path = require ( 'path' ) ;
6+ const fs = require ( 'fs' ) ;
67
78// --------------------------------------------------------------------
89// Dependency check functions
@@ -60,7 +61,7 @@ function checkPython() {
6061 }
6162 }
6263 if ( ! found ) {
63- console . error ( 'Error: Python 3.10 or higher is required but not found.\nPlease install Python and ensure `python3 --version` or `python --version` returns at least 3.10. ' ) ;
64+ console . error ( 'Error: Python 3.10 or higher is required but not found.\nPlease install Python and ensure `python3 --version` or `python --version` returns at least 3.10: https://www.geeksforgeeks.org/how-to-install-python-on-mac/ ' ) ;
6465 process . exit ( 1 ) ;
6566 }
6667}
@@ -69,7 +70,7 @@ function checkCompiler() {
6970 const gccInstalled = checkCommandExists ( 'gcc' ) ;
7071 const clangInstalled = checkCommandExists ( 'clang' ) ;
7172 if ( ! gccInstalled && ! clangInstalled ) {
72- console . error ( 'Error: A C++ compiler (such as gcc or clang) is required but not found. Please install one. ' ) ;
73+ console . error ( 'Error: A C++ compiler (such as gcc or clang) is required but not found. Please install one: https://osxdaily.com/2023/05/02/how-install-gcc-mac/ ' ) ;
7374 process . exit ( 1 ) ;
7475 }
7576}
@@ -162,75 +163,136 @@ const commonOptions = {
162163 consoleDockerRepo : 'console'
163164} ;
164165
165- // Subcommand: generate-metrics-docs.
166+ function runClusterDocs ( mode , tag , options ) {
167+ const script = path . join ( __dirname , '../cli-utils/generate-cluster-docs.sh' ) ;
168+ const args = [ mode , tag , options . dockerRepo , options . consoleTag , options . consoleDockerRepo ] ;
169+ console . log ( `Running ${ script } with arguments: ${ args . join ( ' ' ) } ` ) ;
170+ const r = spawnSync ( 'bash' , [ script , ...args ] , { stdio : 'inherit' , shell : true } ) ;
171+ if ( r . status !== 0 ) process . exit ( r . status ) ;
172+ }
173+
174+ // helper to diff two autogenerated directories
175+ function diffDirs ( kind , oldTag , newTag ) {
176+ const oldDir = path . join ( 'autogenerated' , oldTag , kind ) ;
177+ const newDir = path . join ( 'autogenerated' , newTag , kind ) ;
178+ const diffDir = path . join ( 'autogenerated' , 'diffs' , kind , `${ oldTag } _to_${ newTag } ` ) ;
179+ const patch = path . join ( diffDir , 'changes.patch' ) ;
180+
181+ if ( ! fs . existsSync ( oldDir ) ) {
182+ console . error ( `❌ Cannot diff: missing ${ oldDir } ` ) ;
183+ process . exit ( 1 ) ;
184+ }
185+ if ( ! fs . existsSync ( newDir ) ) {
186+ console . error ( `❌ Cannot diff: missing ${ newDir } ` ) ;
187+ process . exit ( 1 ) ;
188+ }
189+
190+ fs . mkdirSync ( diffDir , { recursive : true } ) ;
191+
192+ const cmd = `diff -ru "${ oldDir } " "${ newDir } " > "${ patch } " || true` ;
193+ const res = spawnSync ( cmd , { stdio : 'inherit' , shell : true } ) ;
194+
195+ if ( res . error ) {
196+ console . error ( `❌ diff failed: ${ res . error . message } ` ) ;
197+ process . exit ( 1 ) ;
198+ }
199+ console . log ( `✅ Wrote patch: ${ patch } ` ) ;
200+ }
201+
166202automation
167203 . command ( 'metrics-docs' )
168204 . description ( 'Extract Redpanda metrics and generate JSON/AsciiDoc docs' )
169205 . option ( '--tag <tag>' , 'Redpanda tag (default: latest)' , commonOptions . tag )
170- . option ( '--docker-repo <repo>' , 'Redpanda Docker repository (default: redpanda or redpanda-unstable when --tag is an RC version)' , commonOptions . dockerRepo )
171- . option ( '--console-tag <tag>' , 'Redpanda Console tag (default: latest)' , commonOptions . consoleTag )
172- . option ( '--console-docker-repo <repo>' , 'Redpanda Console Docker repository (default: console)' , commonOptions . consoleDockerRepo )
206+ . option ( '--docker-repo <repo>' , '...' , commonOptions . dockerRepo )
207+ . option ( '--console-tag <tag>' , '...' , commonOptions . consoleTag )
208+ . option ( '--console-docker-repo <repo>' , '...' , commonOptions . consoleDockerRepo )
209+ . option ( '--diff <oldTag>' , 'Also diff autogenerated metrics from <oldTag> → <tag>' )
173210 . action ( ( options ) => {
174- // Verify dependencies common to these automations.
175211 verifyMetricsDependencies ( ) ;
176- // Build argument array for the Bash automation script.
177- const args = [
178- 'metrics' ,
179- options . tag ,
180- options . dockerRepo ,
181- options . consoleTag ,
182- options . consoleDockerRepo
183- ] ;
184- const scriptPath = path . join ( __dirname , '../cli-utils/generate-cluster-docs.sh' ) ;
185- console . log ( `Running ${ scriptPath } with arguments: ${ args . join ( ' ' ) } ` ) ;
186- const result = spawnSync ( 'bash' , [ scriptPath , ...args ] , { stdio : 'inherit' , shell : true } ) ;
187- process . exit ( result . status ) ;
212+
213+ const newTag = options . tag ;
214+ const oldTag = options . diff ;
215+
216+ if ( oldTag ) {
217+ const oldDir = path . join ( 'autogenerated' , oldTag , 'metrics' ) ;
218+ if ( ! fs . existsSync ( oldDir ) ) {
219+ console . log ( `⏳ Generating metrics docs for old tag ${ oldTag } …` ) ;
220+ runClusterDocs ( 'metrics' , oldTag , options ) ;
221+ }
222+ }
223+
224+ console . log ( `⏳ Generating metrics docs for new tag ${ newTag } …` ) ;
225+ runClusterDocs ( 'metrics' , newTag , options ) ;
226+
227+ if ( oldTag ) {
228+ diffDirs ( 'metrics' , oldTag , newTag ) ;
229+ }
230+
231+ process . exit ( 0 ) ;
188232 } ) ;
189233
190- // Subcommand: generate-property-docs.
191234automation
192235 . command ( 'property-docs' )
193236 . description ( 'Extract properties from Redpanda source' )
194- . option ( '--tag <tag>' , 'Git tag or branch of Redpanda to extract from (default: dev)' , 'dev' )
237+ . option ( '--tag <tag>' , 'Git tag or branch to extract from (default: dev)' , 'dev' )
238+ . option ( '--diff <oldTag>' , 'Also diff autogenerated properties from <oldTag> → <tag>' )
195239 . action ( ( options ) => {
196240 verifyPropertyDependencies ( ) ;
197- const cwd = path . resolve ( __dirname , '../tools/property-extractor' ) ;
198- const tag = options . tag || 'dev' ;
199- const result = spawnSync ( 'make' , [ 'build' , `TAG=${ tag } ` ] , {
200- cwd,
201- stdio : 'inherit'
202- } ) ;
203- if ( result . error ) {
204- console . error ( 'Failed to run `make build`:' , result . error . message ) ;
205- process . exit ( 1 ) ;
206- } else if ( result . status !== 0 ) {
207- console . error ( `make build exited with status code ${ result . status } ` ) ;
208- process . exit ( result . status ) ;
241+
242+ const newTag = options . tag ;
243+ const oldTag = options . diff ;
244+ const cwd = path . resolve ( __dirname , '../tools/property-extractor' ) ;
245+ const make = ( tag ) => {
246+ console . log ( `⏳ Building property docs for ${ tag } …` ) ;
247+ const r = spawnSync ( 'make' , [ 'build' , `TAG=${ tag } ` ] , { cwd, stdio : 'inherit' } ) ;
248+ if ( r . error ) { console . error ( r . error ) ; process . exit ( 1 ) ; }
249+ if ( r . status !== 0 ) process . exit ( r . status ) ;
250+ } ;
251+
252+ if ( oldTag ) {
253+ const oldDir = path . join ( 'autogenerated' , oldTag , 'properties' ) ;
254+ if ( ! fs . existsSync ( oldDir ) ) make ( oldTag ) ;
209255 }
210- process . exit ( result . status ) ;
211- } ) ;
212256
213- // Subcommand: generate-rpk-docs.
257+ make ( newTag ) ;
258+
259+ if ( oldTag ) {
260+ diffDirs ( 'properties' , oldTag , newTag ) ;
261+ }
262+
263+ process . exit ( 0 ) ;
264+ } ) ;
265+
214266automation
215267 . command ( 'rpk-docs' )
216268 . description ( 'Generate documentation for rpk commands' )
217269 . option ( '--tag <tag>' , 'Redpanda tag (default: latest)' , commonOptions . tag )
218- . option ( '--docker-repo <repo>' , 'Redpanda Docker repository (default: redpanda or redpanda-unstable when --tag is an RC version)' , commonOptions . dockerRepo )
219- . option ( '--console-tag <tag>' , 'Redpanda Console tag (default: latest)' , commonOptions . consoleTag )
220- . option ( '--console-docker-repo <repo>' , 'Redpanda Console Docker repository (default: console)' , commonOptions . consoleDockerRepo )
270+ . option ( '--docker-repo <repo>' , '...' , commonOptions . dockerRepo )
271+ . option ( '--console-tag <tag>' , '...' , commonOptions . consoleTag )
272+ . option ( '--console-docker-repo <repo>' , '...' , commonOptions . consoleDockerRepo )
273+ . option ( '--diff <oldTag>' , 'Also diff autogenerated rpk docs from <oldTag> → <tag>' )
221274 . action ( ( options ) => {
222275 verifyMetricsDependencies ( ) ;
223- const args = [
224- 'rpk' ,
225- options . tag ,
226- options . dockerRepo ,
227- options . consoleTag ,
228- options . consoleDockerRepo
229- ] ;
230- const scriptPath = path . join ( __dirname , '../cli-utils/generate-cluster-docs.sh' ) ;
231- console . log ( `Running ${ scriptPath } with arguments: ${ args . join ( ' ' ) } ` ) ;
232- const result = spawnSync ( 'bash' , [ scriptPath , ...args ] , { stdio : 'inherit' , shell : true } ) ;
233- process . exit ( result . status ) ;
276+
277+ const newTag = options . tag ;
278+ const oldTag = options . diff ;
279+
280+ if ( oldTag ) {
281+ const oldDir = path . join ( 'autogenerated' , oldTag , 'rpk' ) ;
282+ if ( ! fs . existsSync ( oldDir ) ) {
283+ console . log ( `⏳ Generating rpk docs for old tag ${ oldTag } …` ) ;
284+ runClusterDocs ( 'rpk' , oldTag , options ) ;
285+ }
286+ }
287+
288+ console . log ( `⏳ Generating rpk docs for new tag ${ newTag } …` ) ;
289+ runClusterDocs ( 'rpk' , newTag , options ) ;
290+
291+ if ( oldTag ) {
292+ diffDirs ( 'rpk' , oldTag , newTag ) ;
293+ }
294+
295+ process . exit ( 0 ) ;
234296 } ) ;
235297
236298programCli
0 commit comments