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 @@ -93,7 +93,7 @@ fileignoreconfig:
- "'SECRET=hunter2'"
- "formFieldKey"
- filename: test/integration/projects-create.test.ts
checksum: 4826ca20f0c7fe1f23b82598ceed0a374199e2bb8b604b676efcf720244ff961
checksum: c567d31fb18d0910410ce455c0a1b2f8ce6c8d46038396ec813ef73bd5fb8105
- filename: test/fixtures/README.md
checksum: d801c84c8b7d7f38f967573e9a3a70da4405b7cbcbf10d2a6e5be3d65fb3daa1
allowed_patterns:
Expand Down Expand Up @@ -236,7 +236,7 @@ fileignoreconfig:
- "a project name passed on argv to the project resolver"
- "expect\\(Object.keys\\(DEPENDENCIES\\)\\).toEqual\\(\\['project', 'env'\\]\\)"
- filename: AGENTS.md
checksum: ba37b6b17d97940a6f7d5e69a531796ab0ee5b6859a9cd5cbdec56e4b7731b26
checksum: 3379e8c597c8306e7377e60667f7df6e599e45e289928bc88b217356aa43feb1
allowed_patterns:
- "Do not add an exemption to a guard to make a new file pass"
- "passes its own error wording to `client.request\\(req, MESSAGES\\)`"
Expand All @@ -259,7 +259,7 @@ fileignoreconfig:
- filename: test/guards/layering.test.ts
checksum: 55893e6e919b1e462afc989110cb9360e38aa18d06de932587beaa9006042639
- filename: src/commands/launch/projects/create.test.ts
checksum: 9c40b4b6ed4f7d678ecb921a71f5af7284cd685c1337f47144456ae43a38b1ad
checksum: 2c407c366f2a1da2647fc6eb7e6ad1bb8cbe3c7f901090432ab0e27df2e5d4d6
- filename: src/commands/launch/projects/delete.test.ts
checksum: d67d8a804d14f9b809153d10b0cf9d5e52b70f9e0b65678ebb1663a7d2da22c1
- filename: test/integration/projects-delete.test.ts
Expand Down Expand Up @@ -311,7 +311,7 @@ fileignoreconfig:
- filename: test/credential-guard.setup.ts
checksum: 519ccbeb0552fdb1d5f27308c56477c0258ee6cc0adec8f618e8e5bc047cbf6d
- filename: src/environments/types.ts
checksum: 6c054295075e51e9ce0f4514ebdc86a48bb74db9932a2baaa9f9158e9c14c6e8
checksum: 2825d0e70757022e518465b134878b8cc8f9eccaf4e9ad088d73a31b9a35a826
- filename: src/commands/launch/env/get.test.ts
checksum: 6e739ef12bc0d887e652e23ee54d9fac30dd6a3505cf464cdaefc0e696cf3e51
- filename: test/integration/environments-delete.test.ts
Expand All @@ -331,13 +331,13 @@ fileignoreconfig:
- filename: src/commands/launch/env/update.test.ts
checksum: 2da219594e7bdd589adc0eff9ff035252a75aaa51dfa20514b18fd3266d7b752
- filename: README.md
checksum: 112b973a24e59f79786df1b85b83ea35b1e355c52a8cbf3b28d98511959c60c8
checksum: d955f95b66f10259eb2ddbe1935a67ce3c7f3a28187f0398e0af39ff75267c01
allowed_patterns:
- "password-protection"
- "-p, --password"
- "-u, --username"
- filename: test/integration/environments-create.test.ts
checksum: 0c64a4408d3c150a33c442403688d1e1d058f415cfdba704e372455927daa7e4
checksum: 538527a26bc002aa21319aa615c9a9f00aaed494d0bdefc8db75c0eff3f8ec54
- filename: src/commands/launch/variables/list.test.ts
checksum: 87ccb762f4931326fc50bca1c01aeb4a16d552e4dfa141d00370fdfafa4363be
- filename: src/commands/launch/variables/set.test.ts
Expand All @@ -353,9 +353,9 @@ fileignoreconfig:
- filename: src/stacks/stacks.api.ts
checksum: d59622ca6411b93d5469a1895b08ccde4b9cd83fbb909c82076ea70d74b5e1e4
- filename: src/variables/variable.chooser.ts
checksum: c776a4184eb1819055622784bc6f3342b67bc1babdca0cdbbe72a4a871cc2498
checksum: 767bbcee93b0c10c5c9d0b526ce57770cc6323be52d8a4cc1c0452c880b5ab8e
- filename: src/variables/variable.create.test.ts
checksum: c888e2a4aca9c6855c79d743368387aeb0ab5db907beca8bef1113bc05a5260d
checksum: a2650ca8e081afcf76c801d20e2df4baa96f814d1513080ba24971db0ae3a1bf
- filename: src/variables/variable.dotenv.test.ts
checksum: c3d5e33189736bb42b7c4fac7375cf0307681b34feca4830b49331f55a130f78
- filename: src/variables/variable.dotenv.ts
Expand All @@ -365,9 +365,9 @@ fileignoreconfig:
- filename: src/variables/variable.files.test.ts
checksum: d8b0efd6bb448c4d501ba52c47119b352ef29d6b5b66cec0e7d373a0ce91a36c
- filename: src/variables/variable.inputs.test.ts
checksum: c434fd7f32ab0bd17db8f214ba54861b7d8ae8d8481dbbc1978f9dab35fd2429
checksum: 4b3b9a6c353d099a65ef8f522cff8e46e5c0edbf66bcdb59e0d6dd4f87c78c84
- filename: src/variables/variable.inputs.ts
checksum: 050b93e158a1ff29f309f1531b085aa9f0327b47a8029038356869b2864531b2
checksum: 0f572a86e0c0edbe707882df623a3a7f69715572812ae655eb6aa5e4579b496b
- filename: src/variables/variable.merge.test.ts
checksum: 581a5cc5d9d23223643bd461525c6755205ebf16f33ae7388c8e88de19461fbf
- filename: src/variables/variable.merge.ts
Expand Down
7 changes: 3 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,10 +361,9 @@ than return a truncated list.
`projects:create` and `env:create` take `--var` and `--dotenv` and reach the same resolver through
`EnvironmentBuilder`, which asks last - after Contentstack Authentication, where V1 asked - with no auto-detection.
With neither flag it offers a checkbox over the sources plus `Skip adding environment variables`, which cannot be
combined with the others; without a terminal it sends an empty list. `--no-vars` is the flag form of that Skip
choice, so a fully flagged create on a terminal asks nothing: it sends an empty list without the checkbox. Combined
with `--var`, `--dotenv` or `--from-stack` it is a usage error from the `noVarsAlone` rule rather than oclif's
`exclusive`, whose `--help` usage line would show those three sources excluding each other.
combined with the others; a bare Enter with nothing ticked skips the same way, and without a terminal it sends an
empty list. There is no `--no-vars` flag: variables are never required, so a fully flagged create on a terminal
stops only at that checkbox, and a pipe or CI run skips it.

