Skip to content
This repository has been archived by the owner on Oct 7, 2024. It is now read-only.

Commit

Permalink
Сreate postunlink scripts (microsoft#1588)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandergoncharov-zz authored and Yuri Kulikov committed May 30, 2019
1 parent 06b875d commit ae074f2
Show file tree
Hide file tree
Showing 8 changed files with 388 additions and 175 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
]
},
"commands": {
"postlink": "node node_modules/react-native-code-push/scripts/postlink/run"
"postlink": "node node_modules/react-native-code-push/scripts/postlink/run",
"postunlink": "node node_modules/react-native-code-push/scripts/postunlink/run"
}
}
}
63 changes: 15 additions & 48 deletions scripts/postlink/android/postlink.js
Original file line number Diff line number Diff line change
@@ -1,70 +1,37 @@
var linkTools = require('../../tools/linkToolsAndroid');
var fs = require("fs");
var glob = require("glob");
var path = require("path");
var inquirer = require('inquirer');

module.exports = () => {

console.log("Running android postlink script");

var ignoreFolders = { ignore: ["node_modules/**", "**/build/**"] };
var buildGradlePath = path.join("android", "app", "build.gradle");
var manifestPath = glob.sync("**/AndroidManifest.xml", ignoreFolders)[0];

function findMainApplication() {
if (!manifestPath) {
return null;
}

var manifest = fs.readFileSync(manifestPath, "utf8");

// Android manifest must include single 'application' element
var matchResult = manifest.match(/application\s+android:name\s*=\s*"(.*?)"/);
if (matchResult) {
var appName = matchResult[1];
} else {
return null;
}

var nameParts = appName.split('.');
var searchPath = glob.sync("**/" + nameParts[nameParts.length - 1] + ".java", ignoreFolders)[0];
return searchPath;
}

var mainApplicationPath = findMainApplication() || glob.sync("**/MainApplication.java", ignoreFolders)[0];
var buildGradlePath = linkTools.getBuildGradlePath();
var mainApplicationPath = linkTools.getMainApplicationLocation();

// 1. Add the getJSBundleFile override
var getJSBundleFileOverride = `
@Override
protected String getJSBundleFile() {
return CodePush.getJSBundleFile();
}
`;

function isAlreadyOverridden(codeContents) {
return /@Override\s*\n\s*protected String getJSBundleFile\(\)\s*\{[\s\S]*?\}/.test(codeContents);
}
var getJSBundleFileOverride = linkTools.getJSBundleFileOverride;

if (mainApplicationPath) {
var mainApplicationContents = fs.readFileSync(mainApplicationPath, "utf8");
if (isAlreadyOverridden(mainApplicationContents)) {
if (linkTools.isJsBundleOverridden(mainApplicationContents)) {
console.log(`"getJSBundleFile" is already overridden`);
} else {
var reactNativeHostInstantiation = "new ReactNativeHost(this) {";
var reactNativeHostInstantiation = linkTools.reactNativeHostInstantiation;
mainApplicationContents = mainApplicationContents.replace(reactNativeHostInstantiation,
`${reactNativeHostInstantiation}\n${getJSBundleFileOverride}`);
`${reactNativeHostInstantiation}${getJSBundleFileOverride}`);
fs.writeFileSync(mainApplicationPath, mainApplicationContents);
}
} else {
var mainActivityPath = glob.sync("**/MainActivity.java", ignoreFolders)[0];
var mainActivityPath = linkTools.getMainActivityPath();
if (mainActivityPath) {
var mainActivityContents = fs.readFileSync(mainActivityPath, "utf8");
if (isAlreadyOverridden(mainActivityContents)) {
if (linkTools.isJsBundleOverridden(mainActivityContents)) {
console.log(`"getJSBundleFile" is already overridden`);
} else {
var mainActivityClassDeclaration = "public class MainActivity extends ReactActivity {";
var mainActivityClassDeclaration = linkTools.mainActivityClassDeclaration;
mainActivityContents = mainActivityContents.replace(mainActivityClassDeclaration,
`${mainActivityClassDeclaration}\n${getJSBundleFileOverride}`);
`${mainActivityClassDeclaration}${getJSBundleFileOverride}`);
fs.writeFileSync(mainActivityPath, mainActivityContents);
}
} else {
Expand All @@ -83,22 +50,22 @@ module.exports = () => {
// 2. Add the codepush.gradle build task definitions
var buildGradleContents = fs.readFileSync(buildGradlePath, "utf8");
var reactGradleLink = buildGradleContents.match(/\napply from: ["'].*?react\.gradle["']/)[0];
var codePushGradleLink = `apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"`;
var codePushGradleLink = linkTools.codePushGradleLink;
if (~buildGradleContents.indexOf(codePushGradleLink)) {
console.log(`"codepush.gradle" is already linked in the build definition`);
} else {
buildGradleContents = buildGradleContents.replace(reactGradleLink,
`${reactGradleLink}\n${codePushGradleLink}`);
`${reactGradleLink}${codePushGradleLink}`);
fs.writeFileSync(buildGradlePath, buildGradleContents);
}

//3. Add deployment key
var stringsResourcesPath = glob.sync("**/strings.xml", ignoreFolders)[0];
var stringsResourcesPath = linkTools.getStringsResourcesPath();
if (!stringsResourcesPath) {
return Promise.reject(new Error(`Couldn't find strings.xml. You might need to update it manually.`));
} else {
var stringsResourcesContent = fs.readFileSync(stringsResourcesPath, "utf8");
var deploymentKeyName = "reactNativeCodePush_androidDeploymentKey";
var deploymentKeyName = linkTools.deploymentKeyName;
if (~stringsResourcesContent.indexOf(deploymentKeyName)) {
console.log(`${deploymentKeyName} already specified in the strings.xml`);
} else {
Expand Down
138 changes: 12 additions & 126 deletions scripts/postlink/ios/postlink.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@

var linkTools = require('../../tools/linkToolsIos');
var fs = require("fs");
var glob = require("glob");
var inquirer = require('inquirer');
var path = require("path");
var plist = require("plist");
var xcode = require("xcode");
var semver = require('semver');

var package = require('../../../../../package.json');
Expand All @@ -12,15 +11,7 @@ module.exports = () => {

console.log("Running ios postlink script");

var ignoreNodeModules = { ignore: "node_modules/**" };
var ignoreNodeModulesAndPods = { ignore: ["node_modules/**", "ios/Pods/**"] };
var appDelegatePaths = glob.sync("**/AppDelegate.+(mm|m)", ignoreNodeModules);

// Fix for https://github.com/Microsoft/react-native-code-push/issues/477
// Typical location of AppDelegate.m for newer RN versions: $PROJECT_ROOT/ios/<project_name>/AppDelegate.m
// Let's try to find that path by filtering the whole array for any path containing <project_name>
// If we can't find it there, play dumb and pray it is the first path we find.
var appDelegatePath = findFileByAppName(appDelegatePaths, package ? package.name : null) || appDelegatePaths[0];
var appDelegatePath = linkTools.getAppDeletePath();

if (!appDelegatePath) {
return Promise.reject(`Couldn't find AppDelegate. You might need to update it manually \
Expand All @@ -31,13 +22,12 @@ module.exports = () => {
var appDelegateContents = fs.readFileSync(appDelegatePath, "utf8");

// 1. Add the header import statement
var codePushHeaderImportStatement = `#import <CodePush/CodePush.h>`;
if (~appDelegateContents.indexOf(codePushHeaderImportStatement)) {
if (~appDelegateContents.indexOf(linkTools.codePushHeaderImportStatement)) {
console.log(`"CodePush.h" header already imported.`);
} else {
var appDelegateHeaderImportStatement = `#import "AppDelegate.h"`;
appDelegateContents = appDelegateContents.replace(appDelegateHeaderImportStatement,
`${appDelegateHeaderImportStatement}\n${codePushHeaderImportStatement}`);
`${appDelegateHeaderImportStatement}${linkTools.codePushHeaderImportStatementFormatted}`);
}

// 2. Modify jsCodeLocation value assignment
Expand All @@ -46,8 +36,8 @@ module.exports = () => {
if (!reactNativeVersion) {
console.log(`Can't take react-native version from package.json`);
} else if (semver.gte(semver.coerce(reactNativeVersion), "0.59.0")) {
var oldBundleUrl = "[[NSBundle mainBundle] URLForResource:@\"main\" withExtension:@\"jsbundle\"]";
var codePushBundleUrl = "[CodePush bundleURL]";
var oldBundleUrl = linkTools.oldBundleUrl;
var codePushBundleUrl = linkTools.codePushBundleUrl;

if (~appDelegateContents.indexOf(codePushBundleUrl)) {
console.log(`"BundleUrl" already pointing to "[CodePush bundleURL]".`);
Expand All @@ -65,21 +55,16 @@ module.exports = () => {
console.log('Couldn\'t find jsCodeLocation setting in AppDelegate.');
}

var newJsCodeLocationAssignmentStatement = "jsCodeLocation = [CodePush bundleURL];";
var newJsCodeLocationAssignmentStatement = linkTools.codePushGradleLink;
if (~appDelegateContents.indexOf(newJsCodeLocationAssignmentStatement)) {
console.log(`"jsCodeLocation" already pointing to "[CodePush bundleURL]".`);
} else {
if (jsCodeLocations.length === 1) {
// If there is one `jsCodeLocation` it means that react-native app version is lower than 0.57.8
// If there is one `jsCodeLocation` it means that react-native app version is not the 0.57.8 or 0.57.0 and lower than 0.59
// and we should replace this line with DEBUG ifdef statement and add CodePush call for Release case

var oldJsCodeLocationAssignmentStatement = jsCodeLocations[0];
var jsCodeLocationPatch = `
#ifdef DEBUG
${oldJsCodeLocationAssignmentStatement}
#else
${newJsCodeLocationAssignmentStatement}
#endif`;
var jsCodeLocationPatch = linkTools.getJsCodeLocationPatch(oldJsCodeLocationAssignmentStatement);
appDelegateContents = appDelegateContents.replace(oldJsCodeLocationAssignmentStatement,
jsCodeLocationPatch);
} else if (jsCodeLocations.length === 2) {
Expand All @@ -94,7 +79,7 @@ module.exports = () => {
}
}

var plistPath = getPlistPath();
var plistPath = linkTools.getPlistPath();

if (!plistPath) {
return Promise.reject(`Couldn't find .plist file. You might need to update it manually \
Expand Down Expand Up @@ -128,103 +113,4 @@ module.exports = () => {
fs.writeFileSync(appDelegatePath, appDelegateContents);
fs.writeFileSync(plistPath, plistContents);
}

// Helper that filters an array with AppDelegate.m paths for a path with the app name inside it
// Should cover nearly all cases
function findFileByAppName(array, appName) {
if (array.length === 0 || !appName) return null;

for (var i = 0; i < array.length; i++) {
var path = array[i];
if (path && path.indexOf(appName) !== -1) {
return path;
}
}

return null;
}

function getDefaultPlistPath() {
//this is old logic in case we are unable to find PLIST from xcode/pbxproj - at least we can fallback to default solution
return glob.sync(`**/${package.name}/*Info.plist`, ignoreNodeModules)[0];
}

// This is enhanced version of standard implementation of xcode 'getBuildProperty' function
// but allows us to narrow results by PRODUCT_NAME property also.
// So we suppose that proj name should be the same as package name, otherwise fallback to default plist path searching logic
function getBuildSettingsPropertyMatchingTargetProductName(parsedXCodeProj, prop, targetProductName, build){
var target;
var COMMENT_KEY = /_comment$/;
var PRODUCT_NAME_PROJECT_KEY = 'PRODUCT_NAME';
var TV_OS_DEPLOYMENT_TARGET_PROPERTY_NAME = 'TVOS_DEPLOYMENT_TARGET';
var TEST_HOST_PROPERTY_NAME = 'TEST_HOST';

var configs = parsedXCodeProj.pbxXCBuildConfigurationSection();
for (var configName in configs) {
if (!COMMENT_KEY.test(configName)) {
var config = configs[configName];
if ( (build && config.name === build) || (build === undefined) ) {
if (targetProductName) {
if (config.buildSettings[prop] !== undefined && config.buildSettings[PRODUCT_NAME_PROJECT_KEY] == targetProductName) {
target = config.buildSettings[prop];
}
} else {
if (config.buildSettings[prop] !== undefined &&
//exclude tvOS projects
config.buildSettings[TV_OS_DEPLOYMENT_TARGET_PROPERTY_NAME] == undefined &&
//exclude test app
config.buildSettings[TEST_HOST_PROPERTY_NAME] == undefined) {
target = config.buildSettings[prop];
}
}
}
}
}
return target;
}

function getPlistPath(){
var xcodeProjectPaths = glob.sync(`**/*.xcodeproj/project.pbxproj`, ignoreNodeModulesAndPods);
if (!xcodeProjectPaths){
return getDefaultPlistPath();
}

if (xcodeProjectPaths.length !== 1) {
console.log('Could not determine correct xcode proj path to retrieve related plist file, there are multiple xcodeproj under the solution.');
return getDefaultPlistPath();
}

var xcodeProjectPath = xcodeProjectPaths[0];
var parsedXCodeProj;

try {
var proj = xcode.project(xcodeProjectPath);
//use sync version because there are some problems with async version of xcode lib as of current version
parsedXCodeProj = proj.parseSync();
}
catch(e) {
console.log('Couldn\'t read info.plist path from xcode project - error: ' + e.message);
return getDefaultPlistPath();
}

var INFO_PLIST_PROJECT_KEY = 'INFOPLIST_FILE';
var RELEASE_BUILD_PROPERTY_NAME = "Release";
var targetProductName = package ? package.name : null;

//Try to get 'Release' build of ProductName matching the package name first and if it doesn't exist then try to get any other if existing
var plistPathValue = getBuildSettingsPropertyMatchingTargetProductName(parsedXCodeProj, INFO_PLIST_PROJECT_KEY, targetProductName, RELEASE_BUILD_PROPERTY_NAME) ||
getBuildSettingsPropertyMatchingTargetProductName(parsedXCodeProj, INFO_PLIST_PROJECT_KEY, targetProductName) ||
getBuildSettingsPropertyMatchingTargetProductName(parsedXCodeProj, INFO_PLIST_PROJECT_KEY, null, RELEASE_BUILD_PROPERTY_NAME) ||
getBuildSettingsPropertyMatchingTargetProductName(parsedXCodeProj, INFO_PLIST_PROJECT_KEY) ||
parsedXCodeProj.getBuildProperty(INFO_PLIST_PROJECT_KEY, RELEASE_BUILD_PROPERTY_NAME) ||
parsedXCodeProj.getBuildProperty(INFO_PLIST_PROJECT_KEY);

if (!plistPathValue){
return getDefaultPlistPath();
}

//also remove surrounding quotes from plistPathValue to get correct path resolved
//(see https://github.com/Microsoft/react-native-code-push/issues/534#issuecomment-302069326 for details)
return path.resolve(path.dirname(xcodeProjectPath), '..', plistPathValue.replace(/^"(.*)"$/, '$1'));
}
}
}
74 changes: 74 additions & 0 deletions scripts/postunlink/android/postunlink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
var linkTools = require('../../tools/linkToolsAndroid');
var fs = require("fs");

module.exports = () => {

console.log("Running android postunlink script");

var mainApplicationPath = linkTools.getMainApplicationLocation();

// 1. Remove the getJSBundleFile override
var getJSBundleFileOverride = linkTools.getJSBundleFileOverride;

if (mainApplicationPath) {
var mainApplicationContents = fs.readFileSync(mainApplicationPath, "utf8");
if (!linkTools.isJsBundleOverridden(mainApplicationContents)) {
console.log(`"getJSBundleFile" is already removed`);
} else {
mainApplicationContents = mainApplicationContents.replace(`${getJSBundleFileOverride}`, "");
fs.writeFileSync(mainApplicationPath, mainApplicationContents);
}
} else {
var mainActivityPath = linkTools.getMainActivityPath();
if (mainActivityPath) {
var mainActivityContents = fs.readFileSync(mainActivityPath, "utf8");
if (!linkTools.isJsBundleOverridden(mainActivityContents)) {
console.log(`"getJSBundleFile" is already removed`);
} else {
mainActivityContents = mainActivityContents.replace(getJSBundleFileOverride, "");
fs.writeFileSync(mainActivityPath, mainActivityContents);
}
} else {
console.log(`Couldn't find Android application entry point. You might need to update it manually. \
Please refer to plugin configuration section for Android at \
https://github.com/microsoft/react-native-code-push#plugin-configuration-android for more details`);
}
}

// 2. Remove the codepush.gradle build task definitions
var buildGradlePath = linkTools.getBuildGradlePath();

if (!fs.existsSync(buildGradlePath)) {
console.log(`Couldn't find build.gradle file. You might need to update it manually. \
Please refer to plugin installation section for Android at \
https://github.com/microsoft/react-native-code-push#plugin-installation-android---manual`);
} else {
var buildGradleContents = fs.readFileSync(buildGradlePath, "utf8");
var codePushGradleLink = linkTools.codePushGradleLink;
if (!~buildGradleContents.indexOf(codePushGradleLink)) {
console.log(`"codepush.gradle" is already unlinked in the build definition`);
} else {
buildGradleContents = buildGradleContents.replace(`${codePushGradleLink}`,"");
fs.writeFileSync(buildGradlePath, buildGradleContents);
}
}

// 3. Remove deployment key
var stringsResourcesPath = linkTools.getStringsResourcesPath();
if (!stringsResourcesPath) {
return Promise.reject(new Error("Couldn't find strings.xml. You might need to update it manually."));
} else {
var stringsResourcesContent = fs.readFileSync(stringsResourcesPath, "utf8");
var deploymentKeyName = linkTools.deploymentKeyName;
if (!~stringsResourcesContent.indexOf(deploymentKeyName)) {
console.log(`${deploymentKeyName} already removed from the strings.xml`);
} else {
var AndroidDeploymentKey = stringsResourcesContent.match(/(<string moduleConfig="true" name="reactNativeCodePush_androidDeploymentKey">.*<\/string>)/);
if (AndroidDeploymentKey) {
stringsResourcesContent = stringsResourcesContent.replace(`\n\t${AndroidDeploymentKey[0]}`,"");
fs.writeFileSync(stringsResourcesPath, stringsResourcesContent);
}
}
}
return Promise.resolve();
}
Loading

0 comments on commit ae074f2

Please sign in to comment.