[cli] Change vc env pull default output file to .env.local (#9892)

`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`.
This commit is contained in:
Dan Stowell
2023-05-22 17:24:38 -05:00
committed by GitHub
parent c52a59809e
commit 1b0d72aba5
11 changed files with 31 additions and 26 deletions

View File

@@ -0,0 +1,5 @@
---
'vercel': major
---
Change `vc env pull` default output file to `.env.local`

View File

@@ -4,7 +4,7 @@
You ran `vercel dev` inside a project that contains a `vercel.json` file with `env` or `build.env` properties that use [Vercel Secrets](https://vercel.com/docs/concepts/projects/environment-variables). You ran `vercel dev` inside a project that contains a `vercel.json` file with `env` or `build.env` properties that use [Vercel Secrets](https://vercel.com/docs/concepts/projects/environment-variables).
In order to use environment variables in your project locally that have values defined using the Vercel Secrets format (e.g. `@my-secret-value`), you will need to provide the value as an environment variable using a `.env`. In order to use environment variables in your project locally that have values defined using the Vercel Secrets format (e.g. `@my-secret-value`), you will need to provide the value as an environment variable using a `.env.local`.
We require this to ensure your app works as you intend it to, in the development environment, and to provide you with a way to mirror or separate private environment variables within your applications, for example when connecting to a database. We require this to ensure your app works as you intend it to, in the development environment, and to provide you with a way to mirror or separate private environment variables within your applications, for example when connecting to a database.
@@ -12,11 +12,11 @@ Read below for how to address this error.
#### Possible Ways to Fix It #### Possible Ways to Fix It
The error message will list environment variables that are required and which file they are required to be included in `.env`. The error message will list environment variables that are required and which file they are required to be included in `.env.local`.
If the file does not exist yet, please create the file that the error message mentions and insert the missing environment variable into it. If the file does not exist yet, please create the file that the error message mentions and insert the missing environment variable into it.
For example, if the error message shows that the environment variable `TEST` is missing from `.env`, then the `.env` file should look like this: For example, if the error message shows that the environment variable `TEST` is missing from `.env.local`, then the `.env.local` file should look like this:
``` ```
TEST=value TEST=value

View File

@@ -26,7 +26,7 @@ const help = () => {
ls [environment] [gitbranch] List all variables for the specified Environment ls [environment] [gitbranch] List all variables for the specified Environment
add [name] [environment] [gitbranch] Add an Environment Variable (see examples below) add [name] [environment] [gitbranch] Add an Environment Variable (see examples below)
rm [name] [environment] [gitbranch] Remove an Environment Variable (see examples below) rm [name] [environment] [gitbranch] Remove an Environment Variable (see examples below)
pull [filename] Pull all Development Environment Variables from the cloud and write to a file [.env] pull [filename] Pull all Development Environment Variables from the cloud and write to a file [.env.local]
${chalk.dim('Options:')} ${chalk.dim('Options:')}

View File

@@ -67,7 +67,7 @@ export default async function pull(
} }
// handle relative or absolute filename // handle relative or absolute filename
const [filename = '.env'] = args; const [filename = '.env.local'] = args;
const fullPath = resolve(cwd, filename); const fullPath = resolve(cwd, filename);
const skipConfirmation = opts['--yes']; const skipConfirmation = opts['--yes'];
const gitBranch = opts['--git-branch']; const gitBranch = opts['--git-branch'];

View File

@@ -751,9 +751,9 @@ test('Deploy `api-env` fixture and test `vercel env` command', async () => {
); );
expect(exitCode, formatOutput({ stdout, stderr })).toBe(0); expect(exitCode, formatOutput({ stdout, stderr })).toBe(0);
expect(stderr).toMatch(/Created .env file/gm); expect(stderr).toMatch(/Created .env.local file/gm);
const contents = fs.readFileSync(path.join(target, '.env'), 'utf8'); const contents = fs.readFileSync(path.join(target, '.env.local'), 'utf8');
expect(contents).toMatch(/^# Created by Vercel CLI\n/); expect(contents).toMatch(/^# Created by Vercel CLI\n/);
expect(contents).toMatch(/MY_NEW_ENV_VAR="my plaintext value"/); expect(contents).toMatch(/MY_NEW_ENV_VAR="my plaintext value"/);
expect(contents).toMatch(/MY_STDIN_VAR="{"expect":"quotes"}"/); expect(contents).toMatch(/MY_STDIN_VAR="{"expect":"quotes"}"/);
@@ -771,12 +771,12 @@ test('Deploy `api-env` fixture and test `vercel env` command', async () => {
); );
expect(exitCode, formatOutput({ stdout, stderr })).toBe(0); expect(exitCode, formatOutput({ stdout, stderr })).toBe(0);
expect(stderr).toMatch(/Overwriting existing .env file/gm); expect(stderr).toMatch(/Overwriting existing .env.local file/gm);
expect(stderr).toMatch(/Updated .env file/gm); expect(stderr).toMatch(/Updated .env.local file/gm);
} }
async function vcEnvPullConfirm() { async function vcEnvPullConfirm() {
fs.writeFileSync(path.join(target, '.env'), 'hahaha'); fs.writeFileSync(path.join(target, '.env.local'), 'hahaha');
const vc = execCli(binaryPath, ['env', 'pull'], { const vc = execCli(binaryPath, ['env', 'pull'], {
cwd: target, cwd: target,
@@ -784,7 +784,7 @@ test('Deploy `api-env` fixture and test `vercel env` command', async () => {
await waitForPrompt( await waitForPrompt(
vc, vc,
'Found existing file ".env". Do you want to overwrite?' 'Found existing file ".env.local". Do you want to overwrite?'
); );
vc.stdin?.end('y\n'); vc.stdin?.end('y\n');
@@ -904,7 +904,7 @@ test('Deploy `api-env` fixture and test `vercel env` command', async () => {
expect(exitCode, formatOutput({ stdout, stderr })).toBe(0); expect(exitCode, formatOutput({ stdout, stderr })).toBe(0);
const contents = fs.readFileSync(path.join(target, '.env'), 'utf8'); const contents = fs.readFileSync(path.join(target, '.env.local'), 'utf8');
const lines = new Set(contents.split('\n')); const lines = new Set(contents.split('\n'));
@@ -1024,11 +1024,11 @@ test('Deploy `api-env` fixture and test `vercel env` command', async () => {
await vcEnvPullConfirm(); await vcEnvPullConfirm();
await vcDeployWithVar(); await vcDeployWithVar();
await vcDevWithEnv(); await vcDevWithEnv();
fs.unlinkSync(path.join(target, '.env')); fs.unlinkSync(path.join(target, '.env.local'));
await vcDevAndFetchCloudVars(); await vcDevAndFetchCloudVars();
await enableAutoExposeSystemEnvs(); await enableAutoExposeSystemEnvs();
await vcEnvPullFetchSystemVars(); await vcEnvPullFetchSystemVars();
fs.unlinkSync(path.join(target, '.env')); fs.unlinkSync(path.join(target, '.env.local'));
await vcDevAndFetchSystemVars(); await vcDevAndFetchSystemVars();
await vcEnvRemove(); await vcEnvRemove();
await vcEnvRemoveWithArgs(); await vcEnvRemoveWithArgs();

View File

@@ -24,10 +24,10 @@ describe('env', () => {
await expect(client.stderr).toOutput( await expect(client.stderr).toOutput(
'Downloading `development` Environment Variables for Project vercel-env-pull' 'Downloading `development` Environment Variables for Project vercel-env-pull'
); );
await expect(client.stderr).toOutput('Created .env file'); await expect(client.stderr).toOutput('Created .env.local file');
await expect(exitCodePromise).resolves.toEqual(0); await expect(exitCodePromise).resolves.toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, '.env')); const rawDevEnv = await fs.readFile(path.join(cwd, '.env.local'));
// check for development env value // check for development env value
const devFileHasDevEnv = rawDevEnv.toString().includes('SPECIAL_FLAG'); const devFileHasDevEnv = rawDevEnv.toString().includes('SPECIAL_FLAG');
@@ -56,11 +56,11 @@ describe('env', () => {
await expect(client.stderr).toOutput( await expect(client.stderr).toOutput(
'Downloading `preview` Environment Variables for Project vercel-env-pull' 'Downloading `preview` Environment Variables for Project vercel-env-pull'
); );
await expect(client.stderr).toOutput('Created .env file'); await expect(client.stderr).toOutput('Created .env.local file');
await expect(exitCodePromise).resolves.toEqual(0); await expect(exitCodePromise).resolves.toEqual(0);
// check for Preview env vars // check for Preview env vars
const rawDevEnv = await fs.readFile(path.join(cwd, '.env'), 'utf8'); const rawDevEnv = await fs.readFile(path.join(cwd, '.env.local'), 'utf8');
expect(rawDevEnv).toContain( expect(rawDevEnv).toContain(
'REDIS_CONNECTION_STRING="redis://abc123@redis.example.com:6379"' 'REDIS_CONNECTION_STRING="redis://abc123@redis.example.com:6379"'
); );
@@ -93,11 +93,11 @@ describe('env', () => {
await expect(client.stderr).toOutput( await expect(client.stderr).toOutput(
'Downloading `preview` Environment Variables for Project vercel-env-pull' 'Downloading `preview` Environment Variables for Project vercel-env-pull'
); );
await expect(client.stderr).toOutput('Created .env file'); await expect(client.stderr).toOutput('Created .env.local file');
await expect(exitCodePromise).resolves.toEqual(0); await expect(exitCodePromise).resolves.toEqual(0);
// check for Preview env vars // check for Preview env vars
const rawDevEnv = await fs.readFile(path.join(cwd, '.env'), 'utf8'); const rawDevEnv = await fs.readFile(path.join(cwd, '.env.local'), 'utf8');
expect(rawDevEnv).toContain( expect(rawDevEnv).toContain(
'REDIS_CONNECTION_STRING="redis://abc123@redis.example.com:6379"' 'REDIS_CONNECTION_STRING="redis://abc123@redis.example.com:6379"'
); );
@@ -159,10 +159,10 @@ describe('env', () => {
await expect(client.stderr).toOutput( await expect(client.stderr).toOutput(
`Downloading \`production\` Environment Variables for Project vercel-env-pull` `Downloading \`production\` Environment Variables for Project vercel-env-pull`
); );
await expect(client.stderr).toOutput('Created .env file'); await expect(client.stderr).toOutput('Created .env.local file');
await expect(exitCodePromise).resolves.toEqual(0); await expect(exitCodePromise).resolves.toEqual(0);
const rawProdEnv = await fs.readFile(path.join(cwd, '.env')); const rawProdEnv = await fs.readFile(path.join(cwd, '.env.local'));
// check for development env value // check for development env value
const envFileHasEnv = rawProdEnv const envFileHasEnv = rawProdEnv
@@ -261,7 +261,7 @@ describe('env', () => {
await expect(client.stderr).toOutput( await expect(client.stderr).toOutput(
'+ SPECIAL_FLAG (Updated)\n+ NEW_VAR\n- TEST\n' '+ SPECIAL_FLAG (Updated)\n+ NEW_VAR\n- TEST\n'
); );
await expect(client.stderr).toOutput('Updated .env file'); await expect(client.stderr).toOutput('Updated .env.local file');
await expect(pullPromise).resolves.toEqual(0); await expect(pullPromise).resolves.toEqual(0);
} finally { } finally {
@@ -282,7 +282,7 @@ describe('env', () => {
client.setArgv('env', 'pull', '--yes', '--cwd', cwd); client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
const pullPromise = env(client); const pullPromise = env(client);
await expect(client.stderr).toOutput('Updated .env file'); await expect(client.stderr).toOutput('Updated .env.local file');
await expect(pullPromise).resolves.toEqual(0); await expect(pullPromise).resolves.toEqual(0);
}); });
@@ -299,7 +299,7 @@ describe('env', () => {
client.setArgv('env', 'pull', '--yes', '--cwd', cwd); client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
const pullPromise = env(client); const pullPromise = env(client);
await expect(client.stderr).toOutput('> No changes found.'); await expect(client.stderr).toOutput('> No changes found.');
await expect(client.stderr).toOutput('Updated .env file'); await expect(client.stderr).toOutput('Updated .env.local file');
await expect(pullPromise).resolves.toEqual(0); await expect(pullPromise).resolves.toEqual(0);
}); });
@@ -335,7 +335,7 @@ describe('env', () => {
'Downloading `development` Environment Variables for Project env-pull-delta' 'Downloading `development` Environment Variables for Project env-pull-delta'
); );
await expect(client.stderr).toOutput('No changes found.\n'); await expect(client.stderr).toOutput('No changes found.\n');
await expect(client.stderr).toOutput('Updated .env file'); await expect(client.stderr).toOutput('Updated .env.local file');
await expect(pullPromise).resolves.toEqual(0); await expect(pullPromise).resolves.toEqual(0);
} finally { } finally {