**`--from-stack` and `--alias`.** `--from-stack` is a bare flag on `variables:set`, `projects:create` and
`env:create`; `--alias <name>` (`-a`) names a delivery token saved with `csdx auth:tokens:add`, and is a usage error
Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ USAGE
$ csdx launch:env:create [-c <value>] [-d <value>] [--org <value>] [--project <value>] [--name <value>]
[--description <value>] [--branch <value>] [--framework <value>] [--build-cmd <value>] [--server-cmd <value>]
[--output-dir <value>] [--res-mode <value>] [--auto-deploy <value>] [--cs-auth <value>] [--var <value>...] [--dotenv
<value>...] [--from-stack] [-a <value>] [--no-vars]
<value>...] [--from-stack] [-a <value>]

FLAGS
-a, --alias=<value> Alias of a delivery token saved with csdx auth:tokens:add, used with --from-stack without a
Expand All @@ -337,7 +337,6 @@ FLAGS
--from-stack Import the variables of a Contentstack stack. Asks for the stack and a delivery token on a
terminal
--name=<value> Environment name (200 characters or fewer)
--no-vars Create the environment with no variables, without asking for them on a terminal
--org=<value> Organization UID
--output-dir=<value> Output directory
--project=<value> Project name or UID
Expand Down Expand Up @@ -611,7 +610,7 @@ USAGE
$ csdx launch:projects:create [-c <value>] [-d <value>] [--org <value>] [--type <value>] [--name <value>] [--description
<value>] [--env-name <value>] [--branch <value>] [--framework <value>] [--build-cmd <value>] [--server-cmd <value>]
[--output-dir <value>] [--res-mode <value>] [--auto-deploy <value>] [--cs-auth <value>] [--var <value>...] [--dotenv
<value>...] [--from-stack] [-a <value>] [--no-vars]
<value>...] [--from-stack] [-a <value>]

