Compare commits

...

9 Commits

Author SHA1 Message Date
Marc Greenstock
5a61bb026b remove build-secret helper 2024-05-24 16:36:18 +02:00
Marc Greenstock
c3438ea5ed Remove secret from ProjectEnvType 2024-05-24 16:36:05 +02:00
Marc Greenstock
699bd6b2e8 remove Secret interface 2024-05-24 16:35:49 +02:00
Marc Greenstock
7fbe60f8cf remove secrets reference from error doc 2024-05-24 16:35:21 +02:00
Marc Greenstock
dadba3b975 Delete env-no-secret.md 2024-05-24 16:35:02 +02:00
Marc Greenstock
125eeecb06 remove secret related known errors 2024-05-23 16:50:14 +02:00
Marc Greenstock
9d6b78b76e delete secrets commands 2024-05-23 16:49:58 +02:00
Marc Greenstock
d0532107dd remove secrets from help 2024-05-23 16:48:30 +02:00
Marc Greenstock
3d58bd8d27 delete integration tests 2024-05-23 16:48:14 +02:00
16 changed files with 0 additions and 828 deletions

View File

@@ -1,17 +0,0 @@
# Secret Not Found
#### Why This Error Occurred
You specified the `--env` or `-e` flag with the value of a secret. However, the secret doesn't exist in the current scope you're in.
#### Possible Ways to Fix It
Make sure to specify the environment variable and secret like that:
```bash
vercel -e VARIABLE_NAME=@secret-name
```
In addition, ensure that the secret (`@secret-name` in the example above) exists in the current scope (the team or user account that you're using).
You can run `vercel switch` or `--scope` to switch to a different team or user.

View File

@@ -11,9 +11,3 @@ Make sure to set the name and value of the variable like this:
```bash
vercel -e VARIABLE_NAME="VALUE"
```
You can also specify a environment variable that contains a secret:
```bash
vercel -e VARIABLE_NAME=@secret-name
```

View File

@@ -7,7 +7,6 @@ import { PROJECT_ENV_TARGET } from '@vercel-internals/constants';
export type ProjectEnvTarget = typeof PROJECT_ENV_TARGET[number];
export type ProjectEnvType =
| 'plain'
| 'secret'
| 'encrypted'
| 'system'
| 'sensitive';
@@ -303,17 +302,6 @@ export interface ProjectAliasTarget {
deployment?: Deployment | undefined;
}
export interface Secret {
uid: string;
name: string;
value: string;
teamId?: string;
userId?: string;
projectId?: string;
created: string;
createdAt: number;
}
export interface ProjectEnvVariable {
id: string;
key: string;

View File

@@ -39,7 +39,6 @@ export const help = () => `
logs [url] Displays the logs for a deployment
projects Manages your Projects
rm | remove [id] Removes a deployment
secrets [name] Manages your global Secrets, for use in Environment Variables
teams Manages your teams
whoami Shows the username of the currently logged in user

View File

@@ -32,8 +32,6 @@ export default new Map([
['remove', 'remove'],
['rm', 'remove'],
['rollback', 'rollback'],
['secret', 'secrets'],
['secrets', 'secrets'],
['switch', 'teams'],
['team', 'teams'],
['teams', 'teams'],

View File

@@ -1,101 +0,0 @@
import { packageName, getCommandName } from '../../util/pkg-name';
export const secretsCommand = {
name: 'secrets',
description:
'WARNING: On May 1st, 2024 secrets will be automatically converted to sensitive Environment Variables for Preview and Production environments. Secrets attached to Development environments will not be migrated.\n' +
`The ${getCommandName(
'secrets'
)} command will be deprecated at this time, please use the ${getCommandName(
'env'
)} command instead.\n` +
'See https://vercel.com/changelog/legacy-environment-variable-secrets-are-being-sunset for more information.',
arguments: [
{
name: 'command',
required: false,
},
],
subcommands: [
{
name: 'ls',
description: 'Show all secrets in a list',
arguments: [],
options: [],
examples: [],
},
{
name: 'add',
description: 'Add a new secret',
arguments: [
{
name: 'name',
required: true,
},
{
name: 'value',
required: true,
},
],
options: [],
examples: [],
},
{
name: 'rename',
description: 'Change the name of a secret',
arguments: [
{
name: 'old-name',
required: true,
},
{
name: 'new-name',
required: true,
},
],
options: [],
examples: [],
},
{
name: 'rm',
description: 'Remove a secret',
arguments: [
{
name: 'name',
required: true,
},
],
options: [],
examples: [],
},
],
options: [
{
name: 'next',
description: 'Show next page of results',
argument: 'MS',
shorthand: 'n',
type: 'string',
deprecated: false,
multi: false,
},
],
examples: [
{
name: 'Add a new secret',
value: `${packageName} secrets add my-secret "my value"
- Once added, a secret's value can't be retrieved in plain text anymore
- If the secret's value is more than one word, wrap it in quotes
- When in doubt, always wrap your value in quotes`,
},
{
name: 'Expose a secret as an environment variable (notice the `@` symbol)',
value: `${packageName} -e MY_SECRET=@my-secret`,
},
{
name: 'Paginate results, where 1584722256178 is the time in milliseconds since the UNIX epoch',
value: `$ ${packageName} secrets ls --next 1584722256178`,
},
],
};

View File

@@ -1,309 +0,0 @@
import isErrnoException from '@vercel/error-utils';
import chalk from 'chalk';
import table from '../../util/output/table';
import ms from 'ms';
import { handleError, error } from '../../util/error';
import NowSecrets from '../../util/secrets';
import getScope from '../../util/get-scope';
import confirm from '../../util/input/confirm';
import getCommandFlags from '../../util/get-command-flags';
import { getCommandName } from '../../util/pkg-name';
import getArgs from '../../util/get-args';
import { help } from '../help';
import { secretsCommand } from './command';
// Options
let argv;
let subcommand;
let nextTimestamp;
const main = async client => {
argv = getArgs(client.argv.slice(2), {
'--yes': Boolean,
'--next': Number,
'--test-warning': Boolean,
'-y': '--yes',
'-N': '--next',
});
argv._ = argv._.slice(1);
subcommand = argv._[0];
nextTimestamp = argv.next;
if (argv.help || !subcommand) {
client.output.print(
help(secretsCommand, { columns: client.stderr.columns })
);
return 2;
}
const {
output,
config: { currentTeam },
} = client;
let contextName = null;
try {
({ contextName } = await getScope(client));
} catch (err) {
if (isErrnoException(err)) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}
}
throw err;
}
return run({ output, contextName, currentTeam, client });
};
export default async client => {
try {
return await main(client);
} catch (err) {
handleError(err);
process.exit(1);
}
};
async function run({ output, contextName, currentTeam, client }) {
const secrets = new NowSecrets({ client, currentTeam });
const args = argv._.slice(1);
const start = Date.now();
const { '--test-warning': testWarningFlag } = argv;
const commandName = getCommandName('secret ' + subcommand);
output.warn(
'On May 1st, 2024 secrets will be automatically converted to sensitive Environment Variables for Preview and Production environments.\n' +
'Secrets attached to Development environments will not be migrated.' +
'See https://vercel.com/changelog/legacy-environment-variable-secrets-are-being-sunset for more information.'
);
if (subcommand === 'ls' || subcommand === 'list') {
output.note(
`The ${getCommandName(
'env ls'
)} command is recommended instead of ${commandName}\n`
);
if (args.length > 1) {
// consider replacing with `output.error`
// eslint-disable-next-line no-console
console.error(
error(
`Invalid number of arguments. Usage: ${chalk.cyan(
`${getCommandName('secret ls')}`
)}`
)
);
return 1;
}
const { secrets: list, pagination } = await secrets.ls(
nextTimestamp,
testWarningFlag
);
const elapsed = ms(Date.now() - start);
output.print(
`${list.length > 0 ? 'Secrets' : 'No secrets'} found under ${chalk.bold(
contextName
)} ${chalk.gray(`[${elapsed}]`)}\n`
);
if (list.length > 0) {
const cur = Date.now();
const header = [['', 'name', 'created'].map(s => chalk.dim(s))];
const out = table(
header.concat(
list.map(secret => [
'',
chalk.bold(secret.name),
chalk.gray(`${ms(cur - new Date(secret.created))} ago`),
])
),
{ hsep: 2 }
);
if (out) {
client.output.print(`\n${out}\n`);
}
}
if (pagination && pagination.count === 20) {
const flags = getCommandFlags(argv, ['_', '--next', '-N', '-d', '-y']);
const nextCmd = `secrets ${subcommand}${flags} --next ${pagination.next}`;
output.log(`To display the next page run ${getCommandName(nextCmd)}`);
}
return secrets.close();
}
if (subcommand === 'rm' || subcommand === 'remove') {
output.note(
`The ${getCommandName(
'env rm'
)} command is recommended instead of ${commandName}\n`
);
if (args.length !== 1) {
// consider replacing with `output.error`
// eslint-disable-next-line no-console
console.error(
error(
`Invalid number of arguments. Usage: ${chalk.cyan(
`${getCommandName('secret rm <name>')}`
)}`
)
);
return 1;
}
const theSecret = await secrets.getSecretByNameOrId(args[0]);
if (theSecret) {
const yes =
argv['--yes'] ||
(await readConfirmation(client, output, theSecret, contextName));
if (!yes) {
output.print(`Canceled. Secret not deleted.\n`);
return 0;
}
} else {
// consider replacing with `output.error`
// eslint-disable-next-line no-console
console.error(
error(
`No secret found by name "${args[0]}" under ${chalk.bold(
contextName
)}`
)
);
return 1;
}
const secret = await secrets.rm(args[0]);
const elapsed = ms(new Date() - start);
output.success(
`Secret ${chalk.bold(secret.name)} under ${chalk.bold(
contextName
)} removed ${chalk.gray(`[${elapsed}]`)}`
);
return secrets.close();
}
if (subcommand === 'rename') {
output.note(
`The ${getCommandName('env rm')} and ${getCommandName(
'env add'
)} commands are recommended instead of ${commandName}\n`
);
if (args.length !== 2) {
// consider replacing with `output.error`
// eslint-disable-next-line no-console
console.error(
error(
`Invalid number of arguments. Usage: ${chalk.cyan(
`${getCommandName('secret rename <old-name> <new-name>')}`
)}`
)
);
return 1;
}
const secret = await secrets.rename(args[0], args[1]);
const elapsed = ms(new Date() - start);
output.success(
`Secret ${chalk.bold(secret.oldName)} renamed to ${chalk.bold(
args[1]
)} under ${chalk.bold(contextName)} ${chalk.gray(`[${elapsed}]`)}`
);
return secrets.close();
}
if (subcommand === 'add' || subcommand === 'set') {
output.note(
`The ${getCommandName(
'env add'
)} command is recommended instead of ${commandName}\n`
);
if (args.length !== 2) {
// consider replacing with `output.error`
// eslint-disable-next-line no-console
console.error(
error(
`Invalid number of arguments. Usage: ${chalk.cyan(
`${getCommandName('secret add <name> <value>')}`
)}`
)
);
if (args.length > 2) {
const example = chalk.cyan(
`$ ${getCommandName('secret add -- "${args[0]}"')}`
);
output.log(
`If your secret has spaces or starts with '-', make sure to terminate command options with double dash and wrap it in quotes. Example: \n ${example} `
);
}
return 1;
}
const [name, value] = args;
if (typeof value === 'boolean') {
const example = chalk.cyan(
`$ ${getCommandName('secret add -- "${name}"')}`
);
output.log(
`If your secret starts with '-', make sure to terminate command options with double dash and wrap it in quotes. Example: \n ${example} `
);
return 1;
}
await secrets.add(name, value);
const elapsed = ms(new Date() - start);
if (name !== name.toLowerCase()) {
output.warn(`Your secret name was converted to lower-case`);
}
output.success(
`Secret ${chalk.bold(name.toLowerCase())} added under ${chalk.bold(
contextName
)} ${chalk.gray(`[${elapsed}]`)}`
);
return secrets.close();
}
// consider replacing with `output.error`
// eslint-disable-next-line no-console
console.error(
error('Please specify a valid subcommand: ls | add | rename | rm')
);
client.output.print(help(secretsCommand, { columns: client.stderr.columns }));
return 2;
}
async function readConfirmation(client, output, secret, contextName) {
const time = chalk.gray(`${ms(new Date() - new Date(secret.created))} ago`);
const tbl = table([[chalk.bold(secret.name), time]], {
align: ['r', 'l'],
hsep: 6,
});
output.print(
`The following secret will be removed permanently from ${chalk.bold(
contextName
)}\n`
);
output.print(` ${tbl}\n`);
return confirm(client, `${chalk.bold.red('Are you sure?')}`, false);
}

View File

@@ -601,9 +601,6 @@ const main = async () => {
case 'rollback':
func = require('./commands/rollback').default;
break;
case 'secrets':
func = require('./commands/secrets').default;
break;
case 'teams':
func = require('./commands/teams').default;
break;

View File

@@ -4,7 +4,6 @@ const knownErrorsCodes = new Set([
'BAD_REQUEST',
'ENV_ALREADY_EXISTS',
'ENV_CONFLICT',
'ENV_SHOULD_BE_A_SECRET',
'EXISTING_KEY_AND_TARGET',
'FORBIDDEN',
'ID_NOT_FOUND',
@@ -21,7 +20,6 @@ const knownErrorsCodes = new Set([
'MISSING_VALUE',
'NOT_AUTHORIZED',
'NOT_DECRYPTABLE',
'SECRET_MISSING',
'SYSTEM_ENV_WITH_VALUE',
'TEAM_NOT_FOUND',
'TOO_MANY_IDS',

View File

@@ -312,35 +312,6 @@ export default class Now extends EventEmitter {
return new Error(error.message || error.errorMessage);
}
async listSecrets(next?: number, testWarningFlag?: boolean) {
const payload = await this.retry(async bail => {
let secretsUrl = '/v3/now/secrets?limit=20';
if (next) {
secretsUrl += `&until=${next}`;
}
if (testWarningFlag) {
secretsUrl += '&testWarning=1';
}
const res = await this._fetch(secretsUrl);
if (res.status === 200) {
// What we want
return res.json();
}
if (res.status > 200 && res.status < 500) {
// If something is wrong with our request, we don't retry
return bail(await responseError(res, 'Failed to list secrets'));
}
// If something is wrong with the server, we retry
throw await responseError(res, 'Failed to list secrets');
});
return payload;
}
async list(
app?: string,
{ version = 4, meta = {}, nextTimestamp, target }: ListOptions = {},

View File

@@ -1,141 +0,0 @@
import Now from '.';
export default class Secrets extends Now {
ls(next, testWarningFlag) {
return this.listSecrets(next, testWarningFlag);
}
getSecretByNameOrId(nameOrId) {
return this.retry(async (bail, attempt) => {
if (this._debug) {
// eslint-disable-next-line no-console
console.time(`> [debug] #${attempt} GET /secrets/${nameOrId}`);
}
const res = await this._fetch(`/now/secrets/${nameOrId}`, {
method: 'GET',
});
if (this._debug) {
// eslint-disable-next-line no-console
console.timeEnd(`> [debug] #${attempt} GET /secrets/${nameOrId}`);
}
if (res.status === 403) {
return bail(new Error('Unauthorized'));
}
if (res.status === 404) {
return bail(new Error('Not Found'));
}
if (res.status === 400) {
return bail(new Error('Bad Request'));
}
const body = await res.json();
if (res.status !== 200) {
throw new Error(body.error.message);
}
return body;
});
}
rm(nameOrId) {
return this.retry(async (bail, attempt) => {
if (this._debug) {
// eslint-disable-next-line no-console
console.time(`> [debug] #${attempt} DELETE /secrets/${nameOrId}`);
}
const res = await this._fetch(`/now/secrets/${nameOrId}`, {
method: 'DELETE',
});
if (this._debug) {
// eslint-disable-next-line no-console
console.timeEnd(`> [debug] #${attempt} DELETE /secrets/${nameOrId}`);
}
if (res.status === 403) {
return bail(new Error('Unauthorized'));
}
const body = await res.json();
if (res.status !== 200) {
throw new Error(body.error.message);
}
return body;
});
}
add(name, value) {
return this.retry(async (bail, attempt) => {
if (this._debug) {
// eslint-disable-next-line no-console
console.time(`> [debug] #${attempt} POST /secrets`);
}
const res = await this._fetch('/now/secrets', {
method: 'POST',
body: {
name,
value: value.toString(),
},
});
if (this._debug) {
// eslint-disable-next-line no-console
console.timeEnd(`> [debug] #${attempt} POST /secrets`);
}
if (res.status === 403) {
return bail(new Error('Unauthorized'));
}
const body = await res.json();
if (res.status !== 200) {
throw new Error(body.error.message);
}
return body;
});
}
rename(nameOrId, newName) {
return this.retry(async (bail, attempt) => {
if (this._debug) {
// eslint-disable-next-line no-console
console.time(`> [debug] #${attempt} PATCH /secrets/${nameOrId}`);
}
const res = await this._fetch(`/now/secrets/${nameOrId}`, {
method: 'PATCH',
body: {
name: newName,
},
});
if (this._debug) {
// eslint-disable-next-line no-console
console.timeEnd(`> [debug] #${attempt} PATCH /secrets/${nameOrId}`);
}
if (res.status === 403) {
return bail(new Error('Unauthorized'));
}
const body = await res.json();
if (res.status !== 200) {
throw new Error(body.error.message);
}
return body;
});
}
}

