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
20 changes: 10 additions & 10 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ fileignoreconfig:
- "'SECRET=hunter2'"
- "formFieldKey"
- filename: test/integration/projects-create.test.ts
checksum: 6985ba0adee092fcf3dd4a432614298839deba0bef2867bc3bc8d6747946dc66
checksum: 0161c3bba176134df0bc1a3a29cda4e1c0b143afbee202c110cdfed19b08d8e0
- filename: test/fixtures/README.md
checksum: d801c84c8b7d7f38f967573e9a3a70da4405b7cbcbf10d2a6e5be3d65fb3daa1
allowed_patterns:
Expand Down Expand Up @@ -330,13 +330,13 @@ fileignoreconfig:
- "-p, --password"
- "-u, --username"
- filename: test/integration/environments-create.test.ts
checksum: 898d30a1a16339ffd0e495eb1b22ea7c08b947c9012ad17a3dc61c905b07a091
checksum: 1b87f454b1534bf0b73ebb52cdaf9af231beb57d5746e521a4504edad819c89a
- filename: src/commands/launch/variables/list.test.ts
checksum: 87ccb762f4931326fc50bca1c01aeb4a16d552e4dfa141d00370fdfafa4363be
- filename: src/commands/launch/variables/set.test.ts
checksum: a6fc222527e946aee1c0015e3961bc3dff13aabec40c2162e7d2d3f2bb2d2cde
- filename: src/commands/launch/variables/unset.test.ts
checksum: 709e849b1ef834bdc433936438110ff3f79b8cc0e7bec0f505c56ffd4f67bd8b
checksum: e732a3f563dea1f788293d108f678fc80c9dad7af1d482f0fd50b35e9a6f51e8
- filename: src/commands/launch/variables/unset.ts
checksum: b25c2b1547a1ed5b216c57b26bc18744ad04746e01efcc40e9469d1a5e472537
- filename: src/environments/environment.errors.test.ts
Expand All @@ -346,9 +346,9 @@ fileignoreconfig:
- filename: src/stacks/stacks.api.ts
checksum: d59622ca6411b93d5469a1895b08ccde4b9cd83fbb909c82076ea70d74b5e1e4
- filename: src/variables/variable.chooser.ts
checksum: 15a6b8a1a974c8d0396ac18d8c9d39c8bc9f70efbf41ce0eef79180ce16b2f04
checksum: c776a4184eb1819055622784bc6f3342b67bc1babdca0cdbbe72a4a871cc2498
- filename: src/variables/variable.create.test.ts
checksum: 908f39b56943d8bc343b93057f83cff1b718ecdc0bcbff5ed9b759c010b4029d
checksum: c888e2a4aca9c6855c79d743368387aeb0ab5db907beca8bef1113bc05a5260d
- filename: src/variables/variable.dotenv.test.ts
checksum: c3d5e33189736bb42b7c4fac7375cf0307681b34feca4830b49331f55a130f78
- filename: src/variables/variable.dotenv.ts
Expand All @@ -368,15 +368,15 @@ fileignoreconfig:
- filename: src/variables/variable.page.test.ts
checksum: 9a4e63c0dbfd35b499149a98ce5642787097e9491746b4b874b2eda88f1d2b50
- filename: src/variables/variable.parse.test.ts
checksum: d620d89c4b5cafb80dcf696f430c8fa4e696dfc744f814bcdd74324243010460
checksum: af1ca4483e3771a346d9e068ffd39cad3e297149c3327f26b5e5e9e8a6a921e0
- filename: src/variables/variable.parse.ts
checksum: 87be9f454ce0ffb48ff5729ccd3c1b105ce9f1030b32278c3359555b983c2142
- filename: src/variables/variable.presenter.test.ts
checksum: 62e8b0eff16ed4408d929092ce6808aeabea001a6cc15780d46c4ac4226eec3d
- filename: src/variables/variable.presenter.ts
checksum: ee8e4dcf75ad7e849d1e8e64ce6a6549d13fc62dc9aa322d96eb2e725d629db5
- filename: src/variables/variable.set.test.ts
checksum: df7d36ff83d1489301afad5418ebe76338adb7bcad51d1e738f989362be46903
checksum: e9b6a1bab4165ead293ca33aa921550cf8e46dd6821d0718f33856685b5542be
- filename: src/variables/variable.sources.test.ts
checksum: 63ad95e998cbc6d33fabe4ce76797e8788b17abec3833b4fed2360eab01a6f79
- filename: src/variables/variable.sources.ts
Expand All @@ -390,9 +390,9 @@ fileignoreconfig:
- filename: src/variables/variable.summary.ts
checksum: 0da287557a5c4280074d1a26aeff4f7d95b205e4266ff47c18b62665134f5bdc
- filename: src/variables/variable.unset.test.ts
checksum: d449ba8483eeb932c155b2b210ce2088785d0961680eaa3efd036553c46579e2
checksum: 8797c7aa2960fc0328d5cab8cfdddf674ac624ace95f79338520851a2c7b732a
- filename: src/variables/variable.unset.ts
checksum: 0d21f5878aa1f05dcafc864dd7dcb23e3155d3d24b97e248e62ad9bf5bcd7745
checksum: 4050a023edd3c6db1507eb2d0f2815d8c0c03622b2810b4977a8fba6e83818fe
- filename: src/variables/variable.values.test.ts
checksum: 56725066fdabce3afea373c70bee10a6db4c1409e3a8f6f1e7f191ea6e602419
- filename: src/variables/variable.values.ts
Expand All @@ -410,5 +410,5 @@ fileignoreconfig:
- filename: test/integration/variables-list.test.ts
checksum: 5ea6a0fcd331496ca19773e90dab81e09eb5290b8f22a1880bffc7916f3778b2
- filename: test/integration/variables-set-unset.test.ts
checksum: cafc4eed5f80fe9763cb9362acce9ba51663da7a615abf683b23545f5342b913
checksum: 7af9dde4711849d0908d0de54347055a7c2a8d8920f9fcc0b24382b02bceaed9
version: "1.0"
8 changes: 4 additions & 4 deletions src/commands/launch/variables/unset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ describe('launch:variables:unset', () => {
await command.run();

expect(inquired).toEqual([
{ type: 'confirm', name: 'confirm', message: 'Are you sure you want to remove API_KEY from "staging"?', default: false },
{ type: 'confirm', name: 'confirm', message: 'Are you sure you want to remove the variable API_KEY from the "staging" environment?', default: false },
]);
expect(calls).toEqual(['get', 'patch']);
expect(updates).toEqual([{ ...SCOPE, unset: ['API_KEY'] }]);
expect(lines).toEqual(['✔ API_KEY removed from staging.']);
expect(lines).toEqual(['✔ Variable API_KEY removed from the "staging" environment.']);
expect(lines.join('')).not.toContain('never-printed-value');
});

