Compare commits

...
Author SHA1 Message Date
Mike Grabowski 796918c94e [0.50.3] Bump version numbers 2017-11-08 13:18:03 +01:00
Mike Grabowski 064ca3c279 Bump Metro version to fix bundling issues 2017-11-08 13:17:57 +01:00
James Ide 26b20bce9d [0.50.2] Bump version numbers 2017-11-07 12:05:40 -08:00
Dmitry ZakharovandJames Ide 9451e17e7d Make RN Events registered disregarding ViewManager use pattern.
Reviewed By: fromcelticpark

Differential Revision: D6260009

fbshipit-source-id: f3d00162f8473976264ebef040d01163950f2170
2017-11-07 11:56:25 -08:00
James Ide f983624f37 [0.50.1] Bump version numbers 2017-11-03 17:08:29 -07:00
James Ide 9b43636198 [podspec] Define pod_target_xcconfig for PrivateDatabase
We don't strictly need this since all the other subspecs that use this set up the header search path for ReactCommon anyway but do it just for correctness.
2017-11-03 17:07:54 -07:00
Joshua MayandJames Ide e33688ef81 Adds PrivateDatabase podspec
Summary:
Resolves #16663.

Walked through the reproduction steps in #16663, but with successful build.

We can also now see the files are included in the `Pods` project:

<img width="236" alt="screen shot 2017-11-03 at 7 43 21 pm" src="https://user-images.githubusercontent.com/33126/32390596-4a06c05c-c0cf-11e7-8616-3ae547f8cca2.png">

(I noticed a subspec called `_ignore_me_subspec_for_linting_` so do we have tests for the podspec?)

[IOS] [BUGFIX] [React.podspec] - Resolves build error about missing symbols for PrivateDatabase.{cpp,h}
Closes https://github.com/facebook/react-native/pull/16664

Differential Revision: D6238178

Pulled By: hramos

fbshipit-source-id: b57007ab8c7c8bd0faf1f2f11269a7a2c28183e5
2017-11-03 16:36:03 -07:00
Mike Grabowski 6ea6684ce2 [0.50.0] Bump version numbers 2017-11-03 17:51:20 +01:00
Mike Grabowski 1f6cd2d37d [0.50.0-rc.2] Bump version numbers 2017-10-30 10:31:12 +01:00
David VaccaandMike Grabowski b12213d017 Fixing RTL HorizontalScrolling in Android
Reviewed By: astreet

Differential Revision: D6170631

fbshipit-source-id: 254e6ed9a4d6e42b6d1215de1ff63aedb2c07a0a
2017-10-30 10:30:44 +01:00
Mike Grabowski 2da00fe871 [0.50.0-rc.1] Bump version numbers 2017-10-19 16:51:18 +02:00
Mike Grabowski e8d30c67d2 Fix test suite 2017-10-19 16:50:52 +02:00
Miron PawlikandJames Ide 4c196aec90 Make react-native link play nicely with CocoaPods-based iOS projects.
Summary:
The core React Native codebase already has full support for CocoaPods. However, `react-native link` doesn’t play nicely with CocoaPods, so installing third-party libs from the RN ecosystem is really hard.

This change will allow to link projects that contains its own `.podspec` file to CocoaPods-based projects. In case `link` detect `Podfile` in `iOS` directory, it will look for related `.podspec` file in linked project directory, and add it to `Podfile`. If `Podfile` and `.podspec` files are not present, it will fall back to previous implementation.

