Skip to content

Commit 776bd34

Browse files
janicduplessiside
authored andcommitted
Validate that JS and Native code versions match for RN releases
Summary: Basic implementation of the proposal in #15271 Note that this should not affect facebook internally since they are not using OSS releases. Points to consider: - How strict should the version match be, right now I just match exact versions. - Wasn't able to use haste for ReactNativeVersion because I was getting duplicate module provider caused by the template file in scripts/versiontemplates. I tried adding the scripts folder to modulePathIgnorePatterns in package.json but that didn't help. - Redscreen vs. warning, I think warning is useless because if the app crashes you won't have time to see the warning. - Should the check and native modules be __DEV__ only? **Test plan** Tested that it works when version match and that it redscreens when versions don't before getting other errors on Android and iOS. Closes #15518 Differential Revision: D5813551 Pulled By: hramos fbshipit-source-id: 901757e25724b0f22bf39de172b56309d0dd5a95
1 parent ac4a214 commit 776bd34

13 files changed

Lines changed: 180 additions & 2 deletions

File tree

Libraries/Core/InitializeCore.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,23 @@ if (!global.__fbDisableExceptionsManager) {
116116
ErrorUtils.setGlobalHandler(handleError);
117117
}
118118

119+
const formatVersion = version =>
120+
`${version.major}.${version.minor}.${version.patch}` +
121+
(version.prerelease !== null ? `-${version.prerelease}` : '');
122+
123+
const ReactNativeVersion = require('ReactNativeVersion');
124+
const nativeVersion = require('NativeModules').PlatformConstants.reactNativeVersion;
125+
if (ReactNativeVersion.version.major !== nativeVersion.major ||
126+
ReactNativeVersion.version.minor !== nativeVersion.minor) {
127+
throw new Error(
128+
`React Native version mismatch.\n\nJavaScript version: ${formatVersion(ReactNativeVersion.version)}\n` +
129+
`Native version: ${formatVersion(nativeVersion)}\n\n` +
130+
'Make sure that you have rebuilt the native code. If the problem persists ' +
131+
'try clearing the watchman and packager caches with `watchman watch-del-all ' +
132+
'&& react-native start --reset-cache`.'
133+
);
134+
}
135+
119136
// Set up collections
120137
const _shouldPolyfillCollection = require('_shouldPolyfillES6Collection');
121138
if (_shouldPolyfillCollection('Map')) {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @generated by scripts/bump-oss-version.js
3+
*
4+
* Copyright (c) 2015-present, Facebook, Inc.
5+
* All rights reserved.
6+
*
7+
* This source code is licensed under the BSD-style license found in the
8+
* LICENSE file in the root directory of this source tree. An additional grant
9+
* of patent rights can be found in the PATENTS file in the same directory.
10+
*
11+
* @flow
12+
* @providesModule ReactNativeVersion
13+
*/
14+
15+
exports.version = {
16+
major: 0,
17+
minor: 0,
18+
patch: 0,
19+
prerelease: null,
20+
};

React/Base/RCTPlatform.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#import <UIKit/UIKit.h>
1313

1414
#import "RCTUtils.h"
15+
#import "RCTVersion.h"
1516

1617
static NSString *interfaceIdiom(UIUserInterfaceIdiom idiom) {
1718
switch(idiom) {
@@ -46,6 +47,7 @@ + (BOOL)requiresMainQueueSetup
4647
@"systemName": [device systemName],
4748
@"interfaceIdiom": interfaceIdiom([device userInterfaceIdiom]),
4849
@"isTesting": @(RCTRunningInTestEnvironment()),
50+
@"reactNativeVersion": REACT_NATIVE_VERSION,
4951
};
5052
}
5153

React/Base/RCTVersion.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* @generated by scripts/bump-oss-version.js
3+
*
4+
* Copyright (c) 2015-present, Facebook, Inc.
5+
* All rights reserved.
6+
*
7+
* This source code is licensed under the BSD-style license found in the
8+
* LICENSE file in the root directory of this source tree. An additional grant
9+
* of patent rights can be found in the PATENTS file in the same directory.
10+
*/
11+
12+
#define REACT_NATIVE_VERSION @{ \
13+
@"major": @(0), \
14+
@"minor": @(0), \
15+
@"patch": @(0), \
16+
@"prerelease": [NSNull null], \
17+
}

