Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[eas-cli] [ENG-10146] Allow submission of builds in progress #2543

Merged
2 changes: 2 additions & 0 deletions packages/eas-cli/src/submit/ArchiveSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@
gitCommitMessage,
channel,
message,
status,
} = build;
const buildDate = new Date(updatedAt);

Expand All @@ -368,6 +369,7 @@
? chalk.bold(message.length > 200 ? `${message.slice(0, 200)}...` : message)
: null,
},
{ name: 'Status', value: status.replace('_', ' ').toLowerCase() }

Check warning on line 372 in packages/eas-cli/src/submit/ArchiveSource.ts

View workflow job for this annotation

GitHub Actions / Test with Node 22

Insert `,`
];

const filteredDescriptionArray: string[] = descriptionItems
Expand Down
60 changes: 59 additions & 1 deletion packages/eas-cli/src/submit/__tests__/ArchiveSource-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import {
AppPlatform,
BuildFragment,
BuildStatus,
SubmissionArchiveSourceType,
UploadSessionType,
} from '../../graphql/generated';
Expand Down Expand Up @@ -41,6 +42,17 @@
appVersion: '1.2.3',
platform: AppPlatform.Android,
updatedAt: Date.now(),
status: BuildStatus.Finished,
};
const MOCK_IN_PROGRESS_BUILD_FRAGMENT: Partial<BuildFragment> = {
id: uuidv4(),
artifacts: {
buildUrl: ARCHIVE_SOURCE.url,
},
appVersion: '1.2.3',
platform: AppPlatform.Android,
updatedAt: Date.now(),
status: BuildStatus.InProgress,
};

const SOURCE_STUB_INPUT = {
Expand Down Expand Up @@ -246,7 +258,7 @@
expect(archive.sourceType).toBe(ArchiveSourceType.url);
});