**Test Plan**
1. Build a React Native project where the iOS part uses CocoaPods to manage its dependencies. The most common scenario here is to have React Native be a Pod dependency, among others.
2. Install a RN-related library, that contains `.podspec` file, with `react-native link` (as an example it could be: [react-native-maps](https://github.com/airbnb/react-native-maps)
3. Building the resulting iOS workspace should succeed (and there should be new entry in `Podfile`)
Closes https://github.com/facebook/react-native/pull/15460

Differential Revision: D6078649

Pulled By: hramos

fbshipit-source-id: 9651085875892fd66299563ca0e42fb2bcc00825
2017-10-18 17:38:04 -07:00
Brian VaughnandJames Ide 87bc732ca7 Removed ProgressBarAndroid.android deprecation warning
Reviewed By: hramos

Differential Revision: D6092598

fbshipit-source-id: 24282a923cf45e749cf33f8a5b7a49995e7fadfe
2017-10-18 17:38:04 -07:00
James Ide 4f709f7b93 Move JS-native version check to its own module + unit tests + prefix Obj-C macro w/RCT
Summary:
- The version check that ensures the JS and native versions match is now in its own module for two reasons: it is easier to test and it allows react-native-windows to override just this module to implement its own version check (ex: more advanced checks for RNW-specific code).
- Added unit tests for the version checking to specify its behavior more clearly, including parity between dev and prod to avoid prod-only behavior and mitigate SEVs.
- Prefixed the Obj-C `#define` with `RCT_` to conform with other RN globals.
Closes https://github.com/facebook/react-native/pull/16403

Differential Revision: D6068491

Pulled By: hramos

fbshipit-source-id: 2b255b93982fb9d1b655fc62cb17b126bd5a939a
2017-10-16 22:25:14 -07:00
Mike Grabowski 63238f6add [0.50.0-rc.0] Bump version numbers
Fix eslint

Fix flow

Revert "Improve Flow Types"

This reverts commit bee33a4400.

[0.50.0-rc.1] Bump version numbers

Fix Analyze step

Remove mockFs dependency and unblock tests

Revert "[0.50.0-rc.1] Bump version numbers"

This reverts commit 5d0bc1d29bfdce40bbcc403190b06324557e5a58.

Revert "Migrate to Circle 2.0"

This reverts commit 8fa9984b11.

Remove old circle

Comment out danger for now
2017-10-11 19:36:23 +02:00
72 changed files with 1172 additions and 712 deletions
-373
View File
@@ -1,373 +0,0 @@
aliases:
- &restore-cache
keys:
- v1-dependencies-{{ .Branch }}-{{ checksum "package.json" }}
# Fallback in case checksum fails
- v1-dependencies-{{ .Branch }}-
- &save-cache
paths:
- node_modules
key: v1-dependencies-{{ .Branch }}-{{ checksum "package.json" }}
- &restore-cache-website
keys:
- v1-website-dependencies-{{ .Branch }}-{{ checksum "website/package.json" }}
# Fallback in case checksum fails
- v1-website-dependencies-{{ .Branch }}-
- &save-cache-website
paths:
- website/node_modules
key: v1-website-dependencies-{{ .Branch }}-{{ checksum "website/package.json" }}
- &restore-cache-danger
keys:
- v1-danger-dependencies-{{ .Branch }}-{{ checksum "danger/package.json" }}
# Fallback in case checksum fails
- v1-danger-dependencies-{{ .Branch }}-
- &save-cache-danger
paths:
- danger/node_modules
key: v1-danger-dependencies-{{ .Branch }}-{{ checksum "danger/package.json" }}
- &restore-cache-android-packages
keys:
- v1-android-sdkmanager-packages-{{ checksum "scripts/circle-ci-android-setup.sh" }}
# Fallback in case checksum fails
- v1-android-sdkmanager-packages-
- &save-cache-android-packages
paths:
- /opt/android/sdk/system-images/android-23
- /opt/android/sdk/system-images/android-19
- /opt/android/sdk/platforms/android-26
- /opt/android/sdk/platforms/android-23
- /opt/android/sdk/platforms/android-19
- /opt/android/sdk/build-tools/23.0.1
- /opt/android/sdk/add-ons/addon-google_apis-google-23
key: v1-android-sdkmanager-packages-{{ checksum "scripts/circle-ci-android-setup.sh" }}
- &restore-cache-ndk
keys:
- v1-android-ndk-r10e-32-64
- &save-cache-ndk
paths:
- /opt/ndk
key: v1-android-ndk-r10e-32-64
- &restore-cache-buck-downloads
keys:
- v1-buck-downloads-{{ .Branch }}-{{ checksum "ReactAndroid/build.gradle" }}
# Fallback in case checksum fails
- v1-buck-downloads-{{ .Branch }}-
- &save-cache-buck-downloads
paths:
- "ReactAndroid/build/downloads"
key: v1-buck-downloads-{{ .Branch }}-{{ checksum "ReactAndroid/build.gradle" }}
- &restore-cache-buck
keys:
- v1-buck-v2017.09.04.02
- &save-cache-buck
paths:
- ~/buck
key: v1-buck-v2017.09.04.02
- &restore-cache-watchman
keys:
- v1-watchman-v4.9.0
- &save-cache-watchman
paths:
- ~/watchman
key: v1-watchman-v4.9.0
defaults: &defaults
working_directory: ~/react-native
version: 2
jobs:
test-node-8:
<<: *defaults
docker:
- image: circleci/node:8
steps:
- checkout
- run: npm install --no-package-lock
- run: |
npm run lint
npm run flow -- check
npm test -- --maxWorkers=2
# eslint
- run:
name: Analyze Code
command: |
npm install github@0.2.4
cat <(echo eslint; npm run lint --silent -- --format=json; echo flow; npm run flow --silent -- check --json) | GITHUB_TOKEN="af6ef0d15709bc91d""06a6217a5a826a226fb57b7" CI_USER=$CIRCLE_PROJECT_USERNAME CI_REPO=$CIRCLE_PROJECT_REPONAME PULL_REQUEST_NUMBER=$CIRCLE_PR_NUMBER node bots/code-analysis-bot.js
test-node-6:
<<: *defaults
docker:
- image: circleci/node:6.11.0
steps:
- checkout
- run: npm install
- run: |
npm run lint
npm run flow -- check
npm test -- --maxWorkers=2
test-node-4:
<<: *defaults
docker:
- image: circleci/node:4.8.4
steps:
- checkout
- run: npm install
- run: |
npm run lint
npm run flow -- check
npm test -- --maxWorkers=2
test-website:
<<: *defaults
docker:
- image: circleci/node:8
steps:
- checkout
- run:
name: Install Dependencies
command: |
cd website
npm install --no-package-lock
- run:
name: Test Website
command: |
cd website
npm test
- run:
name: Test Build Static Website
command: cd website && node ./server/generate.js
deploy-website:
<<: *defaults
docker:
- image: circleci/node:8
steps:
- checkout
- run:
name: Install Dependencies
command: |
cd website
npm install --no-package-lock
- run:
name: Build and Deploy Static Website
command: |
if [[ $CIRCLE_PROJECT_USERNAME == "facebook" && -z $CI_PULL_REQUEST && -z $CIRCLE_PR_USERNAME ]]; then
git config --global user.email "reactjs-bot@users.noreply.github.com"
git config --global user.name "Website Deployment Script"
echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc
echo "Deploying website..."
cd website && GIT_USER=reactjs-bot npm run gh-pages
else
echo "Skipping deploy."
fi
build-js-bundle:
<<: *defaults
docker:
- image: circleci/node:8
steps:
- checkout
- run: npm install --no-package-lock
- run:
name: Build JavaScript Bundle
command: node local-cli/cli.js bundle --max-workers 2 --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
- persist_to_workspace:
root: ReactAndroid/src/androidTest/assets/
paths:
- AndroidTestBundle.js
- store_artifacts:
path: ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
test-android:
<<: *defaults
docker:
- image: circleci/android:api-26-alpha
environment:
- TERM: "dumb"
- ADB_INSTALL_TIMEOUT: 10
- GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx512m -XX:+HeapDumpOnOutOfMemoryError"'
- ANDROID_NDK: '/opt/ndk/android-ndk-r10e'
- BUILD_THREADS: 2
steps:
- checkout
# CircleCI does not support interpolating env variables in the environment: step above.
# https://circleci.com/docs/2.0/env-vars/#interpolating-environment-variables-to-set-other-environment-variables
- run:
name: Configure PATH
command: |
echo 'export PATH=${ANDROID_NDK}:~/react-native/gradle-2.9/bin:~/buck/bin:$PATH' >> $BASH_ENV
source $BASH_ENV
# Configure dependencies
- run:
name: Install Build Dependencies
command: |
sudo apt-get update -y
sudo apt-get install ant autoconf automake g++ gcc libqt5widgets5 lib32z1 lib32stdc++6 make maven python-dev python3-dev qml-module-qtquick-controls qtdeclarative5-dev -y
- restore-cache: *restore-cache-android-packages
- run:
name: Install Android Packages
command: source scripts/circle-ci-android-setup.sh && getAndroidSDK
- save-cache: *save-cache-android-packages
- run:
name: Create Android Virtual Device
command: source scripts/circle-ci-android-setup.sh && createAVD
# Starting emulator in advance as it takes some time to boot.
- run:
name: Launch Android Virtual Device in Background
command: source scripts/circle-ci-android-setup.sh && launchAVD
background: true
# Continue configuring dependencies while AVD boots.
- run:
name: Create Android NDK Directory
command: |
if [[ ! -e /opt/ndk ]]; then
sudo mkdir /opt/ndk
fi
sudo chown ${USER:=$(/usr/bin/id -run)}:$USER /opt/ndk
- restore-cache: *restore-cache-ndk
- run:
name: Install Android NDK
command: source scripts/circle-ci-android-setup.sh && getAndroidNDK
- save-cache: *save-cache-ndk
- restore-cache: *restore-cache-buck
- run:
name: Install Buck
command: |
if [[ ! -e ~/buck ]]; then
git clone https://github.com/facebook/buck.git ~/buck --branch v2017.09.04.02 --depth=1
fi
cd ~/buck && ant
buck --version
- save-cache: *save-cache-buck
- run:
name: Install Node
command: |
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs
- run: npm install
# - restore-cache: *restore-cache-watchman
# - run:
# name: Install Watchman Dependencies
# command: |
# sudo apt-get update -y
# sudo apt-get install libtool pkg-config -y
# - run:
# name: Install Watchman
# command: |
# if [[ ! -e ~/watchman ]]; then
# mkdir ~/watchman
# git clone https://github.com/facebook/watchman.git ~/watchman --branch v4.9.0 --depth=1
# cd ~/watchman
# ./autogen.sh
# ./configure
# make
# fi
# cd ~/watchman
# sudo make install
# - save-cache: *save-cache-watchman
- restore-cache: *restore-cache-buck-downloads
- run:
name: Download Android App Dependencies
command: |
buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
buck fetch ReactAndroid/src/main/java/com/facebook/react
buck fetch ReactAndroid/src/main/java/com/facebook/react/shell
buck fetch ReactAndroid/src/test/...
buck fetch ReactAndroid/src/androidTest/...
./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders
- save-cache: *save-cache-buck-downloads
- run:
name: Build Android App
command: |
buck build ReactAndroid/src/main/java/com/facebook/react
buck build ReactAndroid/src/main/java/com/facebook/react/shell
# Wait for AVD to finish booting before running tests
- run:
name: Wait for Android Virtual Device
command: source scripts/circle-ci-android-setup.sh && waitForAVD
# The JavaScript Bundle is built as part of the build-js-bundle workflow, and is required for instrumentation tests.
- attach_workspace:
at: ReactAndroid/src/androidTest/assets/
- run:
name: Check for JavaScript Bundle
command: |
if [[ ! -e ReactAndroid/src/androidTest/assets/AndroidTestBundle.js ]]; then
echo "JavaScript bundle missing, verify build-js-bundle step"; exit 1;
else
echo "JavaScript bundle found.";
fi
# Tests
- run:
name: Compile Native Libs for Unit and Integration Tests
command: ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=$BUILD_THREADS -Pcom.android.build.threadPoolSize=1
no_output_timeout: 6m
- run:
name: Unit Tests
command: buck test ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS
# Integration Tests
- run:
name: Build and Install Test APK
command: source scripts/circle-ci-android-setup.sh && NO_BUCKD=1 retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=$BUILD_THREADS
# Failing test is expected
# - run:
# name: Run Installed APK with Tests
# command: node ./scripts/run-android-ci-instrumentation-tests.js --retries 3 --path ./ReactAndroid/src/androidTest/java/com/facebook/react/tests --package com.facebook.react.tests
# Should be disabled pending on https://our.intern.facebook.com/intern/tasks?t=16912142
# - run:
# name: Run Android End to End Tests
# command: source scripts/circle-ci-android-setup.sh && retry3 node ./scripts/run-ci-e2e-tests.js --android --js --retries 2
# post (always runs)
- run:
name: Collect Test Results
command: |
mkdir -p ~/junit/
find . -type f -regex ".*/build/test-results/debug/.*xml" -exec cp {} ~/junit/ \;
find . -type f -regex ".*/outputs/androidTest-results/connected/.*xml" -exec cp {} ~/junit/ \;
# TODO circle does not understand Buck's report, maybe need to transform xml slightly
# find . -type f -regex ".*/buck-out/gen/ReactAndroid/src/test/.*/.*xml" -exec cp {} ~/junit/ \;
when: always
- store_test_results:
path: ~/junit
- store_artifacts:
path: ~/junit
# Workflows enables us to run multiple jobs in parallel
workflows:
version: 2
test_node:
jobs:
- test-node-8
- test-node-6
# Node 4 tests are already failing on Circle 1.0
# - test-node-4
website:
jobs:
- test-website
- deploy-website:
requires:
- test-website
filters:
branches:
only:
- /.*-stable/
- master
test_android:
jobs:
- build-js-bundle:
filters:
branches:
ignore: gh-pages
- test-android:
requires:
- build-js-bundle
+2 -2
View File
@@ -46,8 +46,8 @@ suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*[react_native_oss|react_native_fb][a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*[react_native_oss|react_native_fb][a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
@@ -14,8 +14,9 @@
const ColorPropType = require('ColorPropType');
const NativeMethodsMixin = require('NativeMethodsMixin');
const Platform = require('Platform');
const React = require('React');
const ProgressBarAndroid = require('ProgressBarAndroid');
const PropTypes = require('prop-types');
const React = require('React');
const StyleSheet = require('StyleSheet');
const View = require('View');
const ViewPropTypes = require('ViewPropTypes');
@@ -135,16 +136,20 @@ const ActivityIndicator = createReactClass({
break;
}
const nativeProps = {
...props,
style: sizeStyle,
styleAttr: 'Normal',
indeterminate: true,
};
return (
<View
onLayout={onLayout}
style={[styles.container, style]}>
<RCTActivityIndicator
{...props}
style={sizeStyle}
styleAttr="Normal"
indeterminate
/>
<View onLayout={onLayout} style={[styles.container, style]}>
{Platform.OS === 'ios' ? (
<RCTActivityIndicator {...nativeProps} />
) : (
<ProgressBarAndroid {...nativeProps} />
)}
</View>
);
}
@@ -169,18 +174,7 @@ if (Platform.OS === 'ios') {
var RCTActivityIndicator = requireNativeComponent(
'RCTActivityIndicatorView',
ActivityIndicator,
{nativeOnly: {activityIndicatorViewStyle: true}},
);
} else if (Platform.OS === 'android') {
var RCTActivityIndicator = requireNativeComponent(
'AndroidProgressBar',
ActivityIndicator,
// Ignore props that are specific to non inderterminate ProgressBar.
{nativeOnly: {
indeterminate: true,
progress: true,
styleAttr: true,
}},
{ nativeOnly: { activityIndicatorViewStyle: true } }
);
}
@@ -10,13 +10,14 @@
*/
'use strict';
const ActivityIndicator = require('ActivityIndicator');
const ColorPropType = require('ColorPropType');
const PropTypes = require('prop-types');
const React = require('React');
const ReactNative = require('ReactNative');
const ViewPropTypes = require('ViewPropTypes');
const requireNativeComponent = require('requireNativeComponent');
const STYLE_ATTRIBUTES = [
'Horizontal',
'Normal',
@@ -78,6 +79,10 @@ class ProgressBarAndroid extends ReactNative.NativeComponent {
* - LargeInverse
*/
styleAttr: PropTypes.oneOf(STYLE_ATTRIBUTES),
/**
* Whether to show the ProgressBar (true, the default) or hide it (false).
*/
animating: PropTypes.bool,
/**
* If the progress bar will show indeterminate progress. Note that this
* can only be false if styleAttr is Horizontal.
@@ -99,21 +104,23 @@ class ProgressBarAndroid extends ReactNative.NativeComponent {
static defaultProps = {
styleAttr: 'Normal',
indeterminate: true
indeterminate: true,
animating: true,
};
componentDidMount() {
if (this.props.indeterminate && this.props.styleAttr !== 'Horizontal') {
console.warn(
'Circular indeterminate `ProgressBarAndroid`' +
'is deprecated. Use `ActivityIndicator` instead.'
);
}
}
render() {
return <ActivityIndicator {...this.props} animating={true} />;
return <AndroidProgressBar {...this.props} />;
}
}
const AndroidProgressBar = requireNativeComponent(
'AndroidProgressBar',
ProgressBarAndroid,
{
nativeOnly: {
animating: true,
},
}
);
module.exports = ProgressBarAndroid;
@@ -659,10 +659,11 @@ const ScrollView = createReactClass({
} else if (Platform.OS === 'android') {
if (this.props.horizontal) {
ScrollViewClass = AndroidHorizontalScrollView;
ScrollContentContainerViewClass = AndroidHorizontalScrollContentView;
} else {
ScrollViewClass = AndroidScrollView;
ScrollContentContainerViewClass = View;
}
ScrollContentContainerViewClass = View;
}
invariant(
@@ -879,6 +880,7 @@ const styles = StyleSheet.create({
let nativeOnlyProps,
AndroidScrollView,
AndroidHorizontalScrollContentView,
AndroidHorizontalScrollView,
RCTScrollView,
RCTScrollContentView;
@@ -898,6 +900,9 @@ if (Platform.OS === 'android') {
(ScrollView: React.ComponentType<any>),
nativeOnlyProps
);
AndroidHorizontalScrollContentView = requireNativeComponent(
'AndroidHorizontalScrollContentView'
);
} else if (Platform.OS === 'ios') {
nativeOnlyProps = {
nativeOnly: {
+3 -19
View File
@@ -116,25 +116,9 @@ if (!global.__fbDisableExceptionsManager) {
ErrorUtils.setGlobalHandler(handleError);
}
const {PlatformConstants} = require('NativeModules');
if (PlatformConstants) {
const formatVersion = version =>
`${version.major}.${version.minor}.${version.patch}` +
(version.prerelease !== null ? `-${version.prerelease}` : '');
const ReactNativeVersion = require('ReactNativeVersion');
const nativeVersion = PlatformConstants.reactNativeVersion;
if (ReactNativeVersion.version.major !== nativeVersion.major ||
ReactNativeVersion.version.minor !== nativeVersion.minor) {
throw new Error(
`React Native version mismatch.\n\nJavaScript version: ${formatVersion(ReactNativeVersion.version)}\n` +
`Native version: ${formatVersion(nativeVersion)}\n\n` +
'Make sure that you have rebuilt the native code. If the problem persists ' +
'try clearing the watchman and packager caches with `watchman watch-del-all ' +
'&& react-native start --reset-cache`.'
);
}
}
// Check for compatibility between the JS and native code
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
ReactNativeVersionCheck.checkVersions();
// Set up collections
const _shouldPolyfillCollection = require('_shouldPolyfillES6Collection');
+2 -2
View File
@@ -14,7 +14,7 @@
exports.version = {
major: 0,
minor: 0,
patch: 0,
minor: 50,
patch: 3,
prerelease: null,
};
+54
View File
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeVersionCheck
* @flow
* @format
*/
'use strict';
const {PlatformConstants} = require('NativeModules');
const ReactNativeVersion = require('ReactNativeVersion');
/**
* Checks that the version of this React Native JS is compatible with the native
* code, throwing an error if it isn't.
*
* The existence of this module is part of the public interface of React Native
* even though it is used only internally within React Native. React Native
* implementations for other platforms (ex: Windows) may override this module
* and rely on its existence as a separate module.
*/
exports.checkVersions = function checkVersions(): void {
if (!PlatformConstants) {
return;
}
const nativeVersion = PlatformConstants.reactNativeVersion;
if (
ReactNativeVersion.version.major !== nativeVersion.major ||
ReactNativeVersion.version.minor !== nativeVersion.minor
) {
throw new Error(
`React Native version mismatch.\n\nJavaScript version: ${_formatVersion(
ReactNativeVersion.version,
)}\n` +
`Native version: ${_formatVersion(nativeVersion)}\n\n` +
'Make sure that you have rebuilt the native code. If the problem ' +
'persists try clearing the Watchman and packager caches with ' +
'`watchman watch-del-all && react-native start --reset-cache`.',
);
}
};
function _formatVersion(version): string {
return (
`${version.major}.${version.minor}.${version.patch}` +
(version.prerelease !== null ? `-${version.prerelease}` : '')
);
}
@@ -0,0 +1,117 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
*/
'use strict';
describe('checkVersion', () => {
describe('in development', () => {
_setDevelopmentModeForTests(true);
_defineCheckVersionTests();
});
describe('in production', () => {
_setDevelopmentModeForTests(false);
_defineCheckVersionTests();
});
});
function _setDevelopmentModeForTests(dev) {
let originalDev;
beforeAll(() => {
originalDev = global.__DEV__;
global.__DEV__ = dev;
});
afterAll(() => {
global.__DEV__ = originalDev;
});
}
function _defineCheckVersionTests() {
afterEach(() => {
jest.resetModules();
});
it('passes when all the versions are zero', () => {
_mockJsVersion(0, 0, 0);
_mockNativeVersion(0, 0, 0);
const ReactNativeVersion = require('ReactNativeVersion');
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
expect(ReactNativeVersion).toMatchObject({
version: {major: 0, minor: 0, patch: 0, prerelease: null},
});
expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow();
});
it('passes when the minor matches when the major is zero', () => {
_mockJsVersion(0, 1, 0);
_mockNativeVersion(0, 1, 0);
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow();
});
it("throws when the minor doesn't match when the major is zero", () => {
_mockJsVersion(0, 1, 0);
_mockNativeVersion(0, 2, 0);
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
expect(() => ReactNativeVersionCheck.checkVersions()).toThrowError(
/React Native version mismatch/,
);
});
it("throws when the major doesn't match", () => {
_mockJsVersion(1, 0, 0);
_mockNativeVersion(2, 0, 0);
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
expect(() => ReactNativeVersionCheck.checkVersions()).toThrowError(
/React Native version mismatch/,
);
});
it("doesn't throw if the patch doesn't match", () => {
_mockJsVersion(0, 1, 0);
_mockNativeVersion(0, 1, 2);
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow();
});
it("doesn't throw if the prerelease doesn't match", () => {
_mockJsVersion(0, 1, 0, 'beta.0');
_mockNativeVersion(0, 1, 0, 'alpha.1');
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow();
});
}
function _mockJsVersion(major = 0, minor = 0, patch = 0, prerelease = null) {
jest.doMock('ReactNativeVersion', () => ({
version: {major, minor, patch, prerelease},
}));
}
function _mockNativeVersion(
major = 0,
minor = 0,
patch = 0,
prerelease = null,
) {
jest.doMock('NativeModules', () => ({
PlatformConstants: {
reactNativeVersion: {major, minor, patch, prerelease},
},
}));
}
+5 -6
View File
@@ -23,7 +23,7 @@ import type {Props as VirtualizedSectionListProps} from 'VirtualizedSectionList'
type Item = any;
export type SectionBase<SectionItemT> = {
type SectionBase<SectionItemT> = {
/**
* The data for rendering items in this section.
*/
@@ -187,11 +187,10 @@ type OptionalProps<SectionT: SectionBase<any>> = {
legacyImplementation?: ?boolean,
};
export type Props<SectionT> = {
...$Exact<RequiredProps<SectionT>>,
...$Exact<OptionalProps<SectionT>>,
...$Exact<VirtualizedSectionListProps<SectionT>>,
};
export type Props<SectionT> = RequiredProps<SectionT> &
OptionalProps<SectionT> &
VirtualizedSectionListProps<SectionT>;
const defaultProps = {
...VirtualizedSectionList.defaultProps,
stickySectionHeadersEnabled: Platform.OS === 'ios',
+24 -26
View File
@@ -12,6 +12,7 @@
*/
'use strict';
const Platform = require('Platform');
const ReactNativeBridgeEventPlugin = require('ReactNativeBridgeEventPlugin');
const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
const UIManager = require('UIManager');
@@ -47,35 +48,30 @@ const warning = require('fbjs/lib/warning');
*/
import type {ComponentInterface} from 'verifyPropTypes';
let hasAttachedDefaultEventTypes: boolean = false;
function requireNativeComponent(
viewName: string,
componentInterface?: ?ComponentInterface,
extraConfig?: ?{nativeOnly?: Object},
): React$ComponentType<any> | string {
function attachBubblingEventTypes(viewConfig) {
if (UIManager.genericBubblingEventTypes) {
viewConfig.bubblingEventTypes = merge(
viewConfig.bubblingEventTypes,
UIManager.genericBubblingEventTypes,
);
// As genericBubblingEventTypes do not change over time, and there's
// merge of all the events happening in Fiber, we need to pass
// genericBubblingEventTypes to Fiber only once. Therefore, we can delete
// it and forget about it.
delete UIManager.genericBubblingEventTypes;
}
}
function attachDirectEventTypes(viewConfig) {
if (UIManager.genericDirectEventTypes) {
viewConfig.directEventTypes = merge(
viewConfig.directEventTypes,
UIManager.genericDirectEventTypes,
);
// As genericDirectEventTypes do not change over time, and there's merge
// of all the events happening in Fiber, we need to pass genericDirectEventTypes
// to Fiber only once. Therefore, we can delete it and forget about it.
delete UIManager.genericDirectEventTypes;
function attachDefaultEventTypes(viewConfig: any) {
if (Platform.OS === 'android') {
// This is supported on Android platform only,
// as lazy view managers discovery is Android-specific.
if (UIManager.ViewManagerNames) {
// Lazy view managers enabled.
viewConfig = merge(viewConfig, UIManager.getDefaultEventTypes());
} else {
viewConfig.bubblingEventTypes = merge(
viewConfig.bubblingEventTypes,
UIManager.genericBubblingEventTypes,
);
viewConfig.directEventTypes = merge(
viewConfig.directEventTypes,
UIManager.genericDirectEventTypes,
);
}
}
}
@@ -184,8 +180,10 @@ function requireNativeComponent(
);
}
attachBubblingEventTypes(viewConfig);
attachDirectEventTypes(viewConfig);
if (!hasAttachedDefaultEventTypes) {
attachDefaultEventTypes(viewConfig);
hasAttachedDefaultEventTypes = true;
}
// Register this view's event types with the ReactNative renderer.
// This enables view managers to be initialized lazily, improving perf,
+1 -3
View File
@@ -74,9 +74,7 @@ const SwipeableFlatListExample = createReactClass({
<View style={styles.row}>
<Image style={styles.rowIcon} source={item.icon} />
<View style={styles.rowData}>
<Text style={styles.rowDataText}>
{item.data}
</Text>
<Text style={styles.rowDataText}>{item.data}</Text>
</View>
</View>
);
+7
View File
@@ -109,6 +109,7 @@ Pod::Spec.new do |s|
s.subspec "jschelpers" do |ss|
ss.dependency "Folly", "2016.09.26.00"
ss.dependency "React/PrivateDatabase"
ss.compiler_flags = folly_compiler_flags
ss.source_files = "ReactCommon/jschelpers/*.{cpp,h}"
ss.private_header_files = "ReactCommon/jschelpers/*.h"
@@ -116,6 +117,12 @@ Pod::Spec.new do |s|
ss.framework = "JavaScriptCore"
end
s.subspec "PrivateDatabase" do |ss|
ss.source_files = "ReactCommon/privatedata/*.{cpp,h}"
ss.private_header_files = "ReactCommon/privatedata/*.h"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\"" }
end
s.subspec "cxxreact" do |ss|
ss.dependency "React/jschelpers"
ss.dependency "boost"
+1 -1
View File
@@ -47,7 +47,7 @@ RCT_EXPORT_MODULE(PlatformConstants)
@"systemName": [device systemName],
@"interfaceIdiom": interfaceIdiom([device userInterfaceIdiom]),
@"isTesting": @(RCTRunningInTestEnvironment()),
@"reactNativeVersion": REACT_NATIVE_VERSION,
@"reactNativeVersion": RCT_REACT_NATIVE_VERSION,
};
}
+3 -3
View File
@@ -9,9 +9,9 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#define REACT_NATIVE_VERSION @{ \
#define RCT_REACT_NATIVE_VERSION @{ \
@"major": @(0), \
@"minor": @(0), \
@"patch": @(0), \
@"minor": @(50), \
@"patch": @(3), \
@"prerelease": [NSNull null], \
}
+2 -2
View File
@@ -86,7 +86,7 @@ def rn_robolectric_test(name, srcs, vm_args = None, *args, **kwargs):
'-XX:MaxPermSize=620m',
'-Drobolectric.offline=true',
]
if os.path.isdir("/dev/shm") and not os.environ['CIRCLECI']:
if os.path.isdir("/dev/shm"):
extra_vm_args.append('-Djava.io.tmpdir=/dev/shm')
else:
extra_vm_args.append(
@@ -110,4 +110,4 @@ def rn_robolectric_test(name, srcs, vm_args = None, *args, **kwargs):
original_cxx_library = cxx_library
def cxx_library(allow_jni_merging=None, **kwargs):
original_cxx_library(**kwargs)
original_cxx_library(**kwargs)
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION_NAME=1000.0.0-master
VERSION_NAME=0.50.3
GROUP=com.facebook.react
POM_NAME=ReactNative
@@ -18,7 +18,7 @@ import java.util.Map;
public class ReactNativeVersion {
public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
"major", 0,
"minor", 0,
"patch", 0,
"minor", 50,
"patch", 3,
"prerelease", null);
}
@@ -61,6 +61,7 @@ import com.facebook.react.views.modal.ReactModalHostManager;
import com.facebook.react.views.picker.ReactDialogPickerManager;
import com.facebook.react.views.picker.ReactDropdownPickerManager;
import com.facebook.react.views.progressbar.ReactProgressBarViewManager;
import com.facebook.react.views.scroll.ReactHorizontalScrollContainerViewManager;
import com.facebook.react.views.scroll.ReactHorizontalScrollViewManager;
import com.facebook.react.views.scroll.ReactScrollViewManager;
import com.facebook.react.views.slider.ReactSliderManager;
@@ -315,6 +316,7 @@ public class MainReactPackage extends LazyReactPackage {
viewManagers.add(new ReactDrawerLayoutManager());
viewManagers.add(new ReactDropdownPickerManager());
viewManagers.add(new ReactHorizontalScrollViewManager());
viewManagers.add(new ReactHorizontalScrollContainerViewManager());
viewManagers.add(new ReactProgressBarViewManager());
viewManagers.add(new ReactScrollViewManager());
viewManagers.add(new ReactSliderManager());
@@ -17,7 +17,6 @@ import android.content.res.Configuration;
import com.facebook.common.logging.FLog;
import com.facebook.debug.holder.PrinterHolder;
import com.facebook.debug.tags.ReactDebugOverlayTags;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.animation.Animation;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
@@ -43,7 +42,7 @@ import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
/**
* <p>Native module to allow JS to create and update native Views.</p>
*
* <p>
@@ -115,11 +114,6 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements
private int mBatchId = 0;
// Defines if events were already exported to JS. We do not send them more
// than once as they are stored and mixed in with Fiber for every ViewManager
// on JS side.
private boolean mEventsWereSentToJS = false;
public UIManagerModule(
ReactApplicationContext reactContext,
ViewManagerResolver viewManagerResolver,
@@ -233,7 +227,6 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements
}
}
@DoNotStrip
@ReactMethod(isBlockingSynchronousMethod = true)
public @Nullable WritableMap getConstantsForViewManager(final String viewManagerName) {
ViewManager targetView =
@@ -249,13 +242,8 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements
try {
Map<String, Object> viewManagerConstants =
UIManagerModuleConstantsHelper.createConstantsForViewManager(
targetView,
mEventsWereSentToJS ? null : UIManagerModuleConstants.getBubblingEventTypeConstants(),
mEventsWereSentToJS ? null : UIManagerModuleConstants.getDirectEventTypeConstants(),
null,
mCustomDirectEvents);
targetView, null, null, null, mCustomDirectEvents);
if (viewManagerConstants != null) {
mEventsWereSentToJS = true;
return Arguments.makeNativeMap(viewManagerConstants);
}
return null;
@@ -264,9 +252,12 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements
}
}
/**
* Resolves Direct Event name exposed to JS from the one known to the Native side.
*/
@ReactMethod(isBlockingSynchronousMethod = true)
public WritableMap getDefaultEventTypes() {
return Arguments.makeNativeMap(UIManagerModuleConstantsHelper.getDefaultExportableEventTypes());
}
/** Resolves Direct Event name exposed to JS from the one known to the Native side. */
public CustomEventNamesResolver getDirectEventNamesResolver() {
return new CustomEventNamesResolver() {
@Override
@@ -24,6 +24,9 @@ import javax.annotation.Nullable;
*/
/* package */ class UIManagerModuleConstantsHelper {
private static final String BUBBLING_EVENTS_KEY = "bubblingEventTypes";
private static final String DIRECT_EVENTS_KEY = "directEventTypes";
/**
* Generates a lazy discovery enabled version of {@link UIManagerModule} constants. It only
* contains a list of view manager names, so that JS side is aware of the managers there are.
@@ -38,6 +41,12 @@ import javax.annotation.Nullable;
return constants;
}
/* package */ static Map<String, Object> getDefaultExportableEventTypes() {
return MapBuilder.<String, Object>of(
BUBBLING_EVENTS_KEY, UIManagerModuleConstants.getBubblingEventTypeConstants(),
DIRECT_EVENTS_KEY, UIManagerModuleConstants.getDirectEventTypeConstants());
}
/**
* Generates map of constants that is then exposed by {@link UIManagerModule}.
* Provided list of {@param viewManagers} is then used to populate content of
@@ -106,9 +115,6 @@ import javax.annotation.Nullable;
@Nullable Map defaultDirectEvents,
@Nullable Map cumulativeBubblingEventTypes,
@Nullable Map cumulativeDirectEventTypes) {
final String BUBBLING_EVENTS_KEY = "bubblingEventTypes";
final String DIRECT_EVENTS_KEY = "directEventTypes";
Map<String, Object> viewManagerConstants = MapBuilder.newHashMap();
Map viewManagerBubblingEvents = viewManager.getExportedCustomBubblingEventTypeConstants();
@@ -16,6 +16,7 @@ android_library(
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target("java/com/facebook/react/modules/i18nmanager:i18nmanager"),
react_native_target("java/com/facebook/react/touch:touch"),
react_native_target("java/com/facebook/react/uimanager:uimanager"),
react_native_target("java/com/facebook/react/uimanager/annotations:annotations"),
@@ -0,0 +1,40 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.views.scroll;
import android.content.Context;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import com.facebook.react.modules.i18nmanager.I18nUtil;
/** Container of Horizontal scrollViews that supports RTL scrolling. */
public class ReactHorizontalScrollContainerView extends ViewGroup {
private int mLayoutDirection;
public ReactHorizontalScrollContainerView(Context context) {
super(context);
mLayoutDirection =
I18nUtil.getInstance().isRTL(context) ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (mLayoutDirection == LAYOUT_DIRECTION_RTL) {
// When the layout direction is RTL, we expect Yoga to give us a layout
// that extends off the screen to the left so we re-center it with left=0
int newLeft = 0;
int width = right - left;
int newRight = newLeft + width;
setLeft(newLeft);
setRight(newRight);
// Fix the ScrollX position when using RTL language
int offsetX = computeHorizontalScrollRange() - getScrollX();
// Call with the present values in order to re-layout if necessary
HorizontalScrollView parent = (HorizontalScrollView) getParent();
parent.scrollTo(offsetX, parent.getScrollY());
}
}
}
@@ -0,0 +1,27 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.views.scroll;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
/** View manager for {@link ReactHorizontalScrollContainerView} components. */
@ReactModule(name = ReactHorizontalScrollContainerViewManager.REACT_CLASS)
public class ReactHorizontalScrollContainerViewManager
extends ViewGroupManager<ReactHorizontalScrollContainerView> {
protected static final String REACT_CLASS = "AndroidHorizontalScrollContentView";
public ReactHorizontalScrollContainerViewManager() {}
@Override
public String getName() {
return REACT_CLASS;
}
@Override
public ReactHorizontalScrollContainerView createViewInstance(ThemedReactContext context) {
return new ReactHorizontalScrollContainerView(context);
}
}
+122
View File
@@ -0,0 +1,122 @@
general:
branches:
ignore:
- gh-pages # list of branches to ignore
machine:
node:
version: 6.2.0
environment:
PATH: "~/$CIRCLE_PROJECT_REPONAME/gradle-2.9/bin:/home/ubuntu/buck/bin:$PATH"
TERM: "dumb"
ADB_INSTALL_TIMEOUT: 10
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx512m -XX:+HeapDumpOnOutOfMemoryError"'
java:
version: 'oraclejdk8'
dependencies:
override:
# BUCK and android
- if [[ ! -e /home/ubuntu/buck ]]; then git clone https://github.com/facebook/buck.git /home/ubuntu/buck; fi
- cd /home/ubuntu/buck && ant
- buck --version
- source scripts/circle-ci-android-setup.sh && getAndroidSDK
- buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
- buck fetch ReactAndroid/src/main/java/com/facebook/react
- buck fetch ReactAndroid/src/main/java/com/facebook/react/shell
- buck fetch ReactAndroid/src/test/...
- buck fetch ReactAndroid/src/androidTest/...
- ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders
# CIRCLE_NPM_TOKEN is in React Native project settings in Circle CI.
# It was generated for bestander user, easy to replace with anyone's else
- echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc
- npm config set spin=false
- npm config set progress=false
- npm install
# for eslint bot
- npm install github@0.2.4
# for website, danger
- cd website && npm install
- cd danger && npm install
cache_directories:
- "ReactAndroid/build/downloads"
- "/home/ubuntu/buck"
- "website/node_modules"
- "node_modules"
- "danger/node_modules"
test:
pre:
# starting emulator in advance because it takes very long to boot
- $ANDROID_HOME/tools/emulator -avd testAVD -no-skin -no-audio -no-window:
background: true
- source scripts/circle-ci-android-setup.sh && waitForAVD
override:
# Run Danger against PRs. This GitHub token grants public_repo access scope. The associated account has no privileged access to the React Native repo. The token must be split in this manner to avoid revocation by GitHub.
#- cd danger && DANGER_GITHUB_API_TOKEN="e622517d9f1136ea8900""07c6373666312cdfaa69" npm # run danger
# eslint bot. This GitHub token grants public_repo access scope. The token must be split in this manner to avoid revocation by GitHub.
- cat <(echo eslint; npm run lint --silent -- --format=json; echo flow; npm run flow --silent -- check --json) | GITHUB_TOKEN="af6ef0d15709bc91d""06a6217a5a826a226fb57b7" CI_USER=$CIRCLE_PROJECT_USERNAME CI_REPO=$CIRCLE_PROJECT_REPONAME PULL_REQUEST_NUMBER=$CIRCLE_PR_NUMBER node bots/code-analysis-bot.js
- npm run lint
# JS tests for dependencies installed with npm3
# Commenting out Flow tests
# - npm run flow -- check
- npm test -- --maxWorkers=1
# build app
- buck build ReactAndroid/src/main/java/com/facebook/react
- buck build ReactAndroid/src/main/java/com/facebook/react/shell
# compile native libs with Gradle script, we need bridge for unit and
# integration tests
- ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=1 -Pcom.android.build.threadPoolSize=1:
timeout: 360
# unit tests
- buck test ReactAndroid/src/test/... --config build.threads=1
# integration tests
# build JS bundle for instrumentation tests
- node local-cli/cli.js bundle --max-workers 1 --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
# build test APK
# Commented out due to test failures. Please uncomment the next line once these have been fixed. See Issue #15726.
# - buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1
# run installed apk with tests
# - node ./scripts/run-android-ci-instrumentation-tests.js --retries 3 --path ./ReactAndroid/src/androidTest/java/com/facebook/react/tests --package com.facebook.react.tests
# Android e2e test
# disabled pending on https://our.intern.facebook.com/intern/tasks?t=16912142
# - source scripts/circle-ci-android-setup.sh && retry3 node ./scripts/run-ci-e2e-tests.js --android --js --retries 2
# testing docs generation
- cd website && npm test
- cd website && node ./server/generate.js
# if website can be built, deploy regardless of test failures
- if [[ ($CIRCLE_BRANCH =~ .*-stable || $CIRCLE_BRANCH == "master") && $CIRCLE_PROJECT_USERNAME == "facebook" && -z $CI_PULL_REQUEST && -z $CIRCLE_PR_USERNAME ]]; then git config --global user.email "reactjs-bot@users.noreply.github.com" && git config --global user.name "Website Deployment Script" && echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc && cd website && GIT_USER=reactjs-bot npm run gh-pages; fi
post:
# copy test report for Circle CI to display
- mkdir -p $CIRCLE_TEST_REPORTS/junit/
- find . -type f -regex ".*/build/test-results/debug/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
- find . -type f -regex ".*/outputs/androidTest-results/connected/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
# TODO circle does not understand Buck's report, maybe need to transform xml slightly
#- find . -type f -regex ".*/buck-out/gen/ReactAndroid/src/test/.*/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
deployment:
stable:
branch: [/.*-stable/, /master/]
owner: facebook
commands:
- git config --global user.email "reactjs-bot@users.noreply.github.com"
- git config --global user.name "Website Deployment Script"
- echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc
# publish to npm
- node ./scripts/publish-npm.js
experimental:
notify:
branches:
only:
- master
- /.*-stable/
+6 -2
View File
@@ -35,8 +35,8 @@ Install a library with native dependencies:
$ npm install <library-with-native-dependencies> --save
```
**Note:** _`--save` or `--save-dev` flag is very important for this step. React Native will link
your libs based on `dependencies` and `devDependencies` in your `package.json` file._
> ***Note:*** `--save` or `--save-dev` flag is very important for this step. React Native will link
your libs based on `dependencies` and `devDependencies` in your `package.json` file.
#### Step 2
@@ -47,6 +47,10 @@ $ react-native link
Done! All libraries with native dependencies should be successfully linked to your iOS/Android project.
> ***Note:*** If your iOS project is using CocoaPods (contains `Podfile`) and linked library has `podspec` file,
then `react-native link` will link library using Podfile. To support non-trivial Podfiles
add `# Add new pods below this line` comment to places where you expect pods to be added.
### Manual linking
#### Step 1
@@ -94,6 +94,9 @@ fs.readFileSync.mockImplementation(function(filepath, encoding) {
if (isDirNode(node)) {
throw new Error('Error readFileSync a dir: ' + filepath);
}
if (Buffer.isBuffer(node) && typeof encoding !== 'undefined') {
return node.toString();
}
return node;
});
@@ -133,7 +136,12 @@ function fsError(code, message) {
}
function isDirNode(node) {
return node && typeof node === 'object' && node.SYMLINK == null;
return (
node &&
typeof node === 'object' &&
node.SYMLINK == null &&
Buffer.isBuffer(node) === false
);
}
function readlinkSync(filepath) {
+1 -1
View File
@@ -1,4 +1,4 @@
const fs = require('fs');
const fs = require.requireActual('fs');
const path = require('path');
const manifest = fs.readFileSync(path.join(__dirname, './files/AndroidManifest.xml'));
+1 -1
View File
@@ -1,4 +1,4 @@
const fs = require('fs');
const fs = require.requireActual('fs');
const path = require('path');
const android = require('./android');
+6 -1
View File
@@ -1,10 +1,11 @@
const fs = require('fs');
const fs = require.requireActual('fs');
const path = require('path');
exports.valid = {
'demoProject.xcodeproj': {
'project.pbxproj': fs.readFileSync(path.join(__dirname, './files/project.pbxproj')),
},
'TestPod.podspec': 'empty'
};
exports.validTestName = {
@@ -12,3 +13,7 @@ exports.validTestName = {
'project.pbxproj': fs.readFileSync(path.join(__dirname, './files/project.pbxproj')),
},
};
exports.pod = {
'TestPod.podspec': 'empty'
};
+7 -3
View File
@@ -1,11 +1,10 @@
const fs = require('fs');
const path = require('path');
const android = require('./android');
const ios = require('./ios');
const flat = {
android: android.valid,
ios: ios.valid,
Podfile: 'empty'
};
const nested = {
@@ -21,4 +20,9 @@ const withExamples = {
android: android.valid,
};
module.exports = { flat, nested, withExamples };
const withPods = {
Podfile: 'content',
ios: ios.pod
};
module.exports = { flat, nested, withExamples, withPods };
@@ -11,13 +11,15 @@
'use strict';
jest.mock('fs');
const fs = require('fs');
const findAndroidAppFolder = require('../../android/findAndroidAppFolder');
const mockFS = require('mock-fs');
const mocks = require('../../__fixtures__/android');
describe('android::findAndroidAppFolder', () => {
beforeAll(() => {
mockFS({
fs.__setMockFilesystem({
empty: {},
nested: {
android: {
@@ -31,15 +33,11 @@ describe('android::findAndroidAppFolder', () => {
});
it('returns an android app folder if it exists in the given folder', () => {
expect(findAndroidAppFolder('flat')).toBe('android');
expect(findAndroidAppFolder('nested')).toBe('android/app');
expect(findAndroidAppFolder('/flat')).toBe('android');
expect(findAndroidAppFolder('/nested')).toBe('android/app');
});
it('returns `null` if there is no android app folder', () => {
expect(findAndroidAppFolder('empty')).toBeNull();
});
afterAll(() => {
mockFS.restore();
expect(findAndroidAppFolder('/empty')).toBeNull();
});
});
@@ -11,13 +11,15 @@
'use strict';
jest.mock('fs');
const findManifest = require('../../android/findManifest');
const mockFS = require('mock-fs');
const fs = require('fs');
const mocks = require('../../__fixtures__/android');
describe('android::findManifest', () => {
beforeAll(() => {
mockFS({
fs.__setMockFilesystem({
empty: {},
flat: {
android: mocks.valid,
@@ -26,14 +28,10 @@ describe('android::findManifest', () => {
});
it('returns a manifest path if file exists in the folder', () => {
expect(typeof findManifest('flat')).toBe('string');
expect(typeof findManifest('/flat')).toBe('string');
});
it('returns `null` if there is no manifest in the folder', () => {
expect(findManifest('empty')).toBeNull();
});
afterAll(() => {
mockFS.restore();
expect(findManifest('/empty')).toBeNull();
});
});
@@ -11,13 +11,15 @@
'use strict';
jest.mock('fs');
const findPackageClassName = require('../../android/findPackageClassName');
const mockFS = require('mock-fs');
const fs = require('fs');
const mocks = require('../../__fixtures__/android');
describe('android::findPackageClassName', () => {
beforeAll(() => {
mockFS({
fs.__setMockFilesystem({
empty: {},
flatJava: {
android: mocks.valid,
@@ -29,22 +31,20 @@ describe('android::findPackageClassName', () => {
});
it('returns manifest content if file exists in the folder', () => {
expect(typeof findPackageClassName('flatJava')).toBe('string');
expect(typeof findPackageClassName('/flatJava')).toBe('string');
});
it('returns the name of the java class implementing ReactPackage', () => {
expect(findPackageClassName('flatJava')).toBe('SomeExampleJavaPackage');
expect(findPackageClassName('/flatJava')).toBe('SomeExampleJavaPackage');
});
it('returns the name of the kotlin class implementing ReactPackage', () => {
expect(findPackageClassName('flatKotlin')).toBe('SomeExampleKotlinPackage');
expect(findPackageClassName('/flatKotlin')).toBe(
'SomeExampleKotlinPackage',
);
});
it('returns `null` if there are no matches', () => {
expect(findPackageClassName('empty')).toBeNull();
});
afterAll(() => {
mockFS.restore();
expect(findPackageClassName('/empty')).toBeNull();
});
});
@@ -11,15 +11,17 @@
'use strict';
jest.mock('fs');
const getDependencyConfig = require('../../android').dependencyConfig;
const mockFS = require('mock-fs');
const fs = require('fs');
const mocks = require('../../__fixtures__/android');
const userConfig = {};
describe('android::getDependencyConfig', () => {
beforeAll(() => {
mockFS({
fs.__setMockFilesystem({
empty: {},
nested: {
android: {
@@ -38,27 +40,23 @@ describe('android::getDependencyConfig', () => {
});
it('returns an object with android project configuration', () => {
expect(getDependencyConfig('nested', userConfig)).not.toBeNull();
expect(typeof getDependencyConfig('nested', userConfig)).toBe('object');
expect(getDependencyConfig('/nested', userConfig)).not.toBeNull();
expect(typeof getDependencyConfig('/nested', userConfig)).toBe('object');
});
it('returns `null` if manifest file has not been found', () => {
expect(getDependencyConfig('empty', userConfig)).toBeNull();
expect(getDependencyConfig('/empty', userConfig)).toBeNull();
});
it('returns `null` if android project was not found', () => {
expect(getDependencyConfig('empty', userConfig)).toBeNull();
expect(getDependencyConfig('/empty', userConfig)).toBeNull();
});
it('returns `null` if android project does not contain ReactPackage', () => {
expect(getDependencyConfig('noPackage', userConfig)).toBeNull();
expect(getDependencyConfig('/noPackage', userConfig)).toBeNull();
});
it('returns `null` if it cannot find a packageClassName', () => {
expect(getDependencyConfig('corrupted', userConfig)).toBeNull();
});
afterAll(() => {
mockFS.restore();
expect(getDependencyConfig('/corrupted', userConfig)).toBeNull();
});
});
@@ -11,13 +11,15 @@
'use strict';
jest.mock('fs');
const getProjectConfig = require('../../android').projectConfig;
const mockFS = require('mock-fs');
const fs = require('fs');
const mocks = require('../../__fixtures__/android');
describe('android::getProjectConfig', () => {
beforeAll(() => {
mockFS({
fs.__setMockFilesystem({
empty: {},
nested: {
android: {
@@ -38,7 +40,7 @@ describe('android::getProjectConfig', () => {
it("returns `null` if manifest file hasn't been found", () => {
const userConfig = {};
const folder = 'noManifest';
const folder = '/noManifest';
expect(getProjectConfig(folder, userConfig)).toBeNull();
});
@@ -46,7 +48,7 @@ describe('android::getProjectConfig', () => {
describe('returns an object with android project configuration for', () => {
it('nested structure', () => {
const userConfig = {};
const folder = 'nested';
const folder = '/nested';
expect(getProjectConfig(folder, userConfig)).not.toBeNull();
expect(typeof getProjectConfig(folder, userConfig)).toBe('object');
@@ -54,7 +56,7 @@ describe('android::getProjectConfig', () => {
it('flat structure', () => {
const userConfig = {};
const folder = 'flat';
const folder = '/flat';
expect(getProjectConfig(folder, userConfig)).not.toBeNull();
expect(typeof getProjectConfig(folder, userConfig)).toBe('object');
@@ -64,7 +66,7 @@ describe('android::getProjectConfig', () => {
const userConfig = {
manifestPath: 'src/main/AndroidManifest.xml',
};
const folder = 'multiple';
const folder = '/multiple';
expect(getProjectConfig(folder, userConfig)).not.toBeNull();
expect(typeof getProjectConfig(folder, userConfig)).toBe('object');
@@ -73,12 +75,8 @@ describe('android::getProjectConfig', () => {
it('should return `null` if android project was not found', () => {
const userConfig = {};
const folder = 'empty';
const folder = '/empty';
expect(getProjectConfig(folder, userConfig)).toBeNull();
});
afterAll(() => {
mockFS.restore();
});
});
@@ -11,14 +11,16 @@
'use strict';
jest.mock('fs');
const findManifest = require('../../android/findManifest');
const readManifest = require('../../android/readManifest');
const mockFS = require('mock-fs');
const fs = require('fs');
const mocks = require('../../__fixtures__/android');
describe('android::readManifest', () => {
beforeAll(() => {
mockFS({
fs.__setMockFilesystem({
empty: {},
nested: {
android: {
@@ -29,19 +31,15 @@ describe('android::readManifest', () => {
});
it('returns manifest content if file exists in the folder', () => {
const manifestPath = findManifest('nested');
const manifestPath = findManifest('/nested');
expect(readManifest(manifestPath)).not.toBeNull();
expect(typeof readManifest(manifestPath)).toBe('object');
});
it('throws an error if there is no manifest in the folder', () => {
const fakeManifestPath = findManifest('empty');
const fakeManifestPath = findManifest('/empty');
expect(() => {
readManifest(fakeManifestPath);
}).toThrow();
});
afterAll(() => {
mockFS.restore();
});
});
+7 -9
View File
@@ -11,24 +11,26 @@
'use strict';
jest.mock('fs');
const findAssets = require('../findAssets');
const dependencies = require('../__fixtures__/dependencies');
const mockFs = require('mock-fs');
const fs = require('fs');
describe('findAssets', () => {
beforeEach(() => {
mockFs({testDir: dependencies.withAssets});
fs.__setMockFilesystem({testDir: dependencies.withAssets});
});
it('returns an array of all files in given folders', () => {
const assets = findAssets('testDir', ['fonts', 'images']);
const assets = findAssets('/testDir', ['fonts', 'images']);
expect(Array.isArray(assets)).toBeTruthy();
expect(assets).toHaveLength(3);
});
it('prepends assets paths with the folder path', () => {
const assets = findAssets('testDir', ['fonts', 'images']);
const assets = findAssets('/testDir', ['fonts', 'images']);
assets.forEach(assetPath => {
expect(assetPath).toContain('testDir');
@@ -36,10 +38,6 @@ describe('findAssets', () => {
});
it('returns an empty array if given assets are null', () => {
expect(findAssets('testDir', null)).toHaveLength(0);
});
afterEach(() => {
mockFs.restore();
expect(findAssets('/testDir', null)).toHaveLength(0);
});
});
@@ -0,0 +1,20 @@
'use strict';
jest.mock('fs');
const findPodfilePath = require('../../ios/findPodfilePath');
const fs = require('fs');
const projects = require('../../__fixtures__/projects');
const ios = require('../../__fixtures__/ios');
describe('ios::findPodfilePath', () => {
it('returns null if there is no Podfile', () => {
fs.__setMockFilesystem(ios.valid);
expect(findPodfilePath('')).toBeNull();
});
it('returns Podfile path if it exists', () => {
fs.__setMockFilesystem(projects.withPods);
expect(findPodfilePath('/ios')).toContain('Podfile');
});
});
@@ -0,0 +1,44 @@
'use strict';
jest.mock('fs');
const findPodspecName = require('../../ios/findPodspecName');
const fs = require('fs');
const projects = require('../../__fixtures__/projects');
const ios = require('../../__fixtures__/ios');
describe('ios::findPodspecName', () => {
it('returns null if there is not podspec file', () => {
fs.__setMockFilesystem(projects.flat);
expect(findPodspecName('')).toBeNull();
});
it('returns podspec name if only one exists', () => {
fs.__setMockFilesystem(ios.pod);
expect(findPodspecName('/')).toBe('TestPod');
});
it('returns podspec name that match packet directory', () => {
fs.__setMockFilesystem({
user: {
PacketName: {
'Another.podspec': 'empty',
'PacketName.podspec': 'empty'
}
}
});
expect(findPodspecName('/user/PacketName')).toBe('PacketName');
});
it('returns first podspec name if not match in directory', () => {
fs.__setMockFilesystem({
user: {
packet: {
'Another.podspec': 'empty',
'PacketName.podspec': 'empty'
}
}
});
expect(findPodspecName('/user/packet')).toBe('Another');
});
});
@@ -11,49 +11,51 @@
'use strict';
jest.mock('fs');
const findProject = require('../../ios/findProject');
const mockFS = require('mock-fs');
const fs = require('fs');
const projects = require('../../__fixtures__/projects');
const ios = require('../../__fixtures__/ios');
describe('ios::findProject', () => {
it('returns path to xcodeproj if found', () => {
mockFS(projects.flat);
expect(findProject('')).not.toBeNull();
fs.__setMockFilesystem(projects.flat);
expect(findProject('/')).not.toBeNull();
});
it('returns null if there are no projects', () => {
mockFS({testDir: projects});
expect(findProject('')).toBeNull();
fs.__setMockFilesystem({testDir: projects});
expect(findProject('/')).toBeNull();
});
it('returns ios project regardless of its name', () => {
mockFS({ios: ios.validTestName});
expect(findProject('')).not.toBeNull();
fs.__setMockFilesystem({ios: ios.validTestName});
expect(findProject('/')).not.toBeNull();
});
it('ignores node_modules', () => {
mockFS({node_modules: projects.flat});
expect(findProject('')).toBeNull();
fs.__setMockFilesystem({node_modules: projects.flat});
expect(findProject('/')).toBeNull();
});
it('ignores Pods', () => {
mockFS({Pods: projects.flat});
expect(findProject('')).toBeNull();
fs.__setMockFilesystem({Pods: projects.flat});
expect(findProject('/')).toBeNull();
});
it('ignores Pods inside `ios` folder', () => {
mockFS({
fs.__setMockFilesystem({
ios: {
Pods: projects.flat,
DemoApp: projects.flat.ios,
},
});
expect(findProject('')).toBe('ios/DemoApp/demoProject.xcodeproj');
expect(findProject('/')).toBe('ios/DemoApp/demoProject.xcodeproj');
});
it('ignores xcodeproj from example folders', () => {
mockFS({
fs.__setMockFilesystem({
examples: projects.flat,
Examples: projects.flat,
example: projects.flat,
@@ -61,11 +63,11 @@ describe('ios::findProject', () => {
Zpp: projects.flat,
});
expect(findProject('').toLowerCase()).not.toContain('example');
expect(findProject('/').toLowerCase()).not.toContain('example');
});
it('ignores xcodeproj from sample folders', () => {
mockFS({
fs.__setMockFilesystem({
samples: projects.flat,
Samples: projects.flat,
sample: projects.flat,
@@ -73,11 +75,11 @@ describe('ios::findProject', () => {
Zpp: projects.flat,
});
expect(findProject('').toLowerCase()).not.toContain('sample');
expect(findProject('/').toLowerCase()).not.toContain('sample');
});
it('ignores xcodeproj from test folders at any level', () => {
mockFS({
fs.__setMockFilesystem({
test: projects.flat,
IntegrationTests: projects.flat,
tests: projects.flat,
@@ -87,10 +89,6 @@ describe('ios::findProject', () => {
},
});
expect(findProject('').toLowerCase()).not.toContain('test');
});
afterEach(() => {
mockFS.restore();
expect(findProject('/').toLowerCase()).not.toContain('test');
});
});
@@ -11,32 +11,34 @@
'use strict';
jest.mock('fs');
const getProjectConfig = require('../../ios').projectConfig;
const mockFS = require('mock-fs');
const fs = require('fs');
const projects = require('../../__fixtures__/projects');
describe('ios::getProjectConfig', () => {
const userConfig = {};
beforeEach(() => {
mockFS({testDir: projects});
fs.__setMockFilesystem({testDir: projects});
});
it('returns an object with ios project configuration', () => {
const folder = 'testDir/nested';
const folder = '/testDir/nested';
expect(getProjectConfig(folder, userConfig)).not.toBeNull();
expect(typeof getProjectConfig(folder, userConfig)).toBe('object');
});
it('returns `null` if ios project was not found', () => {
const folder = 'testDir/empty';
const folder = '/testDir/empty';
expect(getProjectConfig(folder, userConfig)).toBeNull();
});
it('returns normalized shared library names', () => {
const projectConfig = getProjectConfig('testDir/nested', {
const projectConfig = getProjectConfig('/testDir/nested', {
sharedLibraries: ['libc++', 'libz.tbd', 'HealthKit', 'HomeKit.framework'],
});
@@ -47,8 +49,4 @@ describe('ios::getProjectConfig', () => {
'HomeKit.framework',
]);
});
afterEach(() => {
mockFS.restore();
});
});
+11
View File
@@ -0,0 +1,11 @@
'use strict';
const fs = require('fs');
const path = require('path');
module.exports = function findPodfilePath(projectFolder) {
const podFilePath = path.join(projectFolder, '..', 'Podfile');
const podFileExists = fs.existsSync(podFilePath);
return podFileExists ? podFilePath : null;
};
+28
View File
@@ -0,0 +1,28 @@
'use strict';
const glob = require('glob');
const path = require('path');
module.exports = function findPodspecName(folder) {
const podspecs = glob.sync('*.podspec', { cwd: folder });
let podspecFile = null;
if (podspecs.length === 0) {
return null;
}
else if (podspecs.length === 1) {
podspecFile = podspecs[0];
}
else {
const folderParts = folder.split(path.sep);
const currentFolder = folderParts[folderParts.length - 1];
const toSelect = podspecs.indexOf(currentFolder + '.podspec');
if (toSelect === -1) {
podspecFile = podspecs[0];
}
else {
podspecFile = podspecs[toSelect];
}
}
return podspecFile.replace('.podspec', '');
};
+4
View File
@@ -9,6 +9,8 @@
'use strict';
const findProject = require('./findProject');
const findPodfilePath = require('./findPodfilePath');
const findPodspecName = require('./findPodspecName');
const path = require('path');
/**
@@ -44,6 +46,8 @@ exports.projectConfig = function projectConfigIOS(folder, userConfig) {
sourceDir: path.dirname(projectPath),
folder: folder,
pbxprojPath: path.join(projectPath, 'project.pbxproj'),
podfile: findPodfilePath(projectPath),
podspec: findPodspecName(folder),
projectPath: projectPath,
projectName: path.basename(projectPath),
libraryFolder: userConfig.libraryFolder || 'Libraries',
@@ -0,0 +1,8 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
target 'Testing' do
pod 'TestPod', '~> 3.1'
# test should point to this line
end
@@ -0,0 +1,30 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
target 'none' do
pod 'React',
:path => "../node_modules/react-native",
:subspecs => [
"Core",
"ART",
"RCTActionSheet",
"RCTAnimation",
"RCTCameraRoll",
"RCTGeolocation",
"RCTImage",
"RCTNetwork",
"RCTText",
"RCTVibration",
"RCTWebSocket",
"DevSupport",
"BatchedBridge"
]
pod 'Yoga',
:path => "../node_modules/react-native/ReactCommon/yoga"
# test should point to this line
post_install do |installer|
end
end
@@ -0,0 +1,34 @@
source 'https://github.com/CocoaPods/Specs.git'
# platform :ios, '9.0'
target 'None' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Your 'node_modules' directory is probably in the root of your project, # but if not, adjust the `:path` accordingly
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'BatchedBridge',
'RCTImage',
'RCTWebSocket', # needed for debugging
# Add any other subspecs you want to use in your project
]
# Add new pods below this line
# test should point to this line
target 'NoneTests' do
inherit! :search_paths
# Pods for testing
end
end
target 'Second' do
target 'NoneUITests' do
inherit! :search_paths
# Add new pods below this line
end
end
@@ -0,0 +1,32 @@
source 'https://github.com/CocoaPods/Specs.git'
# platform :ios, '9.0'
target 'None' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Your 'node_modules' directory is probably in the root of your project, # but if not, adjust the `:path` accordingly
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'BatchedBridge',
'RCTImage',
'RCTWebSocket', # needed for debugging
# Add any other subspecs you want to use in your project
]
# Explicitly include Yoga if you are using RN >= 0.42.0
pod "Yoga", :path => "../node_modules/react-native/ReactCommon/yoga"
# test should point to this line
target 'NoneTests' do
inherit! :search_paths
# Pods for testing
end
target 'NoneUITests' do
inherit! :search_paths
# Pods for testing
end
end
@@ -0,0 +1,30 @@
'use strict';
const path = require('path');
const findLineToAddPod = require('../../pods/findLineToAddPod');
const readPodfile = require('../../pods/readPodfile');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
const LINE_AFTER_TARGET_IN_TEST_PODFILE = 4;
describe('pods::findLineToAddPod', () => {
it('returns null if file is not Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, '../Info.plist'));
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toBeNull();
});
it('returns correct line number for Simple Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual({ line: 7, indentation: 2 });
});
it('returns correct line number for Podfile with target', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileWithTarget'));
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual({ line: 21, indentation: 2 });
});
it('returns correct line number for Podfile with function', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileWithFunction'));
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual({ line: 26, indentation: 2 });
});
});
@@ -0,0 +1,26 @@
'use strict';
const path = require('path');
const readPodfile = require('../../pods/readPodfile');
const findMarkedLinesInPodfile = require('../../pods/findMarkedLinesInPodfile');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
const LINE_AFTER_TARGET_IN_TEST_PODFILE = 4;
describe('pods::findMarkedLinesInPodfile', () => {
it('returns empty array if file is not Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, '../Info.plist'));
expect(findMarkedLinesInPodfile(podfile)).toEqual([]);
});
it('returns empty array for Simple Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
expect(findMarkedLinesInPodfile(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual([]);
});
it('returns correct line numbers for Podfile with marker', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileWithMarkers'));
const expectedObject = [{ line: 18, indentation: 2 }, { line: 31, indentation: 4 }];
expect(findMarkedLinesInPodfile(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual(expectedObject);
});
});
@@ -0,0 +1,24 @@
'use strict';
const path = require('path');
const findPodTargetLine = require('../../pods/findPodTargetLine');
const readPodfile = require('../../pods/readPodfile');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
describe('pods::findPodTargetLine', () => {
it('returns null if file is not Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, '../Info.plist'));
expect(findPodTargetLine(podfile, 'name')).toBeNull();
});
it('returns null if there is not matching project name', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
expect(findPodTargetLine(podfile, 'invalidName')).toBeNull();
});
it('returns null if there is not matching project name', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
expect(findPodTargetLine(podfile, 'Testing')).toBe(4);
});
});
@@ -0,0 +1,32 @@
'use strict';
const path = require('path');
const isInstalled = require('../../pods/isInstalled');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
describe('pods::isInstalled', () => {
it('returns false if pod is missing', () => {
const project = { podfile: path.join(PODFILES_PATH, 'PodfileSimple') };
const podspecName = { podspec: 'NotExisting' };
expect(isInstalled(project, podspecName)).toBe(false);
});
it('returns true for existing pod with version number', () => {
const project = { podfile: path.join(PODFILES_PATH, 'PodfileSimple') };
const podspecName = { podspec: 'TestPod' };
expect(isInstalled(project, podspecName)).toBe(true);
});
it('returns true for existing pod with path', () => {
const project = { podfile: path.join(PODFILES_PATH, 'PodfileWithTarget') };
const podspecName = { podspec: 'Yoga' };
expect(isInstalled(project, podspecName)).toBe(true);
});
it('returns true for existing pod with multiline definition', () => {
const project = { podfile: path.join(PODFILES_PATH, 'PodfileWithFunction') };
const podspecName = { podspec: 'React' };
expect(isInstalled(project, podspecName)).toBe(true);
});
});
@@ -0,0 +1,35 @@
'use strict';
const path = require('path');
const removePodEntry = require('../../pods/removePodEntry');
const readPodfile = require('../../pods/readPodfile');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
describe('pods::removePodEntry', () => {
it('should remove one line from Podfile with TestPod', () => {
const { podfileContent, podLinesCount } = readTestPodFile('PodfileSimple');
const podFileWithRemoved = removePodEntry(podfileContent, 'TestPod');
const newLineCount = podFileWithRemoved.split('\n').length;
expect(newLineCount).toBe(podLinesCount - 1);
});
it('should remove one line from Podfile with Yoga', () => {
const { podfileContent, podLinesCount } = readTestPodFile('PodfileWithTarget');
const podFileWithRemoved = removePodEntry(podfileContent, 'Yoga');
const newLineCount = podFileWithRemoved.split('\n').length;
expect(newLineCount).toBe(podLinesCount - 1);
});
it('should remove whole reference to React pod from Podfile', () => {
const { podfileContent, podLinesCount } = readTestPodFile('PodfileWithTarget');
const podFileWithRemoved = removePodEntry(podfileContent, 'React');
const newLineCount = podFileWithRemoved.split('\n').length;
expect(newLineCount).toBe(podLinesCount - 9);
});
});
function readTestPodFile(fileName) {
const podfileLines = readPodfile(path.join(PODFILES_PATH, fileName));
return { podfileContent: podfileLines.join('\n'), podLinesCount: podfileLines.length };
}
+9 -5
View File
@@ -29,9 +29,11 @@ const promiseWaterfall = require('./promiseWaterfall');
const registerDependencyAndroid = require('./android/registerNativeModule');
const registerDependencyWindows = require('./windows/registerNativeModule');
const registerDependencyIOS = require('./ios/registerNativeModule');
const registerDependencyPods = require('./pods/registerNativeModule');
const isInstalledAndroid = require('./android/isInstalled');
const isInstalledWindows = require('./windows/isInstalled');
const isInstalledIOS = require('./ios/isInstalled');
const isInstalledPods = require('./pods/isInstalled');
const copyAssetsAndroid = require('./android/copyAssets');
const copyAssetsIOS = require('./ios/copyAssets');
const getProjectDependencies = require('./getProjectDependencies');
@@ -106,17 +108,19 @@ const linkDependencyIOS = (iOSProject, dependency) => {
return;
}
const isInstalled = isInstalledIOS(iOSProject, dependency.config.ios);
const isInstalled = isInstalledIOS(iOSProject, dependency.config.ios) || isInstalledPods(iOSProject, dependency.config.ios);
if (isInstalled) {
log.info(chalk.grey(`iOS module ${dependency.name} is already linked`));
return;
}
log.info(`Linking ${dependency.name} ios dependency`);
registerDependencyIOS(dependency.config.ios, iOSProject);
if (iOSProject.podfile && dependency.config.ios.podspec) {
registerDependencyPods(dependency, iOSProject);
}
else {
registerDependencyIOS(dependency.config.ios, iOSProject);
}
log.info(`iOS module ${dependency.name} has been successfully linked`);
};
+21
View File
@@ -0,0 +1,21 @@
'use strict';
module.exports = function addPodEntry(podLines, linesToAddEntry, podName, nodePath) {
const newEntry = `pod '${podName}', :path => '../node_modules/${nodePath}'\n`;
if (!linesToAddEntry) {
return;
} else if (Array.isArray(linesToAddEntry)) {
linesToAddEntry.map(({ line, indentation }, idx) =>
podLines.splice(line + idx, 0, getLineToAdd(newEntry, indentation))
);
} else {
const { line, indentation } = linesToAddEntry;
podLines.splice(line, 0, getLineToAdd(newEntry, indentation));
}
};
function getLineToAdd(newEntry, indentation) {
const spaces = Array(indentation + 1).join(' ');
return spaces + newEntry;
}
+24
View File
@@ -0,0 +1,24 @@
'use strict';
module.exports = function findLineToAddPod(podLines, firstTargetLine) {
// match line with new target: target 'project_name' do (most likely target inside podfile main target)
const nextTarget = /target (\'|\")\w+(\'|\") do/g;
// match line that has only 'end' (if we don't catch new target or function, this would mean this is end of current target)
const endOfCurrentTarget = /^\s*end\s*$/g;
// match function definition, like: post_install do |installer| (some Podfiles have function defined inside main target
const functionDefinition = /^\s*[a-z_]+\s+do(\s+\|[a-z]+\|)?/g;
for (let i = firstTargetLine, len = podLines.length; i < len; i++) {
const matchNextConstruct = podLines[i].match(nextTarget) || podLines[i].match(functionDefinition);
const matchEnd = podLines[i].match(endOfCurrentTarget);
if (matchNextConstruct || matchEnd) {
const firstNonSpaceCharacter = podLines[i].search(/\S/);
return {
indentation: firstNonSpaceCharacter + (matchEnd ? 2 : 0),
line: i
};
}
}
return null;
};
@@ -0,0 +1,12 @@
'use strict';
const MARKER_TEXT = '# Add new pods below this line';
module.exports = function findMarkedLinesInPodfile(podLines) {
const result = [];
for (let i = 0, len = podLines.length; i < len; i++) {
if (podLines[i].includes(MARKER_TEXT)) {
result.push({ line: i + 1, indentation: podLines[i].indexOf('#') });
}
}
return result;
};
+14
View File
@@ -0,0 +1,14 @@
'use strict';
module.exports = function findPodTargetLine(podLines, projectName) {
const targetName = projectName.replace('.xcodeproj', '');
//match first target definition in file: target 'target_name' do
const targetRegex = new RegExp('target (\'|\")' + targetName + '(\'|\") do', 'g');
for (let i = 0, len = podLines.length; i < len; i++) {
const match = podLines[i].match(targetRegex);
if (match) {
return i + 1;
}
}
return null;
};
+19
View File
@@ -0,0 +1,19 @@
'use strict';
const readPodfile = require('./readPodfile');
module.exports = function isInstalled(iOSProject, dependencyConfig) {
if (!iOSProject.podfile) {
return false;
}
// match line with pod declaration: pod 'dependencyPodName' (other possible parameters of pod are ignored)
const dependencyRegExp = new RegExp('pod\\s+(\'|\")' + dependencyConfig.podspec + '(\'|\")', 'g');
const podLines = readPodfile(iOSProject.podfile);
for (let i = 0, len = podLines.length; i < len; i++) {
const match = podLines[i].match(dependencyRegExp);
if (match) {
return true;
}
}
return false;
};
+8
View File
@@ -0,0 +1,8 @@
'use strict';
const fs = require('fs');
module.exports = function readPodfile(podfilePath) {
const podContent = fs.readFileSync(podfilePath, 'utf8');
return podContent.split(/\r?\n/g);
};
@@ -0,0 +1,25 @@
'use strict';
const readPodfile = require('./readPodfile');
const findPodTargetLine = require('./findPodTargetLine');
const findLineToAddPod = require('./findLineToAddPod');
const findMarkedLinesInPodfile = require('./findMarkedLinesInPodfile');
const addPodEntry = require('./addPodEntry');
const savePodFile = require('./savePodFile');
module.exports = function registerNativeModulePods(dependency, iOSProject) {
const podLines = readPodfile(iOSProject.podfile);
const linesToAddEntry = getLinesToAddEntry(podLines, iOSProject);
addPodEntry(podLines, linesToAddEntry, dependency.config.ios.podspec, dependency.name);
savePodFile(iOSProject.podfile, podLines);
};
function getLinesToAddEntry(podLines, { projectName }) {
const linesToAddPodWithMarker = findMarkedLinesInPodfile(podLines);
if (linesToAddPodWithMarker.length > 0) {
return linesToAddPodWithMarker;
} else {
const firstTargetLined = findPodTargetLine(podLines, projectName);
return findLineToAddPod(podLines, firstTargetLined);
}
}
+7
View File
@@ -0,0 +1,7 @@
'use strict';
module.exports = function removePodEntry(podfileContent, podName) {
// this regex should catch line(s) with full pod definition, like: pod 'podname', :path => '../node_modules/podname', :subspecs => ['Sub2', 'Sub1']
const podRegex = new RegExp("\\n( |\\t)*pod\\s+(\"|')" + podName + "(\"|')(,\\s*(:[a-z]+\\s*=>)?\\s*((\"|').*?(\"|')|\\[[\\s\\S]*?\\]))*\\n", 'g');
return podfileContent.replace(podRegex, '\n');
};
+8
View File
@@ -0,0 +1,8 @@
'use strict';
const fs = require('fs');
module.exports = function savePodFile(podfilePath, podLines) {
const newPodfile = podLines.join('\n');
fs.writeFileSync(podfilePath, newPodfile);
};
@@ -0,0 +1,13 @@
'use strict';
const fs = require('fs');
const removePodEntry = require('./removePodEntry');
/**
* Unregister native module IOS with CocoaPods
*/
module.exports = function unregisterNativeModule(dependencyConfig, iOSProject) {
const podContent = fs.readFileSync(iOSProject.podfile, 'utf8');
const removed = removePodEntry(podContent, dependencyConfig.podspec);
fs.writeFileSync(iOSProject.podfile, removed);
};
+11 -5
View File
@@ -4,16 +4,17 @@ const getProjectDependencies = require('./getProjectDependencies');
const unregisterDependencyAndroid = require('./android/unregisterNativeModule');
const unregisterDependencyWindows = require('./windows/unregisterNativeModule');
const unregisterDependencyIOS = require('./ios/unregisterNativeModule');
const unregisterDependencyPods = require('./pods/unregisterNativeModule');
const isInstalledAndroid = require('./android/isInstalled');
const isInstalledWindows = require('./windows/isInstalled');
const isInstalledIOS = require('./ios/isInstalled');
const isInstalledPods = require('./pods/isInstalled');
const unlinkAssetsAndroid = require('./android/unlinkAssets');
const unlinkAssetsIOS = require('./ios/unlinkAssets');
const getDependencyConfig = require('./getDependencyConfig');
const compact = require('lodash').compact;
const difference = require('lodash').difference;
const filter = require('lodash').filter;
const find = require('lodash').find;
const flatten = require('lodash').flatten;
const isEmpty = require('lodash').isEmpty;
const promiseWaterfall = require('./promiseWaterfall');
@@ -65,16 +66,21 @@ const unlinkDependencyIOS = (iOSProject, dependency, packageName, iOSDependencie
return;
}
const isInstalled = isInstalledIOS(iOSProject, dependency.ios);
if (!isInstalled) {
const isIosInstalled = isInstalledIOS(iOSProject, dependency.ios);
const isPodInstalled = isInstalledPods(iOSProject, dependency.ios);
if (!isIosInstalled && !isPodInstalled) {
log.info(`iOS module ${packageName} is not installed`);
return;
}
log.info(`Unlinking ${packageName} ios dependency`);
unregisterDependencyIOS(dependency.ios, iOSProject, iOSDependencies);
if (isIosInstalled) {
unregisterDependencyIOS(dependency.ios, iOSProject, iOSDependencies);
}
else if (isPodInstalled) {
unregisterDependencyPods(dependency.ios, iOSProject);
}
log.info(`iOS module ${packageName} has been successfully unlinked`);
};
+3 -4
View File
@@ -1,6 +1,6 @@
{
"name": "react-native",
"version": "1000.0.0",
"version": "0.50.3",
"description": "A framework for building native apps using React",
"license": "BSD-3-Clause",
"repository": {
@@ -162,7 +162,7 @@
"graceful-fs": "^4.1.3",
"inquirer": "^3.0.6",
"lodash": "^4.16.6",
"metro-bundler": "^0.19.0",
"metro-bundler": "^0.20.1",
"mime": "^1.3.4",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
@@ -200,11 +200,10 @@
"eslint-plugin-react": "^7.2.1",
"flow-bin": "^0.56.0",
"jest": "^21",
"mock-fs": "^4.4.1",
"prettier": "1.7.0",
"react": "16.0.0",
"react-test-renderer": "16.0.0",
"shelljs": "^0.7.8",
"sinon": "^2.2.0"
}
}
}
+45 -36
View File
@@ -15,7 +15,7 @@
* After changing the files it makes a commit and tags it.
* All you have to do is push changes to remote and CI will make a new build.
*/
const fs = require('fs');
const {
cat,
echo,
@@ -32,15 +32,15 @@ let argv = minimist(process.argv.slice(2), {
});
// - check we are in release branch, e.g. 0.33-stable
let branch = exec(`git symbolic-ref --short HEAD`, {silent: true}).stdout.trim();
let branch = exec('git symbolic-ref --short HEAD', {silent: true}).stdout.trim();
if (branch.indexOf(`-stable`) === -1) {
echo(`You must be in 0.XX-stable branch to bump a version`);
if (branch.indexOf('-stable') === -1) {
echo('You must be in 0.XX-stable branch to bump a version');
exit(1);
}
// e.g. 0.33
let versionMajor = branch.slice(0, branch.indexOf(`-stable`));
let versionMajor = branch.slice(0, branch.indexOf('-stable'));
// - check that argument version matches branch
// e.g. 0.33.1 or 0.33.0-rc4
@@ -58,34 +58,43 @@ if (!match) {
}
let [, major, minor, patch, prerelease] = match;
cat('scripts/versiontemplates/ReactNativeVersion.java.template')
.replace('${major}', major)
.replace('${minor}', minor)
.replace('${patch}', patch)
.replace('${prerelease}', prerelease !== undefined ? `"${prerelease}"` : 'null')
.to('ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java');
fs.writeFileSync(
'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'
);
cat('scripts/versiontemplates/RCTVersion.h.template')
.replace('${major}', `@(${major})`)
.replace('${minor}', `@(${minor})`)
.replace('${patch}', `@(${patch})`)
.replace('${prerelease}', prerelease !== undefined ? `@"${prerelease}"` : '[NSNull null]')
.to('React/Base/RCTVersion.h');
fs.writeFileSync(
'React/Base/RCTVersion.h',
cat('scripts/versiontemplates/RCTVersion.h.template')
.replace('${major}', `@(${major})`)
.replace('${minor}', `@(${minor})`)
.replace('${patch}', `@(${patch})`)
.replace('${prerelease}', prerelease !== undefined ? `@"${prerelease}"` : '[NSNull null]'),
'utf-8'
);
cat('scripts/versiontemplates/ReactNativeVersion.js.template')
.replace('${major}', major)
.replace('${minor}', minor)
.replace('${patch}', patch)
.replace('${prerelease}', prerelease !== undefined ? `'${prerelease}'` : 'null')
.to('Libraries/Core/ReactNativeVersion.js');
fs.writeFileSync(
'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'
);
let packageJson = JSON.parse(cat(`package.json`));
let packageJson = JSON.parse(cat('package.json'));
packageJson.version = version;
JSON.stringify(packageJson, null, 2).to(`package.json`);
fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2), 'utf-8');
// - change ReactAndroid/gradle.properties
if (sed(`-i`, /^VERSION_NAME=.*/, `VERSION_NAME=${version}`, `ReactAndroid/gradle.properties`).code) {
echo(`Couldn't update version for Gradle`);
if (sed('-i', /^VERSION_NAME=.*/, `VERSION_NAME=${version}`, 'ReactAndroid/gradle.properties').code) {
echo('Couldn\'t update version for Gradle');
exit(1);
}
@@ -93,23 +102,23 @@ if (sed(`-i`, /^VERSION_NAME=.*/, `VERSION_NAME=${version}`, `ReactAndroid/gradl
let numberOfChangedLinesWithNewVersion = exec(`git diff -U0 | grep '^[+]' | grep -c ${version} `, {silent: true})
.stdout.trim();
if (+numberOfChangedLinesWithNewVersion !== 2) {
echo(`Failed to update all the files. package.json and gradle.properties must have versions in them`);
echo(`Fix the issue, revert and try again`);
exec(`git diff`);
echo('Failed to update all the files. package.json and gradle.properties must have versions in them');
echo('Fix the issue, revert and try again');
exec('git diff');
exit(1);
}
// - make commit [0.21.0-rc] Bump version numbers
if (exec(`git commit -a -m "[${version}] Bump version numbers"`).code) {
echo(`failed to commit`);
echo('failed to commit');
exit(1);
}
// - add tag v0.21.0-rc
if (exec(`git tag v${version}`).code) {
echo(`failed to tag the commit with v${version}, are you sure this release wasn't made earlier?`);
echo(`You may want to rollback the last commit`);
echo(`git reset --hard HEAD~1`);
echo('You may want to rollback the last commit');
echo('git reset --hard HEAD~1');
exit(1);
}
@@ -118,10 +127,10 @@ let remote = argv.remote;
exec(`git push ${remote} v${version}`);
// Tag latest if doing stable release
if (version.indexOf(`rc`) === -1) {
exec(`git tag -d latest`);
if (version.indexOf('rc') === -1) {
exec('git tag -d latest');
exec(`git push ${remote} :latest`);
exec(`git tag latest`);
exec('git tag latest');
exec(`git push ${remote} latest`);
}
+3 -55
View File
@@ -1,77 +1,25 @@
# inspired by https://github.com/Originate/guide/blob/master/android/guide/Continuous%20Integration.md
# SDK Built Tools revision, per http://facebook.github.io/react-native/docs/getting-started.html
ANDROID_SDK_BUILD_TOOLS_REVISION=23.0.1
# API Level we build with
ANDROID_SDK_BUILD_API_LEVEL="23"
# Minimum API Level we target, used for emulator image
ANDROID_SDK_TARGET_API_LEVEL="19"
# Emulator name
AVD_NAME="testAVD"
function getAndroidSDK {
export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$PATH"
DEPS="$ANDROID_HOME/installed-dependencies"
if [ ! -e $DEPS ]; then
echo "Installing Android API level $ANDROID_SDK_TARGET_API_LEVEL, Google APIs, ARM EABI v7a system image..."
sdkmanager "system-images;android-$ANDROID_SDK_TARGET_API_LEVEL;google_apis;armeabi-v7a"
# x86 image requires hardware acceleration, which is not supported when running within the CircleCI Docker image
# echo "Installing Android API level $ANDROID_SDK_TARGET_API_LEVEL, Google APIs, Intel x86 Atom system image..."
# sdkmanager "system-images;android-$ANDROID_SDK_TARGET_API_LEVEL;google_apis;x86"
echo "Installing build SDK for Android API level $ANDROID_SDK_BUILD_API_LEVEL..."
sdkmanager "platforms;android-$ANDROID_SDK_BUILD_API_LEVEL"
echo "Installing target SDK for Android API level $ANDROID_SDK_TARGET_API_LEVEL..."
sdkmanager "platforms;android-$ANDROID_SDK_TARGET_API_LEVEL"
echo "Installing SDK build tools, revision $ANDROID_SDK_BUILD_TOOLS_REVISION..."
sdkmanager "build-tools;$ANDROID_SDK_BUILD_TOOLS_REVISION"
echo "Installing Google APIs for Android API level $ANDROID_SDK_BUILD_API_LEVEL..."
sdkmanager "add-ons;addon-google_apis-google-$ANDROID_SDK_BUILD_API_LEVEL"
echo "Installing Android Support Repository"
sdkmanager "extras;android;m2repository"
echo y | android update sdk --no-ui --all --filter extra-android-m2repository
echo no | android create avd -n testAVD -f -t android-19 --abi default/armeabi-v7a &&
touch $DEPS
fi
}
function getAndroidNDK {
NDK_HOME="/opt/ndk"
DEPS="$NDK_HOME/installed-dependencies"
if [ ! -e $DEPS ]; then
cd $NDK_HOME
echo "Downloading NDK..."
curl -o ndk.zip https://dl.google.com/android/repository/android-ndk-r10e-linux-x86.zip
curl -o ndk_64.zip https://dl.google.com/android/repository/android-ndk-r10e-linux-x86_64.zip
unzip -o -q ndk.zip
unzip -o -q ndk_64.zip
echo "Installed Android NDK at $NDK_HOME"
touch $DEPS
rm ndk.zip
rm ndk_64.zip
fi
}
function createAVD {
echo no | avdmanager create avd --name $AVD_NAME --force --package "system-images;android-$ANDROID_SDK_TARGET_API_LEVEL;google_apis;armeabi-v7a" --tag google_apis --abi armeabi-v7a
}
function launchAVD {
# The AVD name here should match the one created in createAVD
# emulator64-arm -avd $AVD_NAME -no-audio -no-window -no-boot-anim -gpu off
emulator -avd $AVD_NAME -no-audio -no-window
}
function waitForAVD {
echo "Waiting for Android Virtual Device to finish booting..."
local bootanim=""
export PATH=$(dirname $(dirname $(which android)))/platform-tools:$PATH
until [[ "$bootanim" =~ "stopped" ]]; do
sleep 5
bootanim=$(adb -e shell getprop init.svc.bootanim 2>&1)
echo "boot animation status=$bootanim"
echo "emulator status=$bootanim"
done
echo "Android Virtual Device is ready."
}
function retry3 {
+4 -3
View File
@@ -48,10 +48,11 @@ try {
const CLI_PACKAGE = path.join(ROOT, 'react-native-cli', 'react-native-cli-*.tgz');
cd('..');
// can skip cli install for non sudo mode
if (!argv['skip-cli-install']) {
if (exec(`sudo npm install -g ${CLI_PACKAGE}`).code) {
echo('Could not install react-native-cli globally.');
echo('Run with --skip-cli-install to skip this step');
if (exec(`npm install -g ${CLI_PACKAGE}`).code) {
echo('Could not install react-native-cli globally, please run in su mode');
echo('Or with --skip-cli-install to skip this step');
exitCode = 1;
throw Error(exitCode);
}
@@ -9,7 +9,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#define REACT_NATIVE_VERSION @{ \
#define RCT_REACT_NATIVE_VERSION @{ \
@"major": ${major}, \
@"minor": ${minor}, \
@"patch": ${patch}, \