View File

@@ -205,21 +205,6 @@ module.exports = async function prepare(session, binaryPath, tmpFixturesDir) {
name: 'nested-level',
}),
},
'build-secret': {
'package.json': JSON.stringify({
private: true,
scripts: {
build: 'mkdir public && echo $MY_SECRET > public/index.txt',
},
}),
'now.json': JSON.stringify({
build: {
env: {
MY_SECRET: '@mysecret',
},
},
}),
},
'api-env': {
'api/get-env.js': 'module.exports = (_, res) => res.json(process.env)',
'print.js': 'console.log(JSON.stringify(process.env))',

View File

@@ -757,42 +757,6 @@ test('Deploy `api-env` fixture and test `vercel env` command', async () => {
expect(previewEnvs[0]).toMatch(/Encrypted .* Preview /gm);
}
// we create a "legacy" env variable that contains a decryptable secret
// to check that vc env pull and vc dev work correctly with decryptable secrets
async function createEnvWithDecryptableSecret() {
// eslint-disable-next-line no-console
console.log('creating an env variable with a decryptable secret');
const name = `my-secret${Math.floor(Math.random() * 10000)}`;
const res = await apiFetch('/v2/now/secrets', {
method: 'POST',
body: JSON.stringify({
name,
value: 'decryptable value',
decryptable: true,
}),
});
expect(res.status).toBe(200);
const json = await res.json();
const link = require(path.join(target, '.vercel/project.json'));
const resEnv = await apiFetch(`/v4/projects/${link.projectId}/env`, {
method: 'POST',
body: JSON.stringify({
key: 'MY_DECRYPTABLE_SECRET_ENV',
value: json.uid,
target: ['development'],
type: 'secret',
}),
});
expect(resEnv.status).toBe(200);
}
async function vcEnvPull() {
const { exitCode, stdout, stderr } = await execCli(
binaryPath,
@@ -809,7 +773,6 @@ test('Deploy `api-env` fixture and test `vercel env` command', async () => {
expect(contents).toMatch(/^# Created by Vercel CLI\n/);
expect(contents).toMatch(/MY_NEW_ENV_VAR="my plaintext value"/);
expect(contents).toMatch(/MY_STDIN_VAR="{"expect":"quotes"}"/);
expect(contents).toMatch(/MY_DECRYPTABLE_SECRET_ENV="decryptable value"/);
expect(contents).not.toMatch(/MY_PREVIEW/);
}
@@ -878,14 +841,12 @@ test('Deploy `api-env` fixture and test `vercel env` command', async () => {
const apiJson = await apiRes.json();
expect(apiJson['MY_NEW_ENV_VAR']).toBe('my plaintext value');
expect(apiJson['MY_DECRYPTABLE_SECRET_ENV']).toBe('decryptable value');
const homeUrl = localhost[0];
const homeRes = await fetch(homeUrl);
const homeJson = await homeRes.json();
expect(homeJson['MY_NEW_ENV_VAR']).toBe('my plaintext value');
expect(homeJson['MY_DECRYPTABLE_SECRET_ENV']).toBe('decryptable value');
// sleep before kill, otherwise the dev process doesn't clean up and exit properly
await sleep(100);
@@ -908,14 +869,12 @@ test('Deploy `api-env` fixture and test `vercel env` command', async () => {
const apiJson = await apiRes.json();
expect(apiJson['MY_NEW_ENV_VAR']).toBe('my plaintext value');
expect(apiJson['MY_STDIN_VAR']).toBe('{"expect":"quotes"}');
expect(apiJson['MY_DECRYPTABLE_SECRET_ENV']).toBe('decryptable value');
const homeUrl = localhost[0];
const homeRes = await fetch(homeUrl);
const homeJson = await homeRes.json();
expect(homeJson['MY_NEW_ENV_VAR']).toBe('my plaintext value');
expect(homeJson['MY_STDIN_VAR']).toBe('{"expect":"quotes"}');
expect(homeJson['MY_DECRYPTABLE_SECRET_ENV']).toBe('decryptable value');
// system env vars are hidden in dev
expect(apiJson['VERCEL']).toBeUndefined();
@@ -1060,7 +1019,6 @@ test('Deploy `api-env` fixture and test `vercel env` command', async () => {
async function vcEnvRemoveAll() {
await vcEnvRemoveByName('MY_PREVIEW');
await vcEnvRemoveByName('MY_STDIN_VAR');
await vcEnvRemoveByName('MY_DECRYPTABLE_SECRET_ENV');
await vcEnvRemoveByName('MY_NEW_ENV_VAR');
}
@@ -1073,7 +1031,6 @@ test('Deploy `api-env` fixture and test `vercel env` command', async () => {
await vcEnvAddFromStdinPreview();
await vcEnvAddFromStdinPreviewWithBranch();
await vcEnvLsIncludesVar();
await createEnvWithDecryptableSecret();
await vcEnvPull();
await vcEnvPullOverwrite();
await vcEnvPullConfirm();

View File

@@ -33,7 +33,6 @@ const binaryPath = path.resolve(__dirname, `../scripts/start.js`);
const deployHelpMessage = `${logo} vercel [options] <command | path>`;
let session = 'temp-session';
let secretName: string | undefined;
function fetchTokenInformation(token: string, retries = 3) {
const url = `https://api.vercel.com/v2/user`;
@@ -294,32 +293,6 @@ test('output the version', async () => {
expect(version).toBe(pkg.version);
});
test('should add secret with hyphen prefix', async () => {
const target = await setupE2EFixture('build-secret');
const key = 'mysecret';
const value = '-foo_bar';
let secretCall = await execCli(
binaryPath,
['secrets', 'add', '--', key, value],
{
cwd: target,
}
);
expect(secretCall.exitCode, formatOutput(secretCall)).toBe(0);
let targetCall = await execCli(binaryPath, ['--yes'], {
cwd: target,
});
expect(targetCall.exitCode, formatOutput(targetCall)).toBe(0);
const { host } = new URL(targetCall.stdout);
const response = await fetch(`https://${host}`);
expect(response.status).toBe(200);
expect(await response.text()).toBe(`${value}\n`);
});
test('login with unregistered user', async () => {
const { stdout, stderr, exitCode } = await execCli(binaryPath, [
'login',
@@ -1151,59 +1124,6 @@ test('next unsupported functions config shows warning link', async () => {
);
});
test('vercel secret add', async () => {
secretName = `my-secret-${Date.now().toString(36)}`;
const value = 'https://my-secret-endpoint.com';
const output = await execCli(binaryPath, [
'secret',
'add',
secretName,
value,
]);
expect(output.exitCode, formatOutput(output)).toBe(0);
});
test('vercel secret ls', async () => {
const output = await execCli(binaryPath, ['secret', 'ls']);
expect(output.exitCode, formatOutput(output)).toBe(0);
expect(output.stderr).toMatch(/Secrets found under/gm);
});
test('vercel secret ls --test-warning', async () => {
const output = await execCli(binaryPath, ['secret', 'ls', '--test-warning']);
expect(output.exitCode, formatOutput(output)).toBe(0);
expect(output.stderr).toMatch(/Test warning message./gm);
expect(output.stderr).toMatch(/Learn more: https:\/\/vercel.com/gm);
expect(output.stderr).toMatch(/No secrets found under/gm);
});
test('vercel secret rename', async () => {
if (!secretName) {
throw new Error('Shared state "secretName" not set.');
}
const nextName = `renamed-secret-${Date.now().toString(36)}`;
const output = await execCli(binaryPath, [
'secret',
'rename',
secretName,
nextName,
]);
expect(output.exitCode, formatOutput(output)).toBe(0);
secretName = nextName;
});
test('vercel secret rm', async () => {
if (!secretName) {
throw new Error('Shared state "secretName" not set.');
}
const output = await execCli(binaryPath, ['secret', 'rm', secretName, '-y']);
expect(output.exitCode, formatOutput(output)).toBe(0);
});
test('deploy a Lambda with 128MB of memory', async () => {
const directory = await setupE2EFixture('lambda-with-128-memory');
const output = await execCli(binaryPath, [directory, '--yes']);

View File

@@ -1,29 +0,0 @@
import chance from 'chance';
import { client } from './client';
export function useSecrets({
name,
created,
}: {
name: string;
created: number;
}) {
const secret = {
uid: chance().guid(),
name: name || chance().name(),
created: created || chance().timestamp(),
};
client.scenario.get('/v3/now/secrets', (_req, res) => {
res.json({
secrets: [secret],
pagination: {
count: 1,
next: 0,
prev: 0,
},
});
});
return secret;
}

View File

@@ -1,38 +0,0 @@
import { describe, expect, it } from 'vitest';
import { client } from '../../mocks/client';
import secrets from '../../../src/commands/secrets';
import { useSecrets } from '../../mocks/secrets';
import { useUser } from '../../mocks/user';
import { useTeams } from '../../mocks/team';
import ms from 'ms';
describe('secrets', () => {
it('errors when no subcommand is provided', async () => {
client.setArgv('secrets');
const exitCode = await secrets(client);
expect(exitCode).toEqual(2);
});
it('lists secrets with ls subcommand', async () => {
useUser();
useTeams('team_dummy');
const name = 'secret-api-password';
const created = 1519555701;
useSecrets({ name, created });
client.setArgv('secrets', 'ls');
await secrets(client);
const timeAgo = `${ms(
new Date().getTime() - new Date(created).getTime()
)} ago`;
await expect(client.stderr).toOutput(
'> NOTE: The `vercel env ls` command is recommended instead of `vercel secret ls`'
);
const output = client.stderr.read();
await expect(output).toMatch(name);
await expect(output).toMatch(timeAgo);
});
});