diff --git a/.circleci/configurations/jobs.yml b/.circleci/configurations/jobs.yml index 6083671dece..6e7a8442004 100644 --- a/.circleci/configurations/jobs.yml +++ b/.circleci/configurations/jobs.yml @@ -259,7 +259,7 @@ jobs: - run_yarn - run: name: Set React Native Version - command: node ./scripts/set-rn-version.js --build-type << parameters.release_type >> + command: node ./scripts/releases/set-rn-version.js --build-type << parameters.release_type >> - with_gradle_cache: steps: @@ -303,7 +303,7 @@ jobs: name: Set React Native Version to "dry-run" # test_android executes only for dry-run builds. We need to bump the version for caching # reasons otherwise we won't reuse the artifacts from the build_android job. - command: node ./scripts/set-rn-version.js --build-type "dry-run" + command: node ./scripts/releases/set-rn-version.js --build-type "dry-run" - attach_workspace: at: . @@ -566,7 +566,7 @@ jobs: if [[ << parameters.architecture >> == "NewArch" ]]; then export RCT_NEW_ARCH_ENABLED=1 fi - + cd packages/rn-tester bundle install diff --git a/packages/react-native/Libraries/Core/ReactNativeVersion.js b/packages/react-native/Libraries/Core/ReactNativeVersion.js index abb9b1ace91..2f45659dc6c 100644 --- a/packages/react-native/Libraries/Core/ReactNativeVersion.js +++ b/packages/react-native/Libraries/Core/ReactNativeVersion.js @@ -1,12 +1,11 @@ /** - * @generated by scripts/set-rn-version.js - * * 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<{ diff --git a/packages/react-native/React/Base/RCTVersion.m b/packages/react-native/React/Base/RCTVersion.m index 1794ca2e521..26d701dd303 100644 --- a/packages/react-native/React/Base/RCTVersion.m +++ b/packages/react-native/React/Base/RCTVersion.m @@ -1,10 +1,10 @@ /** - * @generated by scripts/set-rn-version.js - * * 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" diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java index ea97bff8de8..8f5ef045c9c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java @@ -1,10 +1,10 @@ /** - * @generated by scripts/set-rn-version.js - * * 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; diff --git a/packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h b/packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h index 5d89f4efaae..6be46f63b85 100644 --- a/packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h +++ b/packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h @@ -4,7 +4,7 @@ * 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/set-rn-version.js + * @generated by scripts/releases/set-rn-version.js */ #pragma once diff --git a/scripts/__tests__/publish-npm-test.js b/scripts/__tests__/publish-npm-test.js index 381ea3556b0..41328da51b2 100644 --- a/scripts/__tests__/publish-npm-test.js +++ b/scripts/__tests__/publish-npm-test.js @@ -36,7 +36,7 @@ jest generateAndroidArtifacts: jest.fn(), publishAndroidArtifactsToMaven: publishAndroidArtifactsToMavenMock, })) - .mock('./../set-rn-version', () => setReactNativeVersionMock) + .mock('./../releases/set-rn-version', () => setReactNativeVersionMock) .mock('../monorepo/get-and-update-packages') .mock('../releases/remove-new-arch-flags', () => removeNewArchFlags); diff --git a/scripts/__tests__/set-rn-version-test.js b/scripts/__tests__/set-rn-version-test.js deleted file mode 100644 index 5ec436dab3c..00000000000 --- a/scripts/__tests__/set-rn-version-test.js +++ /dev/null @@ -1,201 +0,0 @@ -/** - * 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. - * - * @format - */ - -const echoMock = jest.fn(); -const catMock = jest.fn(); -const sedMock = jest.fn(); -const writeFileSyncMock = jest.fn(); -const updateTemplatePackageMock = jest.fn(); - -jest - .mock('shelljs', () => ({ - echo: echoMock, - cat: catMock, - sed: sedMock, - })) - .mock('./../update-template-package', () => updateTemplatePackageMock) - .mock('fs', () => ({ - writeFileSync: writeFileSyncMock, - mkdtempSync: () => './rn-set-version/', - })); - -const setReactNativeVersion = require('../set-rn-version'); - -describe('set-rn-version', () => { - afterEach(() => { - jest.resetModules(); - jest.resetAllMocks(); - }); - - it('should set nightly version', () => { - catMock.mockImplementation(path => { - if (path === 'packages/react-native/package.json') { - return '{"name": "myPackage", "version": 2, "dependencies": {"@react-native/package-a": "nightly", "@react-native/package-b": "^0.73.0"}}'; - } else if ( - path === 'scripts/versiontemplates/ReactNativeVersion.java.template' || - path === 'scripts/versiontemplates/RCTVersion.m.template' || - path === 'scripts/versiontemplates/ReactNativeVersion.h.template' || - path === 'scripts/versiontemplates/ReactNativeVersion.js.template' - ) { - return '{major: ${major}, minor: ${minor}, patch: ${patch}, prerelease: ${prerelease}}'; - } else { - throw new Error(`Invalid path passed for package dir. Path: ${path}`); - } - }); - - sedMock.mockReturnValueOnce({code: 0}); - - const version = '0.81.0-nightly-29282302-abcd1234'; - const nightlyVersions = { - '@react-native/package-a': version, - }; - setReactNativeVersion(version, nightlyVersions, 'nightly'); - - expect(sedMock).toHaveBeenCalledWith( - '-i', - /^VERSION_NAME=.*/, - `VERSION_NAME=${version}`, - 'packages/react-native/ReactAndroid/gradle.properties', - ); - expect(writeFileSyncMock.mock.calls.length).toBe(5); - expect(writeFileSyncMock.mock.calls[0][0]).toBe( - 'packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java', - ); - expect(writeFileSyncMock.mock.calls[0][1]).toBe( - '{major: 0, minor: 81, patch: 0, prerelease: "nightly-29282302-abcd1234"}', - ); - expect(writeFileSyncMock.mock.calls[1][0]).toBe( - 'packages/react-native/React/Base/RCTVersion.m', - ); - expect(writeFileSyncMock.mock.calls[2][0]).toBe( - 'packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h', - ); - expect(writeFileSyncMock.mock.calls[3][0]).toBe( - 'packages/react-native/Libraries/Core/ReactNativeVersion.js', - ); - expect(writeFileSyncMock.mock.calls[4][0]).toBe( - 'packages/react-native/package.json', - ); - expect(writeFileSyncMock.mock.calls[4][1]).toBe(`{ - "name": "myPackage", - "version": "${version}", - "dependencies": { - "@react-native/package-a": "0.81.0-nightly-29282302-abcd1234", - "@react-native/package-b": "^0.73.0" - } -}`); - - expect(updateTemplatePackageMock).toHaveBeenCalledWith({ - '@react-native/package-a': '0.81.0-nightly-29282302-abcd1234', - 'react-native': version, - }); - }); - - it('should set release version', () => { - catMock.mockImplementation(path => { - if (path === 'packages/react-native/package.json') { - return '{"name": "myPackage", "version": 2}'; - } - return 'exports.version = {major: ${major}, minor: ${minor}, patch: ${patch}, prerelease: ${prerelease}}'; - }); - - sedMock.mockReturnValueOnce({code: 0}); - - const version = '0.81.0'; - setReactNativeVersion(version, null, 'release'); - - expect(sedMock).toHaveBeenCalledWith( - '-i', - /^VERSION_NAME=.*/, - `VERSION_NAME=${version}`, - 'packages/react-native/ReactAndroid/gradle.properties', - ); - expect(writeFileSyncMock.mock.calls.length).toBe(5); - expect(writeFileSyncMock.mock.calls[0][0]).toBe( - 'packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java', - ); - expect(writeFileSyncMock.mock.calls[0][1]).toBe( - 'exports.version = {major: 0, minor: 81, patch: 0, prerelease: null}', - ); - expect(writeFileSyncMock.mock.calls[4][0]).toBe( - 'packages/react-native/package.json', - ); - expect(writeFileSyncMock.mock.calls[4][1]).toBe( - `{\n "name": "myPackage",\n "version": "${version}"\n}`, - ); - - expect(updateTemplatePackageMock).toHaveBeenCalledWith({ - 'react-native': version, - }); - }); - - it('should set prealpha version', () => { - catMock.mockImplementation(path => { - if (path === 'packages/react-native/package.json') { - return '{"name": "myPackage", "version": 2, "dependencies": {"@react-native/package-a": "prealpha", "@react-native/package-b": "^0.73.0"}}'; - } else if ( - path === 'scripts/versiontemplates/ReactNativeVersion.java.template' || - path === 'scripts/versiontemplates/RCTVersion.m.template' || - path === 'scripts/versiontemplates/ReactNativeVersion.h.template' || - path === 'scripts/versiontemplates/ReactNativeVersion.js.template' - ) { - return '{major: ${major}, minor: ${minor}, patch: ${patch}, prerelease: ${prerelease}}'; - } else { - throw new Error(`Invalid path passed for package dir. Path: ${path}`); - } - }); - - sedMock.mockReturnValueOnce({code: 0}); - - const version = '0.0.0-prealpha-2023100415'; - const nightlyVersions = { - '@react-native/package-a': version, - }; - setReactNativeVersion(version, nightlyVersions, 'prealpha'); - - expect(sedMock).toHaveBeenCalledWith( - '-i', - /^VERSION_NAME=.*/, - `VERSION_NAME=${version}`, - 'packages/react-native/ReactAndroid/gradle.properties', - ); - expect(writeFileSyncMock.mock.calls.length).toBe(5); - expect(writeFileSyncMock.mock.calls[0][0]).toBe( - 'packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java', - ); - expect(writeFileSyncMock.mock.calls[0][1]).toBe( - '{major: 0, minor: 0, patch: 0, prerelease: "prealpha-2023100415"}', - ); - expect(writeFileSyncMock.mock.calls[1][0]).toBe( - 'packages/react-native/React/Base/RCTVersion.m', - ); - expect(writeFileSyncMock.mock.calls[2][0]).toBe( - 'packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h', - ); - expect(writeFileSyncMock.mock.calls[3][0]).toBe( - 'packages/react-native/Libraries/Core/ReactNativeVersion.js', - ); - expect(writeFileSyncMock.mock.calls[4][0]).toBe( - 'packages/react-native/package.json', - ); - expect(writeFileSyncMock.mock.calls[4][1]).toBe(`{ - "name": "myPackage", - "version": "${version}", - "dependencies": { - "@react-native/package-a": "0.0.0-prealpha-2023100415", - "@react-native/package-b": "^0.73.0" - } -}`); - - expect(updateTemplatePackageMock).toHaveBeenCalledWith({ - '@react-native/package-a': '0.0.0-prealpha-2023100415', - 'react-native': version, - }); - }); -}); diff --git a/scripts/npm-utils.js b/scripts/npm-utils.js index 37732e9a623..3059e8b95f0 100644 --- a/scripts/npm-utils.js +++ b/scripts/npm-utils.js @@ -10,12 +10,12 @@ 'use strict'; +const {parseVersion} = require('./releases/version-utils'); const { exitIfNotOnGit, getCurrentCommit, isTaggedLatest, } = require('./scm-utils'); -const {parseVersion} = require('./version-utils'); const {exec} = require('shelljs'); /*:: diff --git a/scripts/prepare-package-for-release.js b/scripts/prepare-package-for-release.js index d90182495ce..f30c8e328e6 100755 --- a/scripts/prepare-package-for-release.js +++ b/scripts/prepare-package-for-release.js @@ -10,7 +10,7 @@ 'use strict'; const {failIfTagExists} = require('./release-utils'); -const {isReleaseBranch, parseVersion} = require('./version-utils'); +const {isReleaseBranch, parseVersion} = require('./releases/version-utils'); /** * This script prepares a release package to be pushed to npm * It is triggered to run on CircleCI @@ -74,7 +74,7 @@ if (version == null) { if ( exec( - `node scripts/set-rn-version.js --to-version ${version} --build-type ${buildType}`, + `node scripts/releases/set-rn-version.js --to-version ${version} --build-type ${buildType}`, ).code ) { echo(`Failed to set React Native version to ${version}`); diff --git a/scripts/publish-npm.js b/scripts/publish-npm.js index 9051f207a90..5a9b0f9774c 100755 --- a/scripts/publish-npm.js +++ b/scripts/publish-npm.js @@ -16,7 +16,7 @@ const { publishAndroidArtifactsToMaven, } = require('./release-utils'); const removeNewArchFlags = require('./releases/remove-new-arch-flags'); -const setReactNativeVersion = require('./set-rn-version'); +const setReactNativeVersion = require('./releases/set-rn-version'); const path = require('path'); const {echo, exit} = require('shelljs'); const yargs = require('yargs'); diff --git a/scripts/releases/__tests__/__snapshots__/set-rn-version-test.js.snap b/scripts/releases/__tests__/__snapshots__/set-rn-version-test.js.snap new file mode 100644 index 00000000000..118911a79e7 --- /dev/null +++ b/scripts/releases/__tests__/__snapshots__/set-rn-version-test.js.snap @@ -0,0 +1,243 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`setReactNativeVersion should set nightly 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 >> + */ + +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[`setReactNativeVersion should set nightly 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 >> + */ + +#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[`setReactNativeVersion should set nightly version: packages/react-native/ReactAndroid/gradle.properties 1`] = ` +"VERSION_NAME=0.81.0-nightly-29282302-abcd1234 +" +`; + +exports[`setReactNativeVersion should set nightly 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 >> + */ + +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[`setReactNativeVersion should set nightly 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 >> + */ + +#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[`setReactNativeVersion should set release 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 >> + */ + +const version: $ReadOnly<{ + major: number, + minor: number, + patch: number, + prerelease: string | null, +}> = { + major: 0, + minor: 81, + patch: 0, + prerelease: null, +}; + +module.exports = {version}; +" +`; + +exports[`setReactNativeVersion should set release 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 >> + */ + +#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: [NSNull null], + }; + }); + return __rnVersion; +} +" +`; + +exports[`setReactNativeVersion should set release version: packages/react-native/ReactAndroid/gradle.properties 1`] = ` +"VERSION_NAME=0.81.0 +" +`; + +exports[`setReactNativeVersion should set release 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 >> + */ + +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\\", null); +} +" +`; + +exports[`setReactNativeVersion should set release 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 >> + */ + +#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 = \\"\\"; +} ReactNativeVersion; + +} // namespace facebook::react +" +`; diff --git a/scripts/releases/__tests__/set-rn-version-test.js b/scripts/releases/__tests__/set-rn-version-test.js new file mode 100644 index 00000000000..e46a27b42ed --- /dev/null +++ b/scripts/releases/__tests__/set-rn-version-test.js @@ -0,0 +1,78 @@ +/** + * 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 + */ + +const readFileMock = jest.fn(); +const writeFileMock = jest.fn(); +const updateTemplatePackageMock = jest.fn(); + +jest.mock('fs', () => ({ + ...jest.requireActual<$FlowFixMe>('fs'), + promises: { + ...jest.requireActual<$FlowFixMe>('fs').promises, + readFile: readFileMock, + writeFile: writeFileMock, + }, +})); +jest.mock('./../update-template-package', () => updateTemplatePackageMock); + +const {setReactNativeVersion} = require('../set-rn-version'); + +describe('setReactNativeVersion', () => { + beforeAll(() => { + readFileMock.mockImplementation(path => { + if (path === 'packages/react-native/ReactAndroid/gradle.properties') { + return 'VERSION_NAME=1000.0.0\n'; + } + }); + }); + + afterEach(() => { + writeFileMock.mockReset(); + }); + + test('should set nightly version', async () => { + const version = '0.81.0-nightly-29282302-abcd1234'; + const dependencyVersions = { + '@react-native/package-a': version, + }; + await setReactNativeVersion(version, dependencyVersions, 'nightly'); + + expect(updateTemplatePackageMock).toHaveBeenCalledWith({ + '@react-native/package-a': version, + 'react-native': version, + }); + + for (const [filePath, contents] of writeFileMock.mock.calls) { + expect(formatGeneratedFile(contents)).toMatchSnapshot(filePath); + } + }); + + test('should set release version', async () => { + const version = '0.81.0'; + await setReactNativeVersion(version, null, 'release'); + + expect(updateTemplatePackageMock).toHaveBeenCalledWith({ + 'react-native': version, + }); + + for (const [filePath, contents] of writeFileMock.mock.calls) { + expect(formatGeneratedFile(contents)).toMatchSnapshot(filePath); + } + }); +}); + +function formatGeneratedFile(source: string) { + // Strip \@\generated annotation + return source.replace( + new RegExp('^ \\* @' + 'generated.*', 'gm'), + ' * << GENERATED >>', + ); +} diff --git a/scripts/__tests__/version-utils-test.js b/scripts/releases/__tests__/version-utils-test.js similarity index 93% rename from scripts/__tests__/version-utils-test.js rename to scripts/releases/__tests__/version-utils-test.js index 86fac6bd644..c527bc3dcf0 100644 --- a/scripts/__tests__/version-utils-test.js +++ b/scripts/releases/__tests__/version-utils-test.js @@ -391,31 +391,15 @@ describe('version-utils', () => { }); describe('Validate version', () => { - it('Throw error if the buildType is unknown', () => { - function testInvalidFunction() { - validateBuildType('wrong_build'); - } - expect(testInvalidFunction).toThrowErrorMatchingInlineSnapshot( - `"Unsupported build type: wrong_build"`, - ); - }); - it('Does not throw if the buildType is release', () => { - function testValidCall() { - validateBuildType('release'); - } - expect(testValidCall).not.toThrowError(); - }); - it('Does not throw if the buildType is nightly', () => { - function testValidCall() { - validateBuildType('nightly'); - } - expect(testValidCall).not.toThrowError(); - }); - it('Does not throw if the buildType is dry-run', () => { - function testValidCall() { - validateBuildType('dry-run'); - } - expect(testValidCall).not.toThrowError(); + test('should return false if the buildType is unknown', () => { + expect(validateBuildType('wrong_build')).toBe(false); }); + + test.each(['release', 'nightly', 'dry-run'])( + 'should return true if the buildType is %s', + buildType => { + expect(validateBuildType(buildType)).toBe(true); + }, + ); }); }); diff --git a/scripts/releases/set-rn-version.js b/scripts/releases/set-rn-version.js new file mode 100755 index 00000000000..44414def9ea --- /dev/null +++ b/scripts/releases/set-rn-version.js @@ -0,0 +1,122 @@ +/** + * 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 {BuildType, Version} from './version-utils'; +*/ + +const {getNpmInfo} = require('../npm-utils'); +const updateTemplatePackage = require('./update-template-package'); +const {parseVersion, validateBuildType} = require('./version-utils'); +const {parseArgs} = require('@pkgjs/parseargs'); +const {promises: fs} = require('fs'); + +const GRADLE_FILE_PATH = 'packages/react-native/ReactAndroid/gradle.properties'; + +const config = { + options: { + 'build-type': { + type: 'string', + short: 'b', + }, + 'to-version': { + type: 'string', + short: 'v', + }, + help: {type: 'boolean'}, + }, +}; + +async function main() { + const { + values: {help, 'build-type': buildType, 'to-version': toVersion}, + } = parseArgs(config); + + if (help) { + console.log(` + Usage: node ./scripts/releases/set-rn-version.js [OPTIONS] + + Updates relevant files in the react-native package and template to + materialize the given release version. + + Options: + --build-type One of ['dry-run', 'nightly', 'release', 'prealpha']. + --to-version The new version string. + `); + return; + } + + if (!validateBuildType(buildType)) { + throw new Error(`Unsupported build type: ${buildType}`); + } + + await setReactNativeVersion( + toVersion ?? getNpmInfo(buildType).version, + {}, + buildType, + ); +} + +async function setReactNativeVersion( + version /*: string */, + dependencyVersions /*: ?Record */, + buildType /*: BuildType */, +) { + const versionInfo = parseVersion(version, buildType); + + updateTemplatePackage({ + ...(dependencyVersions ?? {}), + 'react-native': versionInfo.version, + }); + await updateSourceFiles(versionInfo); + await updateGradleFile(versionInfo.version); +} + +function updateSourceFiles(versionInfo /*: Version */) { + const templateData = {version: versionInfo}; + + return Promise.all([ + fs.writeFile( + 'packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java', + require('./templates/ReactNativeVersion.java-template')(templateData), + ), + fs.writeFile( + 'packages/react-native/React/Base/RCTVersion.m', + require('./templates/RCTVersion.m-template')(templateData), + ), + fs.writeFile( + 'packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h', + require('./templates/ReactNativeVersion.h-template')(templateData), + ), + fs.writeFile( + 'packages/react-native/Libraries/Core/ReactNativeVersion.js', + require('./templates/ReactNativeVersion.js-template')(templateData), + ), + ]); +} + +async function updateGradleFile(version /*: string */) { + const contents = await fs.readFile(GRADLE_FILE_PATH, 'utf-8'); + + return fs.writeFile( + GRADLE_FILE_PATH, + contents.replace(/^VERSION_NAME=.*/, `VERSION_NAME=${version}`), + ); +} + +module.exports = { + setReactNativeVersion, +}; + +if (require.main === module) { + // eslint-disable-next-line no-void + void main(); +} diff --git a/scripts/releases/templates/RCTVersion.m-template.js b/scripts/releases/templates/RCTVersion.m-template.js new file mode 100644 index 00000000000..ff68e597d0f --- /dev/null +++ b/scripts/releases/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 '../version-utils'; +*/ + +module.exports = ({version} /*: {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/templates/ReactNativeVersion.h-template.js b/scripts/releases/templates/ReactNativeVersion.h-template.js new file mode 100644 index 00000000000..19c72e3e795 --- /dev/null +++ b/scripts/releases/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 '../version-utils'; +*/ + +module.exports = ({version} /*: {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/templates/ReactNativeVersion.java-template.js b/scripts/releases/templates/ReactNativeVersion.java-template.js new file mode 100644 index 00000000000..fa211737a54 --- /dev/null +++ b/scripts/releases/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 '../version-utils'; +*/ + +module.exports = ({version} /*: {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/templates/ReactNativeVersion.js-template.js b/scripts/releases/templates/ReactNativeVersion.js-template.js new file mode 100644 index 00000000000..4f5c44134e0 --- /dev/null +++ b/scripts/releases/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 '../version-utils'; +*/ + +module.exports = ({version} /*: {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/update-template-package.js b/scripts/releases/update-template-package.js old mode 100755 new mode 100644 similarity index 82% rename from scripts/update-template-package.js rename to scripts/releases/update-template-package.js index be6abec363c..afb6e8eb000 --- a/scripts/update-template-package.js +++ b/scripts/releases/update-template-package.js @@ -6,11 +6,10 @@ * * @flow strict-local * @format + * @oncall react_native */ -'use strict'; - -const {applyPackageVersions} = require('./npm-utils'); +const {applyPackageVersions} = require('../npm-utils'); const fs = require('fs'); const path = require('path'); @@ -21,12 +20,12 @@ const path = require('path'); * `dependencyMap` is a dict of package name to its version * ex. {"react-native": "0.23.0", "other-dep": "nightly"} */ -function updateTemplatePackage(dependencyMap /*: {[string]: string} */) { +function updateTemplatePackage(dependencyMap /*: Record */) { const jsonPath = path.join( __dirname, - '../packages/react-native/template/package.json', + '../../packages/react-native/template/package.json', ); - // $FlowIgnore[unsupported-syntax] + // $FlowFixMe[unsupported-syntax] const templatePackageJson = require(jsonPath); const updatedPackageJson = applyPackageVersions( diff --git a/scripts/version-utils.js b/scripts/releases/version-utils.js similarity index 89% rename from scripts/version-utils.js rename to scripts/releases/version-utils.js index 2f95e0569d6..a6a980a1596 100644 --- a/scripts/version-utils.js +++ b/scripts/releases/version-utils.js @@ -11,13 +11,13 @@ const VERSION_REGEX = /^v?((\d+)\.(\d+)\.(\d+)(?:-(.+))?)$/; /*:: -type BuildType = 'dry-run' | 'release' | 'nightly' | 'prealpha'; -type Version = { +export type BuildType = 'dry-run' | 'release' | 'nightly' | 'prealpha'; +export type Version = { version: string, major: string, minor: string, patch: string, - prerelease: string + prerelease: ?string, } */ /** @@ -38,7 +38,9 @@ function parseVersion( versionStr /*: string */, buildType /*: BuildType */, ) /*: Version */ { - validateBuildType(buildType); + if (!validateBuildType(buildType)) { + throw new Error(`Unsupported build type: ${buildType}`); + } const match = extractMatchIfValid(versionStr); const [, version, major, minor, patch, prerelease] = match; @@ -56,16 +58,18 @@ function parseVersion( return versionObject; } -function validateBuildType(buildType /*: BuildType */) { +function validateBuildType( + buildType /*: string */, +) /*: buildType is BuildType */ { const validBuildTypes = new Set([ 'release', 'dry-run', 'nightly', 'prealpha', ]); - if (!validBuildTypes.has(buildType)) { - throw new Error(`Unsupported build type: ${buildType}`); - } + + // $FlowFixMe[incompatible-return] + return validBuildTypes.has(buildType); } function extractMatchIfValid(versionStr /*: string */) { @@ -138,10 +142,9 @@ function isStablePrerelease(version /*: Version */) /*: boolean */ { version.major === '0' && version.minor !== '0' && version.patch.match(/^\d+$/) && - version.prerelease != null && - (version.prerelease.startsWith('rc.') || - version.prerelease.startsWith('rc-') || - version.prerelease.match(/^(\d{8})-(\d{4})$/)) + (version.prerelease?.startsWith('rc.') || + version.prerelease?.startsWith('rc-') || + version.prerelease?.match(/^(\d{8})-(\d{4})$/)) ); } diff --git a/scripts/run-ci-e2e-tests.js b/scripts/run-ci-e2e-tests.js index 0a7c3f2f644..209b625b65d 100644 --- a/scripts/run-ci-e2e-tests.js +++ b/scripts/run-ci-e2e-tests.js @@ -65,7 +65,7 @@ try { describe('Create react-native package'); if ( exec( - 'node ./scripts/set-rn-version.js --to-version 1000.0.0 --build-type dry-run', + 'node ./scripts/releases/set-rn-version.js --to-version 1000.0.0 --build-type dry-run', ).code ) { echo('Failed to set version and update package.json ready for release'); diff --git a/scripts/set-rn-version.js b/scripts/set-rn-version.js deleted file mode 100755 index a9f8da60ba4..00000000000 --- a/scripts/set-rn-version.js +++ /dev/null @@ -1,169 +0,0 @@ -/** - * 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. - * - * @format - */ - -'use strict'; - -const {applyPackageVersions, getNpmInfo} = require('./npm-utils'); -const updateTemplatePackage = require('./update-template-package'); -const {parseVersion, validateBuildType} = require('./version-utils'); -const fs = require('fs'); -const {cat, echo, exit, sed} = require('shelljs'); -const yargs = require('yargs'); - -/** - * This script updates relevant React Native files with supplied version: - * * Prepares a package.json suitable for package consumption - * * Updates package.json for template project - * * Updates the version in gradle files and makes sure they are consistent between each other - * * Creates a gemfile - */ -if (require.main === module) { - let argv = yargs - .option('v', { - alias: 'to-version', - type: 'string', - required: false, - }) - .option('d', { - alias: 'dependency-versions', - type: 'string', - describe: - 'JSON string of package versions. Ex. "{"react-native":"0.64.1"}"', - default: null, - }) - .coerce('d', dependencyVersions => { - if (dependencyVersions == null) { - return null; - } - return JSON.parse(dependencyVersions); - }) - .option('b', { - alias: 'build-type', - type: 'string', - choices: ['dry-run', 'nightly', 'release', 'prealpha'], - required: true, - }).argv; - - setReactNativeVersion( - argv.toVersion, - argv.dependencyVersions, - argv.buildType, - ); - exit(0); -} - -function setSource({major, minor, patch, prerelease}) { - fs.writeFileSync( - 'packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java', - cat('scripts/versiontemplates/ReactNativeVersion.java.template') - .replace('${major}', major) - .replace('${minor}', minor) - .replace('${patch}', patch) - .replace( - '${prerelease}', - prerelease !== undefined ? `"${prerelease}"` : 'null', - ), - 'utf-8', - ); - - fs.writeFileSync( - 'packages/react-native/React/Base/RCTVersion.m', - cat('scripts/versiontemplates/RCTVersion.m.template') - .replace('${major}', `@(${major})`) - .replace('${minor}', `@(${minor})`) - .replace('${patch}', `@(${patch})`) - .replace( - '${prerelease}', - prerelease !== undefined ? `@"${prerelease}"` : '[NSNull null]', - ), - 'utf-8', - ); - - fs.writeFileSync( - 'packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h', - cat('scripts/versiontemplates/ReactNativeVersion.h.template') - .replace('${major}', major) - .replace('${minor}', minor) - .replace('${patch}', patch) - .replace( - '${prerelease}', - prerelease !== undefined ? `"${prerelease}"` : '""', - ), - 'utf-8', - ); - - fs.writeFileSync( - 'packages/react-native/Libraries/Core/ReactNativeVersion.js', - cat('scripts/versiontemplates/ReactNativeVersion.js.template') - .replace('${major}', major) - .replace('${minor}', minor) - .replace('${patch}', patch) - .replace( - '${prerelease}', - prerelease !== undefined ? `'${prerelease}'` : 'null', - ), - 'utf-8', - ); -} - -// Change ReactAndroid/gradle.properties -function setGradle({version}) { - const result = sed( - '-i', - /^VERSION_NAME=.*/, - `VERSION_NAME=${version}`, - 'packages/react-native/ReactAndroid/gradle.properties', - ); - if (result.code) { - echo("Couldn't update version for Gradle"); - throw result.stderr; - } -} - -function setPackage({version}, dependencyVersions) { - const originalPackageJson = JSON.parse( - cat('packages/react-native/package.json'), - ); - const packageJson = - dependencyVersions != null - ? applyPackageVersions(originalPackageJson, dependencyVersions) - : originalPackageJson; - - packageJson.version = version; - - fs.writeFileSync( - 'packages/react-native/package.json', - JSON.stringify(packageJson, null, 2), - 'utf-8', - ); -} - -function setReactNativeVersion(argVersion, dependencyVersions, buildType) { - if (!argVersion) { - const {version} = getNpmInfo(buildType); - argVersion = version; - } - validateBuildType(buildType); - - const version = parseVersion(argVersion, buildType); - - setSource(version); - setPackage(version, dependencyVersions); - - const templateDependencyVersions = { - 'react-native': version.version, - ...(dependencyVersions != null ? dependencyVersions : {}), - }; - updateTemplatePackage(templateDependencyVersions); - - setGradle(version); - return; -} - -module.exports = setReactNativeVersion; diff --git a/scripts/test-e2e-local.js b/scripts/test-e2e-local.js index a20c093ddcd..5dbc0ad192f 100644 --- a/scripts/test-e2e-local.js +++ b/scripts/test-e2e-local.js @@ -17,6 +17,7 @@ * and to make it more accessible for other devs to play around with. */ +const updateTemplatePackage = require('./releases/update-template-package'); const { checkPackagerRunning, launchPackagerInSeparateWindow, @@ -24,7 +25,6 @@ const { prepareArtifacts, setupCircleCIArtifacts, } = require('./testing-utils'); -const updateTemplatePackage = require('./update-template-package'); const path = require('path'); const {cd, exec, popd, pushd, pwd, sed} = require('shelljs'); const yargs = require('yargs'); diff --git a/scripts/testing-utils.js b/scripts/testing-utils.js index 0e789980e2a..a46bcc1ece2 100644 --- a/scripts/testing-utils.js +++ b/scripts/testing-utils.js @@ -207,7 +207,7 @@ function buildArtifactsLocally( ) { // this is needed to generate the Android artifacts correctly const exitCode = exec( - `node scripts/set-rn-version.js --to-version ${releaseVersion} --build-type ${buildType}`, + `node scripts/releases/set-rn-version.js --to-version ${releaseVersion} --build-type ${buildType}`, ).code; if (exitCode !== 0) { diff --git a/scripts/trigger-react-native-release.js b/scripts/trigger-react-native-release.js index f5aaf0f2dcf..b917a2fbb4c 100644 --- a/scripts/trigger-react-native-release.js +++ b/scripts/trigger-react-native-release.js @@ -14,8 +14,8 @@ const detectPackageUnreleasedChanges = require('./monorepo/bump-all-updated-pack const checkForGitChanges = require('./monorepo/check-for-git-changes'); const forEachPackage = require('./monorepo/for-each-package'); const {failIfTagExists} = require('./release-utils'); +const {isReleaseBranch, parseVersion} = require('./releases/version-utils'); const {exitIfNotOnGit, getBranchName} = require('./scm-utils'); -const {isReleaseBranch, parseVersion} = require('./version-utils'); const chalk = require('chalk'); const inquirer = require('inquirer'); const path = require('path'); diff --git a/scripts/versiontemplates/RCTVersion.m.template b/scripts/versiontemplates/RCTVersion.m.template deleted file mode 100644 index f79d3fae1d8..00000000000 --- a/scripts/versiontemplates/RCTVersion.m.template +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @generated by scripts/set-rn-version.js - * - * 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. - */ - -#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: ${major}, - RCTVersionMinor: ${minor}, - RCTVersionPatch: ${patch}, - RCTVersionPrerelease: ${prerelease}, - }; - }); - return __rnVersion; -} diff --git a/scripts/versiontemplates/ReactNativeVersion.h.template b/scripts/versiontemplates/ReactNativeVersion.h.template deleted file mode 100644 index 884df9f3fd0..00000000000 --- a/scripts/versiontemplates/ReactNativeVersion.h.template +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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/set-rn-version.js - */ - -#pragma once - -#include -#include - -namespace facebook::react { - -constexpr struct { - int32_t Major = ${major}; - int32_t Minor = ${minor}; - int32_t Patch = ${patch}; - std::string_view Prerelease = ${prerelease}; -} ReactNativeVersion; - -} // namespace facebook::react diff --git a/scripts/versiontemplates/ReactNativeVersion.java.template b/scripts/versiontemplates/ReactNativeVersion.java.template deleted file mode 100644 index 860764317f1..00000000000 --- a/scripts/versiontemplates/ReactNativeVersion.java.template +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @generated by scripts/set-rn-version.js - * - * 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. - */ - -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", ${major}, - "minor", ${minor}, - "patch", ${patch}, - "prerelease", ${prerelease}); -}