UNPKG

react-native-code-push-diff

Version:

This library builds upon the foundational strengths of the react-native-code-push library, adding advanced functionality to precisely identify and manage differences between code-push builds.

207 lines (203 loc) 9.94 kB
"use strict"; import * as fs from 'fs'; import * as path from 'path'; import * as semver from 'semver'; function isValidVersion(version) { return !!semver.valid(version) || /^\d+\.\d+$/.test(version); } export function getReactNativeProjectAppVersion(command, projectName) { if (command.platform === 'ios') { return getIosAppVersion(command, projectName); } if (command.platform === 'android') { return getAndroidAppVersion(command); } throw new Error(`Unsupported platform: ${command.platform}`); } function getAndroidAppVersion(command) { let buildGradlePath = path.join('android', 'app'); if (command.gradleFile) { buildGradlePath = command.gradleFile; } if (fs.lstatSync(buildGradlePath).isDirectory()) { buildGradlePath = path.join(buildGradlePath, 'build.gradle'); } if (fileDoesNotExistOrIsDirectory(buildGradlePath)) { throw new Error(`Unable to find gradle file "${buildGradlePath}".`); } const g2js = require('gradle-to-js/lib/parser'); return g2js.parseFile(buildGradlePath).catch(() => { throw new Error(`Unable to parse the "${buildGradlePath}" file. Please ensure it is a well-formed Gradle file.`); }).then(buildGradle => { let versionName = null; // First 'if' statement was implemented as workaround for case // when 'build.gradle' file contains several 'android' nodes. // In this case 'buildGradle.android' prop represents array instead of object // due to parsing issue in 'g2js.parseFile' method. if (buildGradle.android instanceof Array) { for (let i = 0; i < buildGradle.android.length; i++) { const gradlePart = buildGradle.android[i]; if (gradlePart.defaultConfig && gradlePart.defaultConfig.versionName) { versionName = gradlePart.defaultConfig.versionName; break; } } } else if (buildGradle.android && buildGradle.android.defaultConfig && buildGradle.android.defaultConfig.versionName) { versionName = buildGradle.android.defaultConfig.versionName; } else { throw new Error(`The "${buildGradlePath}" file doesn't specify a value for the "android.defaultConfig.versionName" property.`); } if (typeof versionName !== 'string') { throw new Error(`The "android.defaultConfig.versionName" property value in "${buildGradlePath}" is not a valid string. If this is expected, consider using the --targetBinaryVersion option to specify the value manually.`); } let appVersion = versionName.replace(/"/g, '').trim(); if (isValidVersion(appVersion)) { // The versionName property is a valid semver string, // so we can safely use that and move on. console.log(`Using the target binary version value "${appVersion}" from "${buildGradlePath}".\n`); return appVersion; } else if (/^\d.*/.test(appVersion)) { // The versionName property isn't a valid semver string, // but it starts with a number, and therefore, it can't // be a valid Gradle property reference. throw new Error(`The "android.defaultConfig.versionName" property in the "${buildGradlePath}" file needs to specify a valid semver string, containing both a major and minor version (e.g. 1.3.2, 1.1).`); } // The version property isn't a valid semver string // so we assume it is a reference to a property variable. const propertyName = appVersion.replace('project.', ''); const propertiesFileName = 'gradle.properties'; const knownLocations = [path.join('android', 'app', propertiesFileName), path.join('android', propertiesFileName)]; // Search for gradle properties across all `gradle.properties` files let propertiesFile; for (let i = 0; i < knownLocations.length; i++) { propertiesFile = knownLocations[i]; if (fileExists(propertiesFile)) { const propertiesContent = fs.readFileSync(propertiesFile).toString(); try { const properties = require('properties'); const parsedProperties = properties.parse(propertiesContent); appVersion = parsedProperties[propertyName]; if (appVersion) { break; } } catch (e) { throw new Error(`Unable to parse "${propertiesFile}". Please ensure it is a well-formed properties file.`); } } } if (!appVersion) { throw new Error(`No property named "${propertyName}" exists in the "${propertiesFile}" file.`); } if (!isValidVersion(appVersion)) { throw new Error(`The "${propertyName}" property in the "${propertiesFile}" file needs to specify a valid semver string, containing both a major and minor version (e.g. 1.3.2, 1.1).`); } console.log(`Using the target binary version value "${appVersion}" from the "${propertyName}" key in the "${propertiesFile}" file.\n`); return appVersion.toString(); }); } function getIosAppVersion(command, projectName) { const projectPackageJson = require(path.join(process.cwd(), 'package.json')); if (!projectName && projectPackageJson) { projectName = projectPackageJson.name; } let resolvedPlistFile = command.plistFile; if (resolvedPlistFile) { // If a plist file path is explicitly provided, then we don't // need to attempt to "resolve" it within the well-known locations. if (!fileExists(resolvedPlistFile)) { throw new Error("The specified plist file doesn't exist. Please check that the provided path is correct."); } } else { // Allow the plist prefix to be specified with or without a trailing // separator character, but prescribe the use of a hyphen when omitted, // since this is the most commonly used convetion for plist files. if (command.plistFilePrefix && /.+[^-.]$/.test(command.plistFilePrefix)) { command.plistFilePrefix += '-'; } const iOSDirectory = 'ios'; const plistFileName = `${command.plistFilePrefix || ''}Info.plist`; const knownLocations = [path.join(iOSDirectory, plistFileName)]; if (projectName) { knownLocations.push(path.join(iOSDirectory, projectName, plistFileName)); } resolvedPlistFile = knownLocations.find(fileExists); if (!resolvedPlistFile) { throw new Error(`Unable to find either of the following plist files in order to infer your app's binary version: "${knownLocations.join('", "')}". If your plist has a different name, or is located in a different directory, consider using either the "--plistFile" or "--plistFilePrefix" parameters to help inform the CLI how to find it.`); } } const plistContents = fs.readFileSync(resolvedPlistFile).toString(); let parsedPlist; try { const plist = require('plist'); parsedPlist = plist.parse(plistContents); } catch (e) { throw new Error(`Unable to parse "${resolvedPlistFile}". Please ensure it is a well-formed plist file.`); } if (parsedPlist && parsedPlist.CFBundleShortVersionString) { if (isValidVersion(parsedPlist.CFBundleShortVersionString)) { console.log(`Using the target binary version value "${parsedPlist.CFBundleShortVersionString}" from "${resolvedPlistFile}".\n`); return parsedPlist.CFBundleShortVersionString; } else { if (parsedPlist.CFBundleShortVersionString !== '$(MARKETING_VERSION)') { throw new Error(`The "CFBundleShortVersionString" key in the "${resolvedPlistFile}" file needs to specify a valid semver string, containing both a major and minor version (e.g. 1.3.2, 1.1).`); } return getAppVersionFromXcodeProject(command, projectName); } } else { throw new Error(`The "CFBundleShortVersionString" key doesn't exist within the "${resolvedPlistFile}" file.`); } } function getAppVersionFromXcodeProject(command, projectName) { const pbxprojFileName = 'project.pbxproj'; let resolvedPbxprojFile = command.xcodeProjectFile; if (resolvedPbxprojFile) { // If the xcode project file path is explicitly provided, then we don't // need to attempt to "resolve" it within the well-known locations. if (!resolvedPbxprojFile.endsWith(pbxprojFileName)) { // Specify path to pbxproj file if the provided file path is an Xcode project file. resolvedPbxprojFile = path.join(resolvedPbxprojFile, pbxprojFileName); } if (!fileExists(resolvedPbxprojFile)) { throw new Error("The specified pbx project file doesn't exist. Please check that the provided path is correct."); } } else { const iOSDirectory = 'ios'; const pbxprojKnownLocations = [path.join(iOSDirectory, pbxprojFileName)]; if (projectName) { pbxprojKnownLocations.push(path.join(iOSDirectory, `${projectName}.xcodeproj`, pbxprojFileName)); } resolvedPbxprojFile = pbxprojKnownLocations.find(fileExists); if (!resolvedPbxprojFile) { throw new Error(`Unable to find either of the following pbxproj files in order to infer your app's binary version: "${pbxprojKnownLocations.join('", "')}".`); } } const xcode = require('xcode'); const xcodeProj = xcode.project(resolvedPbxprojFile).parseSync(); const marketingVersion = xcodeProj.getBuildProperty('MARKETING_VERSION', command.buildConfigurationName, command.xcodeTargetName); if (!isValidVersion(marketingVersion)) { throw new Error(`The "MARKETING_VERSION" key in the "${resolvedPbxprojFile}" file needs to specify a valid semver string, containing both a major and minor version (e.g. 1.3.2, 1.1).`); } console.log(`Using the target binary version value "${marketingVersion}" from "${resolvedPbxprojFile}".\n`); return marketingVersion; } function isDirectory(file) { return fs.statSync(file).isDirectory(); } function fileDoesNotExistOrIsDirectory(file) { try { return isDirectory(file); } catch (error) { return true; } } function fileExists(file) { if (!file) { return false; } try { return fs.statSync(file).isFile(); } catch (e) { return false; } } //# sourceMappingURL=getReactNativeProjectAppVersion.js.map