xref: /expo/tools/src/commands/AddSDKVersion.ts (revision e7a5287c)
1import { Command } from '@expo/commander';
2import chalk from 'chalk';
3import inquirer from 'inquirer';
4import semver from 'semver';
5
6import { Platform, getNextSDKVersionAsync } from '../ProjectVersions';
7import * as AndroidVersioning from '../versioning/android';
8import * as IosVersioning from '../versioning/ios';
9
10type ActionOptions = {
11  platform: Platform;
12  sdkVersion?: string;
13  filenames?: string;
14  vendored: string[];
15  reinstall?: boolean;
16  preventReinstall?: boolean;
17  package?: string;
18};
19
20async function getNextOrAskForSDKVersionAsync(platform: Platform): Promise<string | undefined> {
21  const defaultSdkVersion = await getNextSDKVersionAsync(platform);
22
23  if (defaultSdkVersion && process.env.CI) {
24    console.log(
25      `${chalk.red('`--sdkVersion`')} not provided - defaulting to ${chalk.cyan(defaultSdkVersion)}`
26    );
27    return defaultSdkVersion;
28  }
29
30  const { sdkVersion } = await inquirer.prompt<{ sdkVersion: string }>([
31    {
32      type: 'input',
33      name: 'sdkVersion',
34      message: 'What is the SDK version that you want to add?',
35      default: defaultSdkVersion,
36      validate(value) {
37        if (!semver.valid(value)) {
38          return `Invalid version: ${chalk.cyan(value)}`;
39        }
40        return true;
41      },
42    },
43  ]);
44  return sdkVersion;
45}
46
47async function action(options: ActionOptions) {
48  if (!options.platform) {
49    throw new Error('Run with `--platform <ios | android>`.');
50  }
51
52  const sdkVersion = options.sdkVersion || (await getNextOrAskForSDKVersionAsync(options.platform));
53
54  if (!sdkVersion) {
55    throw new Error('Next SDK version not found. Try to run with `--sdkVersion <SDK version>`.');
56  }
57  const sdkNumber = semver.major(sdkVersion);
58
59  switch (options.platform) {
60    case 'ios':
61      if (options.vendored.length > 0) {
62        await IosVersioning.versionVendoredModulesAsync(sdkNumber, options.vendored);
63      } else if (options.filenames) {
64        await IosVersioning.versionReactNativeIOSFilesAsync(options.filenames, sdkVersion);
65      } else if (options.package) {
66        await IosVersioning.regenerateVersionedPackageAsync(sdkVersion, options.package);
67      } else {
68        await IosVersioning.versionVendoredModulesAsync(sdkNumber, null);
69        await IosVersioning.addVersionAsync(sdkVersion);
70      }
71      await IosVersioning.reinstallPodsAsync(options.reinstall, options.preventReinstall);
72      return;
73    case 'android':
74      return AndroidVersioning.addVersionAsync(sdkVersion);
75    default:
76      throw new Error(`Platform '${options.platform}' is not supported.`);
77  }
78}
79
80export default (program: Command) => {
81  program
82    .command('add-sdk-version')
83    .alias('add-sdk')
84    .description('Versions code for the new SDK version.')
85    .usage(
86      `
87
88To version code for the new SDK on iOS, run:
89${chalk.gray('>')} ${chalk.italic.cyan('et add-sdk-version --platform ios')}
90
91To backport changes made in unversioned code into already versioned SDK, run:
92${chalk.gray('>')} ${chalk.italic.cyan(
93        'et add-sdk-version --platform ios --sdkVersion XX.0.0 --filenames */some/glob/expression/**'
94      )}`
95    )
96    .option(
97      '-p, --platform <string>',
98      `Specifies a platform for which the SDK code should be generated. Supported platforms: ${chalk.cyan(
99        'ios'
100      )}.`
101    )
102    .option(
103      '-s, --sdkVersion [string]',
104      'SDK version to add. Defaults to the newest SDK version increased by a major update.'
105    )
106    .option(
107      '-f, --filenames [string]',
108      'Glob pattern of file paths to version. Useful when you want to backport unversioned code into already versioned SDK. Optional. When provided, option `--sdkVersion` is required.'
109    )
110    .option(
111      '-v, --vendored <string>',
112      'Name of the vendored module to (re)version. iOS only.',
113      (value, previous) => (previous ?? []).concat(value),
114      []
115    )
116    .option(
117      '-r, --reinstall',
118      'Whether to force reinstalling pods after generating a new version. iOS only.'
119    )
120    .option(
121      '--prevent-reinstall',
122      'Whether to force not reinstalling pods after generating a new version. iOS only.'
123    )
124    .option(
125      '-x, --package [string]',
126      'Only generate a specific package. When provided, option `--sdkVersion` is required.'
127    )
128    .asyncAction(action);
129};
130