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
5 changes: 5 additions & 0 deletions .changeset/hydrogen-env-push-agent-confirm.md
Comment thread
gonzaloriestra marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/cli-hydrogen': patch
---

Add `--force` and `--dry-run` flags to `hydrogen env push`.
18 changes: 18 additions & 0 deletions packages/cli/oclif.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,24 @@
"hasDynamicHelp": false,
"multiple": false,
"type": "option"
},
"force": {
"char": "f",
"description": "Push environment variable changes without confirmation.",
"env": "SHOPIFY_HYDROGEN_FLAG_FORCE",
"name": "force",
"allowNo": false,
"type": "boolean"
},
"dry-run": {
"description": "Preview environment variable changes without pushing them.",
"env": "SHOPIFY_HYDROGEN_FLAG_DRY_RUN",
"exclusive": [
"force"
],
"name": "dry-run",
"allowNo": false,
"type": "boolean"
}
},
"hasDynamicHelp": false,
Expand Down
90 changes: 90 additions & 0 deletions packages/cli/src/commands/hydrogen/env/push.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -478,4 +478,94 @@ describe('pushVariables', () => {
);
});
});

it('does not prompt for diff confirmation when --force is provided', async () => {
vi.mocked(getStorefrontEnvVariables).mockResolvedValue({
id: SHOPIFY_CONFIG.storefront.id,
environmentVariables: [
{
id: '1',
key: 'EXISTING_TOKEN',
value: '1',
isSecret: false,
readOnly: false,
},
{
id: '2',
key: 'SECOND_TOKEN',
value: '2',
isSecret: false,
readOnly: false,
},
],
});

await inTemporaryDirectory(async (tmpDir) => {
const filePath = joinPath(tmpDir, envFile);
await writeFile(filePath, 'EXISTING_TOKEN=1\nSECOND_TOKEN=NEW_VALUE');
await expect(
runEnvPush({
path: tmpDir,
env: 'preview',
envFile,
force: true,
}),
).resolves.not.toThrow();

expect(renderConfirmationPrompt).not.toHaveBeenCalled();
expect(pushStorefrontEnvVariables).toHaveBeenCalledWith(
{storeFqdn: 'my-shop', token: 'abc123'},
'gid://shopify/HydrogenStorefront/2',
'gid://shopify/HydrogenStorefrontEnvironment/2',
[
{key: 'EXISTING_TOKEN', value: '1'},
{key: 'SECOND_TOKEN', value: 'NEW_VALUE'},
],
);
});
});

it('renders a diff without pushing when --dry-run is provided', async () => {
vi.mocked(getStorefrontEnvVariables).mockResolvedValue({
id: SHOPIFY_CONFIG.storefront.id,
environmentVariables: [
{
id: '1',
key: 'EXISTING_TOKEN',
value: '1',
isSecret: false,
readOnly: false,
},
{
id: '2',
key: 'SECOND_TOKEN',
value: '2',
isSecret: false,
readOnly: false,
},
],
});

await inTemporaryDirectory(async (tmpDir) => {
const filePath = joinPath(tmpDir, envFile);
await writeFile(filePath, 'EXISTING_TOKEN=1\nSECOND_TOKEN=NEW_VALUE');
await expect(
runEnvPush({
path: tmpDir,
env: 'preview',
envFile,
dryRun: true,
}),
).resolves.not.toThrow();

expect(renderConfirmationPrompt).not.toHaveBeenCalled();
expect(pushStorefrontEnvVariables).not.toHaveBeenCalled();
expect(outputMock.info()).toMatch(/The following changes would be made/);
expect(outputMock.info()).toMatch(/Preview/);
expect(outputMock.info()).toMatch(/SECOND_TOKEN=NEW_VALUE/);
expect(outputMock.info()).toMatch(
/No changes were pushed because --dry-run was used/,
);
});
});
});
34 changes: 32 additions & 2 deletions packages/cli/src/commands/hydrogen/env/push.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Command from '@shopify/cli-kit/node/base-command';
import {Flags} from '@oclif/core';
import {diffLines} from 'diff';
import {commonFlags, flagsToCamelObject} from '../../../lib/flags.js';
import {login} from '../../../lib/auth.js';
Expand Down Expand Up @@ -41,6 +42,16 @@ export default class EnvPush extends Command {
...commonFlags.env,
...commonFlags.envFile,
...commonFlags.path,
force: Flags.boolean({
char: 'f',
description: 'Push environment variable changes without confirmation.',
env: 'SHOPIFY_HYDROGEN_FLAG_FORCE',
}),
'dry-run': Flags.boolean({
description: 'Preview environment variable changes without pushing them.',
env: 'SHOPIFY_HYDROGEN_FLAG_DRY_RUN',
exclusive: ['force'],
}),
};

async run(): Promise<void> {
Expand All @@ -50,14 +61,18 @@ export default class EnvPush extends Command {
}

interface EnvPushOptions {
dryRun?: boolean;
env?: string;
envFile: string;
force?: boolean;
path?: string;
}

export async function runEnvPush({
dryRun = false,
env: envHandle,
envFile,
force = false,
path = process.cwd(),
}: EnvPushOptions) {
let validatedEnvironment: Environment;
Expand Down Expand Up @@ -181,8 +196,23 @@ export async function runEnvPush({
body: 'No changes to your environment variables.',
});
return;
} else {
const diff = diffLines(comparableRemoteVars, compareableLocalVars);
}

const diff = diffLines(comparableRemoteVars, compareableLocalVars);

if (dryRun) {
renderInfo({
body: outputContent`The following changes would be made to your environment variables for ${
validatedEnvironment.name
}:

${outputToken.linesDiff(diff)}
No changes were pushed because --dry-run was used.`.value,
});
return;
}

if (!force) {
const confirmPush = await renderConfirmationPrompt({
confirmationMessage: 'Yes, confirm changes',
cancellationMessage: 'No, make changes later',
Expand Down
Loading