FLAGS
-a, --alias=<value> Alias of a delivery token saved with csdx auth:tokens:add, used with --from-stack without a
Expand All @@ -630,7 +629,6 @@ FLAGS
--from-stack Import the variables of a Contentstack stack. Asks for the stack and a delivery token on a
terminal
--name=<value> Project name (200 characters or fewer)
--no-vars Create the environment with no variables, without asking for them on a terminal
--org=<value> Organization UID
--output-dir=<value> Output directory
--res-mode=<value> Response mode (buffered | streaming)
Expand Down
5 changes: 2 additions & 3 deletions src/commands/launch/env/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { catalog, resolution } from '../../../resources';
import { DEPLOYMENT_WAIT_TIMEOUT_MS } from '../../../deployments/deployment.watcher';
import { EnvironmentCreator } from '../../../environments/environment.create';
import { serverCommandFrameworkGate } from '../../../environments/environment.inputs';
import { aliasRequiresFromStack, noVarsAlone } from '../../../variables/variable.rules';
import { aliasRequiresFromStack } from '../../../variables/variable.rules';
import EnvironmentsCreate from './create';

function commandUnderTest(resolved: Record<string, unknown>) {
Expand Down Expand Up @@ -102,10 +102,9 @@ describe('launch:env:create', () => {
dotenv: {},
'from-stack': {},
alias: {},
'no-vars': {},
});
expect(Object.keys(EnvironmentsCreate.flags).sort()).toEqual(Object.keys(EnvironmentsCreate.inputs).sort());
expect(EnvironmentsCreate.rules).toEqual([serverCommandFrameworkGate, aliasRequiresFromStack, noVarsAlone]);
expect(EnvironmentsCreate.rules).toEqual([serverCommandFrameworkGate, aliasRequiresFromStack]);
});

