diff --git a/packages/cli-doctor/package.json b/packages/cli-doctor/package.json index bfb9a15a6..bc2ea81bd 100644 --- a/packages/cli-doctor/package.json +++ b/packages/cli-doctor/package.json @@ -10,6 +10,7 @@ "dependencies": { "@react-native-community/cli-config": "^8.0.0-alpha.0", "@react-native-community/cli-tools": "^8.0.0-alpha.0", + "@react-native-community/cli-platform-ios": "^8.0.0-alpha.0", "chalk": "^4.1.2", "command-exists": "^1.2.8", "envinfo": "^7.7.2", diff --git a/packages/cli-doctor/src/tools/healthchecks/index.ts b/packages/cli-doctor/src/tools/healthchecks/index.ts index e7c870b62..2116ff01b 100644 --- a/packages/cli-doctor/src/tools/healthchecks/index.ts +++ b/packages/cli-doctor/src/tools/healthchecks/index.ts @@ -11,6 +11,7 @@ import cocoaPods from './cocoaPods'; import iosDeploy from './iosDeploy'; import {Healthchecks, HealthCheckCategory} from '../../types'; import loadConfig from '@react-native-community/cli-config'; +import xcodeEnv from './xcodeEnv'; export const HEALTHCHECK_TYPES = { ERROR: 'ERROR', @@ -55,7 +56,7 @@ export const getHealthchecks = ({contributor}: Options): Healthchecks => { ? { ios: { label: 'iOS', - healthchecks: [xcode, cocoaPods, iosDeploy], + healthchecks: [xcode, cocoaPods, iosDeploy, xcodeEnv], }, } : {}), diff --git a/packages/cli-doctor/src/tools/healthchecks/xcodeEnv.ts b/packages/cli-doctor/src/tools/healthchecks/xcodeEnv.ts new file mode 100644 index 000000000..aab5e68ed --- /dev/null +++ b/packages/cli-doctor/src/tools/healthchecks/xcodeEnv.ts @@ -0,0 +1,61 @@ +import {HealthCheckInterface} from '../../types'; +import fs from 'fs'; +import path from 'path'; +import {promisify} from 'util'; +import {findProjectRoot} from '@react-native-community/cli-tools'; +import {findPodfilePaths} from '@react-native-community/cli-platform-ios'; + +const xcodeEnvFile = '.xcode.env'; +const pathSeparator = '/'; + +function removeLastPathComponent(pathString: string): string { + const components = pathString.split(pathSeparator); + components.splice(components.length - 1, 1); + return components.join(pathSeparator); +} + +function pathHasXcodeEnvFile(pathString: string): boolean { + const xcodeEnvPath = pathString + pathSeparator + xcodeEnvFile; + return fs.existsSync(xcodeEnvPath); +} + +function pathDoesNotHaveXcodeEnvFile(pathString: string): boolean { + return !pathHasXcodeEnvFile(pathString); +} + +export default { + label: '.xcode.env', + description: 'File to customize Xcode environment', + getDiagnostics: async () => { + const projectRoot = findProjectRoot(); + const allPathsHasXcodeEnvFile = findPodfilePaths(projectRoot) + .map((pathString) => { + const basePath = removeLastPathComponent(pathString); + return pathHasXcodeEnvFile(basePath); + }) + .reduce((previousValue, currentValue) => previousValue && currentValue); + return { + needsToBeFixed: !allPathsHasXcodeEnvFile, + }; + }, + runAutomaticFix: async () => { + const templateXcodeEnv = '_xcode.env'; + const projectRoot = findProjectRoot(); + + const templateIosPath = path.dirname( + require.resolve('react-native/template/ios'), + ); + + const src = templateIosPath + templateXcodeEnv; + const copyFileAsync = promisify(fs.copyFile); + + findPodfilePaths(projectRoot) + .map(removeLastPathComponent) + // avoid overriding existing .xcode.env + .filter(pathDoesNotHaveXcodeEnvFile) + .forEach(async (pathString) => { + const destFilePath = pathString + pathSeparator + xcodeEnvFile; + await copyFileAsync(src, destFilePath); + }); + }, +} as HealthCheckInterface; diff --git a/packages/cli-doctor/tsconfig.json b/packages/cli-doctor/tsconfig.json index 227855158..e0605db36 100644 --- a/packages/cli-doctor/tsconfig.json +++ b/packages/cli-doctor/tsconfig.json @@ -7,6 +7,7 @@ "references": [ {"path": "../tools"}, {"path": "../cli-types"}, - {"path": "../cli-config"} + {"path": "../cli-config"}, + {"path": "../platform-ios"} ] } diff --git a/packages/cli/src/commands/init/editTemplate.ts b/packages/cli/src/commands/init/editTemplate.ts index cf3a01c15..2902832ce 100644 --- a/packages/cli/src/commands/init/editTemplate.ts +++ b/packages/cli/src/commands/init/editTemplate.ts @@ -69,6 +69,7 @@ const UNDERSCORED_DOTFILES = [ 'editorconfig', 'bundle', 'ruby-version', + 'xcode.env', ]; async function processDotfiles(filePath: string) { diff --git a/packages/cli/src/tools/generator/copyProjectTemplateAndReplace.ts b/packages/cli/src/tools/generator/copyProjectTemplateAndReplace.ts index e7e0b45a3..77b71809c 100644 --- a/packages/cli/src/tools/generator/copyProjectTemplateAndReplace.ts +++ b/packages/cli/src/tools/generator/copyProjectTemplateAndReplace.ts @@ -148,7 +148,8 @@ function translateFilePath(filePath: string) { .replace('_prettierrc.js', '.prettierrc.js') .replace('_bundle', '.bundle') .replace('_ruby-version', '.ruby-version') - .replace('_watchmanconfig', '.watchmanconfig'); + .replace('_watchmanconfig', '.watchmanconfig') + .replace('_xcode.env', '.xcode.env'); } function upgradeFileContentChangedCallback( diff --git a/packages/platform-ios/src/config/findAllPodfilePaths.ts b/packages/platform-ios/src/config/findAllPodfilePaths.ts new file mode 100644 index 000000000..6469cb52c --- /dev/null +++ b/packages/platform-ios/src/config/findAllPodfilePaths.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import glob from 'glob'; + +// These folders will be excluded from search to speed it up +const GLOB_EXCLUDE_PATTERN = ['**/@(Pods|node_modules|Carthage)/**']; + +export default function findAllPodfilePaths(cwd: string) { + return glob.sync('**/Podfile', { + cwd, + ignore: GLOB_EXCLUDE_PATTERN, + }); +} diff --git a/packages/platform-ios/src/config/findPodfilePath.ts b/packages/platform-ios/src/config/findPodfilePath.ts index be7509fa9..7b9313fcd 100644 --- a/packages/platform-ios/src/config/findPodfilePath.ts +++ b/packages/platform-ios/src/config/findPodfilePath.ts @@ -7,8 +7,8 @@ */ import {inlineString, logger} from '@react-native-community/cli-tools'; -import glob from 'glob'; import path from 'path'; +import findAllPodfilePaths from './findAllPodfilePaths'; // Regexp matching all test projects const TEST_PROJECTS = /test|example|sample/i; @@ -16,18 +16,8 @@ const TEST_PROJECTS = /test|example|sample/i; // Base iOS folder const IOS_BASE = 'ios'; -// These folders will be excluded from search to speed it up -const GLOB_EXCLUDE_PATTERN = ['**/@(Pods|node_modules|Carthage)/**']; - export default function findPodfilePath(cwd: string) { - /** - * First, we're going to look for all Podfiles within the `cwd` - */ - const podfiles = glob - .sync('**/Podfile', { - cwd, - ignore: GLOB_EXCLUDE_PATTERN, - }) + const podfiles = findAllPodfilePaths(cwd) /** * Then, we will run a simple test to rule out most example projects, * unless they are located in a `ios` folder diff --git a/packages/platform-ios/src/config/index.ts b/packages/platform-ios/src/config/index.ts index d5ac6a239..088b8f44c 100644 --- a/packages/platform-ios/src/config/index.ts +++ b/packages/platform-ios/src/config/index.ts @@ -10,6 +10,7 @@ import fs from 'fs'; import findPodfilePath from './findPodfilePath'; import findXcodeProject from './findXcodeProject'; import findPodspec from './findPodspec'; +import findAllPodfilePaths from './findAllPodfilePaths'; import { IOSProjectParams, IOSDependencyParams, @@ -65,3 +66,5 @@ export function dependencyConfig( scriptPhases: userConfig.scriptPhases || [], }; } + +export const findPodfilePaths = findAllPodfilePaths; diff --git a/packages/platform-ios/src/index.ts b/packages/platform-ios/src/index.ts index 59589860d..a612686d2 100644 --- a/packages/platform-ios/src/index.ts +++ b/packages/platform-ios/src/index.ts @@ -3,4 +3,4 @@ */ export {default as commands} from './commands'; -export {projectConfig, dependencyConfig} from './config'; +export {projectConfig, dependencyConfig, findPodfilePaths} from './config';