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-11026] Add build:delete command #2178

Merged

Conversation

radoslawkrzemien
Copy link
Contributor

@radoslawkrzemien radoslawkrzemien commented Jan 12, 2024

Why

https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

How

Added a new build:delete command. It shares some similarities with build:cancel command, can be used with build id provided or prompt the user for id if not (not available in non-interactive mode). The difference is that build:cancel has restrictions on build status, and build:delete does not. The shared code has been extracted to a helper file under src/commandUtils. Additionally, 2 filter flags has been added for the case when the user does not provide the ID of the build to be deleted and a list of builds is printed, it can be filtered by platform and/or profile. The same has been applied to build:cancel as well

Screens

With build id

1
Screenshot 2024-01-12 at 15 30 48
2
Screenshot 2024-01-12 at 15 31 30

With prompt

1
Screenshot 2024-01-12 at 15 32 05
2
Screenshot 2024-01-12 at 15 32 16
3
Screenshot 2024-01-12 at 15 32 44

Non-interactive with id

1
Screenshot 2024-01-12 at 15 45 44

Non-interactive with prompt (not allowed)

1
Screenshot 2024-01-12 at 15 47 04

New filter flags

No flags

Screenshot 2024-01-16 at 13 49 46

build:delete with platform/p flag

1
Screenshot 2024-01-16 at 13 50 08
2
Screenshot 2024-01-16 at 13 50 19

build:delete with profile/e flag

1
Screenshot 2024-01-16 at 13 50 50
2
Screenshot 2024-01-16 at 13 51 38

Same for build:cancel command

1
Screenshot 2024-01-16 at 13 52 03
2
Screenshot 2024-01-16 at 13 52 14
3
Screenshot 2024-01-16 at 13 52 24

Using both build ID and filter flags is not allowed

1
Screenshot 2024-01-16 at 15 25 39
2
Screenshot 2024-01-16 at 15 27 06

Test Plan

Added tests similar to those for build:cancel
Tested manually

Deployment Plan

No dependency on other services, just release a new version of CLI when needed

Added a new command build:delete. Shares some code with build:cancel, so I have moved the redundancies to a utils file at src/commandUtils/builds

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command
Added a test file based on existing tests for build:cancel command. Adding more tests

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command
Copy link

linear bot commented Jan 12, 2024

Copy link

github-actions bot commented Jan 12, 2024

Size Change: +5.42 kB (0%)

Total Size: 55.5 MB

Filename Size Change
./packages/eas-cli/dist/eas-linux-x64.tar.gz 55.5 MB +5.42 kB (0%)

compressed-size-action

Copy link

codecov bot commented Jan 12, 2024

Codecov Report

Attention: 50 lines in your changes are missing coverage. Please review.

Comparison is base (189d163) 54.23% compared to head (f2e1d05) 54.24%.
Report is 1 commits behind head on main.

Files Patch % Lines
packages/eas-cli/src/commands/build/delete.ts 50.88% 28 Missing ⚠️
packages/eas-cli/src/commandUtils/builds.ts 63.05% 17 Missing ⚠️
packages/eas-cli/src/commands/build/cancel.ts 50.00% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2178      +/-   ##
==========================================
+ Coverage   54.23%   54.24%   +0.02%     
==========================================
  Files         509      511       +2     
  Lines       18660    18756      +96     
  Branches     3737     3758      +21     
==========================================
+ Hits        10118    10173      +55     
- Misses       8521     8562      +41     
  Partials       21       21              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@radoslawkrzemien radoslawkrzemien marked this pull request as ready for review January 12, 2024 14:41
@radoslawkrzemien radoslawkrzemien requested review from brentvatne and sjchmiela and removed request for brentvatne January 12, 2024 14:42
Copy link
Contributor

@sjchmiela sjchmiela left a comment

Choose a reason for hiding this comment

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

Cool!

limit: 10,
filter: { status },
});
builds.push(...buildsForStatus);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we order them by createdAt?

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 think they are sorted like that implicitly, but need to double check

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They were, but it fell apart when combining results for different statuses, as this would group the builds by status too. Added explicit sorting before returning

