From 9f9fad7369e2daca3009171bb7428aeec747b8f8 Mon Sep 17 00:00:00 2001 From: Luna Wei Date: Tue, 6 Feb 2024 08:52:11 -0800 Subject: [PATCH] Use set-version in nightly publishes Summary: Changelog: [Internal] - Update nightly flow to use set-version This change first fixes `set-version` to update the `packages/react-native` native source and build files (as `set-rn-version` does) -- this was an oversight but not an issue as `set-version` isn't actually used anywhere right now. Then, this change updates `publish-npm` script to use `set-version` only for nightlies -- as `dry-run` and `pre-alpha` are non-functioning build types right now. This does not change the flow of build-type `'release'` -- that will still use `set-rn-version` via CircleCI ([job](https://fburl.com/code/6xo3ijwg), [script](https://fburl.com/code/bo8np0tb)) We will eventually replace that too but that will be later. The reason for this change today is to delete the nightly-specific `get-and-update-packages` script to eventually conform all release flows to use this. Secondly, we want to use this script to bump everything on `main` after we make an rc.0 -- to replace `yarn bump-all-updated-packages --release-branch-cutoff` as detailed in this step: https://reactnative.dev/contributing/release-branch-cut-and-rc0#12-bump-minor-version-of-all-monorepo-packages-in-main Differential Revision: D53463414 --- .../releases-ci/__tests__/publish-npm-test.js | 34 +- scripts/releases-ci/publish-npm.js | 15 +- .../ReactAndroid/gradle.properties | 1 + .../__snapshots__/set-version-test.js.snap | 555 +++++++++++++++++- .../set-version/__tests__/set-version-test.js | 25 +- scripts/releases/set-version/index.js | 95 ++- .../templates/RCTVersion.m-template.js | 51 ++ .../ReactNativeVersion.h-template.js | 42 ++ .../ReactNativeVersion.java-template.js | 40 ++ .../ReactNativeVersion.js-template.js | 41 ++ scripts/releases/utils/version-utils.js | 32 +- 11 files changed, 862 insertions(+), 69 deletions(-) create mode 100644 scripts/releases/set-version/__tests__/__fixtures__/packages/react-native/ReactAndroid/gradle.properties create mode 100644 scripts/releases/set-version/templates/RCTVersion.m-template.js create mode 100644 scripts/releases/set-version/templates/ReactNativeVersion.h-template.js create mode 100644 scripts/releases/set-version/templates/ReactNativeVersion.java-template.js create mode 100644 scripts/releases/set-version/templates/ReactNativeVersion.js-template.js diff --git a/scripts/releases-ci/__tests__/publish-npm-test.js b/scripts/releases-ci/__tests__/publish-npm-test.js index aceab5f9635d02..1fa2c46b491017 100644 --- a/scripts/releases-ci/__tests__/publish-npm-test.js +++ b/scripts/releases-ci/__tests__/publish-npm-test.js @@ -13,7 +13,7 @@ const echoMock = jest.fn(); const exitMock = jest.fn(); const consoleErrorMock = jest.fn(); const isTaggedLatestMock = jest.fn(); -const setReactNativeVersionMock = jest.fn(); +const setVersionMock = jest.fn(); const publishAndroidArtifactsToMavenMock = jest.fn(); const removeNewArchFlags = jest.fn(); const env = process.env; @@ -29,18 +29,15 @@ jest getCurrentCommit: () => 'currentco_mmit', isTaggedLatest: isTaggedLatestMock, })) - .mock('path', () => ({ - ...jest.requireActual('path'), - join: () => '../../packages/react-native', + .mock('fs', () => ({ + ...jest.requireActual('fs'), + writeFileSync: jest.fn(), })) - .mock('fs') .mock('../../releases/utils/release-utils', () => ({ generateAndroidArtifacts: jest.fn(), publishAndroidArtifactsToMaven: publishAndroidArtifactsToMavenMock, })) - .mock('../../releases/set-rn-version', () => ({ - setReactNativeVersion: setReactNativeVersionMock, - })) + .mock('../../releases/set-version', () => setVersionMock) .mock('../../monorepo/get-and-update-packages') .mock('../../releases/remove-new-arch-flags', () => ({ removeNewArchFlags, @@ -49,6 +46,10 @@ jest const date = new Date('2023-04-20T23:52:39.543Z'); const {publishNpm} = require('../publish-npm'); +const path = require('path'); + +const REPO_ROOT = path.resolve(__filename, '../../../..'); + let consoleError; describe('publish-npm', () => { @@ -88,11 +89,7 @@ describe('publish-npm', () => { expect(echoMock).toHaveBeenCalledWith( 'Skipping `npm publish` because --dry-run is set.', ); - expect(setReactNativeVersionMock).toBeCalledWith( - '1000.0.0-currentco', - null, - 'dry-run', - ); + expect(setVersionMock).toBeCalledWith('1000.0.0-currentco'); }); }); @@ -106,6 +103,7 @@ describe('publish-npm', () => { await publishNpm('nightly'); expect(removeNewArchFlags).not.toHaveBeenCalled(); + expect(setVersionMock).toBeCalledWith(expectedVersion); expect(publishAndroidArtifactsToMavenMock).toHaveBeenCalledWith( expectedVersion, 'nightly', @@ -123,7 +121,7 @@ describe('publish-npm', () => { it('should fail to set version', async () => { execMock.mockReturnValueOnce({stdout: '0.81.0-rc.1\n', code: 0}); const expectedVersion = '0.82.0-nightly-20230420-currentco'; - setReactNativeVersionMock.mockImplementation(() => { + setVersionMock.mockImplementation(() => { throw new Error('something went wrong'); }); @@ -166,7 +164,7 @@ describe('publish-npm', () => { ); expect(execMock).toHaveBeenCalledWith( `npm publish --tag 0.81-stable --otp otp`, - {cwd: '../../packages/react-native'}, + {cwd: path.join(REPO_ROOT, 'packages/react-native')}, ); expect(echoMock).toHaveBeenCalledWith( `Published to npm ${expectedVersion}`, @@ -191,7 +189,7 @@ describe('publish-npm', () => { ); expect(execMock).toHaveBeenCalledWith( `npm publish --tag latest --otp ${process.env.NPM_CONFIG_OTP}`, - {cwd: '../../packages/react-native'}, + {cwd: path.join(REPO_ROOT, 'packages/react-native')}, ); expect(echoMock).toHaveBeenCalledWith( `Published to npm ${expectedVersion}`, @@ -216,7 +214,7 @@ describe('publish-npm', () => { ); expect(execMock).toHaveBeenCalledWith( `npm publish --tag latest --otp ${process.env.NPM_CONFIG_OTP}`, - {cwd: '../../packages/react-native'}, + {cwd: path.join(REPO_ROOT, 'packages/react-native')}, ); expect(echoMock).toHaveBeenCalledWith(`Failed to publish package to npm`); expect(exitMock).toHaveBeenCalledWith(1); @@ -239,7 +237,7 @@ describe('publish-npm', () => { ); expect(execMock).toHaveBeenCalledWith( `npm publish --tag next --otp ${process.env.NPM_CONFIG_OTP}`, - {cwd: '../../packages/react-native'}, + {cwd: path.join(REPO_ROOT, 'packages/react-native')}, ); expect(echoMock).toHaveBeenCalledWith( `Published to npm ${expectedVersion}`, diff --git a/scripts/releases-ci/publish-npm.js b/scripts/releases-ci/publish-npm.js index 5e5b6f6a798deb..e552ca69617ed2 100755 --- a/scripts/releases-ci/publish-npm.js +++ b/scripts/releases-ci/publish-npm.js @@ -15,10 +15,9 @@ import type {BuildType} from '../releases/utils/version-utils'; */ -const getAndUpdatePackages = require('../monorepo/get-and-update-packages'); const {getNpmInfo, publishPackage} = require('../npm-utils'); const {removeNewArchFlags} = require('../releases/remove-new-arch-flags'); -const {setReactNativeVersion} = require('../releases/set-rn-version'); +const setVersion = require('../releases/set-version'); const { generateAndroidArtifacts, publishAndroidArtifactsToMaven, @@ -75,17 +74,11 @@ async function publishNpm(buildType /*: BuildType */) /*: Promise */ { } // Here we update the react-native package and template package with the right versions - // For releases, CircleCI job `prepare_package_for_release` handles this + // For stable releases, CircleCI job `prepare_package_for_release` handles this if (['dry-run', 'nightly', 'prealpha'].includes(buildType)) { - // Publish monorepo nightlies and prealphas if there are updates, returns the new version for each package - const monorepoVersions = - // $FlowFixMe[incompatible-call] - buildType === 'dry-run' ? null : getAndUpdatePackages(version, buildType); - + // Update all monorepo packages (incl. react-native) to the same version and update every dependency try { - // Update the react-native and template packages with the react-native version - // and nightly versions of monorepo deps - await setReactNativeVersion(version, monorepoVersions, buildType); + await setVersion(version); } catch (e) { console.error(`Failed to set version number to ${version}`); console.error(e); diff --git a/scripts/releases/set-version/__tests__/__fixtures__/packages/react-native/ReactAndroid/gradle.properties b/scripts/releases/set-version/__tests__/__fixtures__/packages/react-native/ReactAndroid/gradle.properties new file mode 100644 index 00000000000000..0b2a27b00a7747 --- /dev/null +++ b/scripts/releases/set-version/__tests__/__fixtures__/packages/react-native/ReactAndroid/gradle.properties @@ -0,0 +1 @@ +VERSION_NAME=1000.0.0 diff --git a/scripts/releases/set-version/__tests__/__snapshots__/set-version-test.js.snap b/scripts/releases/set-version/__tests__/__snapshots__/set-version-test.js.snap index bbf805d8fc4bde..168a014540291c 100644 --- a/scripts/releases/set-version/__tests__/__snapshots__/set-version-test.js.snap +++ b/scripts/releases/set-version/__tests__/__snapshots__/set-version-test.js.snap @@ -1,28 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`setVersion updates all public packages to version: monorepo-pkg-a 1`] = ` +exports[`setVersion updates monorepo for nightly: packages/monorepo-pkg-a/package.json 1`] = ` "{ \\"name\\": \\"@monorepo/pkg-a\\", - \\"version\\": \\"0.80.0\\", + \\"version\\": \\"0.81.0-nightly-29282302-abcd1234\\", \\"description\\": \\"@monorepo/pkg-a\\", \\"dependencies\\": { - \\"@monorepo/pkg-b\\": \\"0.80.0\\", + \\"@monorepo/pkg-b\\": \\"0.81.0-nightly-29282302-abcd1234\\", \\"@monorepo/other\\": \\"0.0.1\\" }, \\"devDependencies\\": { - \\"@monorepo/pkg-c\\": \\"0.80.0\\" + \\"@monorepo/pkg-c\\": \\"0.81.0-nightly-29282302-abcd1234\\" } } " `; -exports[`setVersion updates all public packages to version: monorepo-pkg-b 1`] = ` +exports[`setVersion updates monorepo for nightly: packages/monorepo-pkg-b/package.json 1`] = ` "{ \\"name\\": \\"@monorepo/pkg-b\\", - \\"version\\": \\"0.80.0\\", + \\"version\\": \\"0.81.0-nightly-29282302-abcd1234\\", \\"description\\": \\"@monorepo/pkg-b\\", \\"dependencies\\": { - \\"@monorepo/pkg-c\\": \\"0.80.0\\", + \\"@monorepo/pkg-c\\": \\"0.81.0-nightly-29282302-abcd1234\\", \\"metro-config\\": \\"^0.80.3\\", \\"metro-runtime\\": \\"^0.80.3\\" } @@ -30,10 +30,10 @@ exports[`setVersion updates all public packages to version: monorepo-pkg-b 1`] = " `; -exports[`setVersion updates all public packages to version: monorepo-pkg-c 1`] = ` +exports[`setVersion updates monorepo for nightly: packages/monorepo-pkg-c/package.json 1`] = ` "{ \\"name\\": \\"@monorepo/pkg-c\\", - \\"version\\": \\"0.80.0\\", + \\"version\\": \\"0.81.0-nightly-29282302-abcd1234\\", \\"description\\": \\"@monorepo/pkg-c\\", \\"dependencies\\": { \\"metro-config\\": \\"^0.80.3\\", @@ -43,15 +43,534 @@ exports[`setVersion updates all public packages to version: monorepo-pkg-c 1`] = " `; -exports[`setVersion updates all public packages to version: react-native 1`] = ` +exports[`setVersion updates monorepo for nightly: packages/react-native/Libraries/Core/ReactNativeVersion.js 1`] = ` +"/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @generated by scripts/releases/set-rn-version.js + */ + +const version: $ReadOnly<{ + major: number, + minor: number, + patch: number, + prerelease: string | null, +}> = { + major: 0, + minor: 81, + patch: 0, + prerelease: 'nightly-29282302-abcd1234', +}; + +module.exports = {version}; +" +`; + +exports[`setVersion updates monorepo for nightly: packages/react-native/React/Base/RCTVersion.m 1`] = ` +"/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by scripts/releases/set-rn-version.js + */ + +#import \\"RCTVersion.h\\" + +NSString* const RCTVersionMajor = @\\"major\\"; +NSString* const RCTVersionMinor = @\\"minor\\"; +NSString* const RCTVersionPatch = @\\"patch\\"; +NSString* const RCTVersionPrerelease = @\\"prerelease\\"; + + +NSDictionary* RCTGetReactNativeVersion(void) +{ + static NSDictionary* __rnVersion; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^(void){ + __rnVersion = @{ + RCTVersionMajor: @(0), + RCTVersionMinor: @(81), + RCTVersionPatch: @(0), + RCTVersionPrerelease: @\\"nightly-29282302-abcd1234\\", + }; + }); + return __rnVersion; +} +" +`; + +exports[`setVersion updates monorepo for nightly: packages/react-native/ReactAndroid/gradle.properties 1`] = ` +"VERSION_NAME=0.81.0-nightly-29282302-abcd1234 +" +`; + +exports[`setVersion updates monorepo for nightly: packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java 1`] = ` +"/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by scripts/releases/set-rn-version.js + */ + +package com.facebook.react.modules.systeminfo; + +import com.facebook.react.common.MapBuilder; + +import java.util.Map; + +public class ReactNativeVersion { + public static final Map VERSION = MapBuilder.of( + \\"major\\", 0, + \\"minor\\", 81, + \\"patch\\", 0, + \\"prerelease\\", \\"nightly-29282302-abcd1234\\"); +} +" +`; + +exports[`setVersion updates monorepo for nightly: packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h 1`] = ` +"/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by scripts/releases/set-rn-version.js + */ + +#pragma once + +#include +#include + +namespace facebook::react { + +constexpr struct { + int32_t Major = 0; + int32_t Minor = 81; + int32_t Patch = 0; + std::string_view Prerelease = \\"nightly-29282302-abcd1234\\"; +} ReactNativeVersion; + +} // namespace facebook::react +" +`; + +exports[`setVersion updates monorepo for nightly: packages/react-native/package.json 1`] = ` +"{ + \\"name\\": \\"react-native\\", + \\"version\\": \\"0.81.0-nightly-29282302-abcd1234\\", + \\"description\\": \\"fake react native package\\", + \\"dependencies\\": { + \\"@monorepo/pkg-a\\": \\"0.81.0-nightly-29282302-abcd1234\\", + \\"@monorepo/pkg-b\\": \\"0.81.0-nightly-29282302-abcd1234\\", + \\"@monorepo/pkg-c\\": \\"0.81.0-nightly-29282302-abcd1234\\", + \\"metro-config\\": \\"^0.80.3\\", + \\"metro-runtime\\": \\"^0.80.3\\" + } +} +" +`; + +exports[`setVersion updates monorepo for nightly: packages/react-native/template/package.json 1`] = ` +"{ + \\"name\\": \\"react-native-test-template\\", + \\"version\\": \\"0.0.1\\", + \\"private\\": true, + \\"dependencies\\": { + \\"react\\": \\"18.2.0\\", + \\"react-native\\": \\"0.81.0-nightly-29282302-abcd1234\\" + }, + \\"devDependencies\\": { + \\"@monorepo/pkg-a\\": \\"0.81.0-nightly-29282302-abcd1234\\", + \\"@monorepo/pkg-c\\": \\"0.81.0-nightly-29282302-abcd1234\\", + \\"@types/react\\": \\"^18.2.6\\", + \\"@types/react-test-renderer\\": \\"^18.0.0\\" + } +} +" +`; + +exports[`setVersion updates monorepo for release-candidate: packages/monorepo-pkg-a/package.json 1`] = ` +"{ + \\"name\\": \\"@monorepo/pkg-a\\", + \\"version\\": \\"0.80.0-rc.3\\", + \\"description\\": \\"@monorepo/pkg-a\\", + \\"dependencies\\": { + \\"@monorepo/pkg-b\\": \\"0.80.0-rc.3\\", + \\"@monorepo/other\\": \\"0.0.1\\" + }, + \\"devDependencies\\": { + \\"@monorepo/pkg-c\\": \\"0.80.0-rc.3\\" + } +} +" +`; + +exports[`setVersion updates monorepo for release-candidate: packages/monorepo-pkg-b/package.json 1`] = ` +"{ + \\"name\\": \\"@monorepo/pkg-b\\", + \\"version\\": \\"0.80.0-rc.3\\", + \\"description\\": \\"@monorepo/pkg-b\\", + \\"dependencies\\": { + \\"@monorepo/pkg-c\\": \\"0.80.0-rc.3\\", + \\"metro-config\\": \\"^0.80.3\\", + \\"metro-runtime\\": \\"^0.80.3\\" + } +} +" +`; + +exports[`setVersion updates monorepo for release-candidate: packages/monorepo-pkg-c/package.json 1`] = ` +"{ + \\"name\\": \\"@monorepo/pkg-c\\", + \\"version\\": \\"0.80.0-rc.3\\", + \\"description\\": \\"@monorepo/pkg-c\\", + \\"dependencies\\": { + \\"metro-config\\": \\"^0.80.3\\", + \\"metro-runtime\\": \\"^0.80.3\\" + } +} +" +`; + +exports[`setVersion updates monorepo for release-candidate: packages/react-native/Libraries/Core/ReactNativeVersion.js 1`] = ` +"/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @generated by scripts/releases/set-rn-version.js + */ + +const version: $ReadOnly<{ + major: number, + minor: number, + patch: number, + prerelease: string | null, +}> = { + major: 0, + minor: 80, + patch: 0, + prerelease: 'rc.3', +}; + +module.exports = {version}; +" +`; + +exports[`setVersion updates monorepo for release-candidate: packages/react-native/React/Base/RCTVersion.m 1`] = ` +"/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by scripts/releases/set-rn-version.js + */ + +#import \\"RCTVersion.h\\" + +NSString* const RCTVersionMajor = @\\"major\\"; +NSString* const RCTVersionMinor = @\\"minor\\"; +NSString* const RCTVersionPatch = @\\"patch\\"; +NSString* const RCTVersionPrerelease = @\\"prerelease\\"; + + +NSDictionary* RCTGetReactNativeVersion(void) +{ + static NSDictionary* __rnVersion; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^(void){ + __rnVersion = @{ + RCTVersionMajor: @(0), + RCTVersionMinor: @(80), + RCTVersionPatch: @(0), + RCTVersionPrerelease: @\\"rc.3\\", + }; + }); + return __rnVersion; +} +" +`; + +exports[`setVersion updates monorepo for release-candidate: packages/react-native/ReactAndroid/gradle.properties 1`] = ` +"VERSION_NAME=0.80.0-rc.3 +" +`; + +exports[`setVersion updates monorepo for release-candidate: packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java 1`] = ` +"/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by scripts/releases/set-rn-version.js + */ + +package com.facebook.react.modules.systeminfo; + +import com.facebook.react.common.MapBuilder; + +import java.util.Map; + +public class ReactNativeVersion { + public static final Map VERSION = MapBuilder.of( + \\"major\\", 0, + \\"minor\\", 80, + \\"patch\\", 0, + \\"prerelease\\", \\"rc.3\\"); +} +" +`; + +exports[`setVersion updates monorepo for release-candidate: packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h 1`] = ` +"/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by scripts/releases/set-rn-version.js + */ + +#pragma once + +#include +#include + +namespace facebook::react { + +constexpr struct { + int32_t Major = 0; + int32_t Minor = 80; + int32_t Patch = 0; + std::string_view Prerelease = \\"rc.3\\"; +} ReactNativeVersion; + +} // namespace facebook::react +" +`; + +exports[`setVersion updates monorepo for release-candidate: packages/react-native/package.json 1`] = ` +"{ + \\"name\\": \\"react-native\\", + \\"version\\": \\"0.80.0-rc.3\\", + \\"description\\": \\"fake react native package\\", + \\"dependencies\\": { + \\"@monorepo/pkg-a\\": \\"0.80.0-rc.3\\", + \\"@monorepo/pkg-b\\": \\"0.80.0-rc.3\\", + \\"@monorepo/pkg-c\\": \\"0.80.0-rc.3\\", + \\"metro-config\\": \\"^0.80.3\\", + \\"metro-runtime\\": \\"^0.80.3\\" + } +} +" +`; + +exports[`setVersion updates monorepo for release-candidate: packages/react-native/template/package.json 1`] = ` +"{ + \\"name\\": \\"react-native-test-template\\", + \\"version\\": \\"0.0.1\\", + \\"private\\": true, + \\"dependencies\\": { + \\"react\\": \\"18.2.0\\", + \\"react-native\\": \\"0.80.0-rc.3\\" + }, + \\"devDependencies\\": { + \\"@monorepo/pkg-a\\": \\"0.80.0-rc.3\\", + \\"@monorepo/pkg-c\\": \\"0.80.0-rc.3\\", + \\"@types/react\\": \\"^18.2.6\\", + \\"@types/react-test-renderer\\": \\"^18.0.0\\" + } +} +" +`; + +exports[`setVersion updates monorepo for stable version: packages/monorepo-pkg-a/package.json 1`] = ` +"{ + \\"name\\": \\"@monorepo/pkg-a\\", + \\"version\\": \\"0.80.1\\", + \\"description\\": \\"@monorepo/pkg-a\\", + \\"dependencies\\": { + \\"@monorepo/pkg-b\\": \\"0.80.1\\", + \\"@monorepo/other\\": \\"0.0.1\\" + }, + \\"devDependencies\\": { + \\"@monorepo/pkg-c\\": \\"0.80.1\\" + } +} +" +`; + +exports[`setVersion updates monorepo for stable version: packages/monorepo-pkg-b/package.json 1`] = ` +"{ + \\"name\\": \\"@monorepo/pkg-b\\", + \\"version\\": \\"0.80.1\\", + \\"description\\": \\"@monorepo/pkg-b\\", + \\"dependencies\\": { + \\"@monorepo/pkg-c\\": \\"0.80.1\\", + \\"metro-config\\": \\"^0.80.3\\", + \\"metro-runtime\\": \\"^0.80.3\\" + } +} +" +`; + +exports[`setVersion updates monorepo for stable version: packages/monorepo-pkg-c/package.json 1`] = ` +"{ + \\"name\\": \\"@monorepo/pkg-c\\", + \\"version\\": \\"0.80.1\\", + \\"description\\": \\"@monorepo/pkg-c\\", + \\"dependencies\\": { + \\"metro-config\\": \\"^0.80.3\\", + \\"metro-runtime\\": \\"^0.80.3\\" + } +} +" +`; + +exports[`setVersion updates monorepo for stable version: packages/react-native/Libraries/Core/ReactNativeVersion.js 1`] = ` +"/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @generated by scripts/releases/set-rn-version.js + */ + +const version: $ReadOnly<{ + major: number, + minor: number, + patch: number, + prerelease: string | null, +}> = { + major: 0, + minor: 80, + patch: 1, + prerelease: null, +}; + +module.exports = {version}; +" +`; + +exports[`setVersion updates monorepo for stable version: packages/react-native/React/Base/RCTVersion.m 1`] = ` +"/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by scripts/releases/set-rn-version.js + */ + +#import \\"RCTVersion.h\\" + +NSString* const RCTVersionMajor = @\\"major\\"; +NSString* const RCTVersionMinor = @\\"minor\\"; +NSString* const RCTVersionPatch = @\\"patch\\"; +NSString* const RCTVersionPrerelease = @\\"prerelease\\"; + + +NSDictionary* RCTGetReactNativeVersion(void) +{ + static NSDictionary* __rnVersion; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^(void){ + __rnVersion = @{ + RCTVersionMajor: @(0), + RCTVersionMinor: @(80), + RCTVersionPatch: @(1), + RCTVersionPrerelease: [NSNull null], + }; + }); + return __rnVersion; +} +" +`; + +exports[`setVersion updates monorepo for stable version: packages/react-native/ReactAndroid/gradle.properties 1`] = ` +"VERSION_NAME=0.80.1 +" +`; + +exports[`setVersion updates monorepo for stable version: packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java 1`] = ` +"/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by scripts/releases/set-rn-version.js + */ + +package com.facebook.react.modules.systeminfo; + +import com.facebook.react.common.MapBuilder; + +import java.util.Map; + +public class ReactNativeVersion { + public static final Map VERSION = MapBuilder.of( + \\"major\\", 0, + \\"minor\\", 80, + \\"patch\\", 1, + \\"prerelease\\", null); +} +" +`; + +exports[`setVersion updates monorepo for stable version: packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h 1`] = ` +"/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by scripts/releases/set-rn-version.js + */ + +#pragma once + +#include +#include + +namespace facebook::react { + +constexpr struct { + int32_t Major = 0; + int32_t Minor = 80; + int32_t Patch = 1; + std::string_view Prerelease = \\"\\"; +} ReactNativeVersion; + +} // namespace facebook::react +" +`; + +exports[`setVersion updates monorepo for stable version: packages/react-native/package.json 1`] = ` "{ \\"name\\": \\"react-native\\", - \\"version\\": \\"0.80.0\\", + \\"version\\": \\"0.80.1\\", \\"description\\": \\"fake react native package\\", \\"dependencies\\": { - \\"@monorepo/pkg-a\\": \\"0.80.0\\", - \\"@monorepo/pkg-b\\": \\"0.80.0\\", - \\"@monorepo/pkg-c\\": \\"0.80.0\\", + \\"@monorepo/pkg-a\\": \\"0.80.1\\", + \\"@monorepo/pkg-b\\": \\"0.80.1\\", + \\"@monorepo/pkg-c\\": \\"0.80.1\\", \\"metro-config\\": \\"^0.80.3\\", \\"metro-runtime\\": \\"^0.80.3\\" } @@ -59,18 +578,18 @@ exports[`setVersion updates all public packages to version: react-native 1`] = ` " `; -exports[`setVersion updates all public packages to version: template 1`] = ` +exports[`setVersion updates monorepo for stable version: packages/react-native/template/package.json 1`] = ` "{ \\"name\\": \\"react-native-test-template\\", \\"version\\": \\"0.0.1\\", \\"private\\": true, \\"dependencies\\": { \\"react\\": \\"18.2.0\\", - \\"react-native\\": \\"0.80.0\\" + \\"react-native\\": \\"0.80.1\\" }, \\"devDependencies\\": { - \\"@monorepo/pkg-a\\": \\"0.80.0\\", - \\"@monorepo/pkg-c\\": \\"0.80.0\\", + \\"@monorepo/pkg-a\\": \\"0.80.1\\", + \\"@monorepo/pkg-c\\": \\"0.80.1\\", \\"@types/react\\": \\"^18.2.6\\", \\"@types/react-test-renderer\\": \\"^18.0.0\\" } diff --git a/scripts/releases/set-version/__tests__/set-version-test.js b/scripts/releases/set-version/__tests__/set-version-test.js index c30ffcd1c9dfeb..8d26a91f970e55 100644 --- a/scripts/releases/set-version/__tests__/set-version-test.js +++ b/scripts/releases/set-version/__tests__/set-version-test.js @@ -28,17 +28,30 @@ describe('setVersion', () => { return { ...originalFs, - writeFileSync: (packagePath, content) => { - expect(content).toMatchSnapshot( - path.basename(path.join(packagePath, '..')), - ); + promises: { + ...originalFs.promises, + writeFile: (filePath, content) => { + expect(content).toMatchSnapshot( + path.relative(path.join(__dirname, '__fixtures__'), filePath), + ); + }, }, }; }); }); - test('updates all public packages to version', () => { - setVersion('0.80.0'); + + test('updates monorepo for release-candidate', async () => { + await setVersion('0.80.0-rc.3'); + }); + + test('updates monorepo for stable version', async () => { + await setVersion('0.80.1'); }); + + test('updates monorepo for nightly', async () => { + await setVersion('0.81.0-nightly-29282302-abcd1234'); + }); + afterAll(() => { jest.unmock('path'); jest.unmock('fs'); diff --git a/scripts/releases/set-version/index.js b/scripts/releases/set-version/index.js index a0cdeee814effb..2d9d67da742fcb 100644 --- a/scripts/releases/set-version/index.js +++ b/scripts/releases/set-version/index.js @@ -12,10 +12,18 @@ 'use strict'; const forEachPackage = require('../../monorepo/for-each-package'); -const {readFileSync, writeFileSync} = require('fs'); +const {getBuildType, parseVersion} = require('../utils/version-utils'); +const {promises: fs, readFileSync} = require('fs'); const path = require('path'); const yargs = require('yargs'); +/*:: +import type {Version, BuildType} from '../utils/version-utils'; +*/ + +const REPO_ROOT = path.join(path.dirname(__filename), '..', '..'); +const GRADLE_FILE_PATH = 'packages/react-native/ReactAndroid/gradle.properties'; + function getPublicPackages() { // eslint-disable-next-line func-call-spacing const packages = new Set /*::*/(); @@ -30,8 +38,51 @@ function getPublicPackages() { return packages; } -function setVersion(version /*: string */) { +async function updateGradleFile(version /*: string */) { + const contents = await fs.readFile( + path.join(REPO_ROOT, GRADLE_FILE_PATH), + 'utf-8', + ); + + return fs.writeFile( + path.join(REPO_ROOT, GRADLE_FILE_PATH), + contents.replace(/^VERSION_NAME=.*/, `VERSION_NAME=${version}`), + ); +} + +function updateSourceFiles(version /*: Version */) { + return Promise.all([ + fs.writeFile( + path.join( + REPO_ROOT, + 'packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java', + ), + require('./templates/ReactNativeVersion.java-template')(version), + ), + fs.writeFile( + path.join(REPO_ROOT, 'packages/react-native/React/Base/RCTVersion.m'), + require('./templates/RCTVersion.m-template')(version), + ), + fs.writeFile( + path.join( + REPO_ROOT, + 'packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h', + ), + require('./templates/ReactNativeVersion.h-template')(version), + ), + fs.writeFile( + path.join( + REPO_ROOT, + 'packages/react-native/Libraries/Core/ReactNativeVersion.js', + ), + require('./templates/ReactNativeVersion.js-template')(version), + ), + ]); +} + +function updatePackages(version /*: string */) { const publicPackages = getPublicPackages(); + const writes = []; forEachPackage( (packageAbsolutePath, _, packageJson) => { @@ -57,10 +108,12 @@ function setVersion(version /*: string */) { } } - writeFileSync( - path.join(packageAbsolutePath, 'package.json'), - JSON.stringify(packageJson, null, 2) + '\n', - 'utf-8', + writes.push( + fs.writeFile( + path.join(packageAbsolutePath, 'package.json'), + JSON.stringify(packageJson, null, 2) + '\n', + 'utf-8', + ), ); // Update template package.json @@ -92,23 +145,34 @@ function setVersion(version /*: string */) { } } } - writeFileSync( - templatePackageJsonPath, - JSON.stringify(templatePackageJson, null, 2) + '\n', - 'utf-8', + writes.push( + fs.writeFile( + templatePackageJsonPath, + JSON.stringify(templatePackageJson, null, 2) + '\n', + 'utf-8', + ), ); } }, {includeReactNative: true}, ); + + return Promise.all(writes); } -module.exports = setVersion; +async function setVersion(version /*: string */) { + const buildType = getBuildType(version); + const parsedVersion = parseVersion(version, buildType); + + await updateSourceFiles(parsedVersion); + await updateGradleFile(parsedVersion.version); + await updatePackages(parsedVersion.version); +} if (require.main === module) { const {toVersion} = yargs(process.argv.slice(2)) .command( - '$0 ', + '$0 ', 'Update all monorepo packages to ', args => args.positional('to-version', { @@ -118,5 +182,10 @@ if (require.main === module) { }), ) .parseSync(); - setVersion(toVersion); + setVersion(toVersion).then( + () => process.exit(0), + () => process.exit(1), + ); } + +module.exports = setVersion; diff --git a/scripts/releases/set-version/templates/RCTVersion.m-template.js b/scripts/releases/set-version/templates/RCTVersion.m-template.js new file mode 100644 index 00000000000000..01b0349c571fde --- /dev/null +++ b/scripts/releases/set-version/templates/RCTVersion.m-template.js @@ -0,0 +1,51 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + * @oncall react_native + */ + +/*:: +import type {Version} from '../../utils/version-utils'; +*/ + +module.exports = (version /*: Version */) /*: string */ => `/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * ${'@'}generated by scripts/releases/set-rn-version.js + */ + +#import "RCTVersion.h" + +NSString* const RCTVersionMajor = @"major"; +NSString* const RCTVersionMinor = @"minor"; +NSString* const RCTVersionPatch = @"patch"; +NSString* const RCTVersionPrerelease = @"prerelease"; + + +NSDictionary* RCTGetReactNativeVersion(void) +{ + static NSDictionary* __rnVersion; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^(void){ + __rnVersion = @{ + RCTVersionMajor: @(${version.major}), + RCTVersionMinor: @(${version.minor}), + RCTVersionPatch: @(${version.patch}), + RCTVersionPrerelease: ${ + version.prerelease != null + ? `@"${version.prerelease}"` + : '[NSNull null]' + }, + }; + }); + return __rnVersion; +} +`; diff --git a/scripts/releases/set-version/templates/ReactNativeVersion.h-template.js b/scripts/releases/set-version/templates/ReactNativeVersion.h-template.js new file mode 100644 index 00000000000000..a375152e32b012 --- /dev/null +++ b/scripts/releases/set-version/templates/ReactNativeVersion.h-template.js @@ -0,0 +1,42 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + * @oncall react_native + */ + +/*:: +import type {Version} from '../../utils/version-utils'; +*/ + +module.exports = (version /*: Version */) /*: string */ => `/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * ${'@'}generated by scripts/releases/set-rn-version.js + */ + +#pragma once + +#include +#include + +namespace facebook::react { + +constexpr struct { + int32_t Major = ${version.major}; + int32_t Minor = ${version.minor}; + int32_t Patch = ${version.patch}; + std::string_view Prerelease = ${ + version.prerelease != null ? `"${version.prerelease}"` : '""' + }; +} ReactNativeVersion; + +} // namespace facebook::react +`; diff --git a/scripts/releases/set-version/templates/ReactNativeVersion.java-template.js b/scripts/releases/set-version/templates/ReactNativeVersion.java-template.js new file mode 100644 index 00000000000000..ea602f76d684a1 --- /dev/null +++ b/scripts/releases/set-version/templates/ReactNativeVersion.java-template.js @@ -0,0 +1,40 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + * @oncall react_native + */ + +/*:: +import type {Version} from '../../utils/version-utils'; +*/ + +module.exports = (version /*: Version */) /*: string */ => `/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * ${'@'}generated by scripts/releases/set-rn-version.js + */ + +package com.facebook.react.modules.systeminfo; + +import com.facebook.react.common.MapBuilder; + +import java.util.Map; + +public class ReactNativeVersion { + public static final Map VERSION = MapBuilder.of( + "major", ${version.major}, + "minor", ${version.minor}, + "patch", ${version.patch}, + "prerelease", ${ + version.prerelease != null ? `"${version.prerelease}"` : 'null' + }); +} +`; diff --git a/scripts/releases/set-version/templates/ReactNativeVersion.js-template.js b/scripts/releases/set-version/templates/ReactNativeVersion.js-template.js new file mode 100644 index 00000000000000..1178e41480845c --- /dev/null +++ b/scripts/releases/set-version/templates/ReactNativeVersion.js-template.js @@ -0,0 +1,41 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + * @oncall react_native + */ + +/*:: +import type {Version} from '../../utils/version-utils'; +*/ + +module.exports = (version /*: Version */) /*: string */ => `/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * ${'@'}generated by scripts/releases/set-rn-version.js + */ + +const version: $ReadOnly<{ + major: number, + minor: number, + patch: number, + prerelease: string | null, +}> = { + major: ${version.major}, + minor: ${version.minor}, + patch: ${version.patch}, + prerelease: ${ + version.prerelease != null ? `'${version.prerelease}'` : 'null' + }, +}; + +module.exports = {version}; +`; diff --git a/scripts/releases/utils/version-utils.js b/scripts/releases/utils/version-utils.js index 5eb34d2d5eac90..6d1e8c0fe54025 100644 --- a/scripts/releases/utils/version-utils.js +++ b/scripts/releases/utils/version-utils.js @@ -43,10 +43,16 @@ function parseVersion( throw new Error(`Unsupported build type: ${buildType}`); } + const parsedVersion = _parse(versionStr); + validateVersion(parsedVersion, buildType); + return parsedVersion; +} + +function _parse(versionStr /*: string */) /*: Version */ { const match = extractMatchIfValid(versionStr); const [, version, major, minor, patch, prerelease] = match; - const versionObject = { + const parsedVersion = { version, major, minor, @@ -54,9 +60,28 @@ function parseVersion( prerelease, }; - validateVersion(versionObject, buildType); + return parsedVersion; +} + +function getBuildType(version /*: string */) /*: BuildType */ { + const parsedVersion = _parse(version); + if (isNightly(parsedVersion)) { + return 'nightly'; + } + + if (isStableRelease(parsedVersion) || isStablePrerelease(parsedVersion)) { + return 'release'; + } + + if (isMain(parsedVersion)) { + return 'dry-run'; + } + + if (isValidPrealpha(parsedVersion)) { + return 'prealpha'; + } - return versionObject; + throw new Error(`Version ${version} is not a valid`); } function validateBuildType( @@ -186,4 +211,5 @@ module.exports = { isMain, isStableRelease, isStablePrerelease, + getBuildType, };