eoas 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,265 @@
1
+ // This file is copied from eas-cli[https://github.com/expo/eas-cli] to ensure consistent user experience across the CLI.
2
+ import { ExpoConfig, getConfig, getConfigFilePaths } from '@expo/config';
3
+ import { Env } from '@expo/eas-build-job';
4
+ import spawnAsync from '@expo/spawn-async';
5
+ import fs from 'fs-extra';
6
+ import Joi from 'joi';
7
+ import jscodeshift, { Collection } from 'jscodeshift';
8
+ import path from 'path';
9
+
10
+ import Log from './log';
11
+ import { isExpoInstalled } from './package';
12
+
13
+ export enum RequestedPlatform {
14
+ Android = 'android',
15
+ Ios = 'ios',
16
+ All = 'all',
17
+ }
18
+
19
+ export type PublicExpoConfig = Omit<
20
+ ExpoConfig,
21
+ '_internal' | 'hooks' | 'ios' | 'android' | 'updates'
22
+ > & {
23
+ ios?: Omit<ExpoConfig['ios'], 'config'>;
24
+ android?: Omit<ExpoConfig['android'], 'config'>;
25
+ updates?: Omit<ExpoConfig['updates'], 'codeSigningCertificate' | 'codeSigningMetadata'>;
26
+ };
27
+
28
+ export interface ExpoConfigOptions {
29
+ env?: Env;
30
+ skipSDKVersionRequirement?: boolean;
31
+ skipPlugins?: boolean;
32
+ }
33
+
34
+ interface ExpoConfigOptionsInternal extends ExpoConfigOptions {
35
+ isPublicConfig?: boolean;
36
+ }
37
+
38
+ let wasExpoConfigWarnPrinted = false;
39
+
40
+ async function getExpoConfigInternalAsync(
41
+ projectDir: string,
42
+ opts: ExpoConfigOptionsInternal = {}
43
+ ): Promise<ExpoConfig> {
44
+ const originalProcessEnv: NodeJS.ProcessEnv = process.env;
45
+ try {
46
+ process.env = {
47
+ ...process.env,
48
+ ...opts.env,
49
+ };
50
+
51
+ let exp: ExpoConfig;
52
+ if (isExpoInstalled(projectDir)) {
53
+ try {
54
+ const { stdout } = await spawnAsync(
55
+ 'npx',
56
+ ['expo', 'config', '--json', ...(opts.isPublicConfig ? ['--type', 'public'] : [])],
57
+
58
+ {
59
+ cwd: projectDir,
60
+ env: {
61
+ ...process.env,
62
+ ...opts.env,
63
+ EXPO_NO_DOTENV: '1',
64
+ },
65
+ }
66
+ );
67
+ exp = JSON.parse(stdout);
68
+ } catch (err: any) {
69
+ if (!wasExpoConfigWarnPrinted) {
70
+ Log.warn(
71
+ `Failed to read the app config from the project using "npx expo config" command: ${err.message}.`
72
+ );
73
+ Log.warn('Falling back to the version of "@expo/config" shipped with the EAS CLI.');
74
+ wasExpoConfigWarnPrinted = true;
75
+ }
76
+ exp = getConfig(projectDir, {
77
+ skipSDKVersionRequirement: true,
78
+ ...(opts.isPublicConfig ? { isPublicConfig: true } : {}),
79
+ ...(opts.skipPlugins ? { skipPlugins: true } : {}),
80
+ }).exp;
81
+ }
82
+ } else {
83
+ exp = getConfig(projectDir, {
84
+ skipSDKVersionRequirement: true,
85
+ ...(opts.isPublicConfig ? { isPublicConfig: true } : {}),
86
+ ...(opts.skipPlugins ? { skipPlugins: true } : {}),
87
+ }).exp;
88
+ }
89
+
90
+ const { error } = MinimalAppConfigSchema.validate(exp, {
91
+ allowUnknown: true,
92
+ abortEarly: true,
93
+ });
94
+ if (error) {
95
+ throw new Error(`Invalid app config.\n${error.message}`);
96
+ }
97
+ return exp;
98
+ } finally {
99
+ process.env = originalProcessEnv;
100
+ }
101
+ }
102
+
103
+ const MinimalAppConfigSchema = Joi.object({
104
+ slug: Joi.string().required(),
105
+ name: Joi.string().required(),
106
+ version: Joi.string(),
107
+ android: Joi.object({
108
+ versionCode: Joi.number().integer(),
109
+ }),
110
+ ios: Joi.object({
111
+ buildNumber: Joi.string(),
112
+ }),
113
+ });
114
+
115
+ export async function getPrivateExpoConfigAsync(
116
+ projectDir: string,
117
+ opts: ExpoConfigOptions = {}
118
+ ): Promise<ExpoConfig> {
119
+ ensureExpoConfigExists(projectDir);
120
+ return await getExpoConfigInternalAsync(projectDir, { ...opts, isPublicConfig: false });
121
+ }
122
+
123
+ export function ensureExpoConfigExists(projectDir: string): void {
124
+ const paths = getConfigFilePaths(projectDir);
125
+ if (!paths?.staticConfigPath && !paths?.dynamicConfigPath) {
126
+ // eslint-disable-next-line node/no-sync
127
+ fs.writeFileSync(path.join(projectDir, 'app.json'), JSON.stringify({ expo: {} }, null, 2));
128
+ }
129
+ }
130
+
131
+ export function isUsingStaticExpoConfig(projectDir: string): boolean {
132
+ const paths = getConfigFilePaths(projectDir);
133
+ return !!(paths.staticConfigPath?.endsWith('app.json') && !paths.dynamicConfigPath);
134
+ }
135
+
136
+ export async function getPublicExpoConfigAsync(
137
+ projectDir: string,
138
+ opts: ExpoConfigOptions = {}
139
+ ): Promise<PublicExpoConfig> {
140
+ ensureExpoConfigExists(projectDir);
141
+
142
+ return await getExpoConfigInternalAsync(projectDir, { ...opts, isPublicConfig: true });
143
+ }
144
+
145
+ export function getExpoConfigUpdateUrl(config: ExpoConfig): string | undefined {
146
+ return config.updates?.url;
147
+ }
148
+
149
+ export async function createOrModifyExpoConfigAsync(
150
+ projectDir: string,
151
+ exp: Partial<ExpoConfig>
152
+ ): Promise<void> {
153
+ try {
154
+ ensureExpoConfigExists(projectDir);
155
+ const configPathJS = path.join(projectDir, 'app.config.js');
156
+ const configPathTS = path.join(projectDir, 'app.config.ts');
157
+
158
+ // eslint-disable-next-line node/no-sync
159
+ const hasJsConfig = fs.existsSync(configPathJS);
160
+
161
+ if (isUsingStaticExpoConfig(projectDir)) {
162
+ Log.withInfo(
163
+ 'You are using a static app config. We will create a dynamic config file for you.'
164
+ );
165
+
166
+ const newConfigContent = `export default ({ config }) => ({
167
+ ...config,
168
+ ...${stringifyWithEnv(exp)}
169
+ });`;
170
+ // eslint-disable-next-line node/no-sync
171
+ fs.writeFileSync(configPathJS, newConfigContent);
172
+ } else if (hasJsConfig) {
173
+ // eslint-disable-next-line node/no-sync
174
+ const existingCode = fs.readFileSync(configPathJS, 'utf8');
175
+ const j = jscodeshift;
176
+ const ast: Collection = j(existingCode);
177
+
178
+ ast.find(j.ArrowFunctionExpression).forEach(path => {
179
+ if (
180
+ path.value.body &&
181
+ j.BlockStatement.check(path.value.body) &&
182
+ path.value.body.body.length > 0
183
+ ) {
184
+ const returnStatement = path.value.body.body.find(node => j.ReturnStatement.check(node));
185
+ if (
186
+ returnStatement &&
187
+ j.ReturnStatement.check(returnStatement) &&
188
+ returnStatement.argument
189
+ ) {
190
+ const configObject = returnStatement.argument;
191
+ if (j.ObjectExpression.check(configObject)) {
192
+ updateObjectExpression(j, configObject, exp);
193
+ }
194
+ }
195
+ }
196
+ });
197
+ const updatedCode = ast.toSource({
198
+ quote: 'auto',
199
+ trailingComma: true,
200
+ reuseWhitespace: true,
201
+ });
202
+
203
+ // eslint-disable-next-line node/no-sync
204
+ fs.writeFileSync(configPathJS, updatedCode);
205
+ } else if (configPathTS) {
206
+ Log.warn('TypeScript support is not yet implemented.');
207
+ throw new Error('TypeScript support is not yet implemented.');
208
+ }
209
+ } catch (e) {
210
+ Log.withInfo('An error occurred while updating the Expo config. Please update it manually.');
211
+ Log.newLine();
212
+ Log.warn('Please modify your app.config.ts file manually by adding the following code:');
213
+ Log.newLine();
214
+ Log.withInfo(`${stringifyWithEnv(exp)}`);
215
+ Log.newLine();
216
+ throw e;
217
+ }
218
+ }
219
+
220
+ function updateObjectExpression(
221
+ j: typeof jscodeshift,
222
+ configObject: ReturnType<typeof j.objectExpression>,
223
+ updates: Record<string, any>
224
+ ): void {
225
+ Object.entries(updates).forEach(([key, value]) => {
226
+ const existingProperty = configObject.properties.find(prop => {
227
+ return (
228
+ prop.type === 'Property' &&
229
+ ((prop.key.type === 'Identifier' && prop.key.name === key) ||
230
+ (prop.key.type === 'StringLiteral' && prop.key.value === key))
231
+ );
232
+ });
233
+
234
+ if (existingProperty) {
235
+ configObject.properties = configObject.properties.filter(prop => prop !== existingProperty);
236
+ }
237
+
238
+ const newProperty = j.objectProperty(j.identifier(key), createValueNode(j, value));
239
+
240
+ configObject.properties.push(newProperty);
241
+ });
242
+ }
243
+
244
+ function createValueNode(j: typeof jscodeshift, value: any): any {
245
+ if (typeof value === 'string' && value.startsWith('process.env.')) {
246
+ return j.memberExpression(
247
+ j.memberExpression(j.identifier('process'), j.identifier('env')),
248
+ j.identifier(value.split('.')[2])
249
+ );
250
+ }
251
+
252
+ if (typeof value === 'object' && value !== null) {
253
+ return j.objectExpression(
254
+ Object.entries(value).map(
255
+ ([key, val]) => j.objectProperty(j.stringLiteral(key), createValueNode(j, val)) // Force stringLiteral pour garder les guillemets
256
+ )
257
+ );
258
+ }
259
+
260
+ return j.literal(value);
261
+ }
262
+
263
+ function stringifyWithEnv(obj: Record<string, any>): string {
264
+ return JSON.stringify(obj, null, 2).replace(/"process\.env\.(\w+)"/g, 'process.env.$1');
265
+ }
package/src/lib/log.ts ADDED
@@ -0,0 +1,122 @@
1
+ // This file is copied from eas-cli[https://github.com/expo/eas-cli] to ensure consistent user experience across the CLI.
2
+ import chalk from 'chalk';
3
+ import figures from 'figures';
4
+ import { boolish } from 'getenv';
5
+ import logSymbols from 'log-symbols';
6
+ import terminalLink from 'terminal-link';
7
+
8
+ type Color = (...text: string[]) => string;
9
+
10
+ export default class Log {
11
+ public static readonly isDebug = boolish('DEBUG', false);
12
+
13
+ public static log(...args: any[]): void {
14
+ Log.consoleLog(...args);
15
+ }
16
+
17
+ public static newLine(): void {
18
+ Log.consoleLog();
19
+ }
20
+
21
+ public static addNewLineIfNone(): void {
22
+ if (!Log.isLastLineNewLine) {
23
+ Log.newLine();
24
+ }
25
+ }
26
+
27
+ public static error(...args: any[]): void {
28
+ Log.consoleLog(...Log.withTextColor(args, chalk.red));
29
+ }
30
+
31
+ public static warn(...args: any[]): void {
32
+ Log.consoleLog(...Log.withTextColor(args, chalk.yellow));
33
+ }
34
+
35
+ public static debug(...args: any[]): void {
36
+ if (Log.isDebug) {
37
+ Log.consoleLog(...args);
38
+ }
39
+ }
40
+
41
+ public static gray(...args: any[]): void {
42
+ Log.consoleLog(...Log.withTextColor(args, chalk.gray));
43
+ }
44
+
45
+ public static warnDeprecatedFlag(flag: string, message: string): void {
46
+ Log.warn(`› ${chalk.bold('--' + flag)} flag is deprecated. ${message}`);
47
+ }
48
+
49
+ public static fail(message: string): void {
50
+ Log.log(`${chalk.red(logSymbols.error)} ${message}`);
51
+ }
52
+
53
+ public static succeed(message: string): void {
54
+ Log.log(`${chalk.green(logSymbols.success)} ${message}`);
55
+ }
56
+
57
+ public static withTick(...args: any[]): void {
58
+ Log.consoleLog(chalk.green(figures.tick), ...args);
59
+ }
60
+
61
+ public static withInfo(...args: any[]): void {
62
+ Log.consoleLog(chalk.green(figures.info), ...args);
63
+ }
64
+
65
+ private static consoleLog(...args: any[]): void {
66
+ Log.updateIsLastLineNewLine(args);
67
+ // eslint-disable-next-line no-console
68
+ console.log(...args);
69
+ }
70
+
71
+ private static withTextColor(args: any[], chalkColor: Color): string[] {
72
+ return args.map(arg => chalkColor(arg));
73
+ }
74
+
75
+ private static isLastLineNewLine = false;
76
+ private static updateIsLastLineNewLine(args: any[]): void {
77
+ if (args.length === 0) {
78
+ Log.isLastLineNewLine = true;
79
+ } else {
80
+ const lastArg = args[args.length - 1];
81
+ if (typeof lastArg === 'string' && (lastArg === '' || lastArg.match(/[\r\n]$/))) {
82
+ Log.isLastLineNewLine = true;
83
+ } else {
84
+ Log.isLastLineNewLine = false;
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Prints a link for given URL, using text if provided, otherwise text is just the URL.
92
+ * Format links as dim (unless disabled) and with an underline.
93
+ *
94
+ * @example https://expo.dev
95
+ */
96
+ export function link(
97
+ url: string,
98
+ { text = url, fallback, dim = true }: { text?: string; dim?: boolean; fallback?: string } = {}
99
+ ): string {
100
+ // Links can be disabled via env variables https://github.com/jamestalmage/supports-hyperlinks/blob/master/index.js
101
+ const output = terminalLink(text, url, {
102
+ fallback: () =>
103
+ fallback ?? (text === url ? chalk.underline(url) : `${text}: ${chalk.underline(url)}`),
104
+ });
105
+ return dim ? chalk.dim(output) : output;
106
+ }
107
+
108
+ /**
109
+ * Provide a consistent "Learn more" link experience.
110
+ * Format links as dim (unless disabled) with an underline.
111
+ *
112
+ * @example Learn more: https://expo.dev
113
+ */
114
+ export function learnMore(
115
+ url: string,
116
+ {
117
+ learnMoreMessage: maybeLearnMoreMessage,
118
+ dim = true,
119
+ }: { learnMoreMessage?: string; dim?: boolean } = {}
120
+ ): string {
121
+ return link(url, { text: maybeLearnMoreMessage ?? 'Learn more', dim });
122
+ }
package/src/lib/ora.ts ADDED
@@ -0,0 +1,113 @@
1
+ // This file is copied from eas-cli[https://github.com/expo/eas-cli] to ensure consistent user experience across the CLI.
2
+ import { boolish } from 'getenv';
3
+ // eslint-disable-next-line
4
+ import oraReal, { Options, Ora } from 'ora';
5
+
6
+ import Log from './log';
7
+
8
+ export { Ora, Options };
9
+
10
+ // eslint-disable-next-line no-console
11
+ const logReal = console.log;
12
+ // eslint-disable-next-line no-console
13
+ const infoReal = console.info;
14
+ // eslint-disable-next-line no-console
15
+ const warnReal = console.warn;
16
+ // eslint-disable-next-line no-console
17
+ const errorReal = console.error;
18
+
19
+ const isCi = boolish('CI', false);
20
+
21
+ /**
22
+ * A custom ora spinner that sends the stream to stdout in CI, or non-TTY, instead of stderr (the default).
23
+ *
24
+ * @param options
25
+ * @returns
26
+ */
27
+ export function ora(options?: Options | string): Ora {
28
+ const inputOptions = typeof options === 'string' ? { text: options } : options ?? {};
29
+ const disabled = Log.isDebug || !process.stdin.isTTY || isCi;
30
+ const spinner = oraReal({
31
+ // Ensure our non-interactive mode emulates CI mode.
32
+ isEnabled: !disabled,
33
+ // In non-interactive mode, send the stream to stdout so it prevents looking like an error.
34
+ stream: disabled ? process.stdout : process.stderr,
35
+ ...inputOptions,
36
+ });
37
+
38
+ const oraStart = spinner.start.bind(spinner);
39
+ const oraStop = spinner.stop.bind(spinner);
40
+ const oraStopAndPersist = spinner.stopAndPersist.bind(spinner);
41
+
42
+ const logWrap = (method: any, args: any[]): void => {
43
+ oraStop();
44
+ method(...args);
45
+ spinner.start();
46
+ };
47
+
48
+ const wrapNativeLogs = (): void => {
49
+ // eslint-disable-next-line no-console
50
+ console.log = (...args: any) => {
51
+ logWrap(logReal, args);
52
+ };
53
+ // eslint-disable-next-line no-console
54
+ console.info = (...args: any) => {
55
+ logWrap(infoReal, args);
56
+ };
57
+ // eslint-disable-next-line no-console
58
+ console.warn = (...args: any) => {
59
+ logWrap(warnReal, args);
60
+ };
61
+ // eslint-disable-next-line no-console
62
+ console.error = (...args: any) => {
63
+ logWrap(errorReal, args);
64
+ };
65
+ };
66
+
67
+ const resetNativeLogs = (): void => {
68
+ // eslint-disable-next-line no-console
69
+ console.log = logReal;
70
+ // eslint-disable-next-line no-console
71
+ console.info = infoReal;
72
+ // eslint-disable-next-line no-console
73
+ console.warn = warnReal;
74
+ // eslint-disable-next-line no-console
75
+ console.error = errorReal;
76
+ };
77
+
78
+ spinner.start = (text): Ora => {
79
+ // wrapNativeLogs wraps calls to console so they always:
80
+ // 1. stop the spinner
81
+ // 2. log the message
82
+ // 3. start the spinner again
83
+ // Every restart of the spinner causes the spinner message to be logged again
84
+ // which makes logs look like
85
+ //
86
+ // - Exporting...
87
+ // [expo-cli] Starting Metro Bundler
88
+ // - Exporting...
89
+ // [expo-cli] Android Bundling complete 3492ms
90
+ // - Exporting...
91
+ //
92
+ // Skipping wrapping native logs removes the repeated interleaved "Exporting..." messages.
93
+ if (!disabled) {
94
+ wrapNativeLogs();
95
+ }
96
+
97
+ return oraStart(text);
98
+ };
99
+
100
+ spinner.stopAndPersist = (options): Ora => {
101
+ const result = oraStopAndPersist(options);
102
+ resetNativeLogs();
103
+ return result;
104
+ };
105
+
106
+ spinner.stop = (): Ora => {
107
+ const result = oraStop();
108
+ resetNativeLogs();
109
+ return result;
110
+ };
111
+
112
+ return spinner;
113
+ }
@@ -0,0 +1,6 @@
1
+ import { getPackageJson } from '@expo/config';
2
+
3
+ export function isExpoInstalled(projectDir: string): boolean {
4
+ const packageJson = getPackageJson(projectDir);
5
+ return !!(packageJson.dependencies && 'expo' in packageJson.dependencies);
6
+ }
@@ -0,0 +1,97 @@
1
+ // This file is copied from eas-cli[https://github.com/expo/eas-cli] to ensure consistent user experience across the CLI.
2
+ import { constants } from 'os';
3
+ import prompts, { Answers, Choice, Options } from 'prompts';
4
+
5
+ export interface ExpoChoice<T> extends Choice {
6
+ value: T;
7
+ }
8
+
9
+ export async function promptAsync<T extends string = string>(
10
+ questions: prompts.PromptObject<T> | prompts.PromptObject<T>[],
11
+ options: Options = {}
12
+ ): Promise<Answers<T>> {
13
+ if (!process.stdin.isTTY) {
14
+ const message = Array.isArray(questions) ? questions[0]?.message : questions.message;
15
+ throw new Error(
16
+ `Input is required, but stdin is not readable. Failed to display prompt: ${message}`
17
+ );
18
+ }
19
+ return await prompts<T>(questions, {
20
+ onCancel() {
21
+ process.exit(constants.signals.SIGINT + 128); // Exit code 130 used when process is interrupted with ctrl+c.
22
+ },
23
+ ...options,
24
+ });
25
+ }
26
+
27
+ export async function confirmAsync(
28
+ question: prompts.PromptObject<any>,
29
+ options?: Options
30
+ ): Promise<boolean> {
31
+ const { value } = await promptAsync(
32
+ {
33
+ initial: true,
34
+ ...question,
35
+ name: 'value',
36
+ type: 'confirm',
37
+ },
38
+ options
39
+ );
40
+ return value;
41
+ }
42
+
43
+ export async function selectAsync<T>(
44
+ message: string,
45
+ choices: ExpoChoice<T>[],
46
+ config?: {
47
+ options?: Options;
48
+ initial?: T;
49
+ warningMessageForDisabledEntries?: string;
50
+ }
51
+ ): Promise<T> {
52
+ const initial = config?.initial ? choices.findIndex(({ value }) => value === config.initial) : 0;
53
+ const { value } = await promptAsync(
54
+ {
55
+ message,
56
+ choices,
57
+ initial,
58
+ name: 'value',
59
+ type: 'select',
60
+ warn: config?.warningMessageForDisabledEntries,
61
+ },
62
+ config?.options ?? {}
63
+ );
64
+ return value ?? null;
65
+ }
66
+
67
+ export async function toggleConfirmAsync(
68
+ questions: prompts.PromptObject<any>,
69
+ options?: Options
70
+ ): Promise<boolean> {
71
+ const { value } = await promptAsync(
72
+ {
73
+ active: 'yes',
74
+ inactive: 'no',
75
+ ...questions,
76
+ name: 'value',
77
+ type: 'toggle',
78
+ },
79
+ options
80
+ );
81
+ return value ?? null;
82
+ }
83
+
84
+ export async function pressAnyKeyToContinueAsync(): Promise<void> {
85
+ process.stdin.setRawMode(true);
86
+ process.stdin.resume();
87
+ process.stdin.setEncoding('utf8');
88
+
89
+ await new Promise<void>(res => {
90
+ process.stdin.on('data', key => {
91
+ if (String(key) === '\u0003') {
92
+ process.exit(constants.signals.SIGINT + 128); // ctrl-c
93
+ }
94
+ res();
95
+ });
96
+ });
97
+ }
@@ -0,0 +1,62 @@
1
+ // This file is copied from eas-cli[https://github.com/expo/eas-cli] to ensure consistent user experience across the CLI.
2
+ import chalk from 'chalk';
3
+
4
+ import Log from './log';
5
+ import { confirmAsync, promptAsync } from './prompts';
6
+ import { Client } from './vcs/vcs';
7
+
8
+ export async function commitPromptAsync(
9
+ vcsClient: Client,
10
+ {
11
+ initialCommitMessage,
12
+ commitAllFiles,
13
+ }: {
14
+ initialCommitMessage?: string;
15
+ commitAllFiles?: boolean;
16
+ } = {}
17
+ ): Promise<void> {
18
+ const { message } = await promptAsync({
19
+ type: 'text',
20
+ name: 'message',
21
+ message: 'Commit message:',
22
+ initial: initialCommitMessage,
23
+ validate: (input: string) => input !== '',
24
+ });
25
+ await vcsClient.commitAsync({
26
+ commitAllFiles,
27
+ commitMessage: message,
28
+ nonInteractive: false,
29
+ });
30
+ }
31
+
32
+ export async function ensureRepoIsCleanAsync(
33
+ vcsClient: Client,
34
+ nonInteractive = false
35
+ ): Promise<void> {
36
+ if (!(await vcsClient.isCommitRequiredAsync())) {
37
+ return;
38
+ }
39
+ Log.addNewLineIfNone();
40
+ Log.warn(`${chalk.bold('Warning!')} Your repository working tree is dirty.`);
41
+ Log.log(
42
+ `This operation needs to be run on a clean working tree. ${chalk.bold(
43
+ 'Commit all your changes before proceeding'
44
+ )}.`
45
+ );
46
+ if (nonInteractive) {
47
+ Log.log('The following files need to be committed:');
48
+ await vcsClient.showChangedFilesAsync();
49
+
50
+ throw new Error('Commit all changes. Aborting...');
51
+ }
52
+ const answer = await confirmAsync({
53
+ message: `Commit changes to git?`,
54
+ type: 'confirm',
55
+ name: 'confirm git commit',
56
+ });
57
+ if (answer) {
58
+ await commitPromptAsync(vcsClient, { commitAllFiles: true });
59
+ } else {
60
+ throw new Error('Commit all changes. Aborting...');
61
+ }
62
+ }