@samitouri / QOS-React-2 / commits / 6b35ff766a

Move @generated from build to sync (#29799)

## Overview Reverts https://github.com/facebook/react/pull/26616 and implements the suggested way instead. This change in #26616 broken the internal sync command, which now results in duplicated `@generated` headers. It also makes it harder to detect changes during the diff train sync. Instead, we will check for changes, and if there are changes sign the files and commit them to the sync branch. ## Strategy The new sync strategy accounts for the generated headers during the sync: - **Revert Version**: Revert the version strings - **Revert @generated**: Re-sign the files (will be the same hash as before if unchanged) - **Check**: Check if there are changes **if not, skip** - **Re-apply Version**: Now add back the new version string - **Re-sign @generated**: And re-generate the headers Then commit to branch. This ensures that if there are no changes, we'll skip. --------- Co-authored-by: Timothy Yung <yungsters@gmail.com>

Ricky committed Jun 10, 2024 at 10:16 UTC 6b35ff766aadedae466ce1db0133b38678d75114
10 files changed +211 -56
.github/workflows/commit_artifacts.yml
+187 -8
@@ -52,6 +52,7 @@ jobs:
52 CIRCLECI_TOKEN: ${{secrets.CIRCLECI_TOKEN_DIFFTRAIN}}
53 with:
54 script: |
55 + // TODO: Move this to a script file.
56 const cp = require('child_process');
57
58 function sleep(ms) {
@@ -250,14 +251,18 @@ jobs:
251 grep -rl "$CURRENT_VERSION_MODERN" ./compiled || echo "No files found with $CURRENT_VERSION_MODERN"
252 grep -rl "$CURRENT_VERSION_MODERN" ./compiled | xargs -r sed -i -e "s/$CURRENT_VERSION_MODERN/$LAST_VERSION_MODERN/g"
253 grep -rl "$CURRENT_VERSION_MODERN" ./compiled || echo "Modern version reverted"
253 - - name: Check if only the REVISION file has changed
254 + - name: Check for changes
255 id: check_should_commit
256 run: |
257 echo "Full git status"
258 + git add .
259 git status
260 echo "===================="
261 if git status --porcelain | grep -qv '/REVISION'; then
262 echo "Changes detected"
263 + echo "===== Changes ====="
264 + git --no-pager diff -U0 | grep '^[+-]' | head -n 50
265 + echo "==================="
266 echo "should_commit=true" >> "$GITHUB_OUTPUT"
267 else
268 echo "No Changes detected"
@@ -322,17 +327,109 @@ jobs:
327 grep -rl "$CURRENT_VERSION" ./compiled-rn || echo "No files found with $CURRENT_VERSION"
328 grep -rl "$CURRENT_VERSION" ./compiled-rn | xargs -r sed -i -e "s/$CURRENT_VERSION/$LAST_VERSION/g"
329 grep -rl "$CURRENT_VERSION" ./compiled-rn || echo "Version reverted"
325 - - name: Check if only the REVISION file has changed
326 - id: check_should_commit
330 + - name: Check changes before signing
331 run: |
332 echo "Full git status"
333 + git add .
334 git status
335 echo "===================="
331 - echo "Checking for changes"
332 - # Check if there are changes in the files other than REVISION or @generated headers
333 - # We also filter out the file name lines with "---" and "+++".
334 - if git diff -- . ':(exclude)*REVISION' | grep -vE "^(@@|diff|index|\-\-\-|\+\+\+|@generated SignedSource)" | grep "^[+-]" > /dev/null; then
336 + if git status --porcelain | grep -qv '/REVISION'; then
337 + echo "Changes detected"
338 + echo "===== Changes ====="
339 + git --no-pager diff -U0 --cached | grep '^[+-]' | head -n 50
340 + echo "==================="
341 + else
342 + echo "No Changes detected"
343 + fi
344 + - name: Revert signatures
345 + uses: actions/github-script@v6
346 + with:
347 + script: |
348 + // TODO: Move this to a script file.
349 + // We currently can't call scripts from the repo because
350 + // at this point in the workflow, we're on the compiled
351 + // artifact branch (so the scripts don't exist).
352 + // We can fix this with a composite action in the main repo.
353 + // This script is duplicated below.
354 + const fs = require('fs');
355 + const crypto = require('crypto');
356 + const {execSync} = require('child_process');
357 +
358 + // TODO: when we move this to a script, we can use this from npm.
359 + // Copy of signedsource since we can't install deps on this branch
360 + const GENERATED = '@' + 'generated';
361 + const NEWTOKEN = '<<SignedSource::*O*zOeWoEQle#+L!plEphiEmie@IsG>>';
362 + const PATTERN = new RegExp(`${GENERATED} (?:SignedSource<<([a-f0-9]{32})>>)`);
363 +
364 + const TokenNotFoundError = new Error(
365 + `SignedSource.signFile(...): Cannot sign file without token: ${NEWTOKEN}`
366 + );
367 +
368 + function hash(data, encoding) {
369 + const md5sum = crypto.createHash('md5');
370 + md5sum.update(data, encoding);
371 + return md5sum.digest('hex');
372 + }
373 +
374 + const SignedSource = {
375 + getSigningToken() {
376 + return `${GENERATED} ${NEWTOKEN}`;
377 + },
378 + isSigned(data) {
379 + return PATTERN.exec(data) != null;
380 + },
381 + signFile(data) {
382 + if (!data.includes(NEWTOKEN)) {
383 + if (SignedSource.isSigned(data)) {
384 + // Signing a file that was previously signed.
385 + data = data.replace(PATTERN, SignedSource.getSigningToken());
386 + } else {
387 + throw TokenNotFoundError;
388 + }
389 + }
390 + return data.replace(NEWTOKEN, `SignedSource<<${hash(data, 'utf8')}>>`);
391 + },
392 + };
393 +
394 + const directory = './compiled-rn';
395 + console.log('Signing files in directory:', directory);
396 + try {
397 + const result = execSync(`git status --porcelain ${directory}`, {encoding: 'utf8'});
398 +
399 + // Parse the git status output to get file paths
400 + const files = result.split('\n').filter(file => file.endsWith('.js'));
401 +
402 + if (files.length === 0) {
403 + throw new Error(
404 + 'git status returned no files to sign. this job should not have run.'
405 + );
406 + } else {
407 + files.forEach(line => {
408 + const file = line.slice(3).trim();
409 + if (file) {
410 + console.log(' Signing file:', file);
411 + const originalContents = fs.readFileSync(file, 'utf8');
412 + const signedContents = SignedSource.signFile(originalContents);
413 + fs.writeFileSync(file, signedContents, 'utf8');
414 + }
415 + });
416 + }
417 + } catch (e) {
418 + process.exitCode = 1;
419 + console.error('Error signing files:', e);
420 + }
421 + - name: Check for changes
422 + id: check_should_commit
423 + run: |
424 + echo "Full git status"
425 + git add .
426 + git status --porcelain
427 + echo "===================="
428 + if git status --porcelain | grep -qv '/REVISION'; then
429 echo "Changes detected"
430 + echo "===== Changes ====="
431 + git --no-pager diff -U0 --cached | grep '^[+-]' | head -n 50
432 + echo "==================="
433 echo "should_commit=true" >> "$GITHUB_OUTPUT"
434 else
435 echo "No Changes detected"
@@ -348,10 +445,92 @@ jobs:
445 grep -rl "$LAST_VERSION" ./compiled-rn || echo "No files found with $LAST_VERSION"
446 grep -rl "$LAST_VERSION" ./compiled-rn | xargs -r sed -i -e "s/$LAST_VERSION/$CURRENT_VERSION/g"
447 grep -rl "$LAST_VERSION" ./compiled-rn || echo "Version re-applied"
351 - - name: Will commit these changes
448 + - name: Add files
449 if: steps.check_should_commit.outputs.should_commit == 'true'
450 run: |
451 echo ":"
452 + git add .
453 + - name: Signing files
454 + if: steps.check_should_commit.outputs.should_commit == 'true'
455 + uses: actions/github-script@v6
456 + with:
457 + script: |
458 + // TODO: Move this to a script file.
459 + // We currently can't call scripts from the repo because
460 + // at this point in the workflow, we're on the compiled
461 + // artifact branch (so the scripts don't exist).
462 + // We can fix this with a composite action in the main repo.
463 + // This script is duplicated above.
464 + const fs = require('fs');
465 + const crypto = require('crypto');
466 + const {execSync} = require('child_process');
467 +
468 + // TODO: when we move this to a script, we can use this from npm.
469 + // Copy of signedsource since we can't install deps on this branch.
470 + const GENERATED = '@' + 'generated';
471 + const NEWTOKEN = '<<SignedSource::*O*zOeWoEQle#+L!plEphiEmie@IsG>>';
472 + const PATTERN = new RegExp(`${GENERATED} (?:SignedSource<<([a-f0-9]{32})>>)`);
473 +
474 + const TokenNotFoundError = new Error(
475 + `SignedSource.signFile(...): Cannot sign file without token: ${NEWTOKEN}`
476 + );
477 +
478 + function hash(data, encoding) {
479 + const md5sum = crypto.createHash('md5');
480 + md5sum.update(data, encoding);
481 + return md5sum.digest('hex');
482 + }
483 +
484 + const SignedSource = {
485 + getSigningToken() {
486 + return `${GENERATED} ${NEWTOKEN}`;
487 + },
488 + isSigned(data) {
489 + return PATTERN.exec(data) != null;
490 + },
491 + signFile(data) {
492 + if (!data.includes(NEWTOKEN)) {
493 + if (SignedSource.isSigned(data)) {
494 + // Signing a file that was previously signed.
495 + data = data.replace(PATTERN, SignedSource.getSigningToken());
496 + } else {
497 + throw TokenNotFoundError;
498 + }
499 + }
500 + return data.replace(NEWTOKEN, `SignedSource<<${hash(data, 'utf8')}>>`);
501 + },
502 + };
503 +
504 + const directory = './compiled-rn';
505 + console.log('Signing files in directory:', directory);
506 + try {
507 + const result = execSync(`git status --porcelain ${directory}`, {encoding: 'utf8'});
508 +
509 + // Parse the git status output to get file paths
510 + const files = result.split('\n').filter(file => file.endsWith('.js'));
511 +
512 + if (files.length === 0) {
513 + throw new Error(
514 + 'git status returned no files to sign. this job should not have run.'
515 + );
516 + } else {
517 + files.forEach(line => {
518 + const file = line.slice(3).trim();
519 + if (file) {
520 + console.log(' Signing file:', file);
521 + const originalContents = fs.readFileSync(file, 'utf8');
522 + const signedContents = SignedSource.signFile(originalContents);
523 + fs.writeFileSync(file, signedContents, 'utf8');
524 + }
525 + });
526 + }
527 + } catch (e) {
528 + process.exitCode = 1;
529 + console.error('Error signing files:', e);
530 + }
531 + - name: Will commit these changes
532 + if: steps.check_should_commit.outputs.should_commit == 'true'
533 + run: |
534 git status -u
535 - name: Commit changes to branch
536 if: steps.check_should_commit.outputs.should_commit == 'true'
packages/react-native-renderer/src/ReactNativeTypes.js
+2 -1
@@ -4,7 +4,8 @@
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 - * @format
7 + * @noformat
8 + * @nolint
9 * @flow strict
10 */
11
packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/deepDiffer.js
-2
@@ -4,8 +4,6 @@
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 - * @format
8 - * @flow
7 */
8
9 'use strict';
scripts/rollup/packaging.js
-20
@@ -7,7 +7,6 @@ const {
7 readFileSync,
8 writeFileSync,
9 } = require('fs');
10 -const path = require('path');
10 const Bundles = require('./bundles');
11 const {
12 asyncCopyTo,
@@ -15,7 +14,6 @@ const {
14 asyncExtractTar,
15 asyncRimRaf,
16 } = require('./utils');
18 -const {getSigningToken, signFile} = require('signedsource');
17
18 const {
19 NODE_ES2015,
@@ -127,24 +125,6 @@ async function copyRNShims() {
125 require.resolve('react-native-renderer/src/ReactNativeTypes.js'),
126 'build/react-native/shims/ReactNativeTypes.js'
127 );
130 - processGenerated('build/react-native/shims');
131 -}
132 -
133 -function processGenerated(directory) {
134 - const files = readdirSync(directory)
135 - .filter(dir => dir.endsWith('.js'))
136 - .map(file => path.join(directory, file));
137 -
138 - files.forEach(file => {
139 - const originalContents = readFileSync(file, 'utf8');
140 - const contents = originalContents
141 - // Replace {@}format with {@}noformat
142 - .replace(/(\r?\n\s*\*\s*)@format\b.*(\n)/, '$1@noformat$2')
143 - // Add {@}nolint and {@}generated
144 - .replace(/(\r?\n\s*\*)\//, `$1 @nolint$1 ${getSigningToken()}$1/`);
145 - const signedContents = signFile(contents);
146 - writeFileSync(file, signedContents, 'utf8');
147 - });
128 }
129
130 async function copyAllShims() {
scripts/rollup/shims/react-native/ReactFabric.js
+2 -1
@@ -4,7 +4,8 @@
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 - * @format
7 + * @noformat
8 + * @nolint
9 * @flow
10 */
11
scripts/rollup/shims/react-native/ReactFeatureFlags.js
+2 -1
@@ -4,7 +4,8 @@
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 - * @format
7 + * @noformat
8 + * @nolint
9 * @flow strict-local
10 */
11
scripts/rollup/shims/react-native/ReactNative.js
+2 -2
@@ -4,10 +4,10 @@
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 - * @format
7 + * @noformat
8 + * @nolint
9 * @flow
10 */
10 -
11 'use strict';
12
13 import type {ReactNativeType} from './ReactNativeTypes';
scripts/rollup/shims/react-native/ReactNativeViewConfigRegistry.js
+2 -1
@@ -4,7 +4,8 @@
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 - * @format
7 + * @noformat
8 + * @nolint
9 * @flow strict-local
10 */
11
scripts/rollup/shims/react-native/createReactNativeComponentClass.js
+2 -1
@@ -4,7 +4,8 @@
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 - * @format
7 + * @noformat
8 + * @nolint
9 * @flow strict-local
10 */
11
scripts/rollup/wrappers.js
+12 -19
@@ -1,6 +1,5 @@
1 'use strict';
2
3 -const {signFile, getSigningToken} = require('signedsource');
3 const {bundleTypes, moduleTypes} = require('./bundles');
4
5 const {
@@ -392,86 +391,80 @@ ${source}`;
391
392 /****************** RN_OSS_DEV ******************/
393 [RN_OSS_DEV](source, globalName, filename, moduleType) {
395 - return signFile(`/**
394 + return `/**
395 ${license}
396 *
397 * @noflow
398 * @nolint
399 * @preventMunge
401 - * ${getSigningToken()}
400 */
401
404 -${source}`);
402 +${source}`;
403 },
404
405 /****************** RN_OSS_PROD ******************/
406 [RN_OSS_PROD](source, globalName, filename, moduleType) {
409 - return signFile(`/**
407 + return `/**
408 ${license}
409 *
410 * @noflow
411 * @nolint
412 * @preventMunge
415 - * ${getSigningToken()}
413 */
414
418 -${source}`);
415 +${source}`;
416 },
417
418 /****************** RN_OSS_PROFILING ******************/
419 [RN_OSS_PROFILING](source, globalName, filename, moduleType) {
423 - return signFile(`/**
420 + return `/**
421 ${license}
422 *
423 * @noflow
424 * @nolint
425 * @preventMunge
429 - * ${getSigningToken()}
426 */
427
432 -${source}`);
428 +${source}`;
429 },
430
431 /****************** RN_FB_DEV ******************/
432 [RN_FB_DEV](source, globalName, filename, moduleType) {
437 - return signFile(`/**
433 + return `/**
434 ${license}
435 *
436 * @noflow
437 * @nolint
438 * @preventMunge
443 - * ${getSigningToken()}
439 */
440
446 -${source}`);
441 +${source}`;
442 },
443
444 /****************** RN_FB_PROD ******************/
445 [RN_FB_PROD](source, globalName, filename, moduleType) {
451 - return signFile(`/**
446 + return `/**
447 ${license}
448 *
449 * @noflow
450 * @nolint
451 * @preventMunge
457 - * ${getSigningToken()}
452 */
453
460 -${source}`);
454 +${source}`;
455 },
456
457 /****************** RN_FB_PROFILING ******************/
458 [RN_FB_PROFILING](source, globalName, filename, moduleType) {
465 - return signFile(`/**
459 + return `/**
460 ${license}
461 *
462 * @noflow
463 * @nolint
464 * @preventMunge
471 - * ${getSigningToken()}
465 */
466
474 -${source}`);
467 +${source}`;
468 },
469 };
470