88 * @format
99 */
1010
11- const { PACKAGES_DIR } = require ( '../shared/consts' ) ;
11+ const { PACKAGES_DIR , REPO_ROOT } = require ( '../shared/consts' ) ;
12+ const { resolveCommitBackend} = require ( './commit-backend' ) ;
1213// $FlowFixMe[untyped-import]: TODO type ansi-styles
1314const ansiStyles = require ( 'ansi-styles' ) ;
1415const { execSync, spawnSync} = require ( 'node:child_process' ) ;
@@ -40,15 +41,7 @@ const config = {
4041} ;
4142
4243/*::
43- type DiffBaseInfo = {
44- packagePath: string,
45- baseGitRevision: string,
46- };
47-
48- type ParsedBuildInfo = {
49- gitRevision: string,
50- isLocalCheckout: boolean,
51- };
44+ import type {CommitBackend} from './commit-backend';
5245*/
5346
5447async function main ( ) {
@@ -95,10 +88,15 @@ async function main() {
9588
9689 await checkRequiredTools ( ) ;
9790 const packagePath = path . join ( PACKAGES_DIR , 'debugger-frontend' ) ;
98- let diffBaseInfo ;
99- if ( createDiff ) {
100- diffBaseInfo = await checkCanCreateDiff ( packagePath ) ;
101- }
91+ // Neither a local checkout nor a skipped build produces assets that provably
92+ // match the revision the commit will claim.
93+ const isUnlandable = localCheckoutPath != null || noBuild ;
94+ const commitBackend = await resolveCommitBackend ( {
95+ createDiff : createDiff === true ,
96+ isUnlandable,
97+ } ) ;
98+ await checkCanCommit ( packagePath , commitBackend ) ;
99+ const baseGitRevision = await readGitRevision ( packagePath ) ;
102100 const { checkoutPath} = await buildDebuggerFrontend (
103101 packagePath ,
104102 scratchPath ,
@@ -109,14 +107,19 @@ async function main() {
109107 noBuild,
110108 } ,
111109 ) ;
112- if ( createDiff && diffBaseInfo ) {
113- await createSyncDiff ( diffBaseInfo , scratchPath , { checkoutPath, noBuild} ) ;
114- }
110+ await commitSync ( {
111+ baseGitRevision,
112+ checkoutPath,
113+ commitBackend,
114+ isUnlandable,
115+ packagePath,
116+ scratchPath,
117+ } ) ;
115118 await cleanup ( scratchPath , keepScratch === true ) ;
116119 if ( ! noBuild ) {
117120 process . stdout . write (
118121 styleText ( 'green' , 'Sync done.' ) +
119- ' Check in any updated files under packages/debugger-frontend.\n' ,
122+ ' Committed updated files under packages/debugger-frontend.\n' ,
120123 ) ;
121124 }
122125}
@@ -128,15 +131,16 @@ function showHelp() {
128131 Sync and build the debugger frontend into @react-native/debugger-frontend.
129132
130133 By default, checks out the currently pinned revision of the DevTools frontend.
131- If an existing checkout path is provided, builds it instead.
134+ If an existing checkout path is provided, builds it instead. The updated files
135+ are committed on completion.
132136
133137 Options:
134138 --branch The DevTools frontend branch to use. Ignored when
135139 providing a local checkout path.
140+ --create-diff Submit the commit as a draft diff (Meta-internal).
136141 --nohooks Don't run gclient hooks in the devtools checkout (useful
137142 for existing checkouts).
138143 --keep-scratch Don't clean up temporary files.
139- --create-diff Create a diff with the updated files.
140144 --no-build Skip actually building and updating the frontend.
141145` ) ;
142146}
@@ -453,70 +457,36 @@ async function spawnSafe(
453457 }
454458}
455459
456- async function checkCanCreateDiff (
460+ async function checkCanCommit (
457461 packagePath /*: string */ ,
458- ) /*: Promise<DiffBaseInfo> */ {
459- process . stdout . write ( 'Checking that we can create a diff' + '\n' ) ;
460- try {
461- const { stdout : hgRootStdout } = await spawnSafe ( 'hg' , [ 'root' ] , {
462- cwd : packagePath ,
463- stdio : [ 'ignore' , 'pipe' , 'inherit' ] ,
464- } ) ;
465- const repoRoot = hgRootStdout . toString ( ) . trim ( ) ;
466- const projectid = (
467- await fs . readFile ( path . join ( repoRoot , '.projectid' ) , 'utf8' )
468- ) . trim ( ) ;
469- if ( projectid !== 'fbsource' ) {
470- throw new Error (
471- 'Expected .projectid to contain "fbsource" but found: ' + projectid ,
472- ) ;
473- }
474- await spawnSafe ( 'jf' , [ '-v' ] , { cwd : packagePath , stdio : 'ignore' } ) ;
475- } catch ( e ) {
476- process . stderr . write (
477- 'Must be in an fbsource checkout (Meta-only) to create a diff\n' ,
478- ) ;
479- throw e ;
480- }
481- try {
482- const { stdout : hgStatusStdout } = await spawnSafe (
483- 'hg' ,
484- [ 'status' , 'BUILD_INFO' ] ,
485- { cwd : packagePath , stdio : [ 'ignore' , 'pipe' , 'inherit' ] } ,
462+ commitBackend /*: CommitBackend */ ,
463+ ) {
464+ process . stdout . write ( 'Checking that we can commit' + '\n' ) ;
465+ const [ statusCmd , statusArgs ] = commitBackend . status ;
466+ const { stdout} = await spawnSafe ( statusCmd , statusArgs , {
467+ cwd : packagePath ,
468+ stdio : [ 'ignore' , 'pipe' , 'inherit' ] ,
469+ } ) ;
470+ const pendingChanges = stdout . toString ( ) . trim ( ) ;
471+ if ( pendingChanges !== '' ) {
472+ throw new Error (
473+ `Must have a clean working copy under ${ path . relative ( REPO_ROOT , packagePath ) } to commit:\n${ pendingChanges } ` ,
486474 ) ;
487- if ( hgStatusStdout . toString ( ) . trim ( ) !== '' ) {
488- throw new Error (
489- 'Must have a clean base BUILD_INFO file to create a diff' ,
490- ) ;
491- }
492- const { gitRevision : baseGitRevision } = await readBuildInfo ( packagePath ) ;
493- return {
494- packagePath,
495- baseGitRevision,
496- } ;
497- } catch ( e ) {
498- process . stderr . write ( 'Must have a BUILD_INFO file to create a diff\n' ) ;
499- throw e ;
500475 }
501476}
502477
503- async function readBuildInfo (
478+ async function readGitRevision (
504479 packagePath /*: string*/ ,
505- ) /*: Promise<ParsedBuildInfo > */ {
480+ ) /*: Promise<string > */ {
506481 const buildInfo = await fs . readFile (
507482 path . join ( packagePath , 'BUILD_INFO' ) ,
508483 'utf8' ,
509484 ) ;
510485 const GIT_REV_RE = / ^ G i t r e v i s i o n : ( [ 0 - 9 a - f ] { 40 } ) / m;
511- const gitRevision = nullthrows (
486+ return nullthrows (
512487 GIT_REV_RE . exec ( buildInfo ) ,
513488 'Could not extract git revision from BUILD_INFO' ,
514489 ) [ 1 ] ;
515- const isLocalCheckout = ! / ^ I s l o c a l c h e c k o u t : f a l s e $ / m. test ( buildInfo ) ;
516- return {
517- isLocalCheckout,
518- gitRevision,
519- } ;
520490}
521491
522492function generateChangelogTable (
@@ -542,7 +512,6 @@ function generateChangelogTable(
542512 const limitedCommits = commits . slice ( 0 , maxCommits ) ;
543513
544514 const tableRows = [
545- '' ,
546515 '### Changelog' ,
547516 '' ,
548517 '| Commit | Author | Date/Time | Subject |' ,
@@ -582,19 +551,26 @@ function generateChangelogTable(
582551 return changelogTable ;
583552}
584553
585- async function createSyncDiff (
586- diffBaseInfo /*: DiffBaseInfo */ ,
587- scratchPath /*: string */ ,
554+ async function commitSync (
588555 {
556+ baseGitRevision,
589557 checkoutPath,
590- noBuild,
591- } /*: Readonly<{checkoutPath: string, noBuild: boolean}> */ ,
558+ commitBackend,
559+ isUnlandable,
560+ packagePath,
561+ scratchPath,
562+ } /*: Readonly<{
563+ baseGitRevision: string,
564+ checkoutPath: string,
565+ commitBackend: CommitBackend,
566+ isUnlandable: boolean,
567+ packagePath: string,
568+ scratchPath: string,
569+ }> */ ,
592570) {
593- process . stdout . write ( 'Creating a sync diff\n' ) ;
594- const { packagePath, baseGitRevision} = diffBaseInfo ;
571+ process . stdout . write ( 'Committing updated files\n' ) ;
595572 const baseGitRevisionShort = baseGitRevision . slice ( 0 , 7 ) ;
596- const { gitRevision : newGitRevision , isLocalCheckout} =
597- await readBuildInfo ( packagePath ) ;
573+ const newGitRevision = await readGitRevision ( packagePath ) ;
598574 const newGitRevisionShort = newGitRevision . slice ( 0 , 7 ) ;
599575
600576 // Generate the changelog table
@@ -604,39 +580,30 @@ async function createSyncDiff(
604580 newGitRevision ,
605581 ) ;
606582
607- const commitMessage = [
608- ( isLocalCheckout || noBuild ? 'DO NOT LAND ' : '' ) +
609- `[RN] Update debugger-frontend from ${ baseGitRevisionShort } ...${ newGitRevisionShort } ` ,
610- '' ,
611- 'Summary:' ,
612- `Changelog: [Internal] - Update \`@react-native/debugger-frontend\` from ${ baseGitRevisionShort } ...${ newGitRevisionShort } ` ,
613- '' ,
614- `Resyncs \`@react-native/debugger-frontend\` from GitHub - see \`rn-chrome-devtools-frontend\` [changelog](${ DEVTOOLS_FRONTEND_REPO_URL } /compare/${ baseGitRevision } ...${ newGitRevision } ).` ,
615- '' ,
616- changelogTable ,
617- '' ,
618- 'Test Plan: CI' ,
619- '' ,
620- 'Reviewers: #rn-debugging' ,
621- '' ,
622- 'Tags: msdkland[metro]' ,
623- '' ,
624- ] . join ( '\n' ) ;
583+ const revisionRange = `${ baseGitRevisionShort } ...${ newGitRevisionShort } ` ;
584+ const title =
585+ ( isUnlandable ? 'DO NOT LAND ' : '' ) +
586+ `[RN] Update debugger-frontend from ${ revisionRange } ` ;
587+ const changelogEntry = `[Internal] - Update \`@react-native/debugger-frontend\` from ${ revisionRange } ` ;
588+ const compareUrl = `${ DEVTOOLS_FRONTEND_REPO_URL } /compare/${ baseGitRevision } ...${ newGitRevision } ` ;
589+ const summary =
590+ 'Resyncs `@react-native/debugger-frontend` from GitHub - see ' +
591+ '`rn-chrome-devtools-frontend` ' +
592+ `[changelog](${ compareUrl } ).` ;
593+
594+ const commitMessage =
595+ commitBackend
596+ . messageBlocks ( { title, summary, changelogTable, changelogEntry} )
597+ . filter ( block => block !== '' )
598+ . join ( '\n\n' ) + '\n' ;
625599
626600 const commitMessageFile = path . join ( scratchPath , 'commit-msg' ) ;
627601 await fs . writeFile ( commitMessageFile , commitMessage ) ;
628- await spawnSafe (
629- 'hg' ,
630- [ 'commit' , packagePath , '--addremove' , '-l' , commitMessageFile ] ,
631- { cwd : packagePath } ,
632- ) ;
633- await spawnSafe ( 'jf' , [ 'submit' , '--draft' ] , {
634- cwd : packagePath ,
635- } ) ;
636- if ( noBuild ) {
637- await spawnSafe ( 'jf' , [ 'action' , '--abandon' ] , {
638- cwd : packagePath ,
639- } ) ;
602+ for ( const [ cmd , args ] of commitBackend . commit (
603+ packagePath ,
604+ commitMessageFile ,
605+ ) ) {
606+ await spawnSafe ( cmd , args , { cwd : packagePath } ) ;
640607 }
641608}
642609
0 commit comments