it('describes --name and --description as the environment its own, leaving the project wording alone', () => {
Expand Down
6 changes: 2 additions & 4 deletions src/commands/launch/env/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { flagsFor, inputs } from '../../../core/inputs';
import { defaultWatchTiming } from '../../../deployments/deployment.watcher';
import { EnvironmentCreator } from '../../../environments/environment.create';
import { serverCommandFrameworkGate, withEnvironmentWording } from '../../../environments/environment.inputs';
import { aliasRequiresFromStack, noVarsAlone } from '../../../variables/variable.rules';
import { aliasRequiresFromStack } from '../../../variables/variable.rules';

const createInputs = inputs({
org: { required: true },
Expand All @@ -22,7 +22,6 @@ const createInputs = inputs({
dotenv: {},
'from-stack': {},
alias: {},
'no-vars': {},
});

export default class EnvironmentsCreate extends LaunchCommand<typeof createInputs> {
Expand All @@ -39,7 +38,7 @@ export default class EnvironmentsCreate extends LaunchCommand<typeof createInput

static flags = withEnvironmentWording(flagsFor(createInputs));

static rules = [serverCommandFrameworkGate, aliasRequiresFromStack, noVarsAlone];
static rules = [serverCommandFrameworkGate, aliasRequiresFromStack];

async run(): Promise<void> {
const resolved = this.resolved;
Expand All @@ -63,7 +62,6 @@ export default class EnvironmentsCreate extends LaunchCommand<typeof createInput
envFiles: resolved['dotenv'],
fromStack: resolved['from-stack'],
alias: resolved.alias,
noVars: resolved['no-vars'],
});
}
}
5 changes: 2 additions & 3 deletions src/commands/launch/projects/create.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { defaultWatchTiming } from '../../../deployments/deployment.watcher';
import { DEPLOYMENT_WAIT_TIMEOUT_MS } from '../../../deployments/deployment.watcher';
import { serverCommandFrameworkGate } from '../../../environments/environment.inputs';
import { aliasRequiresFromStack, noVarsAlone } from '../../../variables/variable.rules';
import { aliasRequiresFromStack } from '../../../variables/variable.rules';
import { ProjectCreator } from '../../../projects/project.create';

type WatchTiming = ReturnType<typeof defaultWatchTiming>;
Expand Down Expand Up @@ -101,7 +101,7 @@ describe('launch:projects:create', () => {
});

it('declares only the framework gate as a rule, leaving GitHub-only flags on FileUpload to a confirmation', () => {
expect(ProjectsCreate.rules).toEqual([serverCommandFrameworkGate, aliasRequiresFromStack, noVarsAlone]);
expect(ProjectsCreate.rules).toEqual([serverCommandFrameworkGate, aliasRequiresFromStack]);
});

it('declares every flag it reads and requires only the organization', () => {
Expand All @@ -123,7 +123,6 @@ describe('launch:projects:create', () => {
'dotenv',
'from-stack',
'alias',
'no-vars',
]);
expect(Object.keys(ProjectsCreate.flags).sort()).toEqual(Object.keys(ProjectsCreate.inputs).sort());
expect(
Expand Down
6 changes: 2 additions & 4 deletions src/commands/launch/projects/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { flagsFor, inputs } from '../../../core/inputs';
import { defaultWatchTiming } from '../../../deployments/deployment.watcher';
import { serverCommandFrameworkGate } from '../../../environments/environment.inputs';
import { ProjectCreator } from '../../../projects/project.create';
import { aliasRequiresFromStack, noVarsAlone } from '../../../variables/variable.rules';
import { aliasRequiresFromStack } from '../../../variables/variable.rules';

const createInputs = inputs({
org: { required: true },
Expand All @@ -23,7 +23,6 @@ const createInputs = inputs({
dotenv: {},
'from-stack': {},
alias: {},
'no-vars': {},
});

export default class ProjectsCreate extends LaunchCommand<typeof createInputs> {
Expand All @@ -42,7 +41,7 @@ export default class ProjectsCreate extends LaunchCommand<typeof createInputs> {

static flags = flagsFor(createInputs);

static rules = [serverCommandFrameworkGate, aliasRequiresFromStack, noVarsAlone];
static rules = [serverCommandFrameworkGate, aliasRequiresFromStack];

async run(): Promise<void> {
const resolved = this.resolved;
Expand All @@ -67,7 +66,6 @@ export default class ProjectsCreate extends LaunchCommand<typeof createInputs> {
envFiles: resolved['dotenv'],
fromStack: resolved['from-stack'],
alias: resolved.alias,
noVars: resolved['no-vars'],
});
}
}
1 change: 0 additions & 1 deletion src/environments/environment.builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export class EnvironmentBuilder {
envFiles: request.envFiles,
fromStack: request.fromStack,
alias: request.alias,
noVars: request.noVars,
dataDir: request.dataDir ?? '.',
},
this.services,
Expand Down
1 change: 0 additions & 1 deletion src/environments/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ export interface EnvironmentRequest {
envFiles?: string[];
fromStack?: boolean;
alias?: string;
noVars?: boolean;
framework?: FrameworkPreset;
buildCmd?: string;
outputDir?: string;
Expand Down
18 changes: 8 additions & 10 deletions src/variables/variable.chooser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,17 @@ export async function chooseSources(
{ name: TYPED_CHOICE, value: TYPED_CHOICE },
...(prompt.skippable ? [{ name: SKIP_CHOICE, value: SKIP_CHOICE }] : []),
];
const picked = await askChoices(services.ux, prompt.message, choices, { requireOne: prompt.skippable });
const picked = await askChoices(services.ux, prompt.message, choices, { requireOne: false });

if (picked.includes(SKIP_CHOICE)) {
if (picked.length > 1) {
throw new UsageError(`'${SKIP_CHOICE}' cannot be combined with the other options.`);
}

services.ux.print('Skipped adding environment variables.');

return undefined;
if (picked.includes(SKIP_CHOICE) && picked.length > 1) {
throw new UsageError(`'${SKIP_CHOICE}' cannot be combined with the other options.`);
}

if (picked.length === 0) {
if (picked.length === 0 || picked.includes(SKIP_CHOICE)) {
if (prompt.skippable) {
services.ux.print('Skipped adding environment variables.');
}

return undefined;
}

Expand Down
22 changes: 11 additions & 11 deletions src/variables/variable.create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,6 @@ describe('variable create', () => {
expect(printed).toEqual([]);
});

it('returns nothing on a terminal with --no-vars, asking nothing, reading nothing and printing nothing', async () => {
const { services, io, asked, reads, printed } = fakes({ files: { '/work/.env': 'X=1' } });

expect(await variablesForCreate({ ...NONE, noVars: true }, services, io)).toEqual([]);
expect(asked).toEqual([]);
expect(reads).toEqual([]);
expect(printed).toEqual([]);
});

it('treats empty flag lists as no flags', async () => {
const { services, io, asked } = fakes({ answers: { 'Add environment variables': [SKIP] } });

Expand All @@ -128,6 +119,15 @@ describe('variable create', () => {
});
});

it('lets a bare Enter through the checkbox with nothing selected', async () => {
const { services, io, asked } = fakes({ answers: { 'Add environment variables': [] } });

await variablesForCreate(NONE, services, io);

const validate = asked[0].payload.validate as (picked: unknown) => true | string;
expect(validate([])).toBe(true);
});

it('returns nothing and says so when Skip is chosen', async () => {
const { services, io, printed } = fakes({ answers: { 'Add environment variables': [SKIP] } });

Expand Down Expand Up @@ -233,11 +233,11 @@ describe('variable create', () => {
expect(output).not.toContain('never-printed-value');
});

it.each([[undefined], [[]]])('returns nothing and prints no summary when nothing was selected (%p)', async (answer) => {
it.each([[undefined], [[]]])('skips and says so, as Skip does, when nothing was selected (%p)', async (answer) => {
const { services, io, printed } = fakes({ answers: { 'Add environment variables': answer } });

expect(await variablesForCreate(NONE, services, io)).toEqual([]);
expect(printed).toEqual([]);
expect(printed).toEqual(['Skipped adding environment variables.']);
});

it('propagates a cancelled path prompt, an unreadable file and a malformed typed line', async () => {
Expand Down
5 changes: 0 additions & 5 deletions src/variables/variable.create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export interface CreateVariablesRequest {
org: string;
fromStack?: boolean;
alias?: string;
noVars?: boolean;
}

const CHOOSE_MESSAGE = 'Add environment variables';
Expand Down Expand Up @@ -46,10 +45,6 @@ export async function variablesForCreate(
io: VariableIo = DEFAULT_VARIABLE_IO,
savedToken: (alias: string) => SavedToken | undefined = readSavedToken,
): Promise<EnvironmentVariableInput[]> {
if (request.noVars === true) {
return [];
}

const stack: StackImporter = (alias) => importFromStack({ org: request.org, alias }, { services, savedToken });

if (given(request.vars) || given(request.envFiles) || request.fromStack === true) {
Expand Down
15 changes: 2 additions & 13 deletions src/variables/variable.inputs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { variableFlags, variableResolution } from './variable.inputs';

describe('variable inputs', () => {
it('names each flag as the Commands Details "All flags" tables name it', () => {
expect(Object.keys(variableFlags)).toEqual(['var', 'dotenv', 'key', 'from-stack', 'alias', 'no-vars']);
expect(Object.keys(variableFlags)).toEqual(['var', 'dotenv', 'key', 'from-stack', 'alias']);
});

it.each(['var', 'dotenv', 'key'] as const)('declares %s repeatable so each occurrence and each value is kept', (name) => {
expect(variableFlags[name].multiple).toBe(true);
});

it.each(['var', 'dotenv', 'key', 'from-stack', 'alias', 'no-vars'] as const)('never marks %s required at parse time', (name) => {
it.each(['var', 'dotenv', 'key', 'from-stack', 'alias'] as const)('never marks %s required at parse time', (name) => {
expect(variableFlags[name].required).toBeFalsy();
});

Expand Down Expand Up @@ -40,17 +40,6 @@ describe('variable inputs', () => {
expect(parsed.flags.alias).toBe('my-prod');
});

it('parses --no-vars as a presence-only flag', async () => {
const parsed = await Parser.parse(['--no-vars'], { flags: { 'no-vars': variableFlags['no-vars'] } });

expect(variableFlags['no-vars'].type).toBe('boolean');
expect(parsed.flags['no-vars']).toBe(true);
});

it('leaves the sources out of an oclif exclusive group, since --help would show them excluding each other', () => {
expect(variableFlags['no-vars'].exclusive).toBeUndefined();
});

it('describes --var without showing a value that looks like a secret', () => {
expect(variableFlags.var.description).toContain('KEY=VALUE');
expect(variableFlags.var.description).not.toMatch(/sk_|ghp_|password/i);
Expand Down
4 changes: 0 additions & 4 deletions src/variables/variable.inputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ export const variableFlags = {
char: 'a',
description: 'Alias of a delivery token saved with csdx auth:tokens:add, used with --from-stack without a prompt',
}),
'no-vars': Flags.boolean({
description: 'Create the environment with no variables, without asking for them on a terminal',
}),
};

export const variableResolution = {
Expand All @@ -32,5 +29,4 @@ export const variableResolution = {
key: {} satisfies ResolutionSpec<string[]>,
'from-stack': {} satisfies ResolutionSpec<boolean>,
alias: {} satisfies ResolutionSpec<string>,
'no-vars': {} satisfies ResolutionSpec<boolean>,
};
Loading
Loading