Relocate and refactor set-rn-version script (#42730)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/42730

Reviewing and modernising this script as part of simplifying our release publish workflow.

- Drop unused `--dependency-versions` arg from CLI entry point
- Simplify templating approach
- Type as Flow
- Drop dependencies on `shelljs` and `yargs`
- Relocate under `scripts/releases/`
- Rewrite tests as snapshot tests

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D53001971

fbshipit-source-id: e55a71a0bb37e3e18ba1e582a5c46ddd58823d81
This commit is contained in:
Alex Hunt 2024-01-30 06:46:50 -08:00 committed by Facebook GitHub Bot
parent 9ef6d606a7
commit e97ba475aa
28 changed files with 664 additions and 509 deletions

View File

@ -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

View File

@ -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<{

View File

@ -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"

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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,
});
});
});

View File

@ -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');
/*::

View File

@ -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}`);

View File

@ -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');

View File

@ -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<String, Object> VERSION = MapBuilder.<String, Object>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 <cstdint>
#include <string_view>
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<String, Object> VERSION = MapBuilder.<String, Object>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 <cstdint>
#include <string_view>
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
"
`;

View File

@ -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 >>',
);
}

View File

@ -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);
},
);
});
});

View File

@ -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<string, string> */,
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();
}

View File

@ -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;
}
`;

View File

@ -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 <cstdint>
#include <string_view>
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
`;

View File

@ -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<String, Object> VERSION = MapBuilder.<String, Object>of(
"major", ${version.major},
"minor", ${version.minor},
"patch", ${version.patch},
"prerelease", ${
version.prerelease != null ? `"${version.prerelease}"` : 'null'
});
}
`;

View File

@ -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};
`;

View File

@ -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<string, string> */) {
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(

View File

@ -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})$/))
);
}

View File

@ -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');

View File

@ -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;

View File

@ -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');

View File

@ -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) {

View File

@ -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');

View File

@ -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;
}

View File

@ -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 <cstdint>
#include <string_view>
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

View File

@ -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<String, Object> VERSION = MapBuilder.<String, Object>of(
"major", ${major},
"minor", ${minor},
"patch", ${patch},
"prerelease", ${prerelease});
}