it('handles build-list-select source', async () => {
it('handles build-list-select source for finished builds', async () => {
const projectId = uuidv4();
jest
.mocked(getRecentBuildsForSubmissionAsync)
Expand All @@ -269,6 +281,52 @@
expect(archive.sourceType).toBe(ArchiveSourceType.build);
});

it('handles build-list-select source for in-progress builds', async () => {
const projectId = uuidv4();
jest
.mocked(getRecentBuildsForSubmissionAsync)
.mockResolvedValueOnce([MOCK_IN_PROGRESS_BUILD_FRAGMENT as BuildFragment]);
jest.mocked(promptAsync).mockResolvedValueOnce({ selectedBuild: MOCK_IN_PROGRESS_BUILD_FRAGMENT });

Check warning on line 289 in packages/eas-cli/src/submit/__tests__/ArchiveSource-test.ts

View workflow job for this annotation

GitHub Actions / Test with Node 22

Replace `.mocked(promptAsync)` with `⏎······.mocked(promptAsync)⏎······`

const archive = await getArchiveAsync(
{ ...SOURCE_STUB_INPUT, graphqlClient, projectId },
{
sourceType: ArchiveSourceType.buildList,
}
);

expect(getRecentBuildsForSubmissionAsync).toBeCalledWith(
graphqlClient,
toAppPlatform(SOURCE_STUB_INPUT.platform),
projectId,
{ limit: BUILD_LIST_ITEM_COUNT }
);
expect(archive.sourceType).toBe(ArchiveSourceType.build);
});

it('handles build-list-select source for both finished and in-progress builds', async () => {
const projectId = uuidv4();
jest
.mocked(getRecentBuildsForSubmissionAsync)
.mockResolvedValueOnce([MOCK_IN_PROGRESS_BUILD_FRAGMENT as BuildFragment, MOCK_BUILD_FRAGMENT as BuildFragment]);

Check warning on line 311 in packages/eas-cli/src/submit/__tests__/ArchiveSource-test.ts

View workflow job for this annotation

GitHub Actions / Test with Node 22

Replace `MOCK_IN_PROGRESS_BUILD_FRAGMENT·as·BuildFragment,·MOCK_BUILD_FRAGMENT·as·BuildFragment` with `⏎········MOCK_IN_PROGRESS_BUILD_FRAGMENT·as·BuildFragment,⏎········MOCK_BUILD_FRAGMENT·as·BuildFragment,⏎······`
jest.mocked(promptAsync).mockResolvedValueOnce({ selectedBuild: MOCK_BUILD_FRAGMENT });

const archive = await getArchiveAsync(
{ ...SOURCE_STUB_INPUT, graphqlClient, projectId },
{
sourceType: ArchiveSourceType.buildList,
}
);

expect(getRecentBuildsForSubmissionAsync).toBeCalledWith(
graphqlClient,
toAppPlatform(SOURCE_STUB_INPUT.platform),
projectId,
{ limit: BUILD_LIST_ITEM_COUNT }
);
expect(archive.sourceType).toBe(ArchiveSourceType.build);
});

it('prompts again if all builds have expired', async () => {
jest.mocked(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([
{
Expand Down
110 changes: 110 additions & 0 deletions packages/eas-cli/src/submit/utils/__tests__/builds-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { getRecentBuildsForSubmissionAsync } from '../builds';

Check warning on line 1 in packages/eas-cli/src/submit/utils/__tests__/builds-test.ts

View workflow job for this annotation

GitHub Actions / Test with Node 22

There should be at least one empty line between import groups

Check warning on line 1 in packages/eas-cli/src/submit/utils/__tests__/builds-test.ts

View workflow job for this annotation

GitHub Actions / Test with Node 22

`../builds` import should occur after import of `../../../commandUtils/context/contextUtils/createGraphqlClient`
import { v4 as uuidv4 } from 'uuid';

Check warning on line 2 in packages/eas-cli/src/submit/utils/__tests__/builds-test.ts

View workflow job for this annotation

GitHub Actions / Test with Node 22

There should be at least one empty line between import groups
import { AppPlatform, BuildFragment, BuildStatus, SubmissionArchiveSourceType } from '../../../graphql/generated';

Check warning on line 3 in packages/eas-cli/src/submit/utils/__tests__/builds-test.ts

View workflow job for this annotation

GitHub Actions / Test with Node 22

`../../../graphql/generated` import should occur after import of `../../../commandUtils/context/contextUtils/createGraphqlClient`

Check warning on line 3 in packages/eas-cli/src/submit/utils/__tests__/builds-test.ts

View workflow job for this annotation

GitHub Actions / Test with Node 22

Replace `·AppPlatform,·BuildFragment,·BuildStatus,·SubmissionArchiveSourceType·` with `⏎··AppPlatform,⏎··BuildFragment,⏎··BuildStatus,⏎··SubmissionArchiveSourceType,⏎`
import { BuildQuery } from "../../../graphql/queries/BuildQuery";

Check warning on line 4 in packages/eas-cli/src/submit/utils/__tests__/builds-test.ts

View workflow job for this annotation

GitHub Actions / Test with Node 22

`../../../graphql/queries/BuildQuery` import should occur after import of `../../../commandUtils/context/contextUtils/createGraphqlClient`

Check warning on line 4 in packages/eas-cli/src/submit/utils/__tests__/builds-test.ts

View workflow job for this annotation

GitHub Actions / Test with Node 22

Replace `"../../../graphql/queries/BuildQuery"` with `'../../../graphql/queries/BuildQuery'`
import { ExpoGraphqlClient } from "../../../commandUtils/context/contextUtils/createGraphqlClient";

jest.mock('../../../graphql/queries/BuildQuery', () => ({
BuildQuery: {
viewBuildsOnAppAsync: jest.fn(),
},
}));

const ARCHIVE_SOURCE = {
type: SubmissionArchiveSourceType.Url,
url: 'https://url.to/archive.tar.gz',
};

const MOCK_BUILD_FRAGMENTS: Partial<BuildFragment>[] = Array(5).map(() => ({
id: uuidv4(),
artifacts: {
buildUrl: ARCHIVE_SOURCE.url,
},
appVersion: '1.2.3',
platform: AppPlatform.Android,
updatedAt: Date.now(),
status: BuildStatus.Finished,
}));
const MOCK_IN_PROGRESS_BUILD_FRAGMENTS: Partial<BuildFragment>[] = Array(5).map(() => ({
id: uuidv4(),
artifacts: {
buildUrl: ARCHIVE_SOURCE.url,
},
appVersion: '1.2.3',
platform: AppPlatform.Android,
updatedAt: Date.now(),
status: BuildStatus.InProgress,
}));

describe(getRecentBuildsForSubmissionAsync, () => {
let graphqlClient: ExpoGraphqlClient;

beforeEach(() => {
graphqlClient = {} as any as ExpoGraphqlClient;
});

it('returns finished builds if there are no in-progress builds', async () => {
const appId = uuidv4();
const limit = 2;
jest.mocked(BuildQuery.viewBuildsOnAppAsync)
.mockResolvedValueOnce([] as BuildFragment[])
.mockResolvedValueOnce(MOCK_BUILD_FRAGMENTS.slice(0, limit) as BuildFragment[]);

const result = await getRecentBuildsForSubmissionAsync(
graphqlClient,
AppPlatform.Android,
appId,
{ limit },
)

expect(result).toMatchObject(MOCK_BUILD_FRAGMENTS.slice(0, limit));
});
it('returns in-progress builds if there are no finished builds', async () => {
const appId = uuidv4();
const limit = 2;
jest.mocked(BuildQuery.viewBuildsOnAppAsync)
.mockResolvedValueOnce(MOCK_IN_PROGRESS_BUILD_FRAGMENTS.slice(0, limit) as BuildFragment[])
.mockResolvedValueOnce([] as BuildFragment[]);

const result = await getRecentBuildsForSubmissionAsync(
graphqlClient,
AppPlatform.Android,
appId,
{ limit },
)

expect(result).toMatchObject(MOCK_IN_PROGRESS_BUILD_FRAGMENTS.slice(0, limit));
});
it('returns in-progress builds if there are finished builds, but in-progress ones fill the limit', async () => {
const appId = uuidv4();
const limit = 2;
jest.mocked(BuildQuery.viewBuildsOnAppAsync)
.mockResolvedValueOnce(MOCK_IN_PROGRESS_BUILD_FRAGMENTS.slice(0, limit) as BuildFragment[])
.mockResolvedValueOnce(MOCK_BUILD_FRAGMENTS.slice(0, limit) as BuildFragment[]);

const result = await getRecentBuildsForSubmissionAsync(
graphqlClient,
AppPlatform.Android,
appId,
{ limit },
)

expect(result).toMatchObject(MOCK_IN_PROGRESS_BUILD_FRAGMENTS.slice(0, limit));
});
it('returns in-progress and finished builds if in-progress ones don\'t fill the limit', async () => {
const appId = uuidv4();
const limit = 4;
jest.mocked(BuildQuery.viewBuildsOnAppAsync)
.mockResolvedValueOnce(MOCK_IN_PROGRESS_BUILD_FRAGMENTS.slice(0, 2) as BuildFragment[])
.mockResolvedValueOnce(MOCK_BUILD_FRAGMENTS.slice(0, 2) as BuildFragment[]);

const result = await getRecentBuildsForSubmissionAsync(
graphqlClient,
AppPlatform.Android,
appId,
{ limit },
)

expect(result).toMatchObject(MOCK_IN_PROGRESS_BUILD_FRAGMENTS.slice(0, 2).concat(MOCK_BUILD_FRAGMENTS.slice(0, 2)));
});
});
19 changes: 17 additions & 2 deletions packages/eas-cli/src/submit/utils/builds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,29 @@ export async function getRecentBuildsForSubmissionAsync(
appId: string,
{ limit = 1 }: { limit?: number } = {}
): Promise<BuildFragment[]> {
return await BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
const builds = await BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
appId,
limit,
offset: 0,
filter: {
platform,
distribution: DistributionType.Store,
status: BuildStatus.Finished,
status: BuildStatus.InProgress,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about NEW and IN_QUEUE builds? I believe we should handle submitting these as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why not, I'll add them

},
});
const remainingLimit = limit - builds.length;
if (remainingLimit > 0) {
const finishedBuilds = await BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
appId,
limit: remainingLimit,
offset: 0,
filter: {
platform,
distribution: DistributionType.Store,
status: BuildStatus.Finished,
},
});
builds.push(...finishedBuilds);
}
radoslawkrzemien marked this conversation as resolved.
Show resolved Hide resolved
return builds;
}
Loading