Expand All @@ -87,7 +87,7 @@ describe('launch:variables:unset', () => {

expect(inquired).toEqual([]);
expect(calls).toEqual(['get', 'patch']);
expect(lines).toEqual(['✔ API_KEY removed from staging.']);
expect(lines).toEqual(['✔ Variable API_KEY removed from the "staging" environment.']);
});

it('says the variables were not removed and exits 3 without writing when the user declines', async () => {
Expand All @@ -108,7 +108,7 @@ describe('launch:variables:unset', () => {
expect(error).toBeInstanceOf(UsageError);
expect(error.exitCode).toBe(2);
expect(error.message).toBe(
'Are you sure you want to remove API_KEY from "staging"? Pass --yes to confirm without an interactive terminal.',
'Are you sure you want to remove the variable API_KEY from the "staging" environment? Pass --yes to confirm without an interactive terminal.',
);
expect(inquired).toEqual([]);
expect(updates).toEqual([]);
Expand Down
7 changes: 4 additions & 3 deletions src/variables/variable.chooser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ export interface ChooserPrompt {

export const STACK_CHOICE = 'Import variables from a stack';
export const FILE_CHOICE = 'Import variables from a dotenv file';
export const TYPED_CHOICE = 'Type KEY=VALUE pairs';
export const TYPED_CHOICE = 'Manually add custom variables';
export const SKIP_CHOICE = 'Skip adding environment variables';
const FILE_QUESTION = 'Path to the dotenv file';
const TYPED_QUESTION = 'KEY=VALUE pairs separated by spaces';
const TYPED_LABEL = '--var';
export const TYPED_QUESTION =
'Enter variables separated by spaces (Ex: KEY1=value1 KEY2:value2 KEY3="value with spaces")';
const TYPED_LABEL = 'manual entry';

async function askForFile(
detected: string | undefined,
Expand Down
17 changes: 9 additions & 8 deletions src/variables/variable.create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const SAVED_TOKEN = `cs-${randomUUID()}`;
const STACK = 'Import variables from a stack';
const SKIP = 'Skip adding environment variables';
const FILE = 'Import variables from a dotenv file';
const TYPED = 'Type KEY=VALUE pairs';
const TYPED = 'Manually add custom variables';

function fakes(options: { isTTY?: boolean; answers?: Record<string, unknown>; files?: Record<string, string> } = {}) {
const files = options.files ?? {};
Expand Down Expand Up @@ -188,7 +188,7 @@ describe('variable create', () => {
answers: {
'Add environment variables': [FILE, TYPED],
'Path to the dotenv file': 'a.env',
'KEY=VALUE pairs separated by spaces': 'API_URL="sk live secret" N=2',
'Enter variables separated by spaces (Ex: KEY1=value1 KEY2:value2 KEY3="value with spaces")': 'API_URL="sk live secret" N=2',
},
});

Expand All @@ -199,15 +199,16 @@ describe('variable create', () => {
{ key: 'F', value: '1' },
{ key: 'N', value: '2' },
]);
expect(printed.join('\n')).toContain('API_URL overridden by --var');
expect(printed.join('\n')).toContain('API_URL overridden by manual entry');
expect(printed.join('\n')).not.toContain('--var');
expect(printed.join('\n')).not.toContain('sk live secret');
});

it('asks a hidden prompt for a typed key that has no value', async () => {
const { services, io, asked } = fakes({
answers: {
'Add environment variables': [TYPED],
'KEY=VALUE pairs separated by spaces': 'API_KEY',
'Enter variables separated by spaces (Ex: KEY1=value1 KEY2:value2 KEY3="value with spaces")': 'API_KEY',
'Value for API_KEY': 's3cret',
},
});
Expand All @@ -220,7 +221,7 @@ describe('variable create', () => {

it('echoes the typed keys with masked values and never a value', async () => {
const { services, io, printed } = fakes({
answers: { 'Add environment variables': [TYPED], 'KEY=VALUE pairs separated by spaces': 'A=never-printed-value' },
answers: { 'Add environment variables': [TYPED], 'Enter variables separated by spaces (Ex: KEY1=value1 KEY2:value2 KEY3="value with spaces")': 'A=never-printed-value' },
});

await variablesForCreate(NONE, services, io);
Expand Down Expand Up @@ -251,7 +252,7 @@ describe('variable create', () => {
);

const malformed = fakes({
answers: { 'Add environment variables': [TYPED], 'KEY=VALUE pairs separated by spaces': 'A="oops' },
answers: { 'Add environment variables': [TYPED], 'Enter variables separated by spaces (Ex: KEY1=value1 KEY2:value2 KEY3="value with spaces")': 'A="oops' },
});

await expect(variablesForCreate(NONE, malformed.services, malformed.io)).rejects.toThrow(
Expand Down Expand Up @@ -301,7 +302,7 @@ describe('variable create', () => {
'Add environment variables': [STACK, TYPED],
Stack: '0',
'Delivery token': '0',
'KEY=VALUE pairs separated by spaces': 'EXTRA=1',
'Enter variables separated by spaces (Ex: KEY1=value1 KEY2:value2 KEY3="value with spaces")': 'EXTRA=1',
},
});

Expand All @@ -311,7 +312,7 @@ describe('variable create', () => {
'Add environment variables',
'Stack',
'Delivery token',
'KEY=VALUE pairs separated by spaces',
'Enter variables separated by spaces (Ex: KEY1=value1 KEY2:value2 KEY3="value with spaces")',
]);
expect(variables.map((variable) => variable.key)).toEqual([
'CONTENTSTACK_API_HOST',
Expand Down
11 changes: 11 additions & 0 deletions src/variables/variable.parse.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { UsageError } from '../core/errors';
import { TYPED_QUESTION } from './variable.chooser';
import { parseAssignment, splitLine } from './variable.parse';

describe('variable parse', () => {
Expand Down Expand Up @@ -80,4 +81,14 @@ describe('variable parse split line', () => {
expect(() => splitLine(line)).toThrow(UsageError);
expect(() => splitLine(line)).toThrow('The line has an opening quote with no closing quote.');
});

it('reads the example in the manual entry question as three pairs, one per separator and one quoted', () => {
const example = /\(Ex: (.+)\)$/.exec(TYPED_QUESTION)?.[1] ?? '';

expect(splitLine(example).map(parseAssignment)).toEqual([
{ key: 'KEY1', value: 'value1' },
{ key: 'KEY2', value: 'value2' },
{ key: 'KEY3', value: 'value with spaces' },
]);
});
});
6 changes: 3 additions & 3 deletions src/variables/variable.set.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ describe('default variable io', () => {
describe('asking which sources to use', () => {
const STACK = 'Import variables from a stack';
const FILE = 'Import variables from a dotenv file';
const TYPED = 'Type KEY=VALUE pairs';
const TYPED = 'Manually add custom variables';

it('offers the three sources, with no Skip, on a terminal when no source flag was given', async () => {
const { setter, asked } = fakes({}, { 'Add variables from': [] });
Expand Down Expand Up @@ -303,7 +303,7 @@ describe('default variable io', () => {
{
'Add variables from': [FILE, TYPED],
'Path to the dotenv file': '/work/.env.local',
'KEY=VALUE pairs separated by spaces': 'TYPED=2',
'Enter variables separated by spaces (Ex: KEY1=value1 KEY2:value2 KEY3="value with spaces")': 'TYPED=2',
},
);

Expand All @@ -314,7 +314,7 @@ describe('default variable io', () => {
{ key: 'FROM_FILE', value: '1' },
{ key: 'TYPED', value: '2' },
]);
expect(printed.at(-1)).toBe('✔ 2 variables set — 1 from /work/.env.local, 1 from --var');
expect(printed.at(-1)).toBe('✔ 2 variables set — 1 from /work/.env.local, 1 from manual entry');
});

it.each([
Expand Down
12 changes: 6 additions & 6 deletions src/variables/variable.unset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,27 +59,27 @@ describe('variable remover', () => {

expect(removed).toBe(true);
expect(order).toEqual(['get', 'confirm', 'patch']);
expect(questions).toEqual(['Are you sure you want to remove API_KEY from "staging"?']);
expect(questions).toEqual(['Are you sure you want to remove the variable API_KEY from the "staging" environment?']);
expect(patches).toEqual([{ ...SCOPE, unset: ['API_KEY'] }]);
expect(printed).toEqual(['✔ API_KEY removed from staging.']);
expect(printed).toEqual(['✔ Variable API_KEY removed from the "staging" environment.']);
});

it('names several keys together', async () => {
const { remover, printed, questions } = fakes(HELD);

await remover.remove({ ...SCOPE, keys: ['API_KEY', 'DEBUG'] });

expect(questions).toEqual(['Are you sure you want to remove API_KEY, DEBUG from "staging"?']);
expect(printed).toEqual(['✔ API_KEY, DEBUG removed from staging.']);
expect(questions).toEqual(['Are you sure you want to remove the variables API_KEY, DEBUG from the "staging" environment?']);
expect(printed).toEqual(['✔ Variables API_KEY, DEBUG removed from the "staging" environment.']);
});

it('falls back to the environment reference when the environment carries no name', async () => {
const { remover, printed, questions } = fakes({ environmentVariables: [{ key: 'A', value: '1' }] });

await remover.remove({ ...SCOPE, keys: ['A'] });

expect(questions).toEqual(['Are you sure you want to remove A from "env1"?']);
expect(printed).toEqual(['✔ A removed from env1.']);
expect(questions).toEqual(['Are you sure you want to remove the variable A from the "env1" environment?']);
expect(printed).toEqual(['✔ Variable A removed from the "env1" environment.']);
});

it('returns false and changes nothing when the user declines', async () => {
Expand Down
14 changes: 11 additions & 3 deletions src/variables/variable.unset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,24 @@ export class VariableRemover {
}

const name = held.name ?? environment;
const listed = [...new Set(request.keys)].join(', ');
const keys = [...new Set(request.keys)];
const listed = keys.join(', ');
const single = keys.length === 1;
const subject = `${single ? 'variable' : 'variables'} ${listed}`;
const question = `Are you sure you want to remove the ${subject} from the "${name}" environment?`;

if (!(await this.confirm(`Are you sure you want to remove ${listed} from "${name}"?`))) {
if (!(await this.confirm(question))) {
return false;
}

await unsetVariables(this.services.api.variables, scope, request.keys);

this.services.ux.print(
styled(`✔ ${listed} removed from ${name}.`, 'green', this.services.outputIsTTY === true),
styled(
`✔ ${single ? 'Variable' : 'Variables'} ${listed} removed from the "${name}" environment.`,
'green',
this.services.outputIsTTY === true,
),
);

return true;
Expand Down
14 changes: 7 additions & 7 deletions test/integration/environments-create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,9 @@ describe('integration: launch:env:create on the wire', () => {
const file = join(DATA_DIR, `env-create-${randomBytes(4).toString('hex')}.env`);
writeFileSync(file, 'API_URL=from-file\nFROM_FILE=1\n');
const prompts = answerPrompts({
'Add environment variables': ['Import variables from a dotenv file', 'Type KEY=VALUE pairs'],
'Add environment variables': ['Import variables from a dotenv file', 'Manually add custom variables'],
'Path to the dotenv file': file,
'KEY=VALUE pairs separated by spaces': 'API_URL=typed DEBUG=true',
'Enter variables separated by spaces (Ex: KEY1=value1 KEY2:value2 KEY3="value with spaces")': 'API_URL=typed DEBUG=true',
});
const sent: { environmentVariables?: unknown }[] = [];
stubCreate(sent);
Expand All @@ -214,7 +214,7 @@ describe('integration: launch:env:create on the wire', () => {
expect(prompts.messages).toEqual([
'Add environment variables',
'Path to the dotenv file',
'KEY=VALUE pairs separated by spaces',
'Enter variables separated by spaces (Ex: KEY1=value1 KEY2:value2 KEY3="value with spaces")',
]);
expect(sent[0].environmentVariables).toEqual([
{ key: 'API_URL', value: 'typed' },
Expand All @@ -233,12 +233,12 @@ describe('integration: launch:env:create on the wire', () => {
'Add environment variables': [
'Import variables from a stack',
'Import variables from a dotenv file',
'Type KEY=VALUE pairs',
'Manually add custom variables',
],
Stack: '0',
'Delivery token': '0',
'Path to the dotenv file': file,
'KEY=VALUE pairs separated by spaces': 'FROM_FILE=typed EXTRA=1',
'Enter variables separated by spaces (Ex: KEY1=value1 KEY2:value2 KEY3="value with spaces")': 'FROM_FILE=typed EXTRA=1',
});
const sent: { environmentVariables?: unknown }[] = [];
stubCreate(sent);
Expand All @@ -253,7 +253,7 @@ describe('integration: launch:env:create on the wire', () => {
'Stack',
'Delivery token',
'Path to the dotenv file',
'KEY=VALUE pairs separated by spaces',
'Enter variables separated by spaces (Ex: KEY1=value1 KEY2:value2 KEY3="value with spaces")',
]);
expect(lookups.every((scope) => scope.isDone())).toBe(true);
expect(sent[0].environmentVariables).toEqual([
Expand Down Expand Up @@ -295,7 +295,7 @@ describe('integration: launch:env:create on the wire', () => {
});

it('refuses Skip combined with another source before creating anything', async () => {
answerPrompts({ 'Add environment variables': ['Skip adding environment variables', 'Type KEY=VALUE pairs'] });
answerPrompts({ 'Add environment variables': ['Skip adding environment variables', 'Manually add custom variables'] });
const sent: unknown[] = [];
stubCreate(sent);

Expand Down
Loading
Loading