Build Improvements (#4884) (#4902)

* Use AppGroupId from Info.plists instead of hardcoded constant

* Update script, ci & Makefile

* Update Cocoapods to 1.9.3

* Split android builds using ABI filters

* Update Fastlane deps & build scripts

* Update CI to use latests scripts

* Display app version & build number in select server screen

* Make generate scripts compatible with node < 12

* Build scripts

* add build script to package.json

* Update to use bundler 2.1.4 and CI with Xcode 12

* Fix script name for build:ios-unsigned

* Fix RN iOS scripts

* Update CI pods-dependencies step

* Add pipefail to android executor

* Update Fastlane

* Fix type in postinstall script

* update android executor and set TERM

* Fix S3 bucket name variable

* Apply suggestions from code review

Co-authored-by: Miguel Alatzar <migbot@users.noreply.github.com>

* Fix master unit tests

* use requireActual in jest setup

* Jest setup to use react instead of React

Co-authored-by: Miguel Alatzar <migbot@users.noreply.github.com>
(cherry picked from commit 30d4aa2a3e)

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Mattermost Build 2020-10-20 02:21:37 +02:00 committed by GitHub
parent 768e2f1b84
commit f23190cf29
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
63 changed files with 586 additions and 674 deletions

View file

@ -13,7 +13,7 @@ executors:
NODE_ENV: production
BABEL_ENV: production
docker:
- image: circleci/android:api-27-node
- image: circleci/android:api-29-node
working_directory: ~/mattermost-mobile
resource_class: <<parameters.resource_class>>
@ -23,7 +23,7 @@ executors:
NODE_ENV: production
BABEL_ENV: production
macos:
xcode: "11.0.0"
xcode: "12.0.0"
working_directory: ~/mattermost-mobile
shell: /bin/bash --login -o pipefail
@ -44,7 +44,6 @@ commands:
for:
type: string
steps:
- ruby-setup
- restore_cache:
name: Restore Fastlane cache
key: v1-gems-<< parameters.for >>-{{ checksum "fastlane/Gemfile.lock" }}-{{ arch }}
@ -82,7 +81,7 @@ commands:
key: v1-assets-{{ checksum "assets/base/config.json" }}-{{ arch }}
- run:
name: Generate assets
command: make dist/assets
command: node ./scripts/generate-assets.js
- save_cache:
name: Save assets cache
key: v1-assets-{{ checksum "assets/base/config.json" }}-{{ arch }}
@ -104,8 +103,8 @@ commands:
paths:
- node_modules
- run:
name: "Run post install scripts"
command: make post-install
name: "Patch dependencies"
command: npx patch-package
pods-dependencies:
description: "Get cocoapods dependencies"
@ -113,10 +112,12 @@ commands:
- restore_cache:
name: Restore cocoapods specs and pods
key: v1-cocoapods-{{ checksum "ios/Podfile.lock" }}-{{ arch }}
- run:
name: iOS gems
command: npm run ios-gems
- run:
name: Getting cocoapods dependencies
working_directory: ios
command: pod install
command: npm run pod-install
- save_cache:
name: Save cocoapods specs and pods cache
key: v1-cocoapods-{{ checksum "ios/Podfile.lock" }}-{{ arch }}
@ -143,11 +144,14 @@ commands:
echo MATTERMOST_RELEASE_STORE_FILE=${STORE_FILE} | tee -a android/gradle.properties > /dev/null
echo ${STORE_ALIAS} | tee -a android/gradle.properties > /dev/null
echo ${STORE_PASSWORD} | tee -a android/gradle.properties > /dev/null
- run:
name: Jetify android libraries
command: ./node_modules/.bin/jetify
- run:
working_directory: fastlane
name: Run fastlane to build android
no_output_timeout: 30m
command: bundle exec fastlane android build
command: export TERM=xterm && bundle exec fastlane android build
build-ios:
description: "Build the iOS app"
@ -165,7 +169,7 @@ commands:
no_output_timeout: 30m
command: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install watchman
bundle exec fastlane ios build
export TERM=xterm && bundle exec fastlane ios build
deploy-to-store:
description: "Deploy build to store"
@ -201,12 +205,6 @@ commands:
- store_artifacts:
path: ~/mattermost-mobile/<<parameters.filename>>
ruby-setup:
steps:
- run:
name: Set Ruby Version
command: echo "ruby-2.6.3" > ~/.ruby-version
jobs:
test:
working_directory: ~/mattermost-mobile
@ -225,19 +223,7 @@ jobs:
command: npm test
- run:
name: Check i18n
command: |
mkdir -p tmp
cp assets/base/i18n/en.json tmp/en.json
mkdir -p tmp/fake-webapp-dir/i18n/
echo '{}' > tmp/fake-webapp-dir/i18n/en.json
npm run mmjstool -- i18n extract-mobile --webapp-dir tmp/fake-webapp-dir --mobile-dir .
diff tmp/en.json assets/base/i18n/en.json
# Address weblate behavior which does not remove whole translation item when translation string is set to empty
npm run mmjstool -- i18n clean-empty --webapp-dir tmp/fake-webapp-dir --mobile-dir . --check
rm -rf tmp
command: ./scripts/precommit/i18n.sh
check-deps:
parameters:
@ -297,7 +283,7 @@ jobs:
- build-android
- persist
- save:
filename: "Mattermost_Beta.apk"
filename: "*.apk"
build-android-release:
executor: android
@ -305,7 +291,7 @@ jobs:
- build-android
- persist
- save:
filename: "Mattermost.apk"
filename: "*.apk"
build-android-pr:
executor: android
@ -314,7 +300,7 @@ jobs:
steps:
- build-android
- save:
filename: "Mattermost_Beta.apk"
filename: "*.apk"
build-android-unsigned:
executor: android
@ -326,6 +312,9 @@ jobs:
- fastlane-dependencies:
for: android
- gradle-dependencies
- run:
name: Jetify Android libraries
command: ./node_modules/.bin/jetify
- run:
working_directory: fastlane
name: Run fastlane to build unsigned android
@ -333,7 +322,7 @@ jobs:
command: bundle exec fastlane android unsigned
- persist
- save:
filename: "Mattermost-unsigned.apk"
filename: "*.apk"
build-ios-beta:
executor: ios
@ -341,7 +330,7 @@ jobs:
- build-ios
- persist
- save:
filename: "Mattermost_Beta.ipa"
filename: "*.ipa"
build-ios-release:
executor: ios
@ -349,7 +338,7 @@ jobs:
- build-ios
- persist
- save:
filename: "Mattermost.ipa"
filename: "*.ipa"
build-ios-pr:
executor: ios
@ -358,14 +347,13 @@ jobs:
steps:
- build-ios
- save:
filename: "Mattermost_Beta.ipa"
filename: "*.ipa"
build-ios-unsigned:
executor: ios
steps:
- checkout:
path: ~/mattermost-mobile
- ruby-setup
- npm-dependencies
- pods-dependencies
- assets
@ -381,16 +369,15 @@ jobs:
- persist_to_workspace:
root: ~/
paths:
- mattermost-mobile/Mattermost-unsigned.ipa
- mattermost-mobile/*.ipa
- save:
filename: "Mattermost-unsigned.ipa"
filename: "*.ipa"
build-ios-simulator:
executor: ios
steps:
- checkout:
path: ~/mattermost-mobile
- ruby-setup
- npm-dependencies
- pods-dependencies
- assets
@ -415,47 +402,42 @@ jobs:
name: android
resource_class: medium
steps:
- ruby-setup
- deploy-to-store:
task: "Deploy to Google Play"
target: android
file: Mattermost.apk
file: "*.apk"
deploy-android-beta:
executor:
name: android
resource_class: medium
steps:
- ruby-setup
- deploy-to-store:
task: "Deploy to Google Play"
target: android
file: Mattermost_Beta.apk
file: "*.apk"
deploy-ios-release:
executor: ios
steps:
- ruby-setup
- deploy-to-store:
task: "Deploy to TestFlight"
target: ios
file: Mattermost.ipa
file: "*.ipa"
deploy-ios-beta:
executor: ios
steps:
- ruby-setup
- deploy-to-store:
task: "Deploy to TestFlight"
target: ios
file: Mattermost_Beta.ipa
file: "*.ipa"
github-release:
executor:
name: android
resource_class: medium
steps:
- ruby-setup
- attach_workspace:
at: ~/
- run:

232
Makefile
View file

@ -1,232 +0,0 @@
.PHONY: pre-run pre-build clean
.PHONY: check-style
.PHONY: start stop
.PHONY: run run-ios run-android
.PHONY: build build-ios build-android unsigned-ios unsigned-android ios-sim-x86_64
.PHONY: build-pr can-build-pr prepare-pr
.PHONY: test help
OS := $(shell sh -c 'uname -s 2>/dev/null')
BASE_ASSETS = $(shell find assets/base -type d) $(shell find assets/base -type f -name '*')
OVERRIDE_ASSETS = $(shell find assets/override -type d 2> /dev/null) $(shell find assets/override -type f -name '*' 2> /dev/null)
MM_UTILITIES_DIR = ../mattermost-utilities
node_modules: package.json
@if ! [ $(shell which npm 2> /dev/null) ]; then \
echo "npm is not installed https://npmjs.com"; \
exit 1; \
fi
@echo Getting Javascript dependencies
@npm install
npm-ci: package.json
@if ! [ $(shell which npm 2> /dev/null) ]; then \
echo "npm is not installed https://npmjs.com"; \
exit 1; \
fi
@echo Getting Javascript dependencies
@npm ci
.podinstall:
ifeq ($(OS), Darwin)
@echo "Required version of Cocoapods is not installed"
@echo Installing gems;
@bundle install
@echo Getting Cocoapods dependencies;
@cd ios && bundle exec pod install;
endif
@touch $@
dist/assets: $(BASE_ASSETS) $(OVERRIDE_ASSETS)
@mkdir -p dist
@if [ -e dist/assets ] ; then \
rm -rf dist/assets; \
fi
@echo "Generating app assets"
@node scripts/make-dist-assets.js
pre-run: | node_modules .podinstall dist/assets ## Installs dependencies and assets
pre-build: | npm-ci .podinstall dist/assets ## Install dependencies and assets before building
check-style: node_modules ## Runs eslint
@echo Checking for style guide compliance
@npm run check
clean: ## Cleans dependencies, previous builds and temp files
@echo Cleaning started
@rm -f .podinstall
@rm -rf ios/Pods
@rm -rf node_modules
@rm -rf dist
@rm -rf ios/build
@rm -rf android/app/build
@echo Cleanup finished
post-install:
@./node_modules/.bin/patch-package
@./node_modules/.bin/jetify
@rm -f node_modules/intl/.babelrc
@# Hack to get react-intl and its dependencies to work with react-native
@# Based off of https://github.com/este/este/blob/master/gulp/native-fix.js
@sed -i'' -e 's|"./locale-data/index.js": false|"./locale-data/index.js": "./locale-data/index.js"|g' node_modules/react-intl/package.json
@sed -i'' -e 's|"./lib/locales": false|"./lib/locales": "./lib/locales"|g' node_modules/intl-messageformat/package.json
@sed -i'' -e 's|"./lib/locales": false|"./lib/locales": "./lib/locales"|g' node_modules/intl-relativeformat/package.json
@sed -i'' -e 's|"./locale-data/complete.js": false|"./locale-data/complete.js": "./locale-data/complete.js"|g' node_modules/intl/package.json
start: | pre-run ## Starts the React Native packager server
$(call start_packager)
stop: ## Stops the React Native packager server
$(call stop_packager)
check-device-ios:
@if ! [ $(shell which xcodebuild) ]; then \
echo "xcode is not installed"; \
exit 1; \
fi
@if ! [ $(shell which watchman) ]; then \
echo "watchman is not installed"; \
exit 1; \
fi
check-device-android:
@if ! [ $(ANDROID_HOME) ]; then \
echo "ANDROID_HOME is not set"; \
exit 1; \
fi
@if ! [ $(shell which adb 2> /dev/null) ]; then \
echo "adb is not installed"; \
exit 1; \
fi
@echo "Connect your Android device or open the emulator"
@adb wait-for-device
@if ! [ $(shell which watchman 2> /dev/null) ]; then \
echo "watchman is not installed"; \
exit 1; \
fi
prepare-android-build:
@rm -rf ./node_modules/react-native/local-cli/templates/HelloWorld
@rm -rf ./node_modules/react-native-linear-gradient/Examples/
@rm -rf ./node_modules/react-native-orientation/demo/
run: run-ios ## alias for run-ios
run-ios: | check-device-ios pre-run ## Runs the app on an iOS simulator
@if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
echo Starting React Native packager server; \
npm start & echo Running iOS app in development; \
if [ ! -z "${SIMULATOR}" ]; then \
react-native run-ios --simulator="${SIMULATOR}"; \
else \
react-native run-ios; \
fi; \
wait; \
else \
echo Running iOS app in development; \
if [ ! -z "${SIMULATOR}" ]; then \
react-native run-ios --simulator="${SIMULATOR}"; \
else \
react-native run-ios; \
fi; \
fi
run-android: | check-device-android pre-run prepare-android-build ## Runs the app on an Android emulator or dev device
@if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
echo Starting React Native packager server; \
npm start & echo Running Android app in development; \
if [ ! -z ${VARIANT} ]; then \
react-native run-android --no-packager --variant=${VARIANT}; \
else \
react-native run-android --no-packager; \
fi; \
wait; \
else \
echo Running Android app in development; \
if [ ! -z ${VARIANT} ]; then \
react-native run-android --no-packager --variant=${VARIANT}; \
else \
react-native run-android --no-packager; \
fi; \
fi
build: | stop pre-build check-style ## Builds the app for Android & iOS
$(call start_packager)
@echo "Building App"
@cd fastlane && BABEL_ENV=production NODE_ENV=production bundle exec fastlane build
$(call stop_packager)
build-ios: | stop pre-build check-style ## Builds the iOS app
$(call start_packager)
@echo "Building iOS app"
@cd fastlane && BABEL_ENV=production NODE_ENV=production bundle exec fastlane ios build
$(call stop_packager)
build-android: | stop pre-build check-style prepare-android-build ## Build the Android app
$(call start_packager)
@echo "Building Android app"
@cd fastlane && BABEL_ENV=production NODE_ENV=production bundle exec fastlane android build
$(call stop_packager)
unsigned-ios: stop pre-build check-style ## Build an unsigned version of the iOS app
$(call start_packager)
@cd fastlane && NODE_ENV=production bundle exec fastlane ios unsigned
$(call stop_packager)
ios-sim-x86_64: stop pre-build check-style ## Build an unsigned x86_64 version of the iOS app for iPhone simulator
$(call start_packager)
@echo "Building unsigned x86_64 iOS app for iPhone simulator"
@cd fastlane && NODE_ENV=production bundle exec fastlane ios simulator
$(call stop_packager)
unsigned-android: stop pre-build check-style prepare-android-build ## Build an unsigned version of the Android app
@cd fastlane && NODE_ENV=production bundle exec fastlane android unsigned
test: | pre-run check-style ## Runs tests
@npm test
build-pr: | can-build-pr stop pre-build check-style ## Build a PR from the mattermost-mobile repo
$(call start_packager)
@echo "Building App from PR ${PR_ID}"
@cd fastlane && BABEL_ENV=production NODE_ENV=production bundle exec fastlane build_pr pr:PR-${PR_ID}
$(call stop_packager)
can-build-pr:
@if [ -z ${PR_ID} ]; then \
echo a PR number needs to be specified; \
exit 1; \
fi
## Help documentation https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
define start_packager
@if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
echo Starting React Native packager server; \
npm start & echo; \
else \
echo React Native packager server already running; \
fi
endef
define stop_packager
@echo Stopping React Native packager server
@if [ $(shell ps -ef | grep -i "cli.js start" | grep -civ grep) -eq 1 ]; then \
ps -ef | grep -i "cli.js start" | grep -iv grep | awk '{print $$2}' | xargs kill -9; \
echo React Native packager server stopped; \
else \
echo No React Native packager server running; \
fi
endef

View file

@ -101,7 +101,7 @@ if (System.getenv("SENTRY_ENABLED") == "true") {
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
def enableSeparateBuildPerCPUArchitecture = project.hasProperty('separateApk') ? project.property('separateApk').toBoolean() : false
/**
* Run Proguard to shrink the Java bytecode in release builds.
@ -135,9 +135,6 @@ android {
versionCode 330
versionName "1.36.0"
multiDexEnabled = true
ndk {
abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
}
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
@ -155,7 +152,7 @@ android {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
universalApk enableSeparateBuildPerCPUArchitecture // If true, also generate a universal APK
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
@ -184,7 +181,7 @@ android {
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
versionCodes.get(abi) * 1000000 + defaultConfig.versionCode
}
}
}

View file

@ -51,8 +51,7 @@ exports[`AnnouncementBanner should match snapshot 1`] = `
value="Banner Text"
/>
</Text>
<Icon
allowFontScaling={false}
<CompassIcon
color="#fff"
name="info-outline"
size={16}
@ -112,8 +111,7 @@ exports[`AnnouncementBanner should match snapshot 2`] = `
value="Banner Text"
/>
</Text>
<Icon
allowFontScaling={false}
<CompassIcon
color="#fff"
name="info-outline"
size={16}

View file

@ -0,0 +1,38 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {StyleSheet, View} from 'react-native';
import DeviceInfo from 'react-native-device-info';
import FormattedText from '@components/formatted_text';
const style = StyleSheet.create({
info: {
alignItems: 'center',
justifyContent: 'flex-end',
},
version: {
fontSize: 12,
},
});
const AppVersion = () => {
return (
<View pointerEvents='none'>
<View style={style.info}>
<FormattedText
id='mobile.about.appVersion'
defaultMessage='App Version: {version} (Build {number})'
style={style.version}
values={{
version: DeviceInfo.getVersion(),
number: DeviceInfo.getBuildNumber(),
}}
/>
</View>
</View>
);
};
export default AppVersion;

View file

@ -13,8 +13,7 @@ exports[`AttachmentButton should match snapshot 1`] = `
}
type="opacity"
>
<Icon
allowFontScaling={false}
<CompassIcon
color="rgba(61,60,64,0.9)"
name="plus"
size={30}

View file

@ -10008,8 +10008,7 @@ exports[`components/emoji_picker/emoji_picker.ios should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="emoticon-happy-outline"
size={17}
style={
@ -10035,8 +10034,7 @@ exports[`components/emoji_picker/emoji_picker.ios should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="leaf-outline"
size={17}
style={
@ -10060,8 +10058,7 @@ exports[`components/emoji_picker/emoji_picker.ios should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="food-apple"
size={17}
style={
@ -10085,8 +10082,7 @@ exports[`components/emoji_picker/emoji_picker.ios should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="basketball"
size={17}
style={
@ -10110,8 +10106,7 @@ exports[`components/emoji_picker/emoji_picker.ios should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="airplane-variant"
size={17}
style={
@ -10135,8 +10130,7 @@ exports[`components/emoji_picker/emoji_picker.ios should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="lightbulb-outline"
size={17}
style={
@ -10160,8 +10154,7 @@ exports[`components/emoji_picker/emoji_picker.ios should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="heart-outline"
size={17}
style={
@ -10185,8 +10178,7 @@ exports[`components/emoji_picker/emoji_picker.ios should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="flag-outline"
size={17}
style={
@ -10210,8 +10202,7 @@ exports[`components/emoji_picker/emoji_picker.ios should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="emoticon-custom-outline"
size={17}
style={

View file

@ -12,6 +12,8 @@ import {shallowWithIntl} from 'test/intl-test-helper';
import {filterEmojiSearchInput} from './emoji_picker_base';
import EmojiPicker from './emoji_picker.ios';
jest.useFakeTimers();
describe('components/emoji_picker/emoji_picker.ios', () => {
const state = {
...initialState,
@ -59,17 +61,17 @@ describe('components/emoji_picker/emoji_picker.ios', () => {
];
testCases.forEach((testCase) => {
test(`'${testCase.input}' should return '${testCase.output}'`, () => {
test(`'${testCase.input}' should return '${testCase.output}'`, async () => {
expect(filterEmojiSearchInput(testCase.input)).toEqual(testCase.output);
});
});
test('should match snapshot', () => {
test('should match snapshot', async () => {
const wrapper = shallowWithIntl(<EmojiPicker {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
test('searchEmojis should return the right values on fuse', () => {
test('searchEmojis should return the right values on fuse', async () => {
const input = '1';
const output = ['100', '1234', '1st_place_medal', '+1', '-1', 'u7121'];
@ -78,7 +80,7 @@ describe('components/emoji_picker/emoji_picker.ios', () => {
expect(result).toEqual(output);
});
test('should set rebuildEmojis to true when deviceWidth changes', () => {
test('should set rebuildEmojis to true when deviceWidth changes', async () => {
const wrapper = shallowWithIntl(<EmojiPicker {...baseProps}/>);
const instance = wrapper.instance();
@ -90,7 +92,7 @@ describe('components/emoji_picker/emoji_picker.ios', () => {
expect(instance.rebuildEmojis).toBe(true);
});
test('should rebuild emojis emojis when emojis change', () => {
test('should rebuild emojis emojis when emojis change', async () => {
const wrapper = shallowWithIntl(<EmojiPicker {...baseProps}/>);
const instance = wrapper.instance();
const renderableEmojis = jest.spyOn(instance, 'renderableEmojis');
@ -103,7 +105,7 @@ describe('components/emoji_picker/emoji_picker.ios', () => {
expect(renderableEmojis).toHaveBeenCalledWith(baseProps.emojisBySection, baseProps.deviceWidth);
});
test('should set rebuilt emojis when rebuildEmojis is true and searchBarAnimationComplete is true', () => {
test('should set rebuilt emojis when rebuildEmojis is true and searchBarAnimationComplete is true', async () => {
const wrapper = shallowWithIntl(<EmojiPicker {...baseProps}/>);
const instance = wrapper.instance();
instance.setState = jest.fn();
@ -118,7 +120,7 @@ describe('components/emoji_picker/emoji_picker.ios', () => {
expect(instance.rebuildEmojis).toBe(false);
});
test('should not set rebuilt emojis when rebuildEmojis is false and searchBarAnimationComplete is true', () => {
test('should not set rebuilt emojis when rebuildEmojis is false and searchBarAnimationComplete is true', async () => {
const wrapper = shallowWithIntl(<EmojiPicker {...baseProps}/>);
const instance = wrapper.instance();
instance.setState = jest.fn();
@ -131,7 +133,7 @@ describe('components/emoji_picker/emoji_picker.ios', () => {
expect(instance.setState).not.toHaveBeenCalled();
});
test('should not set rebuilt emojis when rebuildEmojis is true and searchBarAnimationComplete is false', () => {
test('should not set rebuilt emojis when rebuildEmojis is true and searchBarAnimationComplete is false', async () => {
const wrapper = shallowWithIntl(<EmojiPicker {...baseProps}/>);
const instance = wrapper.instance();
instance.setState = jest.fn();

View file

@ -7,9 +7,6 @@ import {shallow} from 'enzyme';
import InteractiveDialogController from './interactive_dialog_controller';
jest.mock('react-intl');
jest.mock('react-native-vector-icons/MaterialIcons', () => ({
getImageSource: jest.fn().mockResolvedValue({}),
}));
describe('InteractiveDialogController', () => {
test('should open interactive dialog as alert or screen depending on with or without element', () => {

View file

@ -54,8 +54,7 @@ exports[`AttachmentFooter matches snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
color="#FFFFFF"
name="check"
size={20}

View file

@ -12,8 +12,7 @@ exports[`CameraButton should match snapshot 1`] = `
}
type="opacity"
>
<Icon
allowFontScaling={false}
<CompassIcon
color="rgba(61,60,64,0.64)"
name="camera-outline"
size={24}

View file

@ -12,8 +12,7 @@ exports[`FileQuickAction should match snapshot 1`] = `
}
type="opacity"
>
<Icon
allowFontScaling={false}
<CompassIcon
color="rgba(61,60,64,0.64)"
name="file-document-outline"
size={24}

View file

@ -12,8 +12,7 @@ exports[`ImageQuickAction should match snapshot 1`] = `
}
type="opacity"
>
<Icon
allowFontScaling={false}
<CompassIcon
color="rgba(61,60,64,0.64)"
name="image-outline"
size={24}

View file

@ -20,11 +20,9 @@ exports[`PostDraft ReadOnly Should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
color="#3d3c40"
name="glasses"
size={12}
style={
Object {
"fontSize": 20,

View file

@ -27,8 +27,7 @@ exports[`SendAction should change theme backgroundColor to 0.3 opacity 1`] = `
]
}
>
<Icon
allowFontScaling={false}
<CompassIcon
color="rgba(255,255,255,0.5)"
name="send"
size={24}
@ -61,8 +60,7 @@ exports[`SendAction should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
color="#ffffff"
name="send"
size={24}
@ -95,8 +93,7 @@ exports[`SendAction should render theme backgroundColor 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
color="#ffffff"
name="send"
size={24}

View file

@ -531,8 +531,7 @@ exports[`PostHeader should match snapshot when post isBot and shouldRenderReplyB
}
type="opacity"
>
<Icon
allowFontScaling={false}
<CompassIcon
color="#2389d7"
name="reply-outline"
size={18}
@ -684,8 +683,7 @@ exports[`PostHeader should match snapshot when post isBot and shouldRenderReplyB
}
type="opacity"
>
<Icon
allowFontScaling={false}
<CompassIcon
color="#2389d7"
name="reply-outline"
size={18}
@ -884,8 +882,7 @@ exports[`PostHeader should match snapshot when post should display reply button
}
type="opacity"
>
<Icon
allowFontScaling={false}
<CompassIcon
color="#2389d7"
name="reply-outline"
size={18}

View file

@ -27,8 +27,7 @@ exports[`PostPreHeader should match snapshot when flagged and not pinned 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="bookmark-outline"
size={14}
style={
@ -91,8 +90,7 @@ exports[`PostPreHeader should match snapshot when pinned and flagged 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="pin-outline"
size={14}
style={
@ -108,8 +106,7 @@ exports[`PostPreHeader should match snapshot when pinned and flagged 1`] = `
}
}
/>
<Icon
allowFontScaling={false}
<CompassIcon
name="bookmark-outline"
size={14}
style={
@ -170,8 +167,7 @@ exports[`PostPreHeader should match snapshot when pinned and flagged but skippin
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="pin-outline"
size={14}
style={
@ -230,8 +226,7 @@ exports[`PostPreHeader should match snapshot when pinned and flagged but skippin
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="bookmark-outline"
size={14}
style={
@ -290,8 +285,7 @@ exports[`PostPreHeader should match snapshot when pinned and not flagged 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="pin-outline"
size={14}
style={

View file

@ -69,10 +69,8 @@ exports[`MoreMessagesButton should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="arrow-up"
size={12}
style={
Object {
"alignSelf": "center",
@ -118,10 +116,8 @@ exports[`MoreMessagesButton should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="close"
size={12}
style={
Object {
"alignSelf": "center",

View file

@ -247,8 +247,7 @@ exports[`Reactions Should match snapshot with default emojis 1`] = `
]
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="emoticon-plus-outline"
size={31.2}
style={
@ -509,8 +508,7 @@ exports[`Reactions Should match snapshot with default emojis 2`] = `
]
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="emoticon-plus-outline"
size={31.2}
style={

View file

@ -145,8 +145,7 @@ exports[`Reactions should match snapshot 1`] = `
}
type="opacity"
>
<Icon
allowFontScaling={false}
<CompassIcon
name="emoticon-plus-outline"
size={24}
style={

View file

@ -4,8 +4,7 @@ exports[`ShowMoreButton should match, button snapshot 1`] = `
<View
style={Object {}}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="plus"
size={18}
style={Object {}}
@ -22,8 +21,7 @@ exports[`ShowMoreButton should match, button snapshot 2`] = `
<View
style={Object {}}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="minus"
size={18}
style={Object {}}
@ -121,8 +119,7 @@ exports[`ShowMoreButton should match, full snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="plus"
size={18}
style={

View file

@ -1,10 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`UserStatus should match snapshot, away status 1`] = `
<Icon
allowFontScaling={false}
<CompassIcon
name="clock"
size={12}
style={
Object {
"color": "#ffbc42",
@ -15,10 +13,8 @@ exports[`UserStatus should match snapshot, away status 1`] = `
`;
exports[`UserStatus should match snapshot, dnd status 1`] = `
<Icon
allowFontScaling={false}
<CompassIcon
name="minus-circle"
size={12}
style={
Object {
"color": "#f74343",
@ -29,10 +25,8 @@ exports[`UserStatus should match snapshot, dnd status 1`] = `
`;
exports[`UserStatus should match snapshot, online status 1`] = `
<Icon
allowFontScaling={false}
<CompassIcon
name="check-circle"
size={12}
style={
Object {
"color": "#06d6a0",
@ -43,10 +37,8 @@ exports[`UserStatus should match snapshot, online status 1`] = `
`;
exports[`UserStatus should match snapshot, should default to offline status 1`] = `
<Icon
allowFontScaling={false}
<CompassIcon
name="circle-outline"
size={12}
style={
Object {
"color": "rgba(61,60,64,0.3)",

View file

@ -158,10 +158,8 @@ exports[`components/widgets/settings/RadioSetting should match snapshot when err
this is administration
</Text>
</View>
<Icon
allowFontScaling={false}
<CompassIcon
name="check"
size={12}
style={
Object {
"color": "#2389d7",

View file

@ -16,9 +16,6 @@ import {emptyFunction} from '@utils/general';
import ChannelBase from './channel_base';
jest.mock('react-intl');
jest.mock('react-native-vector-icons/MaterialIcons', () => ({
getImageSource: jest.fn().mockResolvedValue(null),
}));
describe('ChannelBase', () => {
const channelBaseComponentId = 'component-0';

View file

@ -24,8 +24,7 @@ exports[`SettingDrawerButton should match, full snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
color="#ffffff"
name="dots-vertical"
size={25}

View file

@ -66,8 +66,7 @@ exports[`ChannelDrawerButton should match, full snapshot 2`] = `
}
>
<View>
<Icon
allowFontScaling={false}
<CompassIcon
color="#ffffff"
name="menu-variant"
size={24}

View file

@ -31,8 +31,7 @@ exports[`ChannelSearchButton should match, full snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="magnify"
size={24}
style={

View file

@ -35,8 +35,7 @@ exports[`ChannelTitle should match snapshot 1`] = `
>
My Channel
</Text>
<Icon
allowFontScaling={false}
<CompassIcon
name="chevron-down"
size={24}
style={
@ -85,8 +84,7 @@ exports[`ChannelTitle should match snapshot when is DM and has guests and the te
>
Other user
</Text>
<Icon
allowFontScaling={false}
<CompassIcon
name="chevron-down"
size={24}
style={
@ -160,8 +158,7 @@ exports[`ChannelTitle should match snapshot when is DM and has guests but the te
>
Other user
</Text>
<Icon
allowFontScaling={false}
<CompassIcon
name="chevron-down"
size={24}
style={
@ -218,8 +215,7 @@ exports[`ChannelTitle should match snapshot when isSelfDMChannel is true 1`] = `
}
/>
</Text>
<Icon
allowFontScaling={false}
<CompassIcon
name="chevron-down"
size={24}
style={

View file

@ -23,8 +23,7 @@ exports[`ForgotPassword match snapshot after success of sendPasswordResetEmail 1
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="mattermost"
size={76}
style={
@ -109,8 +108,7 @@ exports[`ForgotPassword should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="mattermost"
size={76}
style={
@ -223,8 +221,7 @@ exports[`ForgotPassword snapshot for error on failure of email regex 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="mattermost"
size={76}
style={

View file

@ -105,8 +105,7 @@ exports[`ImagePreview should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
color="#fff"
name="close"
size={24}
@ -136,8 +135,7 @@ exports[`ImagePreview should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
color="#fff"
name="download-outline"
size={24}
@ -256,8 +254,7 @@ exports[`ImagePreview should match snapshot, renderDownloadButton 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
color="#fff"
name="download-outline"
size={24}

View file

@ -281,8 +281,7 @@ LongPost {
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
color="#3d3c40"
name="close"
size={20}

View file

@ -75,10 +75,8 @@ exports[`MoreChannels should match snapshot 1`] = `
}
>
<Icon
allowFontScaling={false}
<CompassIcon
name="menu-down"
size={12}
/>
</Text>
</View>

View file

@ -54,8 +54,7 @@ exports[`OptionModalList should match snapshot for Android 1`] = `
]
}
/>
<Icon
allowFontScaling={false}
<CompassIcon
name="camera"
size={18}
style={
@ -100,8 +99,7 @@ exports[`OptionModalList should match snapshot for Android 1`] = `
]
}
/>
<Icon
allowFontScaling={false}
<CompassIcon
name="photo"
size={18}
style={
@ -230,8 +228,7 @@ exports[`OptionModalList should match snapshot for iOS 1`] = `
]
}
/>
<Icon
allowFontScaling={false}
<CompassIcon
name="camera"
size={24}
style={
@ -276,8 +273,7 @@ exports[`OptionModalList should match snapshot for iOS 1`] = `
]
}
/>
<Icon
allowFontScaling={false}
<CompassIcon
name="photo"
size={24}
style={

View file

@ -63,8 +63,7 @@ exports[`Permalink should match snapshot 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
color="#3d3c40"
name="close"
size={20}
@ -133,10 +132,8 @@ exports[`Permalink should match snapshot 1`] = `
exports[`Permalink should match snapshot 2`] = `
<Text>
<Icon
allowFontScaling={false}
<CompassIcon
name="archive-outline"
size={12}
style={
Array [
Object {

View file

@ -43,8 +43,7 @@ exports[`Search RecentItem should match snapshot and respond to events 1`] = `
}
}
>
<Icon
allowFontScaling={false}
<CompassIcon
color="rgba(61,60,64,0.6)"
name="close-circle-outline"
size={20}

View file

@ -27,6 +27,7 @@ import urlParse from 'url-parse';
import {resetToChannel, goToScreen} from '@actions/navigation';
import LocalConfig from '@assets/config';
import AppVersion from '@components/app_version';
import CompassIcon from '@components/compass_icon';
import ErrorText from '@components/error_text';
import FormattedText from '@components/formatted_text';
@ -561,6 +562,7 @@ export default class SelectServer extends PureComponent {
</View>
</View>
</TouchableWithoutFeedback>
<AppVersion/>
</KeyboardAvoidingView>
</SafeAreaView>
);

View file

@ -1,5 +1,4 @@
fastlane_version '2.71.0'
fastlane_require 'aws-sdk-s3'
fastlane_require 'erb'
fastlane_require 'json'
@ -46,10 +45,6 @@ end
after_all do |lane|
if ENV['CIRCLECI'] != 'true'
if lane.to_s == 'build'
submit_to_store
end
if ENV['RESET_GIT_BRANCH'] == 'true'
branch = ENV['BRANCH_TO_BUILD'] || 'master'
package_id = ENV['MAIN_APP_IDENTIFIER'] || 'com.mattermost.rnbeta'
@ -174,106 +169,106 @@ lane :configure do
configured = true
end
desc 'Build the app for Android and iOS'
lane :build do |options|
configure
set_app_version
set_app_build_number
# Build the android app
self.runner.current_platform = :android
build
# Build the ios app
self.runner.current_platform = :ios
build
end
desc 'Upload file to s3'
lane :upload_file_to_s3 do |options|
os_type = options[:os_type]
file = options[:file]
file_plist = ""
if file.nil? || file.empty?
app_name = ENV['APP_NAME'] || 'Mattermost Beta'
filename = app_name.gsub(" ", "_")
if os_type == 'Android'
file = "#{filename}.apk"
elsif os_type == 'iOS'
file = "#{filename}.ipa"
file_plist = "#{filename}.plist"
end
os_type = options[:os_type].downcase
extension = os_type == "android" ? "*.apk" : "*.ipa"
build_folder_path = Dir[File.expand_path('..')].first
files = []
unless options[:file].nil? || options[:file].empty?
files.push("#{build_folder_path}/#{options[:file]}")
else
files = Dir.glob("#{build_folder_path}/#{extension}").select { |f| File.file? f}
end
build_folder_path = Dir[File.expand_path('..')].first
file_path = "#{build_folder_path}/#{file}"
unless ENV['AWS_BUCKET_NAME'].nil? || ENV['AWS_BUCKET_NAME'].empty? || ENV['AWS_REGION'].nil? || ENV['AWS_REGION'].empty? || file_path.nil?
s3_region = ENV['AWS_REGION']
s3_bucket = ENV['AWS_BUCKET_NAME']
s3_folder = ''
unless ENV['AWS_BUCKET_NAME'].nil? || ENV['AWS_BUCKET_NAME'].empty? || ENV['AWS_REGION'].nil? || ENV['AWS_REGION'].empty? || files.length == 0
version_number = ''
build_number = ''
pr_file = ''
plist_file = ''
if is_build_pr
s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{ENV['BRANCH_TO_BUILD']}"
pr_file = File.basename(files.first)
plist_file = "#{File.basename(files.first, '.*')}.plist"
else
if os_type == 'Android'
version_number = android_get_version_name(gradle_file: './android/app/build.gradle')
build_number = android_get_version_code(gradle_file: './android/app/build.gradle')
elsif os_type == 'iOS'
version_number = get_version_number(xcodeproj: './ios/Mattermost.xcodeproj', target: 'Mattermost')
build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj')
end
s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{version_number}/#{build_number}"
version_number = os_type == "android" ?
android_get_version_name(gradle_file: './android/app/build.gradle') :
get_version_number(xcodeproj: './ios/Mattermost.xcodeproj', target: 'Mattermost')
build_number = os_type == "android" ?
android_get_version_code(gradle_file: './android/app/build.gradle') :
get_build_number(xcodeproj: './ios/Mattermost.xcodeproj')
end
s3_region = ENV['AWS_REGION']
s3_bucket_name = ENV['AWS_BUCKET_NAME']
s3_folder = is_build_pr ?
"#{ENV['AWS_FOLDER_NAME']}/#{ENV['BRANCH_TO_BUILD']}" :
"#{ENV['AWS_FOLDER_NAME']}/#{version_number}/#{build_number}"
s3 = Aws::S3::Resource.new(region: s3_region)
file_obj = s3.bucket(s3_bucket).object("#{s3_folder}/#{file}")
file_obj.upload_file("#{file_path}")
s3_bucket = s3.bucket(s3_bucket_name)
mutex = Mutex.new
threads = []
file_number = 0
total_files = files.length
links = []
total_files.times do |i|
threads[i] = Thread.new {
until files.empty?
mutex.synchronize do
file_number += 1
Thread.current["file_number"] = file_number
end
file = files.pop rescue nil
next unless file
filename = File.basename(file)
puts "[#{Thread.current["file_number"]}/#{total_files}] #{filename} uploading..."
s3_file = s3_bucket.object("#{s3_folder}/#{filename}")
s3_file.upload_file("#{file}")
links.push(s3_file.public_url)
end
}
end
threads.each { |t| t.join }
if is_build_pr
if os_type == 'Android'
install_url = "https://#{s3_bucket}/#{s3_folder}/#{file}"
install_url = "https://#{s3_bucket_name}/#{s3_folder}/#{pr_file}"
elsif os_type == 'iOS'
current_build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj')
plist_template = File.read('plist.erb')
plist_body = ERB.new(plist_template).result(binding)
plist_obj = s3.bucket(s3_bucket).object("#{s3_folder}/#{file_plist}")
plist_obj = s3_bucket.object("#{s3_folder}/#{file_plist}")
plist_obj.put(body: plist_body)
install_url = "itms-services://?action=download-manifest&url=https://#{s3_bucket}/#{s3_folder}/#{file_plist}"
install_url = "itms-services://?action=download-manifest&url=https://#{s3_bucket_name}/#{s3_folder}/#{file_plist}"
end
qa_build_message({
:os_type => os_type,
:install_url => install_url
})
:os_type => os_type,
:install_url => install_url
})
end
public_link = "https://#{s3_bucket}/#{s3_folder}/#{file}"
if file == 'Mattermost-simulator-x86_64.app.zip'
if options[:file] == 'Mattermost-simulator-x86_64.app.zip'
pretext = '#### New iOS build for VM/Simulator'
msg = "Download link: #{public_link}"
msg = "Download link: #{links.first}"
send_message_to_mattermost({
:version_number => version_number,
:build_number => build_number,
:pretext => pretext,
:title => '',
:thumb_url => 'https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png',
:msg => msg,
:default_payloads => [],
:success => true,
})
:version_number => version_number,
:build_number => build_number,
:pretext => pretext,
:title => '',
:thumb_url => 'https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png',
:msg => msg,
:default_payloads => [],
:success => true,
})
end
UI.success("S3 bucket @#{s3_bucket}, object @#{s3_folder}/#{file}")
UI.success("S3 public path: #{public_link}")
UI.message("S3 public paths:\n#{links.join("\n")}")
end
end
@ -285,8 +280,15 @@ lane :github do
version = android_get_version_name(gradle_file: './android/app/build.gradle')
build = android_get_version_code(gradle_file: './android/app/build.gradle')
changelog = File.read("metadata/changelog")
changelog.concat("* Android [Mattermost.apk](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost.apk)\n")
changelog.concat("* iOS [Mattermost.ipa](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost.ipa)")
android = [
" * [Mattermost arm64-v8a](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost-arm64-v8a.apk)",
" * [Mattermost armeabi-v7a](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost-armeabi-v7a.apk)",
" * [Mattermost Universal](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost-universal.apk)",
" * [Mattermost x86](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost-x86.apk)",
" * [Mattermost x86_64](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost-x86_64.apk)",
]
changelog.concat("* Android\n#{android.join("\n")}\n")
changelog.concat("* iOS\n * [Mattermost](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost.ipa)")
github_release = set_github_release(
repository_name: "mattermost/mattermost-mobile",
@ -334,12 +336,12 @@ platform :ios do
ENV['APP_NAME'] = 'Mattermost'
ENV['REPLACE_ASSETS'] = 'true'
ENV['BUILD_FOR_RELEASE'] = 'true'
ENV['APP_SCHEME'] = 'mattermost-mobile'
ENV['APP_SCHEME'] = 'mattermost'
update_identifiers
replace_assets
sh 'mkdir -p ../build-ios'
sh 'rm -rf ../build-ios/'
sh 'cd ../ios/ && xcodebuild -workspace Mattermost.xcworkspace/ -scheme Mattermost -sdk iphoneos -configuration Release -parallelizeTargets -resultBundlePath ../build-ios/result -derivedDataPath ../build-ios/ CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO'
sh 'cd ../build-ios/ && mkdir -p Payload && cp -R Build/Products/Release-iphoneos/Mattermost.app Payload/ && zip -r Mattermost-unsigned.ipa Payload/'
sh 'mv ../build-ios/Mattermost-unsigned.ipa ../'
@ -352,20 +354,17 @@ platform :ios do
ENV['APP_NAME'] = 'Mattermost'
ENV['REPLACE_ASSETS'] = 'true'
ENV['BUILD_FOR_RELEASE'] = 'true'
ENV['APP_SCHEME'] = 'mattermost-mobile'
ENV['APP_SCHEME'] = 'mattermost'
data_path = 'build-ios'
update_identifiers
replace_assets
sh 'rm -rf ../build-ios/'
sh 'cd ../ios && xcodebuild -workspace Mattermost.xcworkspace -scheme Mattermost -arch x86_64 -sdk iphonesimulator -configuration Release -parallelizeTargets -derivedDataPath ../build-ios ENABLE_BITCODE=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ENABLE_BITCODE=NO'
data_path = 'ios'
if ENV['CIRCLECI'] == 'true'
data_path = 'build-ios'
end
sh "cd ../#{data_path}/Build/Products/Release-iphonesimulator && zip -r Mattermost-simulator-x86_64.app.zip Mattermost.app"
sh "mv ../#{data_path}/Build/Products/Release-iphonesimulator/Mattermost-simulator-x86_64.app.zip ../"
sh 'rm -rf ../build-ios/'
upload_file_to_s3({
:os_type => "iOS",
@ -457,16 +456,12 @@ platform :ios do
app_group_identifiers: [app_group_id]
)
find_replace_string(
path_to_file: './ios/UploadAttachments/UploadAttachments/MMMConstants.m',
old_string: 'group.com.mattermost.rnbeta',
new_string: app_group_id
)
find_replace_string(
path_to_file: './node_modules/react-native-mmkv-storage/ios/MMKVStorage.m',
old_string: 'group.com.mattermost.rnbeta',
new_string: app_group_id
update_info_plist(
xcodeproj: './ios/Mattermost.xcodeproj',
plist_path: 'Mattermost/Info.plist',
block: proc do |plist|
plist["AppGroupIdentifier"] = app_group_id
end
)
update_app_group_identifiers(
@ -474,11 +469,27 @@ platform :ios do
app_group_identifiers: [app_group_id]
)
update_info_plist(
xcodeproj: './ios/Mattermost.xcodeproj',
plist_path: 'MattermostShare/Info.plist',
block: proc do |plist|
plist["AppGroupIdentifier"] = app_group_id
end
)
update_app_group_identifiers(
entitlements_file: './ios/NotificationService/NotificationService.entitlements',
app_group_identifiers: [app_group_id]
)
update_info_plist(
xcodeproj: './ios/Mattermost.xcodeproj',
plist_path: 'NotificationService/Info.plist',
block: proc do |plist|
plist["AppGroupIdentifier"] = app_group_id
end
)
# Sync the provisioning profiles using match
if ENV['SYNC_PROVISIONING_PROFILES'] == 'true' && !ENV['MATCH_PASSWORD'].nil? && !ENV['MATCH_PASSWORD'].empty?
match(type: ENV['MATCH_TYPE'] || 'adhoc')
@ -590,7 +601,7 @@ platform :android do
ENV['APP_NAME'] = 'Mattermost'
ENV['REPLACE_ASSETS'] = 'true'
ENV['BUILD_FOR_RELEASE'] = 'true'
ENV['APP_SCHEME'] = 'mattermost-mobile'
ENV['APP_SCHEME'] = 'mattermost'
update_identifiers
replace_assets
@ -727,20 +738,37 @@ platform :android do
gradle(
task: 'app:assemble',
build_type: config_mode,
project_dir: 'android/'
project_dir: 'android/',
properties: {
'separateApk' => ENV["SEPARATE_APKS"] || false,
'universalApk' => ENV["SEPARATE_APKS"] || false,
}
)
end
def move_apk_to_root
app_name = ENV['APP_NAME'] || 'Mattermost Beta'
app_name_sub = app_name.gsub(" ", "_")
new_apk_path = "#{Pathname.new(File.expand_path(File.dirname(__FILE__))).parent.to_s}/#{app_name_sub}.apk"
apk_path = lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]
unless apk_path.nil?
sh "mv #{apk_path} #{new_apk_path}"
lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH] = new_apk_path
apks = lane_context[SharedValues::GRADLE_ALL_APK_OUTPUT_PATHS]
outputJson = load_config_json(lane_context[SharedValues::GRADLE_OUTPUT_JSON_OUTPUT_PATH])
paths = []
apks.each do |apk|
filename = File.basename(apk)
output = outputJson.find { |o| o["path"] === filename }
unless output.nil?
filterName = output["apkData"]["filterName"]
name = "#{app_name_sub}"
unless filterName.nil? || filterName.empty?
name += "-#{filterName}"
end
new_apk_path = "#{Pathname.new(File.expand_path(File.dirname(__FILE__))).parent.to_s}/#{name}.apk"
paths.push(new_apk_path)
sh "mv #{apk} #{new_apk_path}"
UI.message("APK PATH \"#{new_apk_path}\", #{filterName}")
end
end
lane_context[SharedValues::GRADLE_ALL_APK_OUTPUT_PATHS] = paths
end
def link_sentry_android
@ -802,23 +830,23 @@ def send_message_to_mattermost(options)
end
def submit_to_testflight(ipa_path)
app_name = ENV['APP_NAME'] || 'Mattermost Beta'
app_name_sub = app_name.gsub(" ", "_")
ipa = (Dir.glob(ipa_path).select { |f| File.file? f}).first
if(File.file?(ipa_path))
UI.success("ipa file #{ipa_path}")
if !ipa.nil?
UI.success("ipa file #{ipa}")
pilot(
ipa: ipa_path
ipa: ipa
)
else
UI.user_error! "ipa file does not exist #{ipa_path}"
UI.user_error! "ipa file does not exist #{ipa}"
return
end
filename = File.basename(ipa)
version_number = get_version_number(xcodeproj: './ios/Mattermost.xcodeproj', target: 'Mattermost')
build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj')
s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{version_number}/#{build_number}"
public_link = "https://#{ENV['AWS_BUCKET_NAME']}/#{s3_folder}/#{app_name_sub}.ipa"
public_link = "https://#{ENV['AWS_BUCKET_NAME']}/#{s3_folder}/#{filename}"
# Send a build message to Mattermost
@ -843,34 +871,35 @@ def submit_to_testflight(ipa_path)
end
def submit_to_google_play(apk_path)
app_name = ENV['APP_NAME'] || 'Mattermost Beta'
app_name_sub = app_name.gsub(" ", "_")
apks = Dir.glob(apk_path).select { |f| File.file? f}
filenames = apks.map {|f| File.basename(f)}
if(File.file?(apk_path))
UI.success("apk file #{apk_path}")
if(apks.length > 0)
unless ENV['SUPPLY_JSON_KEY'].nil? || ENV['SUPPLY_JSON_KEY'].empty?
supply(
package_name: ENV['MAIN_APP_IDENTIFIER'] || ENV['SUPPLY_PACKAGE_NAME'],
track: ENV['SUPPLY_TRACK'] || 'alpha',
apk: apk_path
apk_paths: apks
)
end
else
UI.user_error! "apk file does not exist #{apk_path}"
UI.user_error! "There are no apks in #{apk_path}"
return
end
version_number = android_get_version_name(gradle_file: './android/app/build.gradle')
build_number = android_get_version_code(gradle_file: './android/app/build.gradle')
s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{version_number}/#{build_number}"
public_link = "https://#{ENV['AWS_BUCKET_NAME']}/#{s3_folder}/#{app_name_sub}.apk"
public_path = "https://#{ENV['AWS_BUCKET_NAME']}/#{s3_folder}"
links = (filenames.map { |f| "* #{public_path}/#{f}"}).join("\n")
# Send a build message to Mattermost
pretext = '#### New Android released ready to be published'
msg = "Release has been cut and is on the Beta track ready to be published.\nDownload link: #{public_link}"
msg = "Release has been cut and is on the Beta track ready to be published.\nDownload links:\n#{links}"
if ENV['BETA_BUILD'] == 'true'
pretext = '#### New Android beta published to Google Play'
msg = "Sign up as a beta tester [here](https://play.google.com/apps/testing/com.mattermost.rnbeta).\nDownload link: #{public_link}"
msg = "Sign up as a beta tester [here](https://play.google.com/apps/testing/com.mattermost.rnbeta).\nDownload links:\n#{links}"
end
send_message_to_mattermost({
@ -885,21 +914,6 @@ def submit_to_google_play(apk_path)
})
end
def submit_to_store
apk_path = lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]
ipa_path = lane_context[SharedValues::IPA_OUTPUT_PATH]
# Submit to Google Play if required
if !apk_path.nil? && ENV['SUBMIT_ANDROID_TO_GOOGLE_PLAY'] == 'true'
submit_to_google_play(apk_path)
end
# Submit to TestFlight if required
if !ipa_path.nil? && ENV['SUBMIT_IOS_TO_TESTFLIGHT'] == 'true'
submit_to_testflight(ipa_path)
end
end
def qa_build_message(options)
abbreviated_commit_hash = last_git_commit[:abbreviated_commit_hash]
os_type = options[:os_type]

View file

@ -6,22 +6,22 @@ GEM
public_suffix (>= 2.0.2, < 5.0)
atomos (0.1.3)
aws-eventstream (1.1.0)
aws-partitions (1.366.0)
aws-sdk-core (3.105.0)
aws-partitions (1.381.0)
aws-sdk-core (3.109.1)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.37.0)
aws-sdk-core (~> 3, >= 3.99.0)
aws-sdk-kms (1.39.0)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.79.1)
aws-sdk-core (~> 3, >= 3.104.3)
aws-sdk-s3 (1.83.0)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.2.2)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.3)
babosa (1.0.4)
claide (1.0.3)
colored (1.2)
colored2 (3.1.2)
@ -44,7 +44,7 @@ GEM
faraday_middleware (1.0.0)
faraday (~> 1.0)
fastimage (2.2.0)
fastlane (2.158.0)
fastlane (2.162.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
aws-sdk-s3 (~> 1.0)
@ -100,14 +100,14 @@ GEM
google-cloud-env (1.3.3)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.0.1)
google-cloud-storage (1.28.0)
google-cloud-storage (1.29.1)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-api-client (~> 0.33)
google-cloud-core (~> 1.2)
googleauth (~> 0.9)
mini_mime (~> 1.0)
googleauth (0.13.1)
googleauth (0.14.0)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
@ -165,7 +165,7 @@ GEM
unf_ext (0.0.7.7)
unicode-display_width (1.7.0)
word_wrap (1.0.0)
xcodeproj (1.18.0)
xcodeproj (1.19.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
@ -188,4 +188,4 @@ DEPENDENCIES
nokogiri
BUNDLED WITH
2.0.2
2.1.4

View file

@ -1,3 +1,3 @@
source "https://rubygems.org"
gem "cocoapods", "1.7.5"
gem "cocoapods", "1.10.0.rc.1"

View file

@ -2,23 +2,27 @@ GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.2)
activesupport (4.2.11.1)
i18n (~> 0.7)
activesupport (5.2.4.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
algoliasearch (1.27.4)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
atomos (0.1.3)
claide (1.0.3)
cocoapods (1.7.5)
activesupport (>= 4.0.2, < 5)
cocoapods (1.10.0.rc.1)
addressable (~> 2.6)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.7.5)
cocoapods-core (= 1.10.0.rc.1)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 1.2.2, < 2.0)
cocoapods-downloader (>= 1.4.0, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-stats (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.3.1, < 2.0)
cocoapods-trunk (>= 1.4.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
@ -27,50 +31,63 @@ GEM
molinillo (~> 0.6.6)
nap (~> 1.0)
ruby-macho (~> 1.4)
xcodeproj (>= 1.10.0, < 2.0)
cocoapods-core (1.7.5)
activesupport (>= 4.0.2, < 6)
xcodeproj (>= 1.17.0, < 2.0)
cocoapods-core (1.10.0.rc.1)
activesupport (> 5.0, < 6)
addressable (~> 2.6)
algoliasearch (~> 1.0)
concurrent-ruby (~> 1.1)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
netrc (~> 0.11)
public_suffix
typhoeus (~> 1.0)
cocoapods-deintegrate (1.0.4)
cocoapods-downloader (1.3.0)
cocoapods-downloader (1.4.0)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.0)
cocoapods-stats (1.1.0)
cocoapods-trunk (1.4.1)
cocoapods-trunk (1.5.0)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.1.0)
cocoapods-try (1.2.0)
colored2 (3.1.2)
concurrent-ruby (1.1.5)
concurrent-ruby (1.1.7)
escape (0.0.4)
ethon (0.12.0)
ffi (>= 1.3.0)
ffi (1.13.1)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
i18n (0.9.5)
httpclient (2.8.3)
i18n (1.8.5)
concurrent-ruby (~> 1.0)
minitest (5.14.0)
json (2.3.1)
minitest (5.14.2)
molinillo (0.6.6)
nanaimo (0.2.6)
nanaimo (0.3.0)
nap (1.1.0)
netrc (0.11.0)
public_suffix (4.0.6)
ruby-macho (1.4.0)
thread_safe (0.3.6)
tzinfo (1.2.6)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (1.2.7)
thread_safe (~> 0.1)
xcodeproj (1.15.0)
xcodeproj (1.18.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.2.6)
nanaimo (~> 0.3.0)
PLATFORMS
ruby
DEPENDENCIES
cocoapods (= 1.7.5)
cocoapods (= 1.10.0.rc.1)
BUNDLED WITH
2.0.2
2.1.4

View file

@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AppGroupIdentifier</key>
<string>group.com.mattermost.rnbeta</string>
<key>BundleEntryFilename</key>
<string>index.js</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>

View file

@ -65,11 +65,16 @@ RCT_EXPORT_MODULE();
return safeAreaInsets;
}
- (NSDictionary *)constantsToExport {
-(NSString *)appGroupId {
NSBundle *bundle = [NSBundle mainBundle];
NSString *appGroupId = [bundle objectForInfoDictionaryKey:@"AppGroupIdentifier"];
return appGroupId;
}
- (NSDictionary *)constantsToExport {
return @{
@"hasSafeAreaInsets": @([self hasSafeAreaInsets]),
@"appGroupIdentifier": APP_GROUP_ID
@"appGroupIdentifier": [self appGroupId]
};
}
@ -87,7 +92,7 @@ RCT_EXPORT_MODULE();
- (instancetype)init {
self = [super init];
if (self) {
_sharedUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:APP_GROUP_ID];
_sharedUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:[self appGroupId]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(managedConfigDidChange:) name:@"managedConfigDidChange" object:nil];
[[NSNotificationCenter defaultCenter] addObserverForName:NSUserDefaultsDidChangeNotification

View file

@ -1,5 +1,6 @@
let configurationKey = "com.apple.configuration.managed"
func getManagedConfig() -> [String : Any] {
return UserDefaults.init(suiteName: APP_GROUP_ID)?.dictionary(forKey: configurationKey) ?? [:]
let appGroupId = Bundle.main.infoDictionary!["AppGroupIdentifier"] as! String
return UserDefaults.init(suiteName: appGroupId)?.dictionary(forKey: configurationKey) ?? [:]
}

View file

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AppGroupIdentifier</key>
<string>group.com.mattermost.rnbeta</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>

View file

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AppGroupIdentifier</key>
<string>group.com.mattermost.rnbeta</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>

View file

@ -441,7 +441,7 @@ DEPENDENCIES:
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
https://github.com/CocoaPods/Specs.git:
- boost-for-react-native
- libwebp
- MMKV
@ -669,4 +669,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 1aabd16b6a9ed2037b4d7443027d222817790468
COCOAPODS: 1.7.5
COCOAPODS: 1.10.0.rc.1

View file

@ -1,7 +1,6 @@
#import <Foundation/Foundation.h>
@interface MMMConstants : NSObject
extern NSString *APP_GROUP_ID;
extern UInt64 DEFAULT_SERVER_MAX_IMAGE_PIXELS;
extern UInt64 DEFAULT_SERVER_MAX_FILE_SIZE;
extern UInt64 DEFAULT_SERVER_MAX_POST_SIZE;

View file

@ -1,7 +1,6 @@
#import "MMMConstants.h"
@implementation MMMConstants
NSString *APP_GROUP_ID = @"group.com.mattermost.rnbeta";
UInt64 DEFAULT_SERVER_MAX_IMAGE_PIXELS = 6048 * 4032;
UInt64 DEFAULT_SERVER_MAX_FILE_SIZE = 50 * 1024 * 1024;
UInt64 DEFAULT_SERVER_MAX_POST_SIZE = 4000;

View file

@ -7,13 +7,19 @@
return [[NSUserDefaults alloc] initWithSuiteName: name];
}
-(NSString *)appGroupId {
NSBundle *bundle = [NSBundle mainBundle];
NSString *appGroupId = [bundle objectForInfoDictionaryKey:@"AppGroupIdentifier"];
return appGroupId;
}
-(NSString *)fileUrl:(NSString *)fileName {
NSURL *fileManagerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:APP_GROUP_ID];
NSURL *fileManagerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:[self appGroupId]];
return [NSString stringWithFormat:@"%@/%@", fileManagerURL.path, fileName];
}
-(id) getPreference:(NSString *)key {
NSUserDefaults* bucket = [self bucketByName: APP_GROUP_ID];
NSUserDefaults* bucket = [self bucketByName: [self appGroupId]];
return [bucket objectForKey:key];
}
@ -50,12 +56,12 @@
}
-(void) removePreference:(NSString *)key {
NSUserDefaults* bucket = [self bucketByName: APP_GROUP_ID];
NSUserDefaults* bucket = [self bucketByName: [self appGroupId]];
[bucket removeObjectForKey: key];
}
-(void) setPreference:(NSString *)key value:(NSString *) value {
NSUserDefaults* bucket = [self bucketByName: APP_GROUP_ID];
NSUserDefaults* bucket = [self bucketByName: [self appGroupId]];
if (bucket && [key length] > 0 && [value length] > 0) {
[bucket setObject:value forKey:key];
}

View file

@ -155,8 +155,10 @@
}
-(NSString *)getToken {
NSBundle *bundle = [NSBundle mainBundle];
NSString *appGroupId = [bundle objectForInfoDictionaryKey:@"AppGroupIdentifier"];
NSDictionary *options = @{
@"accessGroup": APP_GROUP_ID
@"accessGroup": appGroupId
};
NSString* serverUrl = [self getServerUrl];

View file

@ -55,7 +55,8 @@ import os.log
os_log(OSLogType.default, "Mattermost Attached session with completionHandler identifier=%{public}@", identifier)
}
let sessionConfig = URLSessionConfiguration.background(withIdentifier: identifier)
sessionConfig.sharedContainerIdentifier = APP_GROUP_ID
let appGroupId = Bundle.main.infoDictionary!["AppGroupIdentifier"] as! String
sessionConfig.sharedContainerIdentifier = appGroupId
if #available(iOS 11.0, *) {
sessionConfig.waitsForConnectivity = true
}
@ -65,7 +66,8 @@ import os.log
public func createURLSession(identifier: String) -> URLSession {
let sessionConfig = URLSessionConfiguration.background(withIdentifier: identifier)
sessionConfig.sharedContainerIdentifier = APP_GROUP_ID
let appGroupId = Bundle.main.infoDictionary!["AppGroupIdentifier"] as! String
sessionConfig.sharedContainerIdentifier = appGroupId
if #available(iOS 11.0, *) {
sessionConfig.waitsForConnectivity = true
}

View file

@ -8,7 +8,7 @@ import Foundation
}
@objc @objcMembers public class UploadSessionManager: NSObject {
private let bucket = MattermostBucket().bucket(byName: APP_GROUP_ID)
private let bucket = MattermostBucket().bucket(byName: Bundle.main.infoDictionary!["AppGroupIdentifier"] as! String)
public class var shared :UploadSessionManager {
struct Singleton {
@ -60,7 +60,8 @@ import Foundation
public func tempContainerURL() -> URL? {
let filemgr = FileManager.default
let containerURL = filemgr.containerURL(forSecurityApplicationGroupIdentifier: APP_GROUP_ID)
let appGroupId = Bundle.main.infoDictionary!["AppGroupIdentifier"] as! String
let containerURL = filemgr.containerURL(forSecurityApplicationGroupIdentifier: appGroupId)
guard let tempDirectoryURL = containerURL?.appendingPathComponent("shareTempItems") else {return nil}
var isDirectory = ObjCBool(false)
let exists = filemgr.fileExists(atPath: tempDirectoryURL.path, isDirectory: &isDirectory)

View file

@ -2,8 +2,6 @@
export NODE_OPTIONS=--max_old_space_size=12000
export BUNDLE_COMMAND="ram-bundle"
export NODE_BINARY=node
if [[ "${SENTRY_ENABLED}" = "true" ]]; then
echo "Sentry native integration is enabled"

View file

@ -67,9 +67,6 @@ esac
PROJECT_ROOT=${PROJECT_ROOT:-$PWD}
cd "$PROJECT_ROOT/.." || exit
# Define NVM_DIR and source the nvm.sh setup script
[ -z "$NVM_DIR" ] && export NVM_DIR="$HOME/.nvm"
# Define entry file
if [[ "$ENTRY_FILE" ]]; then
# Use ENTRY_FILE defined by user
@ -80,27 +77,6 @@ elif [[ -s "index.ios.js" ]]; then
ENTRY_FILE=${1:-index.js}
fi
if [[ -s "$HOME/.nvm/nvm.sh" ]]; then
. "$HOME/.nvm/nvm.sh"
elif [[ -x "$(command -v brew)" && -s "$(brew --prefix nvm)/nvm.sh" ]]; then
. "$(brew --prefix nvm)/nvm.sh"
fi
# Set up the nodenv node version manager if present
if [[ -x "$HOME/.nodenv/bin/nodenv" ]]; then
eval "$("$HOME/.nodenv/bin/nodenv" init -)"
elif [[ -x "$(command -v brew)" && -x "$(brew --prefix nodenv)/bin/nodenv" ]]; then
eval "$("$(brew --prefix nodenv)/bin/nodenv" init -)"
fi
# Set up the ndenv of anyenv if preset
if [[ ! -x node && -d ${HOME}/.anyenv/bin ]]; then
export PATH=${HOME}/.anyenv/bin:${PATH}
if [[ "$(anyenv envs | grep -c ndenv )" -eq 1 ]]; then
eval "$(anyenv init -)"
fi
fi
# Path to react-native folder inside node_modules
REACT_NATIVE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/node_modules/react-native" && pwd)"
# check and assign NODE_BINARY env

9
package-lock.json generated
View file

@ -6065,15 +6065,6 @@
"@types/react-native": "*"
}
},
"@types/react-test-renderer": {
"version": "16.9.3",
"resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.9.3.tgz",
"integrity": "sha512-wJ7IlN5NI82XMLOyHSa+cNN4Z0I+8/YaLl04uDgcZ+W+ExWCmCiVTLT/7fRNqzy4OhStZcUwIqLNF7q+AdW43Q==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/shallow-equals": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/shallow-equals/-/shallow-equals-1.0.0.tgz",

View file

@ -151,19 +151,30 @@
}
},
"scripts": {
"start": "node ./node_modules/react-native/local-cli/cli.js start",
"check": "eslint --ignore-path .gitignore --ignore-pattern node_modules --quiet . && npm run tsc",
"fix": "eslint --ignore-path .gitignore --ignore-pattern node_modules --quiet . --fix",
"postinstall": "make post-install",
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"check": "npm run lint && npm run tsc",
"clean": "./scripts/clean.sh",
"ios-gems": "cd ios && bundle install",
"fix": "npm run lint -- --fix",
"lint": "eslint --ignore-path .gitignore --ignore-pattern node_modules --quiet .",
"pod-install": "cd ios && bundle exec pod install",
"postinstall": "patch-package && ./scripts/postinstall.sh",
"tsc": "NODE_OPTIONS=--max_old_space_size=12000 tsc --noEmit",
"test": "jest --forceExit --runInBand --detectOpenHandles",
"test:watch": "jest --watch --runInBand --detectOpenHandles",
"test:watch": "npm test -- --watch",
"test:coverage": "jest --coverage",
"updatesnapshot": "jest --updateSnapshot",
"mmjstool": "mmjstool",
"i18n-extract": "npm run mmjstool -- i18n extract-mobile",
"i18n-clean-empty": "npm run mmjstool -- i18n clean-empty --mobile-dir .",
"e2e:android": "cd detox && npm run e2e:android-build && npm run e2e:android-test && cd ..",
"e2e:ios": "cd detox && npm run e2e:ios-test && cd .."
"e2e:ios": "cd detox && npm run e2e:ios-test && cd ..",
"build:ios": "./scripts/build.sh ipa",
"build:ios-unsigned": "./scripts/build.sh ipa unsigned",
"build:ios-sim": "./scripts/build.sh ipa simulator",
"build:android": "./scripts/build.sh apk",
"build:android-unsigned": "./scripts/build.sh apk unsigned"
}
}

View file

@ -1,13 +1,14 @@
diff --git a/node_modules/react-native-mmkv-storage/ios/MMKVStorage.m b/node_modules/react-native-mmkv-storage/ios/MMKVStorage.m
index 34c764c..34fd83d 100644
index fe0ee2b..5bdde1d 100644
--- a/node_modules/react-native-mmkv-storage/ios/MMKVStorage.m
+++ b/node_modules/react-native-mmkv-storage/ios/MMKVStorage.m
@@ -37,7 +37,9 @@ - (id)init
@@ -45,7 +45,10 @@ - (id)init
{
self = [super init];
if (self) {
- [MMKV initialize];
+ NSString *APP_GROUP_ID = @"group.com.mattermost.rnbeta";
+ NSBundle *bundle = [NSBundle mainBundle];
+ NSString *APP_GROUP_ID = [bundle objectForInfoDictionaryKey:@"AppGroupIdentifier"];
+ NSString *groupDir = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:APP_GROUP_ID].path;
+ [MMKV initializeMMKV:nil groupDir:groupDir logLevel:MMKVLogInfo];
secureStorage = [[SecureStorage alloc]init];

97
scripts/build.sh Executable file
View file

@ -0,0 +1,97 @@
#!/bin/sh
function execute() {
cd fastlane && NODE_ENV=production bundle exec fastlane $1 $2
}
function apk() {
case $1 in
unsigned)
echo "Building Android unsigned app"
setup android
execute android unsigned
;;
*)
echo "Building Android app"
setup android
execute android build
esac
}
function ipa() {
case $1 in
unsigned)
echo "Building iOS unsigned app"
setup ios
execute ios unsigned
;;
simulator)
echo "Building unsigned x86_64 iOS app for iPhone simulator"
setup ios
execute ios simulator
;;
*)
echo "Building iOS app"
setup ios
execute ios build
esac
}
function setup() {
if [[ -z "$SKIP_SETUP" ]]; then
npm run clean || exit 1
npm install --ignore-scripts || exit 1
npx patch-package || exit 1
if [[ "$1" == "ios"* ]]; then
echo "Installing Gems"
npm run ios-gems &> /dev/null || exit 1
echo "Getting Cocoapods dependencies"
npm run pod-install || exit 1
fi
ASSETS=$(node scripts/generate-assets.js)
if [ -z "$ASSETS" ]; then
echo "Error Generating app assets"
exit 1
else
echo "Generating app assets"
fi
echo "Installing Fastane"
if !gem list bundler -i --version 2.1.4 > /dev/null 2>&1; then
gem install bundler --versio 2.1.4
fi
cd fastlane && bundle install && cd .. || exit 1
fi
if [ "$1" = "android" ]; then
./node_modules/.bin/jetify
fi
}
case $1 in
apk)
apk $2
;;
ipa)
if [[ "$OSTYPE" == "darwin"* ]]; then
ipa $2
else
echo "You need a MacOS to build the iOS mobile app"
exit 1
fi
;;
*)
echo "Build the mobile app for Android or iOS
Usage: build.sh <type> [options]
Type:
apk Builds Android APK(s)
ipa Builds iOS IPA
Options:
apk: unsigned
ipa: unsigned or simulator"
;;
esac

11
scripts/clean.sh Executable file
View file

@ -0,0 +1,11 @@
#!/bin/sh
echo Cleaning started
rm -rf ios/Pods
rm -rf node_modules
rm -rf dist
rm -rf ios/build
rm -rf android/app/build
echo Cleanup finished

View file

@ -4,6 +4,7 @@
/* eslint-disable no-console */
const fs = require('fs');
const fsPath = require('path');
// Takes the files in rootA/path, overwrites or merges them with the corresponding file in rootB/path, and places the
// resulting file in dest/path. JSON files that exist in both places are shallowly (TODO maybe deeply) merged and all
@ -13,7 +14,7 @@ function leftMergeDirs(rootA, rootB, dest, path) {
const pathB = rootB + path;
try {
fs.mkdirSync(dest + path);
fs.mkdirSync(dest + path, {recursive: true});
} catch (e) {
if (e.code !== 'EEXIST') {
console.error('Failed to create destination dir ' + dest + path);
@ -83,6 +84,25 @@ function leftMergeDirs(rootA, rootB, dest, path) {
}
}
const rmdir = (path) => {
const list = fs.readdirSync(path);
for (let i = 0; i < list.length; i++) {
const filename = fsPath.join(path, list[i]);
const stat = fs.statSync(filename);
if (stat.isDirectory()) {
rmdir(filename);
} else {
fs.unlinkSync(filename);
}
}
fs.rmdirSync(path);
};
if (fs.existsSync('dist')) {
rmdir('dist');
}
// Assumes dist/assets exists and is empty
leftMergeDirs('assets/base/', 'assets/override/', 'dist/assets/', '');
/* eslint-enable no-console */

19
scripts/postinstall.sh Executable file
View file

@ -0,0 +1,19 @@
#!/bin/sh
if [[ "$OSTYPE" == "darwin"* ]]; then
if !gem list bundler -i --version 2.1.4 > /dev/null 2>&1; then
gem install bundler --version 2.1.4
fi
echo "Installing Gems"
npm run ios-gems &> /dev/null
echo "Getting Cocoapods dependencies"
npm run pod-install &> /dev/null
fi
ASSETS=$(node scripts/generate-assets.js)
if [ -z "$ASSETS" ]; then
echo "Error Generating app assets"
exit 1
else
echo "Generating app assets"
fi

View file

@ -1,6 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
/* eslint-disable react/no-multi-comp */
import * as ReactNative from 'react-native';
import MockAsyncStorage from 'mock-async-storage';
import {configure} from 'enzyme';
@ -110,8 +112,31 @@ jest.doMock('react-native', () => {
}, ReactNative);
});
jest.mock('react-native-vector-icons/MaterialIcons', () => ({
getImageSource: jest.fn().mockResolvedValue({}),
}));
jest.mock('react-native-vector-icons/MaterialCommunityIcons');
jest.mock('react-native-vector-icons/FontAwesome5');
jest.mock('react-native-vector-icons', () => {
const React = jest.requireActual('react');
const PropTypes = jest.requireActual('prop-types');
class CompassIcon extends React.PureComponent {
render() {
return React.createElement('Icon', this.props);
}
}
CompassIcon.propTypes = {
name: PropTypes.string,
size: PropTypes.number,
style: PropTypes.oneOfType([PropTypes.array, PropTypes.number, PropTypes.object]),
};
CompassIcon.getImageSource = jest.fn().mockResolvedValue({});
return {
createIconSet: () => CompassIcon,
createIconSetFromFontello: () => CompassIcon,
};
});
jest.mock('react-native/Libraries/Animated/src/NativeAnimatedHelper');
jest.mock('../node_modules/react-native/Libraries/EventEmitter/NativeEventEmitter');