[cli] Remove support for single file deployments (#6652)

Deploying a single file has printed a deprecation warning for a long time. Let's finally remove that behavior.
This commit is contained in:
Nathan Rajlich
2021-12-08 11:45:06 -08:00
committed by GitHub
parent bf0d5a7f29
commit 465129e62e
11 changed files with 88 additions and 108 deletions

View File

@@ -0,0 +1,9 @@
# No Single File Deployments
#### Why This Error Occurred
You attempted to create a Vercel deployment where the input is a file, rather than a directory. Previously this was allowed, however this behavior has been removed as of Vercel CLI v24.0.0 because it exposed a potential security risk if the user accidentally created a deployment from a sensitive file.
#### Possible Ways to Fix It
- Run the `vercel deploy` command against a directory, instead of a file.

View File

@@ -166,8 +166,8 @@ export default async (client: Client) => {
return pathValidation.exitCode;
}
const { isFile, path } = pathValidation;
const autoConfirm = argv['--confirm'] || isFile;
const { path } = pathValidation;
const autoConfirm = argv['--confirm'];
// deprecate --name
if (argv['--name']) {
@@ -229,8 +229,7 @@ export default async (client: Client) => {
// user input.
const detectedProjectName = getProjectName({
argv,
nowConfig: localConfig || {},
isFile,
nowConfig: localConfig,
paths,
});
@@ -450,7 +449,6 @@ export default async (client: Client) => {
rootDirectory,
quiet,
wantsPublic: argv['--public'] || localConfig.public,
isFile,
type: null,
nowConfig: localConfig,
regions,
@@ -472,7 +470,7 @@ export default async (client: Client) => {
[sourcePath],
createArgs,
org,
!project && !isFile,
!project,
path
);
@@ -655,8 +653,7 @@ export default async (client: Client) => {
client,
deployment,
deployStamp,
!argv['--no-clipboard'],
isFile
!argv['--no-clipboard']
);
};
@@ -791,8 +788,7 @@ const printDeploymentStatus = async (
};
},
deployStamp: () => string,
isClipboardEnabled: boolean,
isFile: boolean
isClipboardEnabled: boolean
) => {
indications = indications || [];
const isProdDeployment = target === 'production';
@@ -814,7 +810,7 @@ const printDeploymentStatus = async (
// print preview/production url
let previewUrl: string;
let isWildcard: boolean;
if (!isFile && Array.isArray(aliasList) && aliasList.length > 0) {
if (Array.isArray(aliasList) && aliasList.length > 0) {
const previewUrlInfo = await getPreferredPreviewURL(client, aliasList);
if (previewUrlInfo) {
isWildcard = previewUrlInfo.isWildcard;

View File

@@ -4,14 +4,12 @@ import { VercelConfig } from '@vercel/client';
export interface GetProjectNameOptions {
argv: { '--name'?: string };
nowConfig?: VercelConfig;
isFile?: boolean;
paths?: string[];
}
export default function getProjectName({
argv,
nowConfig = {},
isFile = false,
paths = [],
}: GetProjectNameOptions) {
const nameCli = argv['--name'];
@@ -24,10 +22,6 @@ export default function getProjectName({
return nowConfig.name;
}
if (isFile || paths.length > 1) {
return 'files';
}
// Otherwise, use the name of the directory
return basename(paths[0] || '');
}

View File

@@ -30,7 +30,6 @@ export interface NowOptions {
export interface CreateOptions {
// Legacy
nowConfig?: VercelConfig;
isFile?: boolean;
// Latest
name: string;

View File

@@ -11,7 +11,7 @@ export default async function inputProject(
client: Client,
org: Org,
detectedProjectName: string,
autoConfirm: boolean
autoConfirm = false
): Promise<Project | string> {
const { output } = client;
const slugifiedName = slugify(detectedProjectName);

View File

@@ -7,7 +7,7 @@ import { validateRootDirectory } from '../validate-paths';
export async function inputRootDirectory(
cwd: string,
output: Output,
autoConfirm: boolean
autoConfirm = false
) {
if (autoConfirm) {
return null;

View File

@@ -158,7 +158,6 @@ export default async function setupAndLink(
withCache: undefined,
quiet,
wantsPublic: localConfig?.public || false,
isFile,
nowConfig: localConfig,
regions: undefined,
meta: {},
@@ -179,7 +178,7 @@ export default async function setupAndLink(
[sourcePath],
createArgs,
org,
!isFile,
true,
path
);

View File

@@ -4,7 +4,6 @@ import { Output } from './output';
import chalk from 'chalk';
import { homedir } from 'os';
import confirm from './input/confirm';
import { prependEmoji, emoji } from './emoji';
import toHumanPath from './humanize-path';
const stat = promisify(lstatRaw);
@@ -54,10 +53,7 @@ export async function validateRootDirectory(
export default async function validatePaths(
output: Output,
paths: string[]
): Promise<
| { valid: true; path: string; isFile: boolean }
| { valid: false; exitCode: number }
> {
): Promise<{ valid: true; path: string } | { valid: false; exitCode: number }> {
// can't deploy more than 1 path
if (paths.length > 1) {
output.print(`${chalk.red('Error!')} Can't deploy more than one path.\n`);
@@ -78,14 +74,12 @@ export default async function validatePaths(
return { valid: false, exitCode: 1 };
}
const isFile = pathStat && !pathStat.isDirectory();
if (isFile) {
output.print(
`${prependEmoji(
'Deploying files with Vercel is deprecated (https://vercel.link/faq-deploy-file)',
emoji('warning')
)}\n`
);
if (!pathStat.isDirectory()) {
output.prettyError({
message: 'Support for single file deployments has been removed.',
link: 'https://vercel.link/no-single-file-deployments',
});
return { valid: false, exitCode: 1 };
}
// ask confirmation if the directory is home
@@ -101,5 +95,5 @@ export default async function validatePaths(
}
}
return { valid: true, path, isFile };
return { valid: true, path };
}

View File

@@ -0,0 +1,60 @@
import { join } from 'path';
import { fileNameSymbol } from '@vercel/client';
import { client } from '../mocks/client';
import deploy from '../../src/commands/deploy';
describe('deploy', () => {
it('should reject deploying a single file', async () => {
client.setArgv('deploy', __filename);
const exitCode = await deploy(client);
expect(exitCode).toEqual(1);
expect(client.outputBuffer).toEqual(
`Error! Support for single file deployments has been removed.\nLearn More: https://vercel.link/no-single-file-deployments\n`
);
});
it('should reject deploying multiple files', async () => {
client.setArgv('deploy', __filename, join(__dirname, 'inspect.test.ts'));
const exitCode = await deploy(client);
expect(exitCode).toEqual(1);
expect(client.outputBuffer).toEqual(
`Error! Can't deploy more than one path.\n`
);
});
it('should reject deploying a directory that does not exist', async () => {
client.setArgv('deploy', 'does-not-exists');
const exitCode = await deploy(client);
expect(exitCode).toEqual(1);
expect(client.outputBuffer).toEqual(
`Error! The specified file or directory "does-not-exists" does not exist.\n`
);
});
it('should reject deploying "version: 1"', async () => {
client.setArgv('deploy');
client.localConfig = {
[fileNameSymbol]: 'vercel.json',
version: 1,
};
const exitCode = await deploy(client);
expect(exitCode).toEqual(1);
expect(client.outputBuffer).toEqual(
'Error! The value of the `version` property within vercel.json can only be `2`.\n'
);
});
it('should reject deploying "version: {}"', async () => {
client.setArgv('deploy');
client.localConfig = {
[fileNameSymbol]: 'vercel.json',
// @ts-ignore
version: {},
};
const exitCode = await deploy(client);
expect(exitCode).toEqual(1);
expect(client.outputBuffer).toEqual(
'Error! The `version` property inside your vercel.json file must be a number.\n'
);
});
});

View File

@@ -1913,59 +1913,6 @@ test('create a production deployment', async t => {
t.is(deployment.target, 'production', JSON.stringify(deployment, null, 2));
});
test('deploying a file should not show prompts and display deprecation', async t => {
const file = fixture('static-single-file/first.png');
const output = await execute([file], {
reject: false,
});
const { stdout, stderr, exitCode } = output;
// Ensure the exit code is right
t.is(exitCode, 0, formatOutput(output));
t.true(stderr.includes('Deploying files with Vercel is deprecated'));
// Ensure `.vercel` was not created
t.is(
await exists(path.join(path.dirname(file), '.vercel')),
false,
'.vercel should not exists'
);
// Test if the output is really a URL
const { href, host } = new URL(stdout);
t.is(host.split('-')[0], 'files');
// Send a test request to the deployment
const response = await fetch(href);
const contentType = response.headers.get('content-type');
t.is(contentType, 'image/png');
t.deepEqual(await readFile(file), await response.buffer());
});
test('deploying more than 1 path should fail', async t => {
const file1 = fixture('static-multiple-files/first.png');
const file2 = fixture('static-multiple-files/second.png');
const { stdout, stderr, exitCode } = await execa(
binaryPath,
[file1, file2, '--public', '--name', session, ...defaultArgs, '--confirm'],
{
reject: false,
}
);
console.log(stderr);
console.log(stdout);
console.log(exitCode);
// Ensure the exit code is right
t.is(exitCode, 1);
t.true(stderr.trim().endsWith(`Can't deploy more than one path.`));
});
test('use build-env', async t => {
const directory = fixture('build-env');

View File

@@ -10,7 +10,7 @@ describe('getProjectName', () => {
expect(project).toEqual('abc');
});
it('should work with now.json', () => {
it('should work with `vercel.json` config', () => {
const project = getProjectName({
argv: {},
nowConfig: { name: 'abc' },
@@ -18,24 +18,6 @@ describe('getProjectName', () => {
expect(project).toEqual('abc');
});
it('should work with a file', () => {
const project = getProjectName({
argv: {},
nowConfig: {},
isFile: true,
});
expect(project).toEqual('files');
});
it('should work with a multiple files', () => {
const project = getProjectName({
argv: {},
nowConfig: {},
paths: ['/tmp/aa/abc.png', '/tmp/aa/bbc.png'],
});
expect(project).toEqual('files');
});
it('should work with a directory', () => {
const project = getProjectName({
argv: {},