React/React.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@
203203
14F7A0F01BDA714B003C6C10 /* RCTFPSGraph.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F7A0EF1BDA714B003C6C10 /* RCTFPSGraph.m */; };
204204
191E3EBE1C29D9AF00C180A6 /* RCTRefreshControlManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 191E3EBD1C29D9AF00C180A6 /* RCTRefreshControlManager.m */; };
205205
191E3EC11C29DC3800C180A6 /* RCTRefreshControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 191E3EC01C29DC3800C180A6 /* RCTRefreshControl.m */; };
206+
199B8A6F1F44DB16005DEF67 /* RCTVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 199B8A6E1F44DB16005DEF67 /* RCTVersion.h */; };
207+
199B8A761F44DEDA005DEF67 /* RCTVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 199B8A6E1F44DB16005DEF67 /* RCTVersion.h */; };
206208
19F61BFA1E8495CD00571D81 /* bignum-dtoa.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 139D7E3A1E25C5A300323FB7 /* bignum-dtoa.h */; };
207209
19F61BFB1E8495CD00571D81 /* bignum.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 139D7E3C1E25C5A300323FB7 /* bignum.h */; };
208210
19F61BFC1E8495CD00571D81 /* cached-powers.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 139D7E3E1E25C5A300323FB7 /* cached-powers.h */; };
@@ -1842,6 +1844,7 @@
18421844
191E3EBD1C29D9AF00C180A6 /* RCTRefreshControlManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRefreshControlManager.m; sourceTree = "<group>"; };
18431845
191E3EBF1C29DC3800C180A6 /* RCTRefreshControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRefreshControl.h; sourceTree = "<group>"; };
18441846
191E3EC01C29DC3800C180A6 /* RCTRefreshControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRefreshControl.m; sourceTree = "<group>"; };
1847+
199B8A6E1F44DB16005DEF67 /* RCTVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVersion.h; sourceTree = "<group>"; };
18451848
19DED2281E77E29200F089BB /* systemJSCWrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = systemJSCWrapper.cpp; sourceTree = "<group>"; };
18461849
27B958731E57587D0096647A /* JSBigString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSBigString.cpp; sourceTree = "<group>"; };
18471850
2D2A28131D9B038B00D4039D /* libReact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libReact.a; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -2652,6 +2655,7 @@
26522655
1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */,
26532656
83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */,
26542657
83CBBA501A601E3B00E9B192 /* RCTUtils.m */,
2658+
199B8A6E1F44DB16005DEF67 /* RCTVersion.h */,
26552659
);
26562660
path = Base;
26572661
sourceTree = "<group>";
@@ -2792,6 +2796,7 @@
27922796
3D302F411DF828F800D6DDAE /* RCTModuleMethod.h in Headers */,
27932797
3D302F421DF828F800D6DDAE /* RCTMultipartDataTask.h in Headers */,
27942798
3D302F431DF828F800D6DDAE /* RCTMultipartStreamReader.h in Headers */,
2799+
199B8A761F44DEDA005DEF67 /* RCTVersion.h in Headers */,
27952800
3D302F441DF828F800D6DDAE /* RCTNullability.h in Headers */,
27962801
3D302F451DF828F800D6DDAE /* RCTParserUtils.h in Headers */,
27972802
3D302F461DF828F800D6DDAE /* RCTPerformanceLogger.h in Headers */,
@@ -3029,6 +3034,7 @@
30293034
3D80DA191DF820620028D040 /* RCTImageLoader.h in Headers */,
30303035
C654505E1F3BD9280090799B /* RCTManagedPointer.h in Headers */,
30313036
13134C941E296B2A00B9F3CB /* RCTObjcExecutor.h in Headers */,
3037+
199B8A6F1F44DB16005DEF67 /* RCTVersion.h in Headers */,
30323038
3D80DA1A1DF820620028D040 /* RCTImageStoreManager.h in Headers */,
30333039
130443A11E3FEAA900D93A67 /* RCTFollyConvert.h in Headers */,
30343040
59FBEFB41E46D91C0095D885 /* RCTScrollContentViewManager.h in Headers */,

ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public String getName() {
3838
constants.put("Version", Build.VERSION.SDK_INT);
3939
constants.put("ServerHost", AndroidInfoHelpers.getServerHost());
4040
constants.put("isTesting", "true".equals(System.getProperty(IS_TESTING)));
41+
constants.put("reactNativeVersion", ReactNativeVersion.VERSION);
4142
return constants;
4243
}
4344
}

ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ android_library(
44
name = "systeminfo",
55
srcs = [
66
"AndroidInfoModule.java",
7+
"ReactNativeVersion.java",
78
],
89
exported_deps = [
910
":systeminfo-moduleless",
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @generated by scripts/bump-oss-version.js
3+
*
4+
* Copyright (c) 2015-present, Facebook, Inc.
5+
* All rights reserved.
6+
*
7+
* This source code is licensed under the BSD-style license found in the
8+
* LICENSE file in the root directory of this source tree. An additional grant
9+
* of patent rights can be found in the PATENTS file in the same directory.
10+
*/
11+
12+
package com.facebook.react.modules.systeminfo;
13+
14+
import com.facebook.react.common.MapBuilder;
15+
16+
import java.util.Map;
17+
18+
public class ReactNativeVersion {
19+
public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
20+
"major", 0,
21+
"minor", 0,
22+
"patch", 0,
23+
"prerelease", null);
24+
}

jest/setup.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jest
3434
jest.setMock('ErrorUtils', require('ErrorUtils'));
3535

3636
jest
37-
.mock('InitializeCore')
37+
.mock('InitializeCore', () => {})
3838
.mock('Image', () => mockComponent('Image'))
3939
.mock('Text', () => mockComponent('Text'))
4040
.mock('TextInput', () => mockComponent('TextInput'))

scripts/bump-oss-version.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,39 @@ let versionMajor = branch.slice(0, branch.indexOf(`-stable`));
4646
// e.g. 0.33.1 or 0.33.0-rc4
4747
let version = argv._[0];
4848
if (!version || version.indexOf(versionMajor) !== 0) {
49-
echo(`You must pass a tag like ${versionMajor}.[X]-rc[Y] to bump a version`);
49+
echo(`You must pass a tag like 0.${versionMajor}.[X]-rc[Y] to bump a version`);
5050
exit(1);
5151
}
5252

53+
// Generate version files to detect mismatches between JS and native.
54+
let match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
55+
if (!match) {
56+
echo(`You must pass a correctly formatted version; couldn't parse ${version}`);
57+
exit(1);
58+
}
59+
let [, major, minor, patch, prerelease] = match;
60+
61+
cat('scripts/versiontemplates/ReactNativeVersion.java.template')
62+
.replace('${major}', major)
63+
.replace('${minor}', minor)
64+
.replace('${patch}', patch)
65+
.replace('${prerelease}', prerelease !== undefined ? `"${prerelease}"` : 'null')
66+
.to('ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java');
67+
68+
cat('scripts/versiontemplates/RCTVersion.h.template')
69+
.replace('${major}', `@(${major})`)
70+
.replace('${minor}', `@(${minor})`)
71+
.replace('${patch}', `@(${patch})`)
72+
.replace('${prerelease}', prerelease !== undefined ? `@"${prerelease}"` : '[NSNull null]')
73+
.to('React/Base/RCTVersion.h');
74+
75+
cat('scripts/versiontemplates/ReactNativeVersion.js.template')
76+
.replace('${major}', major)
77+
.replace('${minor}', minor)
78+
.replace('${patch}', patch)
79+
.replace('${prerelease}', prerelease !== undefined ? `'${prerelease}'` : 'null')
80+
.to('Libraries/Core/ReactNativeVersion.js');
81+
5382
let packageJson = JSON.parse(cat(`package.json`));
5483
packageJson.version = version;
5584
JSON.stringify(packageJson, null, 2).to(`package.json`);

0 commit comments

Comments
 (0)