mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-07 21:07:46 +00:00
`vc deploy` ignores `.env.local`. To make sure we don't inadvertently push people's secrets to source control, have all environment pulls default to writing to `.env.local`.
389 lines
13 KiB
TypeScript
389 lines
13 KiB
TypeScript
import fs from 'fs-extra';
|
||
import path from 'path';
|
||
import { parse } from 'dotenv';
|
||
import env from '../../../src/commands/env';
|
||
import { setupUnitFixture } from '../../helpers/setup-unit-fixture';
|
||
import { client } from '../../mocks/client';
|
||
import { defaultProject, envs, useProject } from '../../mocks/project';
|
||
import { useTeams } from '../../mocks/team';
|
||
import { useUser } from '../../mocks/user';
|
||
|
||
describe('env', () => {
|
||
describe('pull', () => {
|
||
it('should handle pulling', async () => {
|
||
const cwd = setupUnitFixture('vercel-env-pull');
|
||
useUser();
|
||
useTeams('team_dummy');
|
||
useProject({
|
||
...defaultProject,
|
||
id: 'vercel-env-pull',
|
||
name: 'vercel-env-pull',
|
||
});
|
||
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
|
||
const exitCodePromise = env(client);
|
||
await expect(client.stderr).toOutput(
|
||
'Downloading `development` Environment Variables for Project vercel-env-pull'
|
||
);
|
||
await expect(client.stderr).toOutput('Created .env.local file');
|
||
await expect(exitCodePromise).resolves.toEqual(0);
|
||
|
||
const rawDevEnv = await fs.readFile(path.join(cwd, '.env.local'));
|
||
|
||
// check for development env value
|
||
const devFileHasDevEnv = rawDevEnv.toString().includes('SPECIAL_FLAG');
|
||
expect(devFileHasDevEnv).toBeTruthy();
|
||
});
|
||
|
||
it('should handle pulling from Preview env vars', async () => {
|
||
const cwd = setupUnitFixture('vercel-env-pull');
|
||
useUser();
|
||
useTeams('team_dummy');
|
||
useProject({
|
||
...defaultProject,
|
||
id: 'vercel-env-pull',
|
||
name: 'vercel-env-pull',
|
||
});
|
||
client.setArgv(
|
||
'env',
|
||
'pull',
|
||
'--yes',
|
||
'--cwd',
|
||
cwd,
|
||
'--environment',
|
||
'preview'
|
||
);
|
||
const exitCodePromise = env(client);
|
||
await expect(client.stderr).toOutput(
|
||
'Downloading `preview` Environment Variables for Project vercel-env-pull'
|
||
);
|
||
await expect(client.stderr).toOutput('Created .env.local file');
|
||
await expect(exitCodePromise).resolves.toEqual(0);
|
||
|
||
// check for Preview env vars
|
||
const rawDevEnv = await fs.readFile(path.join(cwd, '.env.local'), 'utf8');
|
||
expect(rawDevEnv).toContain(
|
||
'REDIS_CONNECTION_STRING="redis://abc123@redis.example.com:6379"'
|
||
);
|
||
expect(rawDevEnv).not.toContain(
|
||
'BRANCH_ENV_VAR="env var for a specific branch"'
|
||
);
|
||
});
|
||
|
||
it('should handle pulling from specific Git branch', async () => {
|
||
const cwd = setupUnitFixture('vercel-env-pull');
|
||
useUser();
|
||
useTeams('team_dummy');
|
||
useProject({
|
||
...defaultProject,
|
||
id: 'vercel-env-pull',
|
||
name: 'vercel-env-pull',
|
||
});
|
||
client.setArgv(
|
||
'env',
|
||
'pull',
|
||
'--yes',
|
||
'--cwd',
|
||
cwd,
|
||
'--environment',
|
||
'preview',
|
||
'--git-branch',
|
||
'feat/awesome-thing'
|
||
);
|
||
const exitCodePromise = env(client);
|
||
await expect(client.stderr).toOutput(
|
||
'Downloading `preview` Environment Variables for Project vercel-env-pull'
|
||
);
|
||
await expect(client.stderr).toOutput('Created .env.local file');
|
||
await expect(exitCodePromise).resolves.toEqual(0);
|
||
|
||
// check for Preview env vars
|
||
const rawDevEnv = await fs.readFile(path.join(cwd, '.env.local'), 'utf8');
|
||
expect(rawDevEnv).toContain(
|
||
'REDIS_CONNECTION_STRING="redis://abc123@redis.example.com:6379"'
|
||
);
|
||
expect(rawDevEnv).toContain(
|
||
'BRANCH_ENV_VAR="env var for a specific branch"'
|
||
);
|
||
|
||
const parsed = parse(rawDevEnv);
|
||
const keys = Object.keys(parsed);
|
||
expect(keys).toHaveLength(3);
|
||
expect(keys[0]).toEqual('ANOTHER');
|
||
expect(keys[1]).toEqual('BRANCH_ENV_VAR');
|
||
expect(keys[2]).toEqual('REDIS_CONNECTION_STRING');
|
||
});
|
||
|
||
it('should handle alternate filename', async () => {
|
||
const cwd = setupUnitFixture('vercel-env-pull');
|
||
useUser();
|
||
useTeams('team_dummy');
|
||
useProject({
|
||
...defaultProject,
|
||
id: 'vercel-env-pull',
|
||
name: 'vercel-env-pull',
|
||
});
|
||
client.setArgv('env', 'pull', 'other.env', '--yes', '--cwd', cwd);
|
||
const exitCodePromise = env(client);
|
||
await expect(client.stderr).toOutput(
|
||
'Downloading `development` Environment Variables for Project vercel-env-pull'
|
||
);
|
||
await expect(client.stderr).toOutput('Created other.env file');
|
||
await expect(exitCodePromise).resolves.toEqual(0);
|
||
|
||
const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env'));
|
||
|
||
// check for development env value
|
||
const devFileHasDevEnv = rawDevEnv.toString().includes('SPECIAL_FLAG');
|
||
expect(devFileHasDevEnv).toBeTruthy();
|
||
});
|
||
|
||
it('should use given environment', async () => {
|
||
const cwd = setupUnitFixture('vercel-env-pull');
|
||
useUser();
|
||
useTeams('team_dummy');
|
||
useProject({
|
||
...defaultProject,
|
||
id: 'vercel-env-pull',
|
||
name: 'vercel-env-pull',
|
||
});
|
||
|
||
client.setArgv(
|
||
'env',
|
||
'pull',
|
||
'--environment',
|
||
'production',
|
||
'--cwd',
|
||
cwd
|
||
);
|
||
const exitCodePromise = env(client);
|
||
await expect(client.stderr).toOutput(
|
||
`Downloading \`production\` Environment Variables for Project vercel-env-pull`
|
||
);
|
||
await expect(client.stderr).toOutput('Created .env.local file');
|
||
await expect(exitCodePromise).resolves.toEqual(0);
|
||
|
||
const rawProdEnv = await fs.readFile(path.join(cwd, '.env.local'));
|
||
|
||
// check for development env value
|
||
const envFileHasEnv = rawProdEnv
|
||
.toString()
|
||
.includes('REDIS_CONNECTION_STRING');
|
||
expect(envFileHasEnv).toBeTruthy();
|
||
});
|
||
|
||
it('should throw an error when it does not recognize given environment', async () => {
|
||
const cwd = setupUnitFixture('vercel-env-pull');
|
||
useUser();
|
||
useTeams('team_dummy');
|
||
useProject({
|
||
...defaultProject,
|
||
id: 'vercel-env-pull',
|
||
name: 'vercel-env-pull',
|
||
});
|
||
|
||
client.setArgv(
|
||
'env',
|
||
'pull',
|
||
'.env.production',
|
||
'--environment',
|
||
'something-invalid',
|
||
'--cwd',
|
||
cwd
|
||
);
|
||
|
||
const exitCodePromise = env(client);
|
||
await expect(client.stderr).toOutput(
|
||
`Invalid environment \`something-invalid\`. Valid options: <production | preview | development>`
|
||
);
|
||
|
||
await expect(exitCodePromise).resolves.toEqual(1);
|
||
});
|
||
|
||
it('should expose production system env variables', async () => {
|
||
const cwd = setupUnitFixture('vercel-env-pull');
|
||
useUser();
|
||
useTeams('team_dummy');
|
||
useProject({
|
||
...defaultProject,
|
||
id: 'vercel-env-pull',
|
||
name: 'vercel-env-pull',
|
||
autoExposeSystemEnvs: true,
|
||
});
|
||
|
||
client.setArgv('env', 'pull', 'other.env', '--yes', '--cwd', cwd);
|
||
const exitCodePromise = env(client);
|
||
await expect(client.stderr).toOutput(
|
||
'Downloading `development` Environment Variables for Project vercel-env-pull'
|
||
);
|
||
await expect(client.stderr).toOutput('Created other.env file');
|
||
await expect(exitCodePromise).resolves.toEqual(0);
|
||
|
||
const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env'));
|
||
|
||
const productionFileHasVercelEnv = rawDevEnv
|
||
.toString()
|
||
.includes('VERCEL_ENV="development"');
|
||
expect(productionFileHasVercelEnv).toBeTruthy();
|
||
});
|
||
|
||
it('should show a delta string', async () => {
|
||
const cwd = setupUnitFixture('vercel-env-pull-delta');
|
||
try {
|
||
useUser();
|
||
useTeams('team_dummy');
|
||
useProject({
|
||
...defaultProject,
|
||
id: 'env-pull-delta',
|
||
name: 'env-pull-delta',
|
||
});
|
||
|
||
client.setArgv('env', 'add', 'NEW_VAR', '--cwd', cwd);
|
||
const addPromise = env(client);
|
||
|
||
await expect(client.stderr).toOutput('What’s the value of NEW_VAR?');
|
||
client.stdin.write('testvalue\n');
|
||
|
||
await expect(client.stderr).toOutput(
|
||
'Add NEW_VAR to which Environments (select multiple)?'
|
||
);
|
||
client.stdin.write('\x1B[B'); // Down arrow
|
||
client.stdin.write('\x1B[B');
|
||
client.stdin.write(' ');
|
||
client.stdin.write('\r');
|
||
|
||
await expect(addPromise).resolves.toEqual(0);
|
||
|
||
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
|
||
const pullPromise = env(client);
|
||
await expect(client.stderr).toOutput(
|
||
'Downloading `development` Environment Variables for Project env-pull-delta'
|
||
);
|
||
await expect(client.stderr).toOutput(
|
||
'+ SPECIAL_FLAG (Updated)\n+ NEW_VAR\n- TEST\n'
|
||
);
|
||
await expect(client.stderr).toOutput('Updated .env.local file');
|
||
|
||
await expect(pullPromise).resolves.toEqual(0);
|
||
} finally {
|
||
client.setArgv('env', 'rm', 'NEW_VAR', '--yes', '--cwd', cwd);
|
||
await env(client);
|
||
}
|
||
});
|
||
|
||
it('should not show a delta string when it fails to read a file', async () => {
|
||
const cwd = setupUnitFixture('vercel-env-pull-delta-corrupt');
|
||
useUser();
|
||
useTeams('team_dummy');
|
||
useProject({
|
||
...defaultProject,
|
||
id: 'env-pull-delta-corrupt',
|
||
name: 'env-pull-delta-corrupt',
|
||
});
|
||
|
||
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
|
||
const pullPromise = env(client);
|
||
await expect(client.stderr).toOutput('Updated .env.local file');
|
||
await expect(pullPromise).resolves.toEqual(0);
|
||
});
|
||
|
||
it('should show that no changes were found', async () => {
|
||
const cwd = setupUnitFixture('vercel-env-pull-delta-no-changes');
|
||
useUser();
|
||
useTeams('team_dummy');
|
||
useProject({
|
||
...defaultProject,
|
||
id: 'env-pull-delta-no-changes',
|
||
name: 'env-pull-delta-no-changes',
|
||
});
|
||
|
||
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
|
||
const pullPromise = env(client);
|
||
await expect(client.stderr).toOutput('> No changes found.');
|
||
await expect(client.stderr).toOutput('Updated .env.local file');
|
||
await expect(pullPromise).resolves.toEqual(0);
|
||
});
|
||
|
||
it('should correctly render delta string when env variable has quotes', async () => {
|
||
const cwd = setupUnitFixture('vercel-env-pull-delta-quotes');
|
||
try {
|
||
useUser();
|
||
useTeams('team_dummy');
|
||
useProject(
|
||
{
|
||
...defaultProject,
|
||
id: 'env-pull-delta-quotes',
|
||
name: 'env-pull-delta-quotes',
|
||
},
|
||
[
|
||
...envs,
|
||
{
|
||
type: 'encrypted',
|
||
id: '781dt89g8r2h789g',
|
||
key: 'NEW_VAR',
|
||
value: '"testvalue"',
|
||
target: ['development'],
|
||
configurationId: null,
|
||
updatedAt: 1557241361455,
|
||
createdAt: 1557241361455,
|
||
},
|
||
]
|
||
);
|
||
|
||
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
|
||
const pullPromise = env(client);
|
||
await expect(client.stderr).toOutput(
|
||
'Downloading `development` Environment Variables for Project env-pull-delta'
|
||
);
|
||
await expect(client.stderr).toOutput('No changes found.\n');
|
||
await expect(client.stderr).toOutput('Updated .env.local file');
|
||
|
||
await expect(pullPromise).resolves.toEqual(0);
|
||
} finally {
|
||
client.setArgv('env', 'rm', 'NEW_VAR', '--yes', '--cwd', cwd);
|
||
await env(client);
|
||
}
|
||
});
|
||
|
||
it('should correctly render delta string when local env variable has quotes', async () => {
|
||
const cwd = setupUnitFixture('vercel-env-pull-delta-quotes');
|
||
try {
|
||
useUser();
|
||
useTeams('team_dummy');
|
||
useProject(
|
||
{
|
||
...defaultProject,
|
||
id: 'env-pull-delta-quotes',
|
||
name: 'env-pull-delta-quotes',
|
||
},
|
||
[
|
||
...envs,
|
||
{
|
||
type: 'encrypted',
|
||
id: '781dt89g8r2h789g',
|
||
key: 'NEW_VAR',
|
||
value: 'testvalue',
|
||
target: ['development'],
|
||
configurationId: null,
|
||
updatedAt: 1557241361455,
|
||
createdAt: 1557241361455,
|
||
},
|
||
]
|
||
);
|
||
|
||
client.setArgv('env', 'pull', '.env.testquotes', '--yes', '--cwd', cwd);
|
||
const pullPromise = env(client);
|
||
await expect(client.stderr).toOutput(
|
||
'Downloading `development` Environment Variables for Project env-pull-delta'
|
||
);
|
||
await expect(client.stderr).toOutput('No changes found.\n');
|
||
await expect(client.stderr).toOutput('Updated .env.testquotes file');
|
||
|
||
await expect(pullPromise).resolves.toEqual(0);
|
||
} finally {
|
||
client.setArgv('env', 'rm', 'NEW_VAR', '--yes', '--cwd', cwd);
|
||
await env(client);
|
||
}
|
||
});
|
||
});
|
||
});
|