Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ This is the log of notable changes to EAS CLI and related packages.

### 🎉 New features

- [eas-cli] `eas go` now prompts to select an Expo SDK version interactively when `--sdk-version` is not provided. ([#3768](https://github.com/expo/eas-cli/pull/3768) by [@gwdp](https://github.com/gwdp))

### 🐛 Bug fixes

### 🧹 Chores
Expand Down
161 changes: 160 additions & 1 deletion packages/eas-cli/graphql.schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions packages/eas-cli/src/__tests__/commands/go-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { WorkflowRunStatus } from '../../graphql/generated';
import { WorkflowRunMutation } from '../../graphql/mutations/WorkflowRunMutation';
import { WorkflowRunQuery } from '../../graphql/queries/WorkflowRunQuery';
import Log from '../../log';
import { selectAsync } from '../../prompts';
import { getPrivateExpoConfigAsync } from '../../project/expoConfig';
import { uploadAccountScopedFileAsync } from '../../project/uploadAccountScopedFileAsync';
import { uploadAccountScopedProjectSourceAsync } from '../../project/uploadAccountScopedProjectSourceAsync';
Expand Down Expand Up @@ -50,6 +51,7 @@ jest.mock('../../graphql/mutations/WorkflowRunMutation');
jest.mock('../../project/uploadAccountScopedFileAsync');
jest.mock('../../project/uploadAccountScopedProjectSourceAsync');
jest.mock('../../build/utils/url');
jest.mock('../../prompts');

const mockGetConfigFilePaths = jest.mocked(getConfigFilePaths);
const mockGetPrivateExpoConfigAsync = jest.mocked(getPrivateExpoConfigAsync);
Expand Down Expand Up @@ -146,4 +148,50 @@ describe('Go command', () => {

expect(Log.log).not.toHaveBeenCalledWith(expect.stringContaining('Auto-selected'));
});

it('prompts for SDK version when no project config is found', async () => {
mockGetConfigFilePaths.mockReturnValue({ staticConfigPath: null, dynamicConfigPath: null });
jest.mocked(WorkflowRunQuery.expoGoSupportedSdkVersionsAsync).mockResolvedValue([
{ sdkVersion: '54.0.0', isLatest: false, isBeta: false, isDeprecated: false },
{ sdkVersion: '55.0.0', isLatest: true, isBeta: false, isDeprecated: false },
{ sdkVersion: '56.0.0', isLatest: false, isBeta: true, isDeprecated: false },
]);
jest.mocked(selectAsync).mockResolvedValue('55.0.0');

await makeCmd().run();

expect(selectAsync).toHaveBeenCalledWith(
'Select an Expo SDK version',
expect.arrayContaining([
expect.objectContaining({ title: 'SDK 54', value: '54.0.0' }),
expect.objectContaining({ title: 'SDK 55 (latest)', value: '55.0.0' }),
expect.objectContaining({ title: 'SDK 56 (beta)', value: '56.0.0' }),
]),
expect.objectContaining({ initial: '55.0.0' })
);
});

it('skips prompt when all versions are deprecated', async () => {
mockGetConfigFilePaths.mockReturnValue({ staticConfigPath: null, dynamicConfigPath: null });
jest
.mocked(WorkflowRunQuery.expoGoSupportedSdkVersionsAsync)
.mockResolvedValue([
{ sdkVersion: '54.0.0', isLatest: false, isBeta: false, isDeprecated: true },
]);

await makeCmd().run();

expect(selectAsync).not.toHaveBeenCalled();
});

it('falls back gracefully when supportedSdkVersions fetch fails', async () => {
mockGetConfigFilePaths.mockReturnValue({ staticConfigPath: null, dynamicConfigPath: null });
jest
.mocked(WorkflowRunQuery.expoGoSupportedSdkVersionsAsync)
.mockRejectedValue(new Error('network error'));

await makeCmd().run();

expect(selectAsync).not.toHaveBeenCalled();
});
});
38 changes: 36 additions & 2 deletions packages/eas-cli/src/commands/go.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { AppMutation } from '../graphql/mutations/AppMutation';
import { WorkflowRunMutation } from '../graphql/mutations/WorkflowRunMutation';
import { WorkflowRunQuery } from '../graphql/queries/WorkflowRunQuery';
import Log, { learnMore } from '../log';
import { confirmAsync } from '../prompts';
import { confirmAsync, selectAsync } from '../prompts';
import { ora } from '../ora';
import { getPrivateExpoConfigAsync } from '../project/expoConfig';
import { findProjectIdByAccountNameAndSlugNullableAsync } from '../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
Expand Down Expand Up @@ -210,7 +210,10 @@ export default class Go extends EasCommand {
`Current project using SDK ${detectedSdkVersion.split('.')[0]}. Auto-selected same version. To use a different version, pass --sdk-version.`
);
}
const sdkVersion = flags['sdk-version'] ?? detectedSdkVersion;
let sdkVersion = flags['sdk-version'] ?? detectedSdkVersion;
if (!sdkVersion) {
({ sdkVersion } = await this.selectSdkVersionAsync(graphqlClient));
}
const bundleId = flags['bundle-id'] ?? this.generateBundleId(actor);
if (!isBundleIdentifierValid(bundleId)) {
throw new Error(
Expand Down Expand Up @@ -301,6 +304,37 @@ export default class Go extends EasCommand {
}
}

private async selectSdkVersionAsync(
graphqlClient: ExpoGraphqlClient
): Promise<{ sdkVersion: string | undefined }> {
let versions;
try {
versions = await WorkflowRunQuery.expoGoSupportedSdkVersionsAsync(graphqlClient);
} catch {
return { sdkVersion: undefined };
}
const selectable = versions.filter(v => !v.isDeprecated);
if (selectable.length === 0) {
return { sdkVersion: undefined };
}
const defaultVersion = selectable.find(v => v.isLatest) ?? selectable.at(-1);
return {
sdkVersion: await selectAsync(
'Select an Expo SDK version',
selectable.map(v => {
const major = v.sdkVersion.split('.')[0];
const title = v.isLatest
? `SDK ${major} (latest)`
: v.isBeta
? `SDK ${major} (beta)`
: `SDK ${major}`;
return { title, value: v.sdkVersion };
}),
{ initial: defaultVersion?.sdkVersion }
),
};
}

private generateBundleId(actor: Actor): string {
const username = ensureActorHasPrimaryAccount(actor).name;
const sanitizedUsername = username
Expand Down
Loading
Loading