Comment on lines 20 to 22
graphqlClient: ExpoGraphqlClient,
projectId: string,
statuses?: BuildStatus[]
Copy link
Contributor

Choose a reason for hiding this comment

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

I would use a { graphqlClient, projectId, statuses } argument. This way, if we add more filters (like platform: Platform[]) it's much easier and readable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

throw error;
}
if (builds.length === 0) {
Log.warn(`There aren't any builds for the project ${projectDisplayName}.`);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Log.warn(`There aren't any builds for the project ${projectDisplayName}.`);
Log.warn(`There are no builds for the project ${projectDisplayName}.`);

Or maybe something like "we couldn't find any builds"? I think it feels more human?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The message was copied from an existing command, but I can update both 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

let buildId: string | null = buildIdFromArg;
if (!buildId) {
if (nonInteractive) {
throw new Error('BUILD_ID must not be empty in non-interactive mode');
Copy link
Contributor

Choose a reason for hiding this comment

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

Do users know the argument is called BUILD_ID? If not, maybe we should changes this to say "The build ID argument"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

Copy link
Member

@szdziedzic szdziedzic left a comment

Choose a reason for hiding this comment

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

It looks really good 🙌

Comment on lines 33 to 38
const buildsForStatus = await BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
appId: projectId,
offset: 0,
limit: 10,
filter: { status },
});
Copy link
Member

Choose a reason for hiding this comment

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

Can we just create promises here and await them all using Promise.all to make it faster, instead of executing it sequentially?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's so obvious, and yet I always forget about that 😬

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

export default class BuildDelete extends EasCommand {
static override description = 'delete a build';
static override args = [{ name: 'BUILD_ID' }];
static override flags = {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can allow users to filter by platform and profile 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.

WDYM? If they provide platform to delete all builds from a platform? That seems rather... prone to human errors

Copy link
Member

Choose a reason for hiding this comment

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

Oh, I meant, when displaying the list of builds. So the people can decide what filters should be applied to this list.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added the filter flags for platform and profile

Explicitly sorts fetched builds by descending order of `created_at`. In case multiple results were combined (for different statuses) they would by already sorted, but grouped by status also

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command
Changed `fetchBuildsAsync` arguments into single object argument for readability

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command
Changed the message displayed when no builds are found for deletion/cancellation to be more human-like

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command
Changed the message displayed when no build ID is provided for non-interactive mode, to not include the argument name but rather its description

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command
Changed for loop with async calls into await promise.all()

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command
When not providing an ID of the build to delete, the user can now filter the listed builds by platform and build profile using new flags. The same change has also been applied to `build:cancel` command

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command
@radoslawkrzemien
Copy link
Contributor Author

/changelog-entry new-feature Added build:delete command

@radoslawkrzemien
Copy link
Contributor Author

/changelog-entry new-feature Add filter flags for platform and profile to build:cancel and build:delete commands

Copy link
Member

@szdziedzic szdziedzic left a comment

Choose a reason for hiding this comment

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

Nice! I have a few last suggestions 🙌

let builds: BuildFragment[];
const queryFilters: InputMaybe<BuildFilter> = {};
if (filters?.platform && filters?.platform !== 'all') {
queryFilters['platform'] = filters?.platform.toUpperCase() as AppPlatform;
Copy link
Member

Choose a reason for hiding this comment

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

What do you think about using some mapping from one type to the other type here? By doing this TS will warn us if something type-related changes and it will be more explicit (for example we will see if due to some changes the toUpperCase isn't enough)

Copy link
Contributor Author

@radoslawkrzemien radoslawkrzemien Jan 16, 2024

Choose a reason for hiding this comment

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

Added a mapping function the mapping

});
} else {
builds = (
await Promise.all(
Copy link
Member

Choose a reason for hiding this comment

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

Thanks!

Comment on lines 99 to 109
platform: Flags.string({
char: 'p',
description: 'Platform for which to list builds when ID not provided',
options: ['android', 'ios', 'all'],
}),
profile: Flags.string({
char: 'e',
description:
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
helpValue: 'PROFILE_NAME',
}),
Copy link
Member

Choose a reason for hiding this comment

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

This is probably a nitpick, but can we throw an error if the combination of build ID and these flags is provided? I want to avoid any confusion for the users here. What I mean is a situation where users believe this flag will only delete a build with such id if it is of a given platform and profile.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a valid point

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

export default class BuildCancel extends EasCommand {
static override description = 'cancel a build';

static override args = [{ name: 'BUILD_ID' }];

static override flags = {
...EASNonInteractiveFlag,
platform: Flags.string({
char: 'p',
description: 'Platform for which to list builds when ID not provided',
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
description: 'Platform for which to list builds when ID not provided',
description: 'Filter builds by the platform if BUILD_ID is not provided',

WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

profile: Flags.string({
char: 'e',
description:
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
'Filter builds by profile if BUILD_ID is not provided',

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

Disallow using both the build ID and filter flags when using `build:delete` and `build:cancel` commands

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command
Updated the descriptions of the filter flags as per suggestion during review

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command
Added function for mapping platform flag into generated graphQL value

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command
Comment on lines 93 to 97
function toAppPlatform(platform: string): AppPlatform {
const capitalizedPlatform = (platform[0].toUpperCase() +
platform.substring(1)) as keyof typeof AppPlatform;
return AppPlatform[capitalizedPlatform];
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We could also do

const platformToAppPlatform: Record<string, AppPlatform | undefined> = {
  android: AppPlatform.Android,
  ios: AppPlatform.Ios,
};

A bit simpler and safer.

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 like that

Copy link
Member

@szdziedzic szdziedzic left a comment

Choose a reason for hiding this comment

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

Please don't kill me, these are the last 3 comments that can allow you to type everything better 🙈

Comment on lines 15 to 18
const platformToAppPlatform: Record<string, AppPlatform | undefined> = {
android: AppPlatform.Android,
ios: AppPlatform.Ios,
};
Copy link
Member

@szdziedzic szdziedzic Jan 16, 2024

Choose a reason for hiding this comment

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

Suggested change
const platformToAppPlatform: Record<string, AppPlatform | undefined> = {
android: AppPlatform.Android,
ios: AppPlatform.Ios,
};
const platformToAppPlatform: Record<
Exclude<RequestedPlatform, RequestedPlatform.All>,
AppPlatform
> = {
[RequestedPlatform.Android]: AppPlatform.Android,
[RequestedPlatform.Ios]: AppPlatform.Ios,
};

Comment on lines 99 to 103
platform: Flags.string({
char: 'p',
description: 'Filter builds by the platform if build ID is not provided',
options: ['android', 'ios', 'all'],
}),
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
platform: Flags.string({
char: 'p',
description: 'Filter builds by the platform if build ID is not provided',
options: ['android', 'ios', 'all'],
}),
platform: Flags.enum({
char: 'p',
description: 'Filter builds by the platform if build ID is not provided',
options: Object.values(RequestedPlatform),
}),

Comment on lines +123 to +127
if (buildIdFromArg && (platform || profile)) {
throw new Error(
'Build ID cannot be used together with platform and profile flags. They are used to filter the list of builds when not providing the build ID'
);
}
Copy link
Member

Choose a reason for hiding this comment

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

❤️

Comment on lines 84 to 88
platform: Flags.string({
char: 'p',
description: 'Filter builds by the platform if build ID is not provided',
options: ['android', 'ios', 'all'],
}),
Copy link
Member

@szdziedzic szdziedzic Jan 16, 2024

Choose a reason for hiding this comment

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

Suggested change
platform: Flags.string({
char: 'p',
description: 'Filter builds by the platform if build ID is not provided',
options: ['android', 'ios', 'all'],
}),
platform: Flags.enum({
char: 'p',
description: 'Filter builds by the platform if build ID is not provided',
options: Object.values(RequestedPlatform),
}),

Copy link

✅ Thank you for adding the changelog entry!

@radoslawkrzemien radoslawkrzemien merged commit ccc8d5a into main Jan 16, 2024
9 checks passed
@radoslawkrzemien radoslawkrzemien deleted the @radoslawkrzemien/ENG-11026-add-build-delete-command branch January 16, 2024 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants