Skip to content

Commit

Permalink
[eas-cli] use autocomplete list for secrets:delete (#309)
Browse files Browse the repository at this point in the history
* [eas-cli] use autocomplete list for secrets:delete

* update changelog

* check if secret is account-wide and tell user that it could affect multiple apps
  • Loading branch information
FiberJW authored Apr 7, 2021
1 parent 8336f63 commit eebef3e
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 19 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ This is the log of notable changes to EAS CLI and related packages.
- `secrets:create` now uses flags rather than positional arguments ([#300](https://github.com/expo/eas-cli/pull/300) by [@fiberjw](https://github.com/fiberjw))
- `secrets:create`'s `target` arg is now called `scope` ([#300](https://github.com/expo/eas-cli/pull/300) by [@fiberjw](https://github.com/fiberjw))
- `secrets:list`'s `target` property is now called `scope` ([#300](https://github.com/expo/eas-cli/pull/300) by [@fiberjw](https://github.com/fiberjw))
- `secrets:delete`'s `ID` arg is now optional ([#309](https://github.com/expo/eas-cli/pull/309) by [@fiberjw](https://github.com/fiberjw))
- `secrets:delete`'s now allows users to choose secrets from a list ([#309](https://github.com/expo/eas-cli/pull/309) by [@fiberjw](https://github.com/fiberjw))

### 🎉 New features

Expand Down
55 changes: 47 additions & 8 deletions packages/eas-cli/src/commands/secrets/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,24 @@ import { Command } from '@oclif/command';
import chalk from 'chalk';

import { EnvironmentSecretMutation } from '../../graphql/mutations/EnvironmentSecretMutation';
import {
EnvironmentSecretWithScope,
EnvironmentSecretsQuery,
} from '../../graphql/queries/EnvironmentSecretsQuery';
import Log from '../../log';
import {
isEasEnabledForProjectAsync,
warnEasUnavailable,
} from '../../project/isEasEnabledForProject';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { toggleConfirmAsync } from '../../prompts';
import {
findProjectRootAsync,
getProjectAccountNameAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
import { ensureLoggedInAsync } from '../../user/actions';
import { EnvironmentSecretScope } from './create';

export default class EnvironmentSecretDelete extends Command {
static description = `Delete an environment secret by ID.
Expand All @@ -19,7 +29,7 @@ Unsure where to find the secret's ID? Run ${chalk.bold('eas secrets:list')}`;
static args = [
{
name: 'id',
required: true,
required: false,
description: `ID of the secret to delete`,
},
];
Expand All @@ -30,30 +40,59 @@ Unsure where to find the secret's ID? Run ${chalk.bold('eas secrets:list')}`;
const projectDir = (await findProjectRootAsync()) ?? process.cwd();
const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
const projectId = await getProjectIdAsync(exp);
const projectFullName = await getProjectFullNameAsync(exp);
const projectAccountName = await getProjectAccountNameAsync(exp);

if (!(await isEasEnabledForProjectAsync(projectId))) {
warnEasUnavailable();
process.exitCode = 1;
return;
}

const {
let {
args: { id },
} = this.parse(EnvironmentSecretDelete);
let secret: EnvironmentSecretWithScope | undefined;

if (!id) {
const secrets = await EnvironmentSecretsQuery.allAsync(projectAccountName, projectFullName);

({ secret } = await promptAsync({
type: 'autocomplete',
name: 'secret',
message: 'Pick the secret to be deleted:',
choices: secrets.map(secret => ({
title: `${secret.name} (${secret.scope})`,
value: secret,
})),
}));

id = secret?.id;
}

Log.addNewLineIfNone();
Log.warn(
`You are about to permamently delete secret with id: "${id}".\nThis action is irreversible.`
`You are about to permamently delete secret${
secret?.name ? ` "${secret?.name}"` : ''
} with id: "${id}".\nThis action is irreversible.`
);
Log.newLine();
const confirmed = await toggleConfirmAsync({ message: 'Are you sure you wish to proceed?' });
const confirmed = await toggleConfirmAsync({
message: `Are you sure you wish to proceed?${
secret?.scope === EnvironmentSecretScope.ACCOUNT
? ' This secret is applied across your whole account and may affect multiple apps.'
: ''
}`,
});
if (!confirmed) {
Log.error(`Canceled deletion of secret with id: "${id}".`);
Log.error(
`Canceled deletion of secret${secret?.name ? ` "${secret?.name}"` : ''} with id: "${id}".`
);
process.exit(1);
}

await EnvironmentSecretMutation.delete(id);

Log.withTick(`️Deleted secret with id "${id}".`);
Log.withTick(`️Deleted secret${secret?.name ? ` "${secret?.name}"` : ''} with id "${id}".`);
}
}
12 changes: 1 addition & 11 deletions packages/eas-cli/src/commands/secrets/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import chalk from 'chalk';
import Table from 'cli-table3';
import dateFormat from 'dateformat';

import { EnvironmentSecretFragment } from '../../graphql/generated';
import { EnvironmentSecretsQuery } from '../../graphql/queries/EnvironmentSecretsQuery';
import Log from '../../log';
import {
Expand All @@ -18,7 +17,6 @@ import {
getProjectIdAsync,
} from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
import { EnvironmentSecretScope } from './create';

export default class EnvironmentSecretsList extends Command {
static description = 'Lists environment secrets available for your current app';
Expand All @@ -43,15 +41,7 @@ export default class EnvironmentSecretsList extends Command {
throw new Error("Please run this command inside your project's directory");
}

const [accountSecrets, appSecrets] = await Promise.all([
EnvironmentSecretsQuery.byAcccountNameAsync(projectAccountName),
EnvironmentSecretsQuery.byAppFullNameAsync(projectFullName),
]);

const secrets = [
...appSecrets.map(s => ({ ...s, scope: EnvironmentSecretScope.PROJECT })),
...accountSecrets.map(s => ({ ...s, scope: EnvironmentSecretScope.ACCOUNT })),
] as (EnvironmentSecretFragment & { scope: EnvironmentSecretScope })[];
const secrets = await EnvironmentSecretsQuery.allAsync(projectAccountName, projectFullName);

const table = new Table({
head: ['Name', 'Scope', 'ID', 'Updated at'],
Expand Down
19 changes: 19 additions & 0 deletions packages/eas-cli/src/graphql/queries/EnvironmentSecretsQuery.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { print } from 'graphql';
import gql from 'graphql-tag';

import { EnvironmentSecretScope } from '../../commands/secrets/create';
import { graphqlClient, withErrorHandlingAsync } from '../client';
import {
EnvironmentSecretFragment,
Expand All @@ -9,6 +10,10 @@ import {
} from '../generated';
import { EnvironmentSecretFragmentNode } from '../types/EnvironmentSecret';

export type EnvironmentSecretWithScope = EnvironmentSecretFragment & {
scope: EnvironmentSecretScope;
};

export const EnvironmentSecretsQuery = {
async byAcccountNameAsync(accountName: string): Promise<EnvironmentSecretFragment[]> {
const data = await withErrorHandlingAsync(
Expand Down Expand Up @@ -60,4 +65,18 @@ export const EnvironmentSecretsQuery = {

return data.app?.byFullName.environmentSecrets ?? [];
},
async allAsync(
projectAccountName: string,
projectFullName: string
): Promise<EnvironmentSecretWithScope[]> {
const [accountSecrets, appSecrets] = await Promise.all([
this.byAcccountNameAsync(projectAccountName),
this.byAppFullNameAsync(projectFullName),
]);

return [
...appSecrets.map(s => ({ ...s, scope: EnvironmentSecretScope.PROJECT })),
...accountSecrets.map(s => ({ ...s, scope: EnvironmentSecretScope.ACCOUNT })),
];
},
};

0 comments on commit eebef3e

Please sign in to comment.