Skip to content

Commit

Permalink
[eas-cli] use account-wide vs shared in env commands
Browse files Browse the repository at this point in the history
  • Loading branch information
szdziedzic committed Nov 20, 2024
1 parent 753df2c commit c08076f
Show file tree
Hide file tree
Showing 14 changed files with 182 additions and 103 deletions.
12 changes: 7 additions & 5 deletions packages/eas-cli/src/commandUtils/flags.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Flags } from '@oclif/core';

import { EnvironmentVariableEnvironment, EnvironmentVariableScope } from '../graphql/generated';
import { EnvironmentVariableEnvironment } from '../graphql/generated';

// NOTE: not exactly true, but, provided mapToLowercase and upperCaseAsync
// are used in tandem, it saves on unnecessary typying in commands
Expand Down Expand Up @@ -58,12 +58,14 @@ export const EASVariableVisibilityFlag = {
}),
};

export const EASVariableScopeFlag = {
scope: Flags.enum<EnvironmentVariableScope>({
export type EASEnvironmentVariableScopeFlagValue = 'project' | 'account';

export const EASEnvironmentVariableScopeFlag = {
scope: Flags.enum<EASEnvironmentVariableScopeFlagValue>({
description: 'Scope for the variable',
options: mapToLowercase([EnvironmentVariableScope.Shared, EnvironmentVariableScope.Project]),
options: ['project', 'account'],
parse: upperCaseAsync,
default: EnvironmentVariableScope.Project,
default: 'project',
}),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ describe(EnvironmentVariableCreate, () => {
});
});

it('creates a shared variable', async () => {
it('creates an account-wide variable', async () => {
const command = new EnvironmentVariableCreate(
[
'--name',
Expand All @@ -181,7 +181,7 @@ describe(EnvironmentVariableCreate, () => {
'--environment',
'production',
'--scope',
'shared',
'account',
],
mockConfig
);
Expand All @@ -207,7 +207,7 @@ describe(EnvironmentVariableCreate, () => {
);
});

it('throws if a shared variable already exists', async () => {
it('throws if an account-wide variable already exists', async () => {
const command = new EnvironmentVariableCreate(
[
'--name',
Expand All @@ -217,7 +217,7 @@ describe(EnvironmentVariableCreate, () => {
'--environment',
'production',
'--scope',
'shared',
'account',
],
mockConfig
);
Expand All @@ -242,7 +242,7 @@ describe(EnvironmentVariableCreate, () => {
await expect(command.runAsync()).rejects.toThrow();
});

it('updates if a shared variable already exists and --force flag is set', async () => {
it('updates if an account-wide variable already exists and --force flag is set', async () => {
const command = new EnvironmentVariableCreate(
[
'--name',
Expand All @@ -253,7 +253,7 @@ describe(EnvironmentVariableCreate, () => {
'production',
'--force',
'--scope',
'shared',
'account',
],
mockConfig
);
Expand Down Expand Up @@ -288,7 +288,7 @@ describe(EnvironmentVariableCreate, () => {
});
});

it('creates a shared variable and links it', async () => {
it('creates an account-wide variable and links it', async () => {
const command = new EnvironmentVariableCreate(
[
'--name',
Expand All @@ -300,7 +300,7 @@ describe(EnvironmentVariableCreate, () => {
'--environment',
'development',
'--scope',
'shared',
'account',
'--link',
],
mockConfig
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe(EnvironmentVariableLink, () => {
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
});

it('links a shared variable to the current project in non-interactive mode', async () => {
it('links an account-wide variable to the current project in non-interactive mode', async () => {
const mockVariables = [
{
id: variableId,
Expand Down Expand Up @@ -75,7 +75,7 @@ describe(EnvironmentVariableLink, () => {
);
});

it('links a shared variable to the current project to a specified environment', async () => {
it('links an account-wide variable to the current project to a specified environment', async () => {
const mockVariables = [
{
id: variableId,
Expand Down Expand Up @@ -215,7 +215,7 @@ describe(EnvironmentVariableLink, () => {
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue(mockContext);
await expect(command.runAsync()).rejects.toThrow(
"Shared variable NON_EXISTENT_VARIABLE doesn't exist"
"Account-wide variable NON_EXISTENT_VARIABLE doesn't exist"
);
});

Expand Down Expand Up @@ -271,7 +271,7 @@ describe(EnvironmentVariableLink, () => {
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue(mockContext);
await expect(command.runAsync()).rejects.toThrow(
"Shared variable NON_EXISTENT_VARIABLE doesn't exist"
"Account-wide variable NON_EXISTENT_VARIABLE doesn't exist"
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,10 @@ describe(EnvironmentVariableList, () => {
expect(Log.log).toHaveBeenCalledWith(expect.stringContaining('TEST_VAR_2'));
});

it('lists shared environment variables successfully', async () => {
it('lists account-wide environment variables successfully', async () => {
jest.mocked(EnvironmentVariablesQuery.sharedAsync).mockResolvedValueOnce(mockVariables);

const command = new EnvironmentVariableList(['--scope', 'shared'], mockConfig);
const command = new EnvironmentVariableList(['--scope', 'account'], mockConfig);

// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
Expand All @@ -142,13 +142,13 @@ describe(EnvironmentVariableList, () => {
expect(Log.log).toHaveBeenCalledWith(expect.stringContaining('TEST_VAR_2'));
});

it('lists shared environment variables including sensitive values', async () => {
it('lists account-wide environment variables including sensitive values', async () => {
jest
.mocked(EnvironmentVariablesQuery.sharedWithSensitiveAsync)
.mockResolvedValueOnce(mockVariables);

const command = new EnvironmentVariableList(
['--include-sensitive', '--scope', 'shared'],
['--include-sensitive', '--scope', 'account'],
mockConfig
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe(EnvironmentVariableUnlink, () => {
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
});

it('unlinks a shared variable from the current project in non-interactive mode', async () => {
it('unlinks an account-wide variable from the current project in non-interactive mode', async () => {
const mockVariables = [
{
id: variableId,
Expand Down Expand Up @@ -76,7 +76,7 @@ describe(EnvironmentVariableUnlink, () => {
);
});

it('unlinks a shared variable from the current project in a specified environment', async () => {
it('unlinks an account-wide variable from the current project in a specified environment', async () => {
const mockVariables = [
{
id: variableId,
Expand Down Expand Up @@ -170,7 +170,7 @@ describe(EnvironmentVariableUnlink, () => {
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue(mockContext);
await expect(command.runAsync()).rejects.toThrow(
"Shared variable NON_EXISTENT_VARIABLE doesn't exist"
"Account-wide variable NON_EXISTENT_VARIABLE doesn't exist"
);
});

Expand Down Expand Up @@ -226,7 +226,7 @@ describe(EnvironmentVariableUnlink, () => {
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue(mockContext);
await expect(command.runAsync()).rejects.toThrow(
"Shared variable NON_EXISTENT_VARIABLE doesn't exist"
"Account-wide variable NON_EXISTENT_VARIABLE doesn't exist"
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ describe(EnvironmentVariableUpdate, () => {
);
});

it('updates a shared variable by selected name in non-interactive mode', async () => {
it('updates an account-wide variable by selected name in non-interactive mode', async () => {
const mockVariables = [
{
id: variableId,
Expand All @@ -107,7 +107,7 @@ describe(EnvironmentVariableUpdate, () => {
'--environment',
'production',
'--scope',
'shared',
'account',
],
mockConfig
);
Expand Down
64 changes: 40 additions & 24 deletions packages/eas-cli/src/commands/env/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import path from 'path';

import EasCommand from '../../commandUtils/EasCommand';
import {
EASEnvironmentVariableScopeFlag,
EASEnvironmentVariableScopeFlagValue,
EASMultiEnvironmentFlag,
EASNonInteractiveFlag,
EASVariableScopeFlag,
EASVariableVisibilityFlag,
} from '../../commandUtils/flags';
import {
Expand All @@ -34,17 +35,29 @@ import {
} from '../../utils/prompts';
import { isEnvironment, performForEnvironmentsAsync } from '../../utils/variableUtils';

type CreateFlags = {
interface RawCreateFlags {
name?: string;
value?: string;
link?: boolean;
force?: boolean;
type?: 'string' | 'file';
visibility?: 'plaintext' | 'sensitive' | 'secret';
scope?: EnvironmentVariableScope;
scope: EASEnvironmentVariableScopeFlagValue;
environment?: EnvironmentVariableEnvironment[];
'non-interactive': boolean;
};
}

interface CreateFlags {
name?: string;
value?: string;
link?: boolean;
force?: boolean;
type?: 'string' | 'file';
visibility?: 'plaintext' | 'sensitive' | 'secret';
scope: EnvironmentVariableScope;
environment?: EnvironmentVariableEnvironment[];
'non-interactive': boolean;
}

export default class EnvironmentVariableCreate extends EasCommand {
static override description =
Expand All @@ -69,7 +82,8 @@ export default class EnvironmentVariableCreate extends EasCommand {
description: 'Text value or the variable',
}),
link: Flags.boolean({
description: 'Link shared variable to the current project',
description: 'Link account-wide variable to the current project',
hidden: true, // every account-wide variable is global for now so it's not user facing
}),
force: Flags.boolean({
description: 'Overwrite existing variable',
Expand All @@ -80,7 +94,7 @@ export default class EnvironmentVariableCreate extends EasCommand {
options: ['string', 'file'],
}),
...EASVariableVisibilityFlag,
...EASVariableScopeFlag,
...EASEnvironmentVariableScopeFlag,
...EASMultiEnvironmentFlag,
...EASNonInteractiveFlag,
};
Expand All @@ -94,7 +108,7 @@ export default class EnvironmentVariableCreate extends EasCommand {
async runAsync(): Promise<void> {
const { args, flags } = await this.parse(EnvironmentVariableCreate);

const validatedFlags = this.validateFlags(flags);
const validatedFlags = this.sanitizeFlags(flags);

const {
name,
Expand Down Expand Up @@ -147,12 +161,12 @@ export default class EnvironmentVariableCreate extends EasCommand {
await this.promptForOverwriteAsync({
nonInteractive,
force,
message: `Shared variable with ${name} name already exists on this account.`,
message: `Account-wide variable with ${name} name already exists on this account.`,
suggestion: 'Do you want to unlink it first?',
});

Log.withTick(
`Unlinking shared variable ${chalk.bold(name)} on project ${chalk.bold(
`Unlinking account-wide variable ${chalk.bold(name)} on project ${chalk.bold(
projectDisplayName
)}.`
);
Expand Down Expand Up @@ -216,8 +230,8 @@ export default class EnvironmentVariableCreate extends EasCommand {
overwrite = true;
} else {
throw new Error(
`Shared variable with ${name} name already exists on this account.\n` +
`Use a different name or delete the existing variable on website or by using eas env:delete --name ${name} --scope shared command.`
`Account-wide variable with ${name} name already exists on this account.\n` +
`Use a different name or delete the existing variable on website or by using the "eas env:delete --name ${name} --scope account" command.`
);
}
}
Expand Down Expand Up @@ -256,7 +270,7 @@ export default class EnvironmentVariableCreate extends EasCommand {

if (link) {
Log.withTick(
`Linking shared variable ${chalk.bold(name)} to project ${chalk.bold(
`Linking account-wide variable ${chalk.bold(name)} to project ${chalk.bold(
projectDisplayName
)}.`
);
Expand All @@ -271,7 +285,9 @@ export default class EnvironmentVariableCreate extends EasCommand {
});

Log.withTick(
`Linked shared variable ${chalk.bold(name)} to project ${chalk.bold(projectDisplayName)}.`
`Linked account-wide variable ${chalk.bold(name)} to project ${chalk.bold(
projectDisplayName
)}.`
);
}
}
Expand Down Expand Up @@ -390,32 +406,32 @@ export default class EnvironmentVariableCreate extends EasCommand {
visibility: newVisibility,
link: rest.link ?? false,
force: rest.force ?? false,
scope: rest.scope ?? EnvironmentVariableScope.Project,
'non-interactive': nonInteractive,
type: newType,
fileName,
...rest,
};
}

private validateFlags(flags: CreateFlags & { type?: string; visibility?: string }): CreateFlags {
if (flags.scope !== EnvironmentVariableScope.Shared && flags.link) {
private sanitizeFlags(flags: RawCreateFlags): CreateFlags {
if (flags.scope !== 'account' && flags.link) {
throw new Error(
`Unexpected argument: --link can only be used when creating shared variables`
`Unexpected argument: --link can only be used when creating account-wide variables`
);
}

if (
flags.scope === EnvironmentVariableScope.Shared &&
flags.environment &&
!flags.link &&
flags['non-interactive']
) {
if (flags.scope === 'account' && flags.environment && !flags.link && flags['non-interactive']) {
throw new Error(
'Unexpected argument: --environment in non-interactive mode can only be used with --link flag.'
);
}

return flags;
return {
...flags,
scope:
flags.scope === 'account'
? EnvironmentVariableScope.Shared
: EnvironmentVariableScope.Project,
};
}
}
Loading

0 comments on commit c08076f

Please sign in to comment.