diff --git a/README.md b/README.md index 3accc74b3..d737cb1f2 100644 --- a/README.md +++ b/README.md @@ -330,7 +330,7 @@ Create a new release history for a specific binary app version. **Example:** - Create a new release history for the binary app version `1.0.0`. -``` +```bash npx code-push create-history --binary-version 1.0.0 --platform ios --identifier staging ``` @@ -342,7 +342,7 @@ Display the release history for a specific binary app version. **Example:** - Show the release history for the binary app version `1.0.0`. -``` +```bash npx code-push show-history --binary-version 1.0.0 --platform ios --identifier staging ``` @@ -354,11 +354,15 @@ Release a CodePush update for a specific binary app version. **Example:** - Release a CodePush update `1.0.1` targeting the binary app version `1.0.0`. -``` +```bash npx code-push release --binary-version 1.0.0 --app-version 1.0.1 \ --platform ios --identifier staging --entry-file index.js \ --mandatory true + +# Expo project +npx code-push release --framework expo --binary-version 1.0.0 --app-version 1.0.1 --platform ios ``` +- `--framework`(`-f`) : Framework type (expo) - `--binary-version`: The version of the binary app that the CodePush update is targeting. - `--app-version`: The version of the CodePush update itself. @@ -375,7 +379,7 @@ Update the release history for a specific CodePush update. **Example:** - Rollback the CodePush update `1.0.1` (targeting the binary app version `1.0.0`). -``` +```bash npx code-push update-history --binary-version 1.0.0 --app-version 1.0.1 \ --platform ios --identifier staging \ --enable false @@ -386,10 +390,17 @@ npx code-push update-history --binary-version 1.0.0 --app-version 1.0.1 \ Create a CodePush bundle file. **Example:** -``` +```bash npx code-push bundle --platform android --entry-file index.js + +# Expo project +npx code-push bundle --framework expo --platform android --entry-file index.js ``` +- `--framework`(`-f`): Framework type (expo) By default, the bundle file is created in the `/build/bundleOutput` directory. +> [!NOTE] +> For Expo projects, the CLI uses `expo export:embed` command for bundling instead of React Native's bundle command. + (The file name represents a hash value of the bundle content.) diff --git a/cli/commands/bundleCommand/bundleCodePush.js b/cli/commands/bundleCommand/bundleCodePush.js index e19e9c266..727a03cd3 100644 --- a/cli/commands/bundleCommand/bundleCodePush.js +++ b/cli/commands/bundleCommand/bundleCodePush.js @@ -1,12 +1,14 @@ const fs = require('fs'); const { prepareToBundleJS } = require('../../functions/prepareToBundleJS'); const { runReactNativeBundleCommand } = require('../../functions/runReactNativeBundleCommand'); +const { runExpoBundleCommand } = require('../../functions/runExpoBundleCommand'); const { getReactTempDir } = require('../../functions/getReactTempDir'); const { runHermesEmitBinaryCommand } = require('../../functions/runHermesEmitBinaryCommand'); const { makeCodePushBundle } = require('../../functions/makeCodePushBundle'); const { ROOT_OUTPUT_DIR, ENTRY_FILE } = require('../../constant'); /** + * @param framework {string|undefined} 'expo' * @param platform {string} 'ios' | 'android' * @param outputRootPath {string} * @param entryFile {string} @@ -15,6 +17,7 @@ const { ROOT_OUTPUT_DIR, ENTRY_FILE } = require('../../constant'); * @return {Promise} CodePush bundle file name (equals to packageHash) */ async function bundleCodePush( + framework, platform = 'ios', outputRootPath = ROOT_OUTPUT_DIR, entryFile = ENTRY_FILE, @@ -32,13 +35,24 @@ async function bundleCodePush( prepareToBundleJS({ deleteDirs: [outputRootPath, getReactTempDir()], makeDir: OUTPUT_CONTENT_PATH }); - runReactNativeBundleCommand( - _jsBundleName, - OUTPUT_CONTENT_PATH, - platform, - SOURCEMAP_OUTPUT, - entryFile, - ); + if (framework === 'expo') { + runExpoBundleCommand( + _jsBundleName, + OUTPUT_CONTENT_PATH, + platform, + SOURCEMAP_OUTPUT, + entryFile, + ); + } else { + runReactNativeBundleCommand( + _jsBundleName, + OUTPUT_CONTENT_PATH, + platform, + SOURCEMAP_OUTPUT, + entryFile, + ); + } + console.log('log: JS bundling complete'); await runHermesEmitBinaryCommand( diff --git a/cli/commands/bundleCommand/index.js b/cli/commands/bundleCommand/index.js index 3b819d94e..7cf2d82da 100644 --- a/cli/commands/bundleCommand/index.js +++ b/cli/commands/bundleCommand/index.js @@ -4,6 +4,7 @@ const { OUTPUT_BUNDLE_DIR, ROOT_OUTPUT_DIR, ENTRY_FILE } = require('../../consta program.command('bundle') .description('Creates a CodePush bundle file (assumes Hermes is enabled).') + .addOption(new Option('-f, --framework ', 'framework type (expo)').choices(['expo'])) .addOption(new Option('-p, --platform ', 'platform').choices(['ios', 'android']).default('ios')) .option('-o, --output-path ', 'path to output root directory', ROOT_OUTPUT_DIR) .option('-e, --entry-file ', 'path to JS/TS entry file', ENTRY_FILE) @@ -11,6 +12,7 @@ program.command('bundle') .option('--output-bundle-dir ', 'name of directory containing the bundle file created by the "bundle" command', OUTPUT_BUNDLE_DIR) /** * @param {Object} options + * @param {string} options.framework * @param {string} options.platform * @param {string} options.outputPath * @param {string} options.entryFile @@ -20,6 +22,7 @@ program.command('bundle') */ .action((options) => { bundleCodePush( + options.framework, options.platform, options.outputPath, options.entryFile, diff --git a/cli/commands/releaseCommand/index.js b/cli/commands/releaseCommand/index.js index 533f84d90..6863a885f 100644 --- a/cli/commands/releaseCommand/index.js +++ b/cli/commands/releaseCommand/index.js @@ -7,6 +7,7 @@ program.command('release') .description('Deploys a new CodePush update for a target binary app.\nAfter creating the CodePush bundle, it uploads the file and updates the ReleaseHistory information.\n`bundleUploader`, `getReleaseHistory`, and `setReleaseHistory` functions should be implemented in the config file.') .requiredOption('-b, --binary-version ', '(Required) The target binary version') .requiredOption('-v, --app-version ', '(Required) The app version to be released. It must be greater than the binary version.') + .addOption(new Option('-f, --framework ', 'framework type (expo)').choices(['expo'])) .addOption(new Option('-p, --platform ', 'platform').choices(['ios', 'android']).default('ios')) .option('-i, --identifier ', 'reserved characters to distinguish the release.') .option('-c, --config ', 'set config file name (JS/TS)', CONFIG_FILE_NAME) @@ -22,6 +23,7 @@ program.command('release') * @param {Object} options * @param {string} options.binaryVersion * @param {string} options.appVersion + * @param {string} options.framework * @param {string} options.platform * @param {string} options.identifier * @param {string} options.config @@ -44,6 +46,7 @@ program.command('release') config.setReleaseHistory, options.binaryVersion, options.appVersion, + options.framework, options.platform, options.identifier, options.outputPath, diff --git a/cli/commands/releaseCommand/release.js b/cli/commands/releaseCommand/release.js index 7cfd5a9ba..2aa1745fb 100644 --- a/cli/commands/releaseCommand/release.js +++ b/cli/commands/releaseCommand/release.js @@ -25,6 +25,7 @@ const { addToReleaseHistory } = require("./addToReleaseHistory"); * ): Promise} * @param binaryVersion {string} * @param appVersion {string} + * @param framework {string|undefined} 'expo' * @param platform {"ios" | "android"} * @param identifier {string?} * @param outputPath {string} @@ -43,6 +44,7 @@ async function release( setReleaseHistory, binaryVersion, appVersion, + framework, platform, identifier, outputPath, @@ -56,7 +58,7 @@ async function release( ) { const bundleFileName = skipBundle ? readBundleFileNameFrom(bundleDirectory) - : await bundleCodePush(platform, outputPath, entryFile, jsBundleName, bundleDirectory); + : await bundleCodePush(framework, platform, outputPath, entryFile, jsBundleName, bundleDirectory); const bundleFilePath = `${bundleDirectory}/${bundleFileName}`; const downloadUrl = await (async () => { diff --git a/cli/functions/runExpoBundleCommand.js b/cli/functions/runExpoBundleCommand.js new file mode 100644 index 000000000..7ed3ab972 --- /dev/null +++ b/cli/functions/runExpoBundleCommand.js @@ -0,0 +1,53 @@ +const path = require('path'); +const shell = require('shelljs'); + +/** + * Run `expo bundle` CLI command + * + * @param bundleName {string} JS bundle file name + * @param entryFile {string} App code entry file name (default: index.ts) + * @param outputPath {string} Path to output JS bundle file and assets + * @param platform {string} Platform (ios | android) + * @param sourcemapOutput {string} Path to output sourcemap file (Warning: if sourcemapOutput points to the outputPath, the sourcemap will be included in the CodePush bundle and increase the deployment size) + * @return {void} + */ +function runExpoBundleCommand( + bundleName, + outputPath, + platform, + sourcemapOutput, + entryFile, +) { + /** + * @return {string} + */ + function getCliPath() { + return path.join('node_modules', '.bin', 'expo'); + } + + /** + * @type {string[]} + */ + const expoBundleArgs = [ + 'export:embed', + '--assets-dest', + outputPath, + '--bundle-output', + path.join(outputPath, bundleName), + '--dev', + 'false', + '--entry-file', + entryFile, + '--platform', + platform, + '--sourcemap-output', + sourcemapOutput, + '--reset-cache', + ]; + + console.log('Running "expo export:embed" command:\n'); + + shell.exec(`${getCliPath()} ${expoBundleArgs.join(' ')}`); +} + +module.exports = { runExpoBundleCommand };