99 'head:: ' ,
1010 'origin:: ' ,
1111 'tag:: ' ,
12+ 'calculate-splits ' ,
1213 'help ' ,
1314]);
1415
1516if (isset ($ options ['help ' ])) {
1617 fwrite (STDOUT , <<<'HELP'
1718Usage:
18- php .github/scripts/monorepo-split-plan.php [--config=.github/monorepo-split.json] [--base=<git-ref>] [--head=<git-ref>] [--origin=<git-ref>] [--tag=<tag>]
19+ php .github/scripts/monorepo-split-plan.php [--config=.github/monorepo-split.json] [--base=<git-ref>] [--head=<git-ref>] [--origin=<git-ref>] [--tag=<tag>] [--calculate-splits]
1920
2021Validates the monorepo split map and prints the read-only repository split plan.
21- This is a dry-run planner only. It prints splitsh-lite commands but never runs
22- them and never pushes.
22+ This is a dry-run planner only. By default it prints splitsh-lite commands but
23+ does not run them. With --calculate-splits it runs splitsh-lite and prints split
24+ SHAs, but still never pushes.
2325
2426HELP);
2527 exit (0 );
3234$ head = isset ($ options ['head ' ]) ? (string ) $ options ['head ' ] : null ;
3335$ origin = isset ($ options ['origin ' ]) ? (string ) $ options ['origin ' ] : 'HEAD ' ;
3436$ tag = isset ($ options ['tag ' ]) ? (string ) $ options ['tag ' ] : null ;
37+ $ calculateSplits = isset ($ options ['calculate-splits ' ]);
3538
3639$ errors = [];
3740$ warnings = [];
149152 $ errors [] = 'Origin cannot be empty when --origin is provided. ' ;
150153}
151154
155+ if ($ calculateSplits && !commandExists ('splitsh-lite ' )) {
156+ $ errors [] = 'splitsh-lite is required when --calculate-splits is provided. ' ;
157+ }
158+
152159if (null !== $ base || null !== $ head ) {
153160 if (null === $ base || null === $ head ) {
154161 $ errors [] = 'Both --base and --head are required when checking changed paths. ' ;
158165}
159166
160167if ([] !== $ errors ) {
161- printSummary ($ configFile , $ packages , $ statusCounts , $ warnings , $ changedFiles , [], [], $ origin , $ tag , $ root );
168+ printSummary ($ configFile , $ packages , $ statusCounts , $ warnings , $ changedFiles , [], [], $ origin , $ tag , $ root, $ calculateSplits , $ errors );
162169 fail ($ errors );
163170}
164171
165172[$ plannedPackages , $ skippedPackages ] = planPackages ($ packages , $ changedFiles , null !== $ tag );
166- printSummary ($ configFile , $ packages , $ statusCounts , $ warnings , $ changedFiles , $ plannedPackages , $ skippedPackages , $ origin , $ tag , $ root );
173+ $ splitErrors = [];
174+ printSummary ($ configFile , $ packages , $ statusCounts , $ warnings , $ changedFiles , $ plannedPackages , $ skippedPackages , $ origin , $ tag , $ root , $ calculateSplits , $ splitErrors );
175+
176+ if ([] !== $ splitErrors ) {
177+ fail ($ splitErrors );
178+ }
167179
168180exit (0 );
169181
@@ -307,7 +319,7 @@ function packageChanged(string $path, array $changedFiles): bool
307319 * @param list<array<string, mixed>> $plannedPackages
308320 * @param list<array{name: string, reason: string}> $skippedPackages
309321 */
310- function printSummary (string $ configFile , array $ packages , array $ statusCounts , array $ warnings , array $ changedFiles , array $ plannedPackages , array $ skippedPackages , string $ origin , ?string $ tag , string $ root ): void
322+ function printSummary (string $ configFile , array $ packages , array $ statusCounts , array $ warnings , array $ changedFiles , array $ plannedPackages , array $ skippedPackages , string $ origin , ?string $ tag , string $ root, bool $ calculateSplits , array & $ splitErrors ): void
311323{
312324 fwrite (STDOUT , 'Monorepo split dry-run plan ' . PHP_EOL );
313325 fwrite (STDOUT , 'Config: ' . $ configFile . PHP_EOL );
@@ -352,7 +364,15 @@ function printSummary(string $configFile, array $packages, array $statusCounts,
352364 }
353365
354366 fwrite (STDOUT , $ line . PHP_EOL );
355- fwrite (STDOUT , ' split: ' . splitshCommand ((string ) $ package ['path ' ], $ origin , $ root ) . PHP_EOL );
367+ $ command = splitshCommand ((string ) $ package ['path ' ], $ origin , $ root );
368+ fwrite (STDOUT , ' split: ' . $ command . PHP_EOL );
369+
370+ if ($ calculateSplits ) {
371+ $ sha = splitshSha ((string ) $ package ['path ' ], $ origin , $ root , $ splitErrors );
372+ if (null !== $ sha ) {
373+ fwrite (STDOUT , ' sha: ' . $ sha . PHP_EOL );
374+ }
375+ }
356376 }
357377
358378 $ skipCounts = [];
@@ -369,6 +389,12 @@ function printSummary(string $configFile, array $packages, array $statusCounts,
369389 }
370390 }
371391
392+ if ($ calculateSplits ) {
393+ fwrite (STDOUT , PHP_EOL . 'Dry run only. splitsh-lite calculated split SHAs, but nothing was pushed. ' . PHP_EOL );
394+
395+ return ;
396+ }
397+
372398 fwrite (STDOUT , PHP_EOL . 'Dry run only. splitsh-lite commands were printed but not run, and nothing was pushed. ' . PHP_EOL );
373399}
374400
@@ -381,3 +407,30 @@ function splitshCommand(string $path, string $origin, string $root): string
381407 escapeshellarg ($ root ),
382408 );
383409}
410+
411+ function splitshSha (string $ path , string $ origin , string $ root , array &$ errors ): ?string
412+ {
413+ $ command = splitshCommand ($ path , $ origin , $ root ) . ' 2>&1 ' ;
414+ exec ($ command , $ output , $ exitCode );
415+ if (0 !== $ exitCode ) {
416+ $ errors [] = sprintf ('splitsh-lite failed for %s: %s ' , $ path , implode (PHP_EOL , $ output ));
417+
418+ return null ;
419+ }
420+
421+ $ sha = trim ((string ) end ($ output ));
422+ if (!preg_match ('/^[a-f0-9]{40}$/ ' , $ sha )) {
423+ $ errors [] = sprintf ('splitsh-lite returned an invalid SHA for %s: %s ' , $ path , $ sha );
424+
425+ return null ;
426+ }
427+
428+ return $ sha ;
429+ }
430+
431+ function commandExists (string $ command ): bool
432+ {
433+ exec (sprintf ('command -v %s >/dev/null 2>&1 ' , escapeshellarg ($ command )), $ output , $ exitCode );
434+
435+ return 0 === $ exitCode ;
436+ }
0 commit comments