mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-10 04:22:12 +00:00
[cli] New vc redeploy command (#9956)
This adds a new `vc redeploy <url-or-id>` command. It fetches the requested deployment, then performs a redeploy with similar output to `vc deploy` including the ability to pipe the deployment URL into a file or program. ### Redeploy Example: <img width="650" alt="image" src="https://github.com/vercel/vercel/assets/97262/b17fc424-558b-415c-8b74-63e450f4b753"> ### Bad deployment URL: <img width="579" alt="image" src="https://github.com/vercel/vercel/assets/97262/0cb53209-396e-4490-b5d0-744d5d870aaf"> ### No args: <img width="622" alt="image" src="https://github.com/vercel/vercel/assets/97262/cb36d625-991b-41fa-bb49-d7d36c1a201b"> Linear: https://linear.app/vercel/issue/VCCLI-558/cli-new-command-to-redeploy
This commit is contained in:
@@ -4,7 +4,7 @@ import { parse } from 'dotenv';
|
||||
import env from '../../../src/commands/env';
|
||||
import { setupUnitFixture } from '../../helpers/setup-unit-fixture';
|
||||
import { client } from '../../mocks/client';
|
||||
import { defaultProject, useProject } from '../../mocks/project';
|
||||
import { defaultProject, envs, useProject } from '../../mocks/project';
|
||||
import { useTeams } from '../../mocks/team';
|
||||
import { useUser } from '../../mocks/user';
|
||||
|
||||
@@ -308,21 +308,26 @@ describe('env', () => {
|
||||
try {
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
defaultProject.env.push({
|
||||
type: 'encrypted',
|
||||
id: '781dt89g8r2h789g',
|
||||
key: 'NEW_VAR',
|
||||
value: '"testvalue"',
|
||||
target: ['development'],
|
||||
configurationId: null,
|
||||
updatedAt: 1557241361455,
|
||||
createdAt: 1557241361455,
|
||||
});
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'env-pull-delta-quotes',
|
||||
name: 'env-pull-delta-quotes',
|
||||
});
|
||||
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);
|
||||
@@ -336,7 +341,6 @@ describe('env', () => {
|
||||
} finally {
|
||||
client.setArgv('env', 'rm', 'NEW_VAR', '--yes', '--cwd', cwd);
|
||||
await env(client);
|
||||
defaultProject.env.pop();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -345,21 +349,26 @@ describe('env', () => {
|
||||
try {
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
defaultProject.env.push({
|
||||
type: 'encrypted',
|
||||
id: '781dt89g8r2h789g',
|
||||
key: 'NEW_VAR',
|
||||
value: 'testvalue',
|
||||
target: ['development'],
|
||||
configurationId: null,
|
||||
updatedAt: 1557241361455,
|
||||
createdAt: 1557241361455,
|
||||
});
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'env-pull-delta-quotes',
|
||||
name: 'env-pull-delta-quotes',
|
||||
});
|
||||
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);
|
||||
@@ -373,7 +382,6 @@ describe('env', () => {
|
||||
} finally {
|
||||
client.setArgv('env', 'rm', 'NEW_VAR', '--yes', '--cwd', cwd);
|
||||
await env(client);
|
||||
defaultProject.env.pop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
103
packages/cli/test/unit/commands/redeploy.test.ts
Normal file
103
packages/cli/test/unit/commands/redeploy.test.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { client } from '../../mocks/client';
|
||||
import { defaultProject, useProject } from '../../mocks/project';
|
||||
import redeploy from '../../../src/commands/redeploy';
|
||||
import { setupUnitFixture } from '../../helpers/setup-unit-fixture';
|
||||
import { useDeployment } from '../../mocks/deployment';
|
||||
import { useTeams } from '../../mocks/team';
|
||||
import { useUser } from '../../mocks/user';
|
||||
|
||||
describe('redeploy', () => {
|
||||
it('should error if missing deployment url', async () => {
|
||||
client.setArgv('redeploy');
|
||||
const exitCodePromise = redeploy(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
'Missing required deployment id or url:'
|
||||
);
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should error if deployment not found', async () => {
|
||||
initRedeployTest();
|
||||
client.setArgv('redeploy', 'foo');
|
||||
const exitCodePromise = redeploy(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Fetching deployment "foo" in ');
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error: Can\'t find the deployment "foo" under the context'
|
||||
);
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should error if deployment belongs to another team', async () => {
|
||||
const { fromDeployment } = initRedeployTest();
|
||||
fromDeployment.team = {
|
||||
id: 'abc',
|
||||
name: 'abc',
|
||||
slug: 'abc',
|
||||
};
|
||||
client.setArgv('rollback', fromDeployment.id);
|
||||
const exitCodePromise = redeploy(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${fromDeployment.id}" in ${fromDeployment.creator?.username}`
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error: Deployment belongs to a different team'
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should redeploy an existing deployment', async () => {
|
||||
const { fromDeployment } = initRedeployTest();
|
||||
client.setArgv('rollback', fromDeployment.id);
|
||||
|
||||
const exitCodePromise = redeploy(client);
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${fromDeployment.id}" in ${fromDeployment.creator?.username}`
|
||||
);
|
||||
await expect(client.stderr).toOutput('Production');
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
});
|
||||
|
||||
it('should redeploy and not wait for completion', async () => {
|
||||
const { fromDeployment, toDeployment } = initRedeployTest();
|
||||
toDeployment.readyState = 'QUEUED';
|
||||
client.setArgv('rollback', fromDeployment.id, '--no-wait');
|
||||
|
||||
const exitCodePromise = redeploy(client);
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${fromDeployment.id}" in ${fromDeployment.creator?.username}`
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
'Note: Deployment is still processing'
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
function initRedeployTest() {
|
||||
setupUnitFixture('commands/redeploy/simple-static');
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
const { project } = useProject({
|
||||
...defaultProject,
|
||||
id: 'vercel-redeploy',
|
||||
name: 'vercel-redeploy',
|
||||
});
|
||||
const fromDeployment = useDeployment({ creator: user });
|
||||
const toDeployment = useDeployment({ creator: user });
|
||||
|
||||
client.scenario.post(`/v13/deployments`, (req, res) => {
|
||||
res.json(toDeployment);
|
||||
});
|
||||
|
||||
return {
|
||||
project,
|
||||
fromDeployment,
|
||||
toDeployment,
|
||||
};
|
||||
}
|
||||
@@ -40,7 +40,7 @@ describe('rollback', () => {
|
||||
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error: The provided argument "????" is not a valid deployment or project'
|
||||
'Error: The provided argument "????" is not a valid deployment ID or URL'
|
||||
);
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
@@ -51,9 +51,8 @@ describe('rollback', () => {
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput('Fetching deployment "foo" in ');
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error: Error: Can\'t find the deployment "foo" under the context'
|
||||
'Error: Can\'t find the deployment "foo" under the context'
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
@@ -147,14 +146,14 @@ describe('rollback', () => {
|
||||
});
|
||||
|
||||
client.setArgv('rollback', previousDeployment.id, '--yes', '--cwd', cwd);
|
||||
const exitCodePromise = rollback(client);
|
||||
const exitCode = await rollback(client);
|
||||
|
||||
expect(exitCode).toBe(1);
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).rejects.toThrow('Response Error (500)');
|
||||
await expect(client.stderr).toOutput('Response Error (500)');
|
||||
});
|
||||
|
||||
it('should error if rollback fails (no aliases)', async () => {
|
||||
@@ -222,7 +221,7 @@ describe('rollback', () => {
|
||||
'--cwd',
|
||||
cwd,
|
||||
'--timeout',
|
||||
'2s'
|
||||
'1s'
|
||||
);
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user