mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-11 12:57:46 +00:00
Compare commits
78 Commits
update/cli
...
@vercel-in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f43e413ba5 | ||
|
|
0945d24cbe | ||
|
|
a8ecf40d6f | ||
|
|
2995781a58 | ||
|
|
e2096c268d | ||
|
|
663d8e6437 | ||
|
|
153ca440fa | ||
|
|
c5e0cb4812 | ||
|
|
5bf1fe4c74 | ||
|
|
011f836aa7 | ||
|
|
ec107d7c91 | ||
|
|
0d27ae3b1a | ||
|
|
08da4b9c92 | ||
|
|
877f09ff5c | ||
|
|
5cca9b6c5c | ||
|
|
4af242af86 | ||
|
|
0cbdae1411 | ||
|
|
85dd667781 | ||
|
|
7d3dda7341 | ||
|
|
2144d0b2a9 | ||
|
|
976e6aedf9 | ||
|
|
6328751e14 | ||
|
|
8cb49a5136 | ||
|
|
3fa4f344cc | ||
|
|
27610896ed | ||
|
|
b9dae36e37 | ||
|
|
1537ff9c38 | ||
|
|
7e0317775f | ||
|
|
2dd27976b3 | ||
|
|
25e2b7efba | ||
|
|
3d23d1270c | ||
|
|
fde40e731a | ||
|
|
f353527421 | ||
|
|
c1cdfb3e75 | ||
|
|
fc413707d0 | ||
|
|
e842a8870e | ||
|
|
d1b0dbe3a7 | ||
|
|
d614709308 | ||
|
|
d5b588bc06 | ||
|
|
4a8622a10d | ||
|
|
6469ef1b8c | ||
|
|
b8d42a521b | ||
|
|
b1e8c9cb6e | ||
|
|
7c30b13ccb | ||
|
|
e5e757de34 | ||
|
|
2661f56347 | ||
|
|
b1c14cde03 | ||
|
|
8dd6d021df | ||
|
|
88ec6e69d6 | ||
|
|
a6f2e7b136 | ||
|
|
e906365909 | ||
|
|
7b01a07394 | ||
|
|
ce4633fe4d | ||
|
|
fdf86fda03 | ||
|
|
56178e6a46 | ||
|
|
5439d7c0c9 | ||
|
|
c670e51712 | ||
|
|
b56639b624 | ||
|
|
12bbae098c | ||
|
|
9969f0ba18 | ||
|
|
24e1e3c3be | ||
|
|
b61674cb2d | ||
|
|
cae60155f3 | ||
|
|
a91bde5287 | ||
|
|
0750517af9 | ||
|
|
70f6782954 | ||
|
|
5f1e37ee16 | ||
|
|
680d666fdc | ||
|
|
06a5dccfed | ||
|
|
f85df894c0 | ||
|
|
e9ec779f1c | ||
|
|
4333d1e6b2 | ||
|
|
493a31091d | ||
|
|
8d7206f5b6 | ||
|
|
4bf2ca55ff | ||
|
|
5e5332fbc9 | ||
|
|
281ec776a5 | ||
|
|
ee8f9292b4 |
@@ -1,2 +0,0 @@
|
||||
---
|
||||
---
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@vercel/build-utils": patch
|
||||
---
|
||||
|
||||
Revert "[build-utils] Allow file-ref sema to be controlled through env flag"
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'vercel': patch
|
||||
---
|
||||
|
||||
Allow additional project settings in `createProject()`
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'vercel': patch
|
||||
---
|
||||
|
||||
Added trailing new line at end of help output
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
'@vercel-internals/constants': patch
|
||||
'vercel': patch
|
||||
---
|
||||
|
||||
Create new help output and arg parsing for deploy command
|
||||
@@ -1,2 +0,0 @@
|
||||
---
|
||||
---
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@vercel/remix-builder': patch
|
||||
---
|
||||
|
||||
Update `@remix-run/dev` fork to v1.18.1
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"vercel": patch
|
||||
---
|
||||
|
||||
[cli] Remove `preinstall` script
|
||||
14
.github/CODEOWNERS
vendored
14
.github/CODEOWNERS
vendored
@@ -2,19 +2,19 @@
|
||||
# https://help.github.com/en/articles/about-code-owners
|
||||
|
||||
# Restricted Paths
|
||||
* @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood
|
||||
/.github/workflows @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
|
||||
/packages/fs-detectors @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @agadzik @chloetedder
|
||||
/packages/next @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
|
||||
/packages/routing-utils @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
|
||||
/packages/edge @vercel/edge-compute
|
||||
* @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @trek
|
||||
/.github/workflows @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @trek @ijjk
|
||||
/packages/fs-detectors @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @trek @agadzik @chloetedder
|
||||
/packages/next @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @trek @ijjk
|
||||
/packages/routing-utils @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @trek @ijjk
|
||||
/packages/edge @vercel/compute
|
||||
/examples @leerob
|
||||
/examples/create-react-app @Timer
|
||||
/examples/nextjs @timneutkens @ijjk @styfle
|
||||
/examples/hugo @styfle
|
||||
/examples/jekyll @styfle
|
||||
/examples/zola @styfle
|
||||
/packages/node @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @Kikobeats
|
||||
/packages/node @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @trek @Kikobeats
|
||||
|
||||
# Unrestricted Paths
|
||||
.changeset/
|
||||
|
||||
4
.github/DISCUSSION_TEMPLATE/general.yml
vendored
4
.github/DISCUSSION_TEMPLATE/general.yml
vendored
@@ -2,7 +2,9 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> **Note**: For discussions not related to Vercel CLI or Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions)
|
||||
**Note**: This category is intended for discussions related to Vercel CLI or Runtimes.
|
||||
|
||||
If you post in this repository seeking help with other Vercel tools and features, it may be missed by our support team. For help with topics other than the CLI and Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions).
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
|
||||
6
.github/DISCUSSION_TEMPLATE/help.yml
vendored
6
.github/DISCUSSION_TEMPLATE/help.yml
vendored
@@ -2,9 +2,11 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> **Note**: For discussions not related to Vercel CLI or Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions/categories/help)
|
||||
**Note**: This category is intended for discussions related to Vercel CLI or Runtimes.
|
||||
|
||||
If you post in this repository seeking help with other Vercel tools and features, it may be missed by our support team. For help with topics other than the CLI and Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions).
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Problem Description
|
||||
label: Question
|
||||
validations:
|
||||
required: true
|
||||
|
||||
4
.github/DISCUSSION_TEMPLATE/ideas.yml
vendored
4
.github/DISCUSSION_TEMPLATE/ideas.yml
vendored
@@ -2,7 +2,9 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> **Note**: For discussions not related to Vercel CLI or Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions/categories/ideas)
|
||||
**Note**: This category is intended for sharing ideas related to Vercel CLI or Runtimes.
|
||||
|
||||
Please visit the [Vercel Community](https://github.com/orgs/vercel/discussions) to share ideas for other Vercel tools and features.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Idea
|
||||
|
||||
10
.github/DISCUSSION_TEMPLATE/polls.yml
vendored
10
.github/DISCUSSION_TEMPLATE/polls.yml
vendored
@@ -1,10 +0,0 @@
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> **Note**: For discussions not related to Vercel CLI or Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions)
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
validations:
|
||||
required: true
|
||||
@@ -2,7 +2,9 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> **Note**: For discussions not related to Vercel CLI or Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions)
|
||||
**Note**: This category is intended for discussions related to Vercel CLI or Runtimes.
|
||||
|
||||
For topics related to other Vercel features, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions).
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
|
||||
5
.github/DISCUSSION_TEMPLATE/temporary.yml
vendored
5
.github/DISCUSSION_TEMPLATE/temporary.yml
vendored
@@ -1,5 +0,0 @@
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> **Note**: This category should not be used for new discussions. Please visit the [Vercel Community](https://github.com/orgs/vercel/discussions)
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -81,11 +81,11 @@ jobs:
|
||||
run: echo | openssl s_client -showcerts -servername 'api.vercel.com' -connect 76.76.21.21:443
|
||||
|
||||
- name: Build ${{matrix.packageName}} and all its dependencies
|
||||
run: node utils/gen.js && node_modules/.bin/turbo run build --cache-dir=".turbo" --scope=${{matrix.packageName}} --include-dependencies --no-deps
|
||||
run: node utils/gen.js && node_modules/.bin/turbo run build --cache-dir=".turbo" --log-order=stream --scope=${{matrix.packageName}} --include-dependencies --no-deps
|
||||
env:
|
||||
FORCE_COLOR: '1'
|
||||
- name: Test ${{matrix.packageName}}
|
||||
run: node utils/gen.js && node_modules/.bin/turbo run test --cache-dir=".turbo" --scope=${{matrix.packageName}} --no-deps -- ${{ join(matrix.testPaths, ' ') }}
|
||||
run: node utils/gen.js && node_modules/.bin/turbo run test --cache-dir=".turbo" --log-order=stream --scope=${{matrix.packageName}} --no-deps -- ${{ join(matrix.testPaths, ' ') }}
|
||||
shell: bash
|
||||
env:
|
||||
VERCEL_CLI_VERSION: ${{ needs.setup.outputs.dplUrl }}/tarballs/vercel.tgz
|
||||
|
||||
@@ -44,6 +44,5 @@
|
||||
"react-use": "^17.4.0",
|
||||
"title": "^3.4.4",
|
||||
"typographic-base": "^1.0.4"
|
||||
},
|
||||
"author": "nrajlich"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(25%, auto));
|
||||
width: var(--max-width);
|
||||
max-width: 100%;
|
||||
width: var(--max-width);
|
||||
}
|
||||
|
||||
.card {
|
||||
|
||||
566
examples/nextjs/package-lock.json
generated
566
examples/nextjs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,9 +9,9 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint": "8.44.0",
|
||||
"eslint-config-next": "13.4.8",
|
||||
"next": "13.4.8",
|
||||
"eslint": "8.46.0",
|
||||
"eslint-config-next": "13.4.13",
|
||||
"next": "13.4.13",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
|
||||
@@ -45,4 +45,4 @@ Locally preview production build:
|
||||
npm run preview
|
||||
```
|
||||
|
||||
Checkout the [deployment documentation](https://v3.nuxtjs.org/guide/deploy/presets) for more information.
|
||||
Checkout the [deployment documentation](https://nuxt.com/docs/getting-started/deployment#presets) for more information.
|
||||
|
||||
2
examples/package.json
vendored
2
examples/package.json
vendored
@@ -9,6 +9,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.4.1",
|
||||
"@vercel/frameworks": "1.4.3"
|
||||
"@vercel/frameworks": "1.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @vercel-internals/constants
|
||||
|
||||
## 1.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Create new help output and arg parsing for deploy command ([#10090](https://github.com/vercel/vercel/pull/10090))
|
||||
|
||||
## 1.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@vercel-internals/constants",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"types": "dist/index.d.ts",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# @vercel-internals/types
|
||||
|
||||
## 1.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`a8ecf40d6`](https://github.com/vercel/vercel/commit/a8ecf40d6f50e2fc8b13b02c8ef50b3dcafad3a6)]:
|
||||
- @vercel/build-utils@6.8.3
|
||||
|
||||
## 1.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`0750517af`](https://github.com/vercel/vercel/commit/0750517af99aea41410d4f1f772ce427699554e7)]:
|
||||
- @vercel/build-utils@6.8.2
|
||||
|
||||
## 1.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`7021279b2`](https://github.com/vercel/vercel/commit/7021279b284f314a4d1bdbb4306b4c22291efa08), [`718bbd365`](https://github.com/vercel/vercel/commit/718bbd365a50271a980bdca231ca801a0eead32b)]:
|
||||
- @vercel/build-utils@6.8.1
|
||||
- @vercel-internals/constants@1.0.4
|
||||
|
||||
## 1.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@vercel-internals/types",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.6",
|
||||
"types": "index.d.ts",
|
||||
"main": "index.d.ts",
|
||||
"dependencies": {
|
||||
"@types/node": "14.14.31",
|
||||
"@vercel-internals/constants": "1.0.3",
|
||||
"@vercel/build-utils": "6.8.0",
|
||||
"@vercel-internals/constants": "1.0.4",
|
||||
"@vercel/build-utils": "6.8.3",
|
||||
"@vercel/routing-utils": "2.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"source-map-support": "0.5.12",
|
||||
"ts-eager": "2.0.2",
|
||||
"ts-jest": "29.1.0",
|
||||
"turbo": "1.10.7",
|
||||
"turbo": "1.10.12",
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# @vercel/build-utils
|
||||
|
||||
## 6.8.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fix `getPrefixedEnvVars()` to handle `VERCEL_BRANCH_URL` ([#10315](https://github.com/vercel/vercel/pull/10315))
|
||||
|
||||
## 6.8.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Push back `nodejs16.x` discontinue date to `2024-02-06` ([#10209](https://github.com/vercel/vercel/pull/10209))
|
||||
|
||||
## 6.8.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Revert "[build-utils] Allow file-ref sema to be controlled through env flag" ([#10167](https://github.com/vercel/vercel/pull/10167))
|
||||
|
||||
## 6.8.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "6.8.0",
|
||||
"version": "6.8.3",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -10,7 +10,7 @@ function getOptions() {
|
||||
major: 16,
|
||||
range: '16.x',
|
||||
runtime: 'nodejs16.x',
|
||||
discontinueDate: new Date('2023-08-15'),
|
||||
discontinueDate: new Date('2024-02-06'),
|
||||
},
|
||||
{
|
||||
major: 14,
|
||||
|
||||
@@ -14,7 +14,12 @@ export function getPrefixedEnvVars({
|
||||
envs: Envs;
|
||||
}): Envs {
|
||||
const vercelSystemEnvPrefix = 'VERCEL_';
|
||||
const allowed = ['VERCEL_URL', 'VERCEL_ENV', 'VERCEL_REGION'];
|
||||
const allowed = [
|
||||
'VERCEL_URL',
|
||||
'VERCEL_ENV',
|
||||
'VERCEL_REGION',
|
||||
'VERCEL_BRANCH_URL',
|
||||
];
|
||||
const newEnvs: Envs = {};
|
||||
if (envPrefix && envs.VERCEL_URL) {
|
||||
Object.keys(envs)
|
||||
|
||||
@@ -13,6 +13,8 @@ describe('Test `getPrefixedEnvVars()`', () => {
|
||||
envs: {
|
||||
VERCEL: '1',
|
||||
VERCEL_URL: 'example.vercel.sh',
|
||||
VERCEL_ENV: 'production',
|
||||
VERCEL_BRANCH_URL: 'example-git-main-acme.vercel.app',
|
||||
USER_ENV_VAR_NOT_VERCEL: 'example.com',
|
||||
VERCEL_ARTIFACTS_TOKEN: 'abc123',
|
||||
FOO: 'bar',
|
||||
@@ -20,6 +22,8 @@ describe('Test `getPrefixedEnvVars()`', () => {
|
||||
},
|
||||
want: {
|
||||
NEXT_PUBLIC_VERCEL_URL: 'example.vercel.sh',
|
||||
NEXT_PUBLIC_VERCEL_ENV: 'production',
|
||||
NEXT_PUBLIC_VERCEL_BRANCH_URL: 'example-git-main-acme.vercel.app',
|
||||
TURBO_CI_VENDOR_ENV_KEY: 'NEXT_PUBLIC_VERCEL_',
|
||||
},
|
||||
},
|
||||
|
||||
6
packages/build-utils/test/unit.test.ts
vendored
6
packages/build-utils/test/unit.test.ts
vendored
@@ -238,7 +238,7 @@ it('should get latest node version', async () => {
|
||||
it('should throw for discontinued versions', async () => {
|
||||
// Mock a future date so that Node 8 and 10 become discontinued
|
||||
const realDateNow = Date.now.bind(global.Date);
|
||||
global.Date.now = () => new Date('2023-10-01').getTime();
|
||||
global.Date.now = () => new Date('2024-02-13').getTime();
|
||||
|
||||
expect(getSupportedNodeVersion('8.10.x', false)).rejects.toThrow();
|
||||
expect(getSupportedNodeVersion('8.10.x', true)).rejects.toThrow();
|
||||
@@ -306,8 +306,8 @@ it('should warn for deprecated versions, soon to be discontinued', async () => {
|
||||
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-03 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
|
||||
'Error: Node.js version 14.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.',
|
||||
'Error: Node.js version 14.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
|
||||
'Error: Node.js version 16.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.',
|
||||
'Error: Node.js version 16.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
|
||||
'Error: Node.js version 16.x has reached End-of-Life. Deployments created on or after 2024-02-06 will fail to build. Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.',
|
||||
'Error: Node.js version 16.x has reached End-of-Life. Deployments created on or after 2024-02-06 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
|
||||
]);
|
||||
|
||||
global.Date.now = realDateNow;
|
||||
|
||||
@@ -1,5 +1,151 @@
|
||||
# vercel
|
||||
|
||||
## 31.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Be looser in tests with mock server urls ([#10300](https://github.com/vercel/vercel/pull/10300))
|
||||
|
||||
- Handle calls for deployment aliases when mocking deployments ([#10303](https://github.com/vercel/vercel/pull/10303))
|
||||
|
||||
- Remove unused code ([#10309](https://github.com/vercel/vercel/pull/10309))
|
||||
|
||||
- Updated dependencies [[`5bf1fe4c7`](https://github.com/vercel/vercel/commit/5bf1fe4c743f6be3f7d5a24447ea5b083a68dc67), [`a8ecf40d6`](https://github.com/vercel/vercel/commit/a8ecf40d6f50e2fc8b13b02c8ef50b3dcafad3a6), [`08da4b9c9`](https://github.com/vercel/vercel/commit/08da4b9c923501d9d28eb6e3f26f4605fee83042), [`0945d24cb`](https://github.com/vercel/vercel/commit/0945d24cbe901ca3f0eedd011251ad499c72d472)]:
|
||||
- @vercel/next@3.9.4
|
||||
- @vercel/build-utils@6.8.3
|
||||
- @vercel/remix-builder@1.10.0
|
||||
- @vercel/node@2.15.9
|
||||
- @vercel/static-build@1.3.45
|
||||
|
||||
## 31.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Migrate list command to new structure ([#10284](https://github.com/vercel/vercel/pull/10284))
|
||||
|
||||
- Migrate whoami command to new structure ([#10266](https://github.com/vercel/vercel/pull/10266))
|
||||
|
||||
- Migrate logs command to new structure ([#10281](https://github.com/vercel/vercel/pull/10281))
|
||||
|
||||
- Migrate login command to new structure ([#10283](https://github.com/vercel/vercel/pull/10283))
|
||||
|
||||
- Migrate pull command to new structure ([#10280](https://github.com/vercel/vercel/pull/10280))
|
||||
|
||||
- Migrate logout command to new structure ([#10282](https://github.com/vercel/vercel/pull/10282))
|
||||
|
||||
- Migrate build command to new structure ([#10286](https://github.com/vercel/vercel/pull/10286))
|
||||
|
||||
- Migrate inspect command to new structure ([#10277](https://github.com/vercel/vercel/pull/10277))
|
||||
|
||||
- Migrate redeploy command to new structure ([#10279](https://github.com/vercel/vercel/pull/10279))
|
||||
|
||||
- Migrate link command to new structure ([#10285](https://github.com/vercel/vercel/pull/10285))
|
||||
|
||||
- Update spacing of --help output for CLI ([#10287](https://github.com/vercel/vercel/pull/10287))
|
||||
|
||||
- Updated dependencies [[`4af242af8`](https://github.com/vercel/vercel/commit/4af242af8633e58b6a9bf920564416da3ef22ad4), [`0cbdae141`](https://github.com/vercel/vercel/commit/0cbdae1411aa7936ff7dfe551919ca5e56cd6e98), [`85dd66778`](https://github.com/vercel/vercel/commit/85dd667781693539d753d587566e53964bbe189d)]:
|
||||
- @vercel/node@2.15.8
|
||||
- @vercel/remix-builder@1.9.1
|
||||
- @vercel/static-build@1.3.44
|
||||
|
||||
## 31.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Migrate bisect command to new structure ([#10276](https://github.com/vercel/vercel/pull/10276))
|
||||
|
||||
- Migrate remove command to new structure ([#10268](https://github.com/vercel/vercel/pull/10268))
|
||||
|
||||
- Updated dependencies [[`fc413707d`](https://github.com/vercel/vercel/commit/fc413707d017e234d5013b761d885f65f9b981bc)]:
|
||||
- @vercel/node@2.15.7
|
||||
- @vercel/static-build@1.3.43
|
||||
|
||||
## 31.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Add a "Global Options" section to help output ([#10250](https://github.com/vercel/vercel/pull/10250))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`d1b0dbe3a`](https://github.com/vercel/vercel/commit/d1b0dbe3a7d8754286aa2b7ba0c8b55d3adafdea), [`4a8622a10`](https://github.com/vercel/vercel/commit/4a8622a10d52260cb629a1c4a6f797ade05ea154), [`6469ef1b8`](https://github.com/vercel/vercel/commit/6469ef1b8ce37e93f50ab4a108aa0953d7631fe8)]:
|
||||
- @vercel/remix-builder@1.9.0
|
||||
- @vercel/next@3.9.3
|
||||
|
||||
## 31.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`7c30b13cc`](https://github.com/vercel/vercel/commit/7c30b13ccb79bdf0ac240282bba4c084f1d0d122)]:
|
||||
- @vercel/next@3.9.2
|
||||
|
||||
## 31.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Add 'Environment' column to 'vc list' with new '--environment' filter and pipe URLs to stdout ([#10239](https://github.com/vercel/vercel/pull/10239))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Update `proxy-agent` to v6.3.0 ([#10226](https://github.com/vercel/vercel/pull/10226))
|
||||
|
||||
- Use `getNodeBinPaths()` in `vc dev` ([#10225](https://github.com/vercel/vercel/pull/10225))
|
||||
|
||||
- Updated dependencies [[`b1c14cde0`](https://github.com/vercel/vercel/commit/b1c14cde03f94b2c15ba12c9be9d19c72df2fdbb), [`ce4633fe4`](https://github.com/vercel/vercel/commit/ce4633fe4d00cb5c251cdabbfab08f39ec3f3b5f)]:
|
||||
- @vercel/next@3.9.1
|
||||
- @vercel/static-build@1.3.42
|
||||
|
||||
## 31.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Detect multiple frameworks within the same root directory during `vc link --repo` ([#10203](https://github.com/vercel/vercel/pull/10203))
|
||||
|
||||
- Updated dependencies [[`b56639b62`](https://github.com/vercel/vercel/commit/b56639b624e9ad1df048a4c85083e26888696060), [`cae60155f`](https://github.com/vercel/vercel/commit/cae60155f34883f08a5e4f51b547e2a1a5fee694), [`c670e5171`](https://github.com/vercel/vercel/commit/c670e51712022193e078bd68b055f7e61013015d), [`5439d7c0c`](https://github.com/vercel/vercel/commit/5439d7c0c9b79e7161bf4fa84ffdb357365f9e7e)]:
|
||||
- @vercel/node@2.15.6
|
||||
- @vercel/next@3.9.0
|
||||
- @vercel/remix-builder@1.8.18
|
||||
- @vercel/static-build@1.3.41
|
||||
|
||||
## 31.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fix redeploy target to be undefined when null ([#10201](https://github.com/vercel/vercel/pull/10201))
|
||||
|
||||
- Respect forbidden API responses ([#10178](https://github.com/vercel/vercel/pull/10178))
|
||||
|
||||
- Update `supports-hyperlinks` to v3 ([#10208](https://github.com/vercel/vercel/pull/10208))
|
||||
|
||||
- Updated dependencies [[`0750517af`](https://github.com/vercel/vercel/commit/0750517af99aea41410d4f1f772ce427699554e7)]:
|
||||
- @vercel/build-utils@6.8.2
|
||||
- @vercel/static-build@1.3.40
|
||||
- @vercel/node@2.15.5
|
||||
- @vercel/remix-builder@1.8.17
|
||||
|
||||
## 31.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Allow additional project settings in `createProject()` ([#10172](https://github.com/vercel/vercel/pull/10172))
|
||||
|
||||
- Run local Project detection during `vc link --repo`. ([#10094](https://github.com/vercel/vercel/pull/10094))
|
||||
This allows for creation of new Projects that do not yet exist under the selected scope.
|
||||
|
||||
- Redeploy command no longer redeploys preview deployments to production ([#10186](https://github.com/vercel/vercel/pull/10186))
|
||||
|
||||
- Added trailing new line at end of help output ([#10170](https://github.com/vercel/vercel/pull/10170))
|
||||
|
||||
- Create new help output and arg parsing for deploy command ([#10090](https://github.com/vercel/vercel/pull/10090))
|
||||
|
||||
- [cli] Remove `preinstall` script ([#10157](https://github.com/vercel/vercel/pull/10157))
|
||||
|
||||
- Updated dependencies [[`7021279b2`](https://github.com/vercel/vercel/commit/7021279b284f314a4d1bdbb4306b4c22291efa08), [`5e5332fbc`](https://github.com/vercel/vercel/commit/5e5332fbc9317a8f3cc4ed0b72ec1a2c76020891), [`027bce00b`](https://github.com/vercel/vercel/commit/027bce00b3821d9b4a8f7ec320cd1c43ab9f4215)]:
|
||||
- @vercel/build-utils@6.8.1
|
||||
- @vercel/node@2.15.4
|
||||
- @vercel/remix-builder@1.8.16
|
||||
- @vercel/static-build@1.3.39
|
||||
|
||||
## 31.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "31.0.1",
|
||||
"version": "31.2.3",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -31,16 +31,16 @@
|
||||
"node": ">= 14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "6.8.0",
|
||||
"@vercel/build-utils": "6.8.3",
|
||||
"@vercel/go": "2.5.1",
|
||||
"@vercel/hydrogen": "0.0.64",
|
||||
"@vercel/next": "3.8.8",
|
||||
"@vercel/node": "2.15.3",
|
||||
"@vercel/next": "3.9.4",
|
||||
"@vercel/node": "2.15.9",
|
||||
"@vercel/python": "3.1.60",
|
||||
"@vercel/redwood": "1.1.15",
|
||||
"@vercel/remix-builder": "1.8.15",
|
||||
"@vercel/remix-builder": "1.10.0",
|
||||
"@vercel/ruby": "1.3.76",
|
||||
"@vercel/static-build": "1.3.38"
|
||||
"@vercel/static-build": "1.3.45"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alex_neo/jest-expect-message": "1.0.5",
|
||||
@@ -84,13 +84,13 @@
|
||||
"@types/which": "3.0.0",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@types/yauzl-promise": "2.1.0",
|
||||
"@vercel-internals/constants": "1.0.3",
|
||||
"@vercel-internals/constants": "1.0.4",
|
||||
"@vercel-internals/get-package-json": "1.0.0",
|
||||
"@vercel-internals/types": "1.0.3",
|
||||
"@vercel/client": "12.6.3",
|
||||
"@vercel-internals/types": "1.0.6",
|
||||
"@vercel/client": "12.6.6",
|
||||
"@vercel/error-utils": "1.0.10",
|
||||
"@vercel/frameworks": "1.4.3",
|
||||
"@vercel/fs-detectors": "4.0.1",
|
||||
"@vercel/frameworks": "1.5.0",
|
||||
"@vercel/fs-detectors": "4.1.1",
|
||||
"@vercel/fun": "1.0.4",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/routing-utils": "2.2.1",
|
||||
@@ -149,7 +149,7 @@
|
||||
"pluralize": "7.0.0",
|
||||
"promisepipe": "3.0.0",
|
||||
"proxy": "2.0.0",
|
||||
"proxy-agent": "6.1.1",
|
||||
"proxy-agent": "6.3.0",
|
||||
"psl": "1.1.31",
|
||||
"qr-image": "3.2.0",
|
||||
"raw-body": "2.4.1",
|
||||
@@ -157,8 +157,7 @@
|
||||
"semver": "5.5.0",
|
||||
"serve-handler": "6.1.1",
|
||||
"strip-ansi": "6.0.1",
|
||||
"stripe": "5.1.0",
|
||||
"supports-hyperlinks": "2.2.0",
|
||||
"supports-hyperlinks": "3.0.0",
|
||||
"tar-fs": "1.16.3",
|
||||
"test-listen": "1.1.0",
|
||||
"text-table": "0.2.0",
|
||||
|
||||
69
packages/cli/src/commands/bisect/command.ts
Normal file
69
packages/cli/src/commands/bisect/command.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { Command } from '../help';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
export const bisectCommand: Command = {
|
||||
name: 'bisect',
|
||||
description: 'Bisect the current project interactively.',
|
||||
arguments: [],
|
||||
options: [
|
||||
{
|
||||
name: 'bad',
|
||||
description: 'Known bad URL',
|
||||
argument: 'URL',
|
||||
shorthand: 'b',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'good',
|
||||
description: 'Known good URL',
|
||||
argument: 'URL',
|
||||
shorthand: 'g',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'open',
|
||||
description: 'Automatically open each URL in the browser',
|
||||
argument: 'URL',
|
||||
shorthand: 'o',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'path',
|
||||
description: 'Subpath of the deployment URL to test',
|
||||
argument: 'URL',
|
||||
shorthand: 'p',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'run',
|
||||
description: 'Test script to run for each deployment',
|
||||
argument: 'URL',
|
||||
shorthand: 'r',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
name: 'Bisect the current project interactively',
|
||||
value: `${getPkgName()} bisect`,
|
||||
},
|
||||
{
|
||||
name: 'Bisect with a known bad deployment',
|
||||
value: `${getPkgName()} bisect --bad example-310pce9i0.vercel.app`,
|
||||
},
|
||||
{
|
||||
name: 'Automated bisect with a run script',
|
||||
value: `${getPkgName()} bisect --run ./test.sh`,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -8,53 +8,19 @@ import { URLSearchParams, parse } from 'url';
|
||||
import box from '../../util/output/box';
|
||||
import formatDate from '../../util/format-date';
|
||||
import link from '../../util/output/link';
|
||||
import logo from '../../util/output/logo';
|
||||
import getArgs from '../../util/get-args';
|
||||
import Client from '../../util/client';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { Deployment } from '@vercel-internals/types';
|
||||
import { normalizeURL } from '../../util/bisect/normalize-url';
|
||||
import getScope from '../../util/get-scope';
|
||||
import getDeployment from '../../util/get-deployment';
|
||||
import { help } from '../help';
|
||||
import { bisectCommand } from './command';
|
||||
|
||||
interface Deployments {
|
||||
deployments: Deployment[];
|
||||
}
|
||||
|
||||
const pkgName = getPkgName();
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(`${logo} ${pkgName} bisect`)} [options]
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-d, --debug Debug mode [off]
|
||||
--no-color No color mode [off]
|
||||
-b, --bad Known bad URL
|
||||
-g, --good Known good URL
|
||||
-o, --open Automatically open each URL in the browser
|
||||
-p, --path Subpath of the deployment URL to test
|
||||
-r, --run Test script to run for each deployment
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Bisect the current project interactively
|
||||
|
||||
${chalk.cyan(`$ ${pkgName} bisect`)}
|
||||
|
||||
${chalk.gray('–')} Bisect with a known bad deployment
|
||||
|
||||
${chalk.cyan(`$ ${pkgName} bisect --bad example-310pce9i0.vercel.app`)}
|
||||
|
||||
${chalk.gray('–')} Automated bisect with a run script
|
||||
|
||||
${chalk.cyan(`$ ${pkgName} bisect --run ./test.sh`)}
|
||||
`);
|
||||
};
|
||||
|
||||
export default async function main(client: Client): Promise<number> {
|
||||
export default async function bisect(client: Client): Promise<number> {
|
||||
const { output } = client;
|
||||
const scope = await getScope(client);
|
||||
const { contextName } = scope;
|
||||
@@ -73,7 +39,7 @@ export default async function main(client: Client): Promise<number> {
|
||||
});
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
output.print(help(bisectCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
46
packages/cli/src/commands/build/command.ts
Normal file
46
packages/cli/src/commands/build/command.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Command } from '../help';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
export const buildCommand: Command = {
|
||||
name: 'build',
|
||||
description: 'Build the project.',
|
||||
arguments: [],
|
||||
options: [
|
||||
{
|
||||
name: 'prod',
|
||||
description: 'Build a production deployment',
|
||||
shorthand: null,
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'output',
|
||||
description: 'Directory where built assets should be written to',
|
||||
shorthand: null,
|
||||
argument: 'PATH',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'yes',
|
||||
description:
|
||||
'Skip the confirmation prompt about pulling environment variables and project settings when not found locally',
|
||||
shorthand: 'y',
|
||||
type: 'boolean',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
name: 'Build the project',
|
||||
value: `${getPkgName()} build`,
|
||||
},
|
||||
{
|
||||
name: 'Build the project in a specific directory',
|
||||
value: `${getPkgName()} build --cwd ./path-to-project`,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -38,35 +38,37 @@ import {
|
||||
import { fileNameSymbol } from '@vercel/client';
|
||||
import type { VercelConfig } from '@vercel/client';
|
||||
|
||||
import pull from './pull';
|
||||
import { staticFiles as getFiles } from '../util/get-files';
|
||||
import Client from '../util/client';
|
||||
import getArgs from '../util/get-args';
|
||||
import cmd from '../util/output/cmd';
|
||||
import * as cli from '../util/pkg-name';
|
||||
import cliPkg from '../util/pkg';
|
||||
import readJSONFile from '../util/read-json-file';
|
||||
import { CantParseJSONFile } from '../util/errors-ts';
|
||||
import pull from '../pull';
|
||||
import { staticFiles as getFiles } from '../../util/get-files';
|
||||
import Client from '../../util/client';
|
||||
import getArgs from '../../util/get-args';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import * as cli from '../../util/pkg-name';
|
||||
import cliPkg from '../../util/pkg';
|
||||
import readJSONFile from '../../util/read-json-file';
|
||||
import { CantParseJSONFile } from '../../util/errors-ts';
|
||||
import {
|
||||
pickOverrides,
|
||||
ProjectLinkAndSettings,
|
||||
readProjectSettings,
|
||||
} from '../util/projects/project-settings';
|
||||
import { getProjectLink, VERCEL_DIR } from '../util/projects/link';
|
||||
import confirm from '../util/input/confirm';
|
||||
import { emoji, prependEmoji } from '../util/emoji';
|
||||
import stamp from '../util/output/stamp';
|
||||
} from '../../util/projects/project-settings';
|
||||
import { getProjectLink, VERCEL_DIR } from '../../util/projects/link';
|
||||
import confirm from '../../util/input/confirm';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import {
|
||||
OUTPUT_DIR,
|
||||
PathOverride,
|
||||
writeBuildResult,
|
||||
} from '../util/build/write-build-result';
|
||||
import { importBuilders } from '../util/build/import-builders';
|
||||
import { initCorepack, cleanupCorepack } from '../util/build/corepack';
|
||||
import { sortBuilders } from '../util/build/sort-builders';
|
||||
import { toEnumerableError } from '../util/error';
|
||||
import { validateConfig } from '../util/validate-config';
|
||||
import { setMonorepoDefaultSettings } from '../util/build/monorepo';
|
||||
} from '../../util/build/write-build-result';
|
||||
import { importBuilders } from '../../util/build/import-builders';
|
||||
import { initCorepack, cleanupCorepack } from '../../util/build/corepack';
|
||||
import { sortBuilders } from '../../util/build/sort-builders';
|
||||
import { toEnumerableError } from '../../util/error';
|
||||
import { validateConfig } from '../../util/validate-config';
|
||||
import { setMonorepoDefaultSettings } from '../../util/build/monorepo';
|
||||
import { help } from '../help';
|
||||
import { buildCommand } from './command';
|
||||
|
||||
type BuildResult = BuildResultV2 | BuildResultV3;
|
||||
|
||||
@@ -103,35 +105,6 @@ export interface BuildsManifest {
|
||||
builds?: SerializedBuilder[];
|
||||
}
|
||||
|
||||
const help = () => {
|
||||
return console.log(`
|
||||
${chalk.bold(`${cli.logo} ${cli.name} build`)}
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
--cwd [path] The current working directory
|
||||
--output [path] Directory where built assets should be written to
|
||||
--prod Build a production deployment
|
||||
-d, --debug Debug mode [off]
|
||||
--no-color No color mode [off]
|
||||
-y, --yes Skip the confirmation prompt about pulling environment variables and project settings when not found locally
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('-')} Build the project
|
||||
|
||||
${chalk.cyan(`$ ${cli.name} build`)}
|
||||
${chalk.cyan(`$ ${cli.name} build --cwd ./path-to-project`)}
|
||||
`);
|
||||
};
|
||||
|
||||
export default async function main(client: Client): Promise<number> {
|
||||
let { cwd } = client;
|
||||
const { output } = client;
|
||||
@@ -162,7 +135,7 @@ export default async function main(client: Client): Promise<number> {
|
||||
});
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
output.print(help(buildCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -57,12 +57,13 @@ export default async function dev(
|
||||
|
||||
let projectSettings: ProjectSettings | undefined;
|
||||
let envValues: Record<string, string> = {};
|
||||
let repoRoot: string | undefined;
|
||||
if (link.status === 'linked') {
|
||||
const { project, org, repoRoot } = link;
|
||||
const { project, org } = link;
|
||||
|
||||
// If repo linked, update `cwd` to the repo root
|
||||
if (repoRoot) {
|
||||
cwd = repoRoot;
|
||||
if (link.repoRoot) {
|
||||
repoRoot = cwd = link.repoRoot;
|
||||
}
|
||||
|
||||
client.config.currentTeam = org.type === 'team' ? org.id : undefined;
|
||||
@@ -82,6 +83,7 @@ export default async function dev(
|
||||
output,
|
||||
projectSettings,
|
||||
envValues,
|
||||
repoRoot,
|
||||
});
|
||||
|
||||
// listen to SIGTERM for graceful shutdown
|
||||
|
||||
@@ -30,6 +30,86 @@ export interface Command {
|
||||
examples: CommandExample[];
|
||||
}
|
||||
|
||||
const globalCommandOptions: CommandOption[] = [
|
||||
{
|
||||
name: 'help',
|
||||
shorthand: 'h',
|
||||
type: 'string',
|
||||
description: 'Output usage information',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'version',
|
||||
shorthand: 'v',
|
||||
type: 'string',
|
||||
description: 'Output the version number',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'cwd',
|
||||
shorthand: null,
|
||||
type: 'string',
|
||||
argument: 'DIR',
|
||||
description:
|
||||
'Sets the current working directory for a single run of a command',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'local-config',
|
||||
shorthand: 'A',
|
||||
type: 'string',
|
||||
argument: 'FILE',
|
||||
description: 'Path to the local `vercel.json` file',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'global-config',
|
||||
shorthand: 'Q',
|
||||
type: 'string',
|
||||
argument: 'DIR',
|
||||
description: 'Path to the global `.vercel` directory',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'debug',
|
||||
shorthand: 'd',
|
||||
type: 'string',
|
||||
description: 'Debug mode (default off)',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'no-color',
|
||||
shorthand: null,
|
||||
type: 'string',
|
||||
description: 'No color mode (default off)',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'scope',
|
||||
shorthand: 'S',
|
||||
type: 'string',
|
||||
description: 'Set a custom scope',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'token',
|
||||
shorthand: 't',
|
||||
type: 'string',
|
||||
argument: 'TOKEN',
|
||||
description: 'Login token',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
];
|
||||
|
||||
export function calcLineLength(line: string[]) {
|
||||
return stripAnsi(lineToString(line)).length;
|
||||
}
|
||||
@@ -52,8 +132,8 @@ export function lineToString(line: string[]) {
|
||||
return string;
|
||||
}
|
||||
|
||||
export function outputArrayToString(outputArray: string[]) {
|
||||
return outputArray.join(NEWLINE);
|
||||
export function outputArrayToString(outputArray: (string | null)[]) {
|
||||
return outputArray.filter(line => line !== null).join(NEWLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +142,12 @@ export function outputArrayToString(outputArray: string[]) {
|
||||
* @returns
|
||||
*/
|
||||
export function buildCommandSynopsisLine(command: Command) {
|
||||
const line: string[] = [LOGO, chalk.bold(NAME), chalk.bold(command.name)];
|
||||
const line: string[] = [
|
||||
INDENT,
|
||||
LOGO,
|
||||
chalk.bold(NAME),
|
||||
chalk.bold(command.name),
|
||||
];
|
||||
if (command.arguments.length > 0) {
|
||||
for (const argument of command.arguments) {
|
||||
line.push(argument.required ? argument.name : `[${argument.name}]`);
|
||||
@@ -71,32 +156,39 @@ export function buildCommandSynopsisLine(command: Command) {
|
||||
if (command.options.length > 0) {
|
||||
line.push('[options]');
|
||||
}
|
||||
|
||||
line.push(NEWLINE);
|
||||
return lineToString(line);
|
||||
}
|
||||
|
||||
export function buildCommandOptionLines(
|
||||
command: Command,
|
||||
options: BuildHelpOutputOptions
|
||||
commandOptions: CommandOption[],
|
||||
options: BuildHelpOutputOptions,
|
||||
sectionTitle: String
|
||||
) {
|
||||
// Filter out deprecated and intentionally undocumented options
|
||||
command.options = command.options.filter(
|
||||
commandOptions = commandOptions.filter(
|
||||
option => !option.deprecated && option.description !== undefined
|
||||
);
|
||||
|
||||
if (commandOptions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Initialize output array with header and empty line
|
||||
const outputArray: string[] = [chalk.dim(`Options:`), ''];
|
||||
const outputArray: string[] = [`${INDENT}${chalk.dim(sectionTitle)}:`, ''];
|
||||
|
||||
// Start building option lines
|
||||
const optionLines: string[][] = [];
|
||||
// Sort command options alphabetically
|
||||
command.options.sort((a, b) =>
|
||||
commandOptions.sort((a, b) =>
|
||||
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
||||
);
|
||||
// Keep track of longest "start" of an option line to determine description spacing
|
||||
let maxLineStartLength = 0;
|
||||
// Iterate over options and create the "start" of each option (e.g. ` -b, --build-env <key=value>`)
|
||||
for (const option of command.options) {
|
||||
const startLine: string[] = [INDENT];
|
||||
for (const option of commandOptions) {
|
||||
const startLine: string[] = [INDENT, INDENT, INDENT];
|
||||
if (option.shorthand) {
|
||||
startLine.push(`-${option.shorthand},`);
|
||||
}
|
||||
@@ -135,7 +227,7 @@ export function buildCommandOptionLines(
|
||||
*/
|
||||
for (let i = 0; i < optionLines.length; i++) {
|
||||
const optionLine = optionLines[i];
|
||||
const option = command.options[i];
|
||||
const option = commandOptions[i];
|
||||
// Add only 2 spaces to the longest line, and then make all shorter lines the same length.
|
||||
optionLine.push(
|
||||
' '.repeat(2 + (maxLineStartLength - calcLineLength(optionLine)))
|
||||
@@ -162,16 +254,13 @@ export function buildCommandOptionLines(
|
||||
for (const line of lines) {
|
||||
outputArray.push(lineToString(line));
|
||||
}
|
||||
// add an empty line in between in each option block for readability (skip the last block)
|
||||
if (i !== optionLines.length - 1) outputArray.push('');
|
||||
}
|
||||
|
||||
// return the entire list of options as a single string after delete the last '\n' added to the option list
|
||||
return outputArrayToString(outputArray);
|
||||
return `${outputArrayToString(outputArray)}${NEWLINE}`;
|
||||
}
|
||||
|
||||
export function buildCommandExampleLines(command: Command) {
|
||||
const outputArray: string[] = [chalk.dim(`Examples:`), ''];
|
||||
const outputArray: string[] = [`${INDENT}${chalk.dim('Examples:')}`, ''];
|
||||
for (const example of command.examples) {
|
||||
const nameLine: string[] = [INDENT];
|
||||
nameLine.push(chalk.gray('-'));
|
||||
@@ -190,12 +279,15 @@ export function buildCommandExampleLines(command: Command) {
|
||||
}
|
||||
outputArray.push('');
|
||||
}
|
||||
// delete the last newline added after examples iteration
|
||||
outputArray.splice(-1);
|
||||
|
||||
return outputArrayToString(outputArray);
|
||||
}
|
||||
|
||||
function buildDescriptionLine(command: Command) {
|
||||
const line: string[] = [INDENT, command.description, NEWLINE];
|
||||
return lineToString(line);
|
||||
}
|
||||
|
||||
interface BuildHelpOutputOptions {
|
||||
columns: number;
|
||||
}
|
||||
@@ -204,13 +296,12 @@ export function buildHelpOutput(
|
||||
command: Command,
|
||||
options: BuildHelpOutputOptions
|
||||
) {
|
||||
const outputArray: string[] = [
|
||||
const outputArray: (string | null)[] = [
|
||||
'',
|
||||
buildCommandSynopsisLine(command),
|
||||
'',
|
||||
command.description,
|
||||
'',
|
||||
buildCommandOptionLines(command, options),
|
||||
'',
|
||||
buildDescriptionLine(command),
|
||||
buildCommandOptionLines(command.options, options, 'Options'),
|
||||
buildCommandOptionLines(globalCommandOptions, options, 'Global Options'),
|
||||
buildCommandExampleLines(command),
|
||||
'',
|
||||
];
|
||||
|
||||
50
packages/cli/src/commands/inspect/command.ts
Normal file
50
packages/cli/src/commands/inspect/command.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Command } from '../help';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
export const inspectCommand: Command = {
|
||||
name: 'inspect',
|
||||
description: 'Show information about a deployment.',
|
||||
arguments: [
|
||||
{
|
||||
name: 'url',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: 'timeout',
|
||||
description: 'Time to wait for deployment completion [3m]',
|
||||
argument: 'TIME',
|
||||
shorthand: null,
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'wait',
|
||||
description: 'Blocks until deployment completes',
|
||||
shorthand: null,
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
name: 'Get information about a deployment by its unique URL',
|
||||
value: `${getPkgName()} inspect my-deployment-ji2fjij2.vercel.app`,
|
||||
},
|
||||
{
|
||||
name: 'Get information about the deployment an alias points to',
|
||||
value: `${getPkgName()} inspect my-deployment.vercel.app`,
|
||||
},
|
||||
{
|
||||
name: 'Get information about a deployment by piping in the URL',
|
||||
value: `echo my-deployment.vercel.app | ${getPkgName()} inspect`,
|
||||
},
|
||||
{
|
||||
name: 'Wait up to 90 seconds for deployment to complete',
|
||||
value: `${getPkgName()} inspect my-deployment.vercel.app --wait --timeout 90s`,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,71 +1,27 @@
|
||||
import chalk from 'chalk';
|
||||
import getArgs from '../util/get-args';
|
||||
import buildsList from '../util/output/builds';
|
||||
import routesList from '../util/output/routes';
|
||||
import indent from '../util/output/indent';
|
||||
import logo from '../util/output/logo';
|
||||
import elapsed from '../util/output/elapsed';
|
||||
import { handleError } from '../util/error';
|
||||
import getScope from '../util/get-scope';
|
||||
import { getPkgName, getCommandName } from '../util/pkg-name';
|
||||
import Client from '../util/client';
|
||||
import getDeployment from '../util/get-deployment';
|
||||
import getArgs from '../../util/get-args';
|
||||
import buildsList from '../../util/output/builds';
|
||||
import routesList from '../../util/output/routes';
|
||||
import indent from '../../util/output/indent';
|
||||
import elapsed from '../../util/output/elapsed';
|
||||
import { handleError } from '../../util/error';
|
||||
import getScope from '../../util/get-scope';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import Client from '../../util/client';
|
||||
import getDeployment from '../../util/get-deployment';
|
||||
import type { Build, Deployment } from '@vercel-internals/types';
|
||||
import title from 'title';
|
||||
import { isErrnoException } from '@vercel/error-utils';
|
||||
import { URL } from 'url';
|
||||
import readStandardInput from '../util/input/read-standard-input';
|
||||
import sleep from '../util/sleep';
|
||||
import readStandardInput from '../../util/input/read-standard-input';
|
||||
import sleep from '../../util/sleep';
|
||||
import ms from 'ms';
|
||||
import { isDeploying } from '../util/deploy/is-deploying';
|
||||
import { isDeploying } from '../../util/deploy/is-deploying';
|
||||
import { help } from '../help';
|
||||
import { inspectCommand } from './command';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(`${logo} ${getPkgName()} inspect`)} <url>
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
-d, --debug Debug mode [off]
|
||||
--no-color No color mode [off]
|
||||
-S, --scope Set a custom scope
|
||||
--timeout=${chalk.bold.underline(
|
||||
'TIME'
|
||||
)} Time to wait for deployment completion [3m]
|
||||
--wait Blocks until deployment completes
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Get information about a deployment by its unique URL
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} inspect my-deployment-ji2fjij2.vercel.app`)}
|
||||
|
||||
${chalk.gray('-')} Get information about the deployment an alias points to
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} inspect my-deployment.vercel.app`)}
|
||||
|
||||
${chalk.gray('-')} Get information about a deployment by piping in the URL
|
||||
|
||||
${chalk.cyan(`$ echo my-deployment.vercel.app | ${getPkgName()} inspect`)}
|
||||
|
||||
${chalk.gray('-')} Wait up to 90 seconds for deployment to complete
|
||||
|
||||
${chalk.cyan(
|
||||
`$ ${getPkgName()} inspect my-deployment.vercel.app --wait --timeout 90s`
|
||||
)}
|
||||
`);
|
||||
};
|
||||
|
||||
export default async function main(client: Client) {
|
||||
export default async function inspect(client: Client) {
|
||||
const { output } = client;
|
||||
let argv;
|
||||
|
||||
try {
|
||||
@@ -79,7 +35,7 @@ export default async function main(client: Client) {
|
||||
}
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
output.print(help(inspectCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -99,7 +55,7 @@ export default async function main(client: Client) {
|
||||
|
||||
if (!deploymentIdOrHost) {
|
||||
error(`${getCommandName('inspect <url>')} expects exactly one argument`);
|
||||
help();
|
||||
output.print(help(inspectCommand, { columns: client.stderr.columns }));
|
||||
return 1;
|
||||
}
|
||||
|
||||
54
packages/cli/src/commands/link/command.ts
Normal file
54
packages/cli/src/commands/link/command.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Command } from '../help';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
export const linkCommand: Command = {
|
||||
name: 'link',
|
||||
description: 'Link a local directory to a Vercel Project.',
|
||||
arguments: [],
|
||||
options: [
|
||||
{
|
||||
name: 'repo',
|
||||
description: 'Link multiple projects based on Git repository (alpha)',
|
||||
shorthand: 'r',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'project',
|
||||
description: 'Specify a project name',
|
||||
shorthand: 'p',
|
||||
argument: 'NAME',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'yes',
|
||||
description:
|
||||
'Skip questions when setting up new project using default scope and settings',
|
||||
shorthand: 'y',
|
||||
type: 'boolean',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
name: 'Link current directory to a Vercel Project',
|
||||
value: `${getPkgName()} link`,
|
||||
},
|
||||
{
|
||||
name: 'Link current directory with default options and skip questions',
|
||||
value: `${getPkgName()} link --yes`,
|
||||
},
|
||||
{
|
||||
name: 'Link a specific directory to a Vercel Project',
|
||||
value: `${getPkgName()} link --cwd /path/to/project`,
|
||||
},
|
||||
{
|
||||
name: 'Link to the current Git repository, allowing for multiple Vercel Projects to be linked simultaneously (useful for monorepos)',
|
||||
value: `${getPkgName()} link --repo`,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,62 +1,12 @@
|
||||
import chalk from 'chalk';
|
||||
import Client from '../../util/client';
|
||||
import getArgs from '../../util/get-args';
|
||||
import logo from '../../util/output/logo';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { ensureLink } from '../../util/link/ensure-link';
|
||||
import { ensureRepoLink } from '../../util/link/repo';
|
||||
import { help } from '../help';
|
||||
import { linkCommand } from './command';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(`${logo} ${getPkgName()} link`)} [options]
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-r, --repo Link multiple projects based on Git repository (alpha)
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
--no-color No color mode [off]
|
||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
-p ${chalk.bold.underline('NAME')}, --project=${chalk.bold.underline(
|
||||
'NAME'
|
||||
)} Project name
|
||||
-y, --yes Skip questions when setting up new project using default scope and settings
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Link current directory to a Vercel Project
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} link`)}
|
||||
|
||||
${chalk.gray(
|
||||
'–'
|
||||
)} Link current directory with default options and skip questions
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} link --yes`)}
|
||||
|
||||
${chalk.gray('–')} Link a specific directory to a Vercel Project
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} link --cwd /path/to/project`)}
|
||||
|
||||
${chalk.gray('–')} ${chalk.yellow(
|
||||
'(alpha)'
|
||||
)} Link to the current Git repository, allowing for multiple
|
||||
Vercel Projects to be linked simultaneously (useful for monorepos)
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} link --repo`)}
|
||||
`);
|
||||
};
|
||||
|
||||
export default async function main(client: Client) {
|
||||
export default async function link(client: Client) {
|
||||
const argv = getArgs(client.argv.slice(2), {
|
||||
'--yes': Boolean,
|
||||
'-y': '--yes',
|
||||
@@ -71,7 +21,7 @@ export default async function main(client: Client) {
|
||||
});
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
client.output.print(help(linkCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -97,7 +47,7 @@ export default async function main(client: Client) {
|
||||
client.output.warn(
|
||||
`The ${cmd('--repo')} flag is in alpha, please report issues`
|
||||
);
|
||||
await ensureRepoLink(client, cwd, yes);
|
||||
await ensureRepoLink(client, cwd, { yes, overwrite: true });
|
||||
} else {
|
||||
const link = await ensureLink('link', client, cwd, {
|
||||
autoConfirm: yes,
|
||||
|
||||
61
packages/cli/src/commands/list/command.ts
Normal file
61
packages/cli/src/commands/list/command.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Command } from '../help';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
export const listCommand: Command = {
|
||||
name: 'list',
|
||||
description: 'List app deployments for an app.',
|
||||
arguments: [
|
||||
{
|
||||
name: 'app',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: 'meta',
|
||||
description:
|
||||
'Filter deployments by metadata (e.g.: `-m KEY=value`). Can appear many times.',
|
||||
argument: 'KEY=value',
|
||||
shorthand: null,
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: true,
|
||||
},
|
||||
{
|
||||
name: 'environment',
|
||||
description: '',
|
||||
argument: 'production|preview',
|
||||
shorthand: null,
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'next',
|
||||
description: 'Show next page of results',
|
||||
argument: 'MS',
|
||||
shorthand: 'n',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
name: 'List all deployments for the currently linked project',
|
||||
value: `${getPkgName()} list`,
|
||||
},
|
||||
{
|
||||
name: 'List all deployments for the project `my-app` in the team of the currently linked project',
|
||||
value: `${getPkgName()} list my-app`,
|
||||
},
|
||||
{
|
||||
name: 'Filter deployments by metadata',
|
||||
value: `${getPkgName()} list -m key1=value1 -m key2=value2`,
|
||||
},
|
||||
{
|
||||
name: 'Paginate deployments for a project, where `1584722256178` is the time in milliseconds since the UNIX epoch',
|
||||
value: `${getPkgName()} list my-app --next 1584722256178`,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -2,85 +2,37 @@ import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import table from 'text-table';
|
||||
import title from 'title';
|
||||
import Now from '../util';
|
||||
import getArgs from '../util/get-args';
|
||||
import { handleError } from '../util/error';
|
||||
import logo from '../util/output/logo';
|
||||
import elapsed from '../util/output/elapsed';
|
||||
import strlen from '../util/strlen';
|
||||
import toHost from '../util/to-host';
|
||||
import parseMeta from '../util/parse-meta';
|
||||
import { isValidName } from '../util/is-valid-name';
|
||||
import getCommandFlags from '../util/get-command-flags';
|
||||
import { getPkgName, getCommandName } from '../util/pkg-name';
|
||||
import Client from '../util/client';
|
||||
import Now from '../../util';
|
||||
import getArgs from '../../util/get-args';
|
||||
import { handleError } from '../../util/error';
|
||||
import elapsed from '../../util/output/elapsed';
|
||||
import strlen from '../../util/strlen';
|
||||
import toHost from '../../util/to-host';
|
||||
import parseMeta from '../../util/parse-meta';
|
||||
import { isValidName } from '../../util/is-valid-name';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import Client from '../../util/client';
|
||||
import { Deployment } from '@vercel/client';
|
||||
import { getLinkedProject } from '../util/projects/link';
|
||||
import { ensureLink } from '../util/link/ensure-link';
|
||||
import getScope from '../util/get-scope';
|
||||
import { isAPIError } from '../util/errors-ts';
|
||||
import { getLinkedProject } from '../../util/projects/link';
|
||||
import { ensureLink } from '../../util/link/ensure-link';
|
||||
import getScope from '../../util/get-scope';
|
||||
import { isAPIError } from '../../util/errors-ts';
|
||||
import { isErrnoException } from '@vercel/error-utils';
|
||||
import { help } from '../help';
|
||||
import { listCommand } from './command';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(`${logo} ${getPkgName()} list`)} [app]
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
--no-color No color mode [off]
|
||||
-y, --yes Skip questions when setting up new project using default scope and settings
|
||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
-S, --scope Set a custom scope
|
||||
-m, --meta Filter deployments by metadata (e.g.: ${chalk.dim(
|
||||
'`-m KEY=value`'
|
||||
)}). Can appear many times.
|
||||
--prod Filter for production URLs
|
||||
-N, --next Show next page of results
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} List all deployments for the currently linked project
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} ls`)}
|
||||
|
||||
${chalk.gray('–')} List all deployments for the project ${chalk.dim(
|
||||
'`my-app`'
|
||||
)} in the team of the currently linked project
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} ls my-app`)}
|
||||
|
||||
${chalk.gray('–')} Filter deployments by metadata
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} ls -m key1=value1 -m key2=value2`)}
|
||||
|
||||
${chalk.gray('–')} Paginate deployments for a project, where ${chalk.dim(
|
||||
'`1584722256178`'
|
||||
)} is the time in milliseconds since the UNIX epoch.
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} ls my-app --next 1584722256178`)}
|
||||
`);
|
||||
};
|
||||
|
||||
export default async function main(client: Client) {
|
||||
export default async function list(client: Client) {
|
||||
let argv;
|
||||
|
||||
try {
|
||||
argv = getArgs(client.argv.slice(2), {
|
||||
'--environment': String,
|
||||
'--meta': [String],
|
||||
'-m': '--meta',
|
||||
'--next': Number,
|
||||
'-N': '--next',
|
||||
'--prod': Boolean,
|
||||
'--prod': Boolean, // this can be deprecated someday
|
||||
'--yes': Boolean,
|
||||
'-y': '--yes',
|
||||
|
||||
@@ -108,14 +60,19 @@ export default async function main(client: Client) {
|
||||
}
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
output.print(help(listCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
const autoConfirm = !!argv['--yes'];
|
||||
const prod = argv['--prod'] || false;
|
||||
const meta = parseMeta(argv['--meta']);
|
||||
|
||||
const target = argv['--prod']
|
||||
? 'production'
|
||||
: typeof argv['--environment'] === 'string'
|
||||
? argv['--environment'].toLowerCase()
|
||||
: undefined;
|
||||
|
||||
// retrieve `project` and `org` from .vercel
|
||||
let link = await getLinkedProject(client, cwd);
|
||||
|
||||
@@ -219,15 +176,12 @@ export default async function main(client: Client) {
|
||||
|
||||
debug('Fetching deployments');
|
||||
|
||||
const response = await now.list(
|
||||
app,
|
||||
{
|
||||
version: 6,
|
||||
meta,
|
||||
nextTimestamp,
|
||||
},
|
||||
prod
|
||||
);
|
||||
const response = await now.list(app, {
|
||||
version: 6,
|
||||
meta,
|
||||
nextTimestamp,
|
||||
target,
|
||||
});
|
||||
|
||||
let {
|
||||
deployments,
|
||||
@@ -280,9 +234,11 @@ export default async function main(client: Client) {
|
||||
}
|
||||
|
||||
log(
|
||||
`${prod ? `Production deployments` : `Deployments`} for ${chalk.bold(
|
||||
app
|
||||
)} under ${chalk.bold(contextName)} ${elapsed(Date.now() - start)}`
|
||||
`${
|
||||
target === 'production' ? `Production deployments` : `Deployments`
|
||||
} for ${chalk.bold(app)} under ${chalk.bold(contextName)} ${elapsed(
|
||||
Date.now() - start
|
||||
)}`
|
||||
);
|
||||
|
||||
// information to help the user find other deployments or instances
|
||||
@@ -292,8 +248,9 @@ export default async function main(client: Client) {
|
||||
|
||||
print('\n');
|
||||
|
||||
const headers = ['Age', 'Deployment', 'Status', 'Duration'];
|
||||
const headers = ['Age', 'Deployment', 'Status', 'Environment', 'Duration'];
|
||||
if (showUsername) headers.push('Username');
|
||||
const urls: string[] = [];
|
||||
|
||||
client.output.print(
|
||||
`${table(
|
||||
@@ -301,18 +258,17 @@ export default async function main(client: Client) {
|
||||
headers.map(header => chalk.bold(chalk.cyan(header))),
|
||||
...deployments
|
||||
.sort(sortRecent())
|
||||
.map(dep => [
|
||||
[
|
||||
.map(dep => {
|
||||
urls.push(`https://${dep.url}`);
|
||||
return [
|
||||
chalk.gray(ms(Date.now() - dep.createdAt)),
|
||||
`https://${dep.url}`,
|
||||
stateString(dep.state || ''),
|
||||
dep.target === 'production' ? 'Production' : 'Preview',
|
||||
chalk.gray(getDeploymentDuration(dep)),
|
||||
showUsername ? chalk.gray(dep.creator?.username) : '',
|
||||
],
|
||||
])
|
||||
// flatten since the previous step returns a nested
|
||||
// array of the deployment and (optionally) its instances
|
||||
.flat()
|
||||
];
|
||||
})
|
||||
.filter(app =>
|
||||
// if an app wasn't supplied to filter by,
|
||||
// we only want to render one deployment per app
|
||||
@@ -327,6 +283,11 @@ export default async function main(client: Client) {
|
||||
).replace(/^/gm, ' ')}\n\n`
|
||||
);
|
||||
|
||||
if (!client.stdout.isTTY) {
|
||||
client.stdout.write(urls.join('\n'));
|
||||
client.stdout.write('\n');
|
||||
}
|
||||
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(argv, ['_', '--next']);
|
||||
log(
|
||||
49
packages/cli/src/commands/login/command.ts
Normal file
49
packages/cli/src/commands/login/command.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Command } from '../help';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
export const loginCommand: Command = {
|
||||
name: 'login',
|
||||
description: 'Authenticate using your email or team id.',
|
||||
arguments: [
|
||||
{
|
||||
name: 'email or team id',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: 'github',
|
||||
description: 'Log in with GitHub',
|
||||
shorthand: null,
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'oob',
|
||||
description: 'Log in with "out of band" authentication',
|
||||
shorthand: null,
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
name: 'Log into the Vercel platform',
|
||||
value: `${getPkgName()} login`,
|
||||
},
|
||||
{
|
||||
name: 'Log in using a specific email address',
|
||||
value: `${getPkgName()} login username@example.com`,
|
||||
},
|
||||
{
|
||||
name: 'Log in using a specific team "slug" for SAML Single Sign-On',
|
||||
value: `${getPkgName()} login acme`,
|
||||
},
|
||||
{
|
||||
name: 'Log in using GitHub in "out-of-band" mode',
|
||||
value: `${getPkgName()} login --github --oob`,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,55 +1,24 @@
|
||||
import { validate as validateEmail } from 'email-validator';
|
||||
import chalk from 'chalk';
|
||||
import hp from '../util/humanize-path';
|
||||
import getArgs from '../util/get-args';
|
||||
import logo from '../util/output/logo';
|
||||
import prompt from '../util/login/prompt';
|
||||
import doSamlLogin from '../util/login/saml';
|
||||
import doEmailLogin from '../util/login/email';
|
||||
import doGithubLogin from '../util/login/github';
|
||||
import doGitlabLogin from '../util/login/gitlab';
|
||||
import doBitbucketLogin from '../util/login/bitbucket';
|
||||
import { prependEmoji, emoji } from '../util/emoji';
|
||||
import { getCommandName, getPkgName } from '../util/pkg-name';
|
||||
import getGlobalPathConfig from '../util/config/global-path';
|
||||
import { writeToAuthConfigFile, writeToConfigFile } from '../util/config/files';
|
||||
import Client from '../util/client';
|
||||
import { LoginResult } from '../util/login/types';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(`${logo} ${getPkgName()} login`)} <email or team>
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
--no-color No color mode [off]
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Log into the Vercel platform
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} login`)}
|
||||
|
||||
${chalk.gray('–')} Log in using a specific email address
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} login john@doe.com`)}
|
||||
|
||||
${chalk.gray('–')} Log in using a specific team "slug" for SAML Single Sign-On
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} login acme`)}
|
||||
|
||||
${chalk.gray('–')} Log in using GitHub in "out-of-band" mode
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} login --github --oob`)}
|
||||
`);
|
||||
};
|
||||
import hp from '../../util/humanize-path';
|
||||
import getArgs from '../../util/get-args';
|
||||
import prompt from '../../util/login/prompt';
|
||||
import doSamlLogin from '../../util/login/saml';
|
||||
import doEmailLogin from '../../util/login/email';
|
||||
import doGithubLogin from '../../util/login/github';
|
||||
import doGitlabLogin from '../../util/login/gitlab';
|
||||
import doBitbucketLogin from '../../util/login/bitbucket';
|
||||
import { prependEmoji, emoji } from '../../util/emoji';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import getGlobalPathConfig from '../../util/config/global-path';
|
||||
import {
|
||||
writeToAuthConfigFile,
|
||||
writeToConfigFile,
|
||||
} from '../../util/config/files';
|
||||
import Client from '../../util/client';
|
||||
import { LoginResult } from '../../util/login/types';
|
||||
import { help } from '../help';
|
||||
import { loginCommand } from './command';
|
||||
|
||||
export default async function login(client: Client): Promise<number> {
|
||||
const { output } = client;
|
||||
@@ -62,7 +31,7 @@ export default async function login(client: Client): Promise<number> {
|
||||
});
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
output.print(help(loginCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
15
packages/cli/src/commands/logout/command.ts
Normal file
15
packages/cli/src/commands/logout/command.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Command } from '../help';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
export const logoutCommand: Command = {
|
||||
name: 'logout',
|
||||
description: 'Logout the current authenticated user or team.',
|
||||
arguments: [],
|
||||
options: [],
|
||||
examples: [
|
||||
{
|
||||
name: 'Logout from the CLI',
|
||||
value: `${getPkgName()} logout`,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,37 +1,19 @@
|
||||
import chalk from 'chalk';
|
||||
import logo from '../util/output/logo';
|
||||
import { handleError } from '../util/error';
|
||||
import { writeToConfigFile, writeToAuthConfigFile } from '../util/config/files';
|
||||
import getArgs from '../util/get-args';
|
||||
import Client from '../util/client';
|
||||
import { getCommandName, getPkgName } from '../util/pkg-name';
|
||||
import { isAPIError } from '../util/errors-ts';
|
||||
import { handleError } from '../../util/error';
|
||||
import {
|
||||
writeToConfigFile,
|
||||
writeToAuthConfigFile,
|
||||
} from '../../util/config/files';
|
||||
import getArgs from '../../util/get-args';
|
||||
import Client from '../../util/client';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import { isAPIError } from '../../util/errors-ts';
|
||||
import { errorToString } from '@vercel/error-utils';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(`${logo} ${getPkgName()} logout`)}
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Logout from the CLI:
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} logout`)}
|
||||
`);
|
||||
};
|
||||
import { help } from '../help';
|
||||
import { logoutCommand } from './command';
|
||||
|
||||
export default async function main(client: Client): Promise<number> {
|
||||
let argv;
|
||||
const { authConfig, config, output } = client;
|
||||
|
||||
try {
|
||||
argv = getArgs(client.argv.slice(2), {
|
||||
@@ -44,12 +26,10 @@ export default async function main(client: Client): Promise<number> {
|
||||
}
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
output.print(help(logoutCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
const { authConfig, config, output } = client;
|
||||
|
||||
if (!authConfig.token) {
|
||||
output.note(
|
||||
`Not currently logged in, so ${getCommandName('logout')} did nothing`
|
||||
66
packages/cli/src/commands/logs/command.ts
Normal file
66
packages/cli/src/commands/logs/command.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { Command } from '../help';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
export const logsCommand: Command = {
|
||||
name: 'logs',
|
||||
description: 'Display logs for a specific deployment.',
|
||||
arguments: [
|
||||
{
|
||||
name: 'url|deploymentId',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: 'follow',
|
||||
shorthand: 'f',
|
||||
description: 'Wait for additional data [off]',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'limit',
|
||||
shorthand: 'n',
|
||||
description: 'Number of log entries [100]',
|
||||
argument: 'NUMBER',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'since',
|
||||
shorthand: null,
|
||||
description: 'Only return logs after date (ISO 8601)',
|
||||
argument: 'SINCE',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'until',
|
||||
shorthand: null,
|
||||
description:
|
||||
'Only return logs before date (ISO 8601), ignored when used with --follow',
|
||||
argument: 'UNTIL',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'output',
|
||||
shorthand: 'o',
|
||||
description: `Specify the output format (short|raw) [short]`,
|
||||
argument: 'MODE',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
name: 'Print the logs for the deployment DEPLOYMENT_ID',
|
||||
value: `${getPkgName()} logs DEPLOYMENT_ID`,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,60 +1,15 @@
|
||||
import chalk from 'chalk';
|
||||
import logo from '../util/output/logo';
|
||||
import elapsed from '../util/output/elapsed';
|
||||
import { maybeURL, normalizeURL } from '../util/url';
|
||||
import printEvents, { DeploymentEvent } from '../util/events';
|
||||
import getScope from '../util/get-scope';
|
||||
import { getPkgName } from '../util/pkg-name';
|
||||
import getArgs from '../util/get-args';
|
||||
import Client from '../util/client';
|
||||
import getDeployment from '../util/get-deployment';
|
||||
import elapsed from '../../util/output/elapsed';
|
||||
import { maybeURL, normalizeURL } from '../../util/url';
|
||||
import printEvents, { DeploymentEvent } from '../../util/events';
|
||||
import getScope from '../../util/get-scope';
|
||||
import getArgs from '../../util/get-args';
|
||||
import Client from '../../util/client';
|
||||
import getDeployment from '../../util/get-deployment';
|
||||
import { help } from '../help';
|
||||
import { logsCommand } from './command';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(`${logo} ${getPkgName()} logs`)} <url|deploymentId>
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
--no-color No color mode [off]
|
||||
-f, --follow Wait for additional data [off]
|
||||
-n ${chalk.bold.underline(
|
||||
'NUMBER'
|
||||
)} Number of logs [100]
|
||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
--since=${chalk.bold.underline(
|
||||
'SINCE'
|
||||
)} Only return logs after date (ISO 8601)
|
||||
--until=${chalk.bold.underline(
|
||||
'UNTIL'
|
||||
)} Only return logs before date (ISO 8601), ignored for ${'`-f`'}
|
||||
-S, --scope Set a custom scope
|
||||
-o ${chalk.bold.underline('MODE')}, --output=${chalk.bold.underline(
|
||||
'MODE'
|
||||
)} Specify the output format (${Object.keys(logPrinters).join(
|
||||
'|'
|
||||
)}) [short]
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Print the logs for the deployment ${chalk.dim(
|
||||
'`deploymentId`'
|
||||
)}
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} logs deploymentId`)}
|
||||
`);
|
||||
};
|
||||
|
||||
export default async function main(client: Client) {
|
||||
export default async function logs(client: Client) {
|
||||
let head;
|
||||
let limit;
|
||||
let follow;
|
||||
@@ -76,14 +31,13 @@ export default async function main(client: Client) {
|
||||
|
||||
argv._ = argv._.slice(1);
|
||||
deploymentIdOrURL = argv._[0];
|
||||
const { output } = client;
|
||||
|
||||
if (argv['--help'] || !deploymentIdOrURL || deploymentIdOrURL === 'help') {
|
||||
help();
|
||||
output.print(help(logsCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
const { output } = client;
|
||||
|
||||
try {
|
||||
since = argv['--since'] ? toTimestamp(argv['--since']) : 0;
|
||||
} catch (err) {
|
||||
@@ -38,6 +38,10 @@ export default async function rm(client: Client, args: string[]) {
|
||||
client.output.error('No such project exists');
|
||||
return 1;
|
||||
}
|
||||
if (isAPIError(err) && err.status === 403) {
|
||||
client.output.error(err.message);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
const elapsed = ms(Date.now() - start);
|
||||
client.output.log(
|
||||
|
||||
53
packages/cli/src/commands/pull/command.ts
Normal file
53
packages/cli/src/commands/pull/command.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Command } from '../help';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getEnvTargetPlaceholder } from '../../util/env/env-target';
|
||||
|
||||
export const pullCommand: Command = {
|
||||
name: 'pull',
|
||||
description:
|
||||
'Pull latest environment variables and project settings from Vercel. ',
|
||||
arguments: [
|
||||
{
|
||||
name: 'project-path',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: 'environment',
|
||||
description: 'Deployment environment [development]',
|
||||
argument: 'environment',
|
||||
shorthand: null,
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'yes',
|
||||
description:
|
||||
'Skip questions when setting up new project using default scope and settings',
|
||||
shorthand: 'y',
|
||||
type: 'string',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
name: 'Pull the latest Environment Variables and Project Settings from the cloud',
|
||||
value: `${getPkgName()} pull`,
|
||||
},
|
||||
{
|
||||
name: 'Pull the latest Environment Variables and Project Settings from the cloud targeting a directory',
|
||||
value: `${getPkgName()} pull ./path-to-project`,
|
||||
},
|
||||
{
|
||||
name: 'Pull for a specific environment',
|
||||
value: `${getPkgName()} pull --environment=${getEnvTargetPlaceholder()}`,
|
||||
},
|
||||
{
|
||||
name: 'If you want to download environment variables to a specific file, use `vercel env pull` instead',
|
||||
value: `${getPkgName()} env pull`,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,65 +1,26 @@
|
||||
import chalk from 'chalk';
|
||||
import { join } from 'path';
|
||||
import Client from '../util/client';
|
||||
import Client from '../../util/client';
|
||||
import type {
|
||||
Project,
|
||||
ProjectEnvTarget,
|
||||
ProjectLinked,
|
||||
} from '@vercel-internals/types';
|
||||
import { emoji, prependEmoji } from '../util/emoji';
|
||||
import getArgs from '../util/get-args';
|
||||
import logo from '../util/output/logo';
|
||||
import stamp from '../util/output/stamp';
|
||||
import { getPkgName } from '../util/pkg-name';
|
||||
import { VERCEL_DIR, VERCEL_DIR_PROJECT } from '../util/projects/link';
|
||||
import { writeProjectSettings } from '../util/projects/project-settings';
|
||||
import envPull from './env/pull';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
import getArgs from '../../util/get-args';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import { VERCEL_DIR, VERCEL_DIR_PROJECT } from '../../util/projects/link';
|
||||
import { writeProjectSettings } from '../../util/projects/project-settings';
|
||||
import envPull from '../env/pull';
|
||||
import {
|
||||
isValidEnvTarget,
|
||||
getEnvTargetPlaceholder,
|
||||
} from '../util/env/env-target';
|
||||
import { ensureLink } from '../util/link/ensure-link';
|
||||
import humanizePath from '../util/humanize-path';
|
||||
} from '../../util/env/env-target';
|
||||
import { ensureLink } from '../../util/link/ensure-link';
|
||||
import humanizePath from '../../util/humanize-path';
|
||||
|
||||
const help = () => {
|
||||
return console.log(`
|
||||
${chalk.bold(`${logo} ${getPkgName()} pull`)} [project-path]
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
--no-color No color mode [off]
|
||||
--environment [environment] Deployment environment [development]
|
||||
-y, --yes Skip questions when setting up new project using default scope and settings
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray(
|
||||
'–'
|
||||
)} Pull the latest Environment Variables and Project Settings from the cloud
|
||||
and stores them in \`.vercel/.env.\${target}.local\` and \`.vercel/project.json\` respectively.
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} pull`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} pull ./path-to-project`)}
|
||||
|
||||
${chalk.gray('–')} Pull for a specific environment
|
||||
|
||||
${chalk.cyan(
|
||||
`$ ${getPkgName()} pull --environment=${getEnvTargetPlaceholder()}`
|
||||
)}
|
||||
|
||||
${chalk.gray(
|
||||
'If you want to download environment variables to a specific file, use `vercel env pull` instead.'
|
||||
)}
|
||||
`);
|
||||
};
|
||||
import { help } from '../help';
|
||||
import { pullCommand } from './command';
|
||||
|
||||
function processArgs(client: Client) {
|
||||
return getArgs(client.argv.slice(2), {
|
||||
@@ -76,7 +37,7 @@ function parseArgs(client: Client) {
|
||||
const argv = processArgs(client);
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
client.output.print(help(pullCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
33
packages/cli/src/commands/redeploy/command.ts
Normal file
33
packages/cli/src/commands/redeploy/command.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Command } from '../help';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
export const redeployCommand: Command = {
|
||||
name: 'redeploy',
|
||||
description: 'Rebuild and deploy a previous deployment.',
|
||||
arguments: [
|
||||
{
|
||||
name: 'deploymentId|deploymentName',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: 'no-wait',
|
||||
shorthand: null,
|
||||
description: "Don't wait for the redeploy to finish",
|
||||
type: 'boolean',
|
||||
deprecated: false,
|
||||
multi: false,
|
||||
},
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
name: 'Rebuild and deploy an existing deployment using id or url',
|
||||
value: `${getPkgName()} redeploy my-deployment.vercel.app`,
|
||||
},
|
||||
{
|
||||
name: 'Write Deployment URL to a file',
|
||||
value: `${getPkgName()} redeploy my-deployment.vercel.app > deployment-url.txt`,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,66 +1,29 @@
|
||||
import chalk from 'chalk';
|
||||
import { checkDeploymentStatus } from '@vercel/client';
|
||||
import type Client from '../util/client';
|
||||
import { emoji, prependEmoji } from '../util/emoji';
|
||||
import getArgs from '../util/get-args';
|
||||
import { getCommandName, getPkgName } from '../util/pkg-name';
|
||||
import { getDeploymentByIdOrURL } from '../util/deploy/get-deployment-by-id-or-url';
|
||||
import getScope from '../util/get-scope';
|
||||
import handleError from '../util/handle-error';
|
||||
import type Client from '../../util/client';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
import getArgs from '../../util/get-args';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import { getDeploymentByIdOrURL } from '../../util/deploy/get-deployment-by-id-or-url';
|
||||
import getScope from '../../util/get-scope';
|
||||
import handleError from '../../util/handle-error';
|
||||
import { isErrnoException } from '@vercel/error-utils';
|
||||
import logo from '../util/output/logo';
|
||||
import Now from '../util';
|
||||
import { printDeploymentStatus } from '../util/deploy/print-deployment-status';
|
||||
import stamp from '../util/output/stamp';
|
||||
import ua from '../util/ua';
|
||||
import Now from '../../util';
|
||||
import { printDeploymentStatus } from '../../util/deploy/print-deployment-status';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import ua from '../../util/ua';
|
||||
import type { VercelClientOptions } from '@vercel/client';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(
|
||||
`${logo} ${getPkgName()} redeploy`
|
||||
)} [deploymentId|deploymentName]
|
||||
|
||||
Rebuild and deploy a previous deployment.
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
--no-color No color mode [off]
|
||||
--no-wait Don't wait for the redeploy to finish
|
||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
-y, --yes Skip questions when setting up new project using default scope and settings
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Rebuild and deploy an existing deployment using id or url
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} redeploy my-deployment.vercel.app`)}
|
||||
|
||||
${chalk.gray('–')} Write Deployment URL to a file
|
||||
|
||||
${chalk.cyan(
|
||||
`$ ${getPkgName()} redeploy my-deployment.vercel.app > deployment-url.txt`
|
||||
)}
|
||||
`);
|
||||
};
|
||||
import { help } from '../help';
|
||||
import { redeployCommand } from './command';
|
||||
|
||||
/**
|
||||
* `vc redeploy` command
|
||||
* @param {Client} client
|
||||
* @returns {Promise<number>} Resolves an exit code; 0 on success
|
||||
*/
|
||||
export default async (client: Client): Promise<number> => {
|
||||
export default async function redeploy(client: Client): Promise<number> {
|
||||
let argv;
|
||||
const { output } = client;
|
||||
try {
|
||||
argv = getArgs(client.argv.slice(2), {
|
||||
'--no-wait': Boolean,
|
||||
@@ -73,11 +36,10 @@ export default async (client: Client): Promise<number> => {
|
||||
}
|
||||
|
||||
if (argv['--help'] || argv._[0] === 'help') {
|
||||
help();
|
||||
output.print(help(redeployCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
const { output } = client;
|
||||
const deployIdOrUrl = argv._[1];
|
||||
if (!deployIdOrUrl) {
|
||||
output.error(
|
||||
@@ -108,7 +70,7 @@ export default async (client: Client): Promise<number> => {
|
||||
action: 'redeploy',
|
||||
},
|
||||
name: fromDeployment.name,
|
||||
target: fromDeployment.target || 'production',
|
||||
target: fromDeployment.target ?? undefined,
|
||||
},
|
||||
method: 'POST',
|
||||
});
|
||||
@@ -201,4 +163,4 @@ export default async (client: Client): Promise<number> => {
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
45
packages/cli/src/commands/remove/command.ts
Normal file
45
packages/cli/src/commands/remove/command.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Command } from '../help';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
export const removeCommand: Command = {
|
||||
name: 'remove',
|
||||
description: 'Remove a deployment by name or id.',
|
||||
arguments: [
|
||||
{
|
||||
name: '...deploymentId|deploymentName',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: 'yes',
|
||||
shorthand: 'y',
|
||||
type: 'boolean',
|
||||
deprecated: false,
|
||||
description: 'Skip confirmation',
|
||||
multi: false,
|
||||
},
|
||||
{
|
||||
name: 'safe',
|
||||
shorthand: 's',
|
||||
type: 'boolean',
|
||||
deprecated: false,
|
||||
description: 'Skip deployments with an active alias',
|
||||
multi: false,
|
||||
},
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
name: 'Remove a deployment identified by `deploymentId`',
|
||||
value: `${getPkgName()} remove my-app`,
|
||||
},
|
||||
{
|
||||
name: 'Remove all deployments with name `my-app`',
|
||||
value: `${getPkgName()} remove deploymentId`,
|
||||
},
|
||||
{
|
||||
name: 'Remove two deployments with IDs `eyWt6zuSdeus` and `uWHoA9RQ1d1o`',
|
||||
value: `${getPkgName()} remove eyWt6zuSdeus uWHoA9RQ1d1o`,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -2,74 +2,31 @@ import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import table from 'text-table';
|
||||
import Now from '../util';
|
||||
import getAliases from '../util/alias/get-aliases';
|
||||
import logo from '../util/output/logo';
|
||||
import elapsed from '../util/output/elapsed';
|
||||
import { normalizeURL } from '../util/url';
|
||||
import getScope from '../util/get-scope';
|
||||
import { isValidName } from '../util/is-valid-name';
|
||||
import removeProject from '../util/projects/remove-project';
|
||||
import getProjectByIdOrName from '../util/projects/get-project-by-id-or-name';
|
||||
import getDeployment from '../util/get-deployment';
|
||||
import getDeploymentsByProjectId from '../util/deploy/get-deployments-by-project-id';
|
||||
import { getPkgName, getCommandName } from '../util/pkg-name';
|
||||
import getArgs from '../util/get-args';
|
||||
import handleError from '../util/handle-error';
|
||||
import type Client from '../util/client';
|
||||
import { Output } from '../util/output';
|
||||
import Now from '../../util';
|
||||
import getAliases from '../../util/alias/get-aliases';
|
||||
import elapsed from '../../util/output/elapsed';
|
||||
import { normalizeURL } from '../../util/url';
|
||||
import getScope from '../../util/get-scope';
|
||||
import { isValidName } from '../../util/is-valid-name';
|
||||
import removeProject from '../../util/projects/remove-project';
|
||||
import getProjectByIdOrName from '../../util/projects/get-project-by-id-or-name';
|
||||
import getDeployment from '../../util/get-deployment';
|
||||
import getDeploymentsByProjectId from '../../util/deploy/get-deployments-by-project-id';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import getArgs from '../../util/get-args';
|
||||
import handleError from '../../util/handle-error';
|
||||
import type Client from '../../util/client';
|
||||
import { Output } from '../../util/output';
|
||||
import { Alias, Deployment, Project } from '@vercel-internals/types';
|
||||
import { NowError } from '../util/now-error';
|
||||
import { NowError } from '../../util/now-error';
|
||||
import { help } from '../help';
|
||||
import { removeCommand } from './command';
|
||||
|
||||
type DeploymentWithAliases = Deployment & {
|
||||
aliases: Alias[];
|
||||
};
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(
|
||||
`${logo} ${getPkgName()} remove`
|
||||
)} [...deploymentId|deploymentName]
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
--no-color No color mode [off]
|
||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
-y, --yes Skip confirmation
|
||||
-s, --safe Skip deployments with an active alias
|
||||
-S, --scope Set a custom scope
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Remove a deployment identified by ${chalk.dim(
|
||||
'`deploymentId`'
|
||||
)}
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} rm deploymentId`)}
|
||||
|
||||
${chalk.gray('–')} Remove all deployments with name ${chalk.dim('`my-app`')}
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} rm my-app`)}
|
||||
|
||||
${chalk.gray('–')} Remove two deployments with IDs ${chalk.dim(
|
||||
'`eyWt6zuSdeus`'
|
||||
)} and ${chalk.dim('`uWHoA9RQ1d1o`')}
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} rm eyWt6zuSdeus uWHoA9RQ1d1o`)}
|
||||
`);
|
||||
};
|
||||
|
||||
export default async function main(client: Client) {
|
||||
export default async function remove(client: Client) {
|
||||
let argv;
|
||||
|
||||
try {
|
||||
@@ -98,13 +55,13 @@ export default async function main(client: Client) {
|
||||
const { success, error, log } = output;
|
||||
|
||||
if (argv['--help'] || ids[0] === 'help') {
|
||||
help();
|
||||
output.print(help(removeCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (ids.length < 1) {
|
||||
error(`${getCommandName('rm')} expects at least one argument`);
|
||||
help();
|
||||
output.print(help(removeCommand, { columns: client.stderr.columns }));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
import logo from '../util/output/logo';
|
||||
import getScope from '../util/get-scope';
|
||||
import { getPkgName } from '../util/pkg-name';
|
||||
import getArgs from '../util/get-args';
|
||||
import Client from '../util/client';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(`${logo} ${getPkgName()} whoami`)}
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
--no-color No color mode [off]
|
||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Shows the username of the currently logged in user
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} whoami`)}
|
||||
`);
|
||||
};
|
||||
|
||||
export default async (client: Client): Promise<number> => {
|
||||
const { output } = client;
|
||||
const argv = getArgs(client.argv.slice(2), {});
|
||||
argv._ = argv._.slice(1);
|
||||
|
||||
if (argv['--help'] || argv._[0] === 'help') {
|
||||
help();
|
||||
return 2;
|
||||
}
|
||||
|
||||
const { contextName } = await getScope(client, { getTeam: false });
|
||||
|
||||
if (client.stdout.isTTY) {
|
||||
output.log(contextName);
|
||||
} else {
|
||||
// If stdout is not a TTY, then only print the username
|
||||
// to support piping the output to another file / exe
|
||||
client.stdout.write(`${contextName}\n`);
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
15
packages/cli/src/commands/whoami/command.ts
Normal file
15
packages/cli/src/commands/whoami/command.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Command } from '../help';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
export const whoamiCommand: Command = {
|
||||
name: 'whoami',
|
||||
description: 'Shows the username of the currently logged in user.',
|
||||
arguments: [],
|
||||
options: [],
|
||||
examples: [
|
||||
{
|
||||
name: 'Shows the username of the currently logged in user',
|
||||
value: `${getPkgName()} whoami`,
|
||||
},
|
||||
],
|
||||
};
|
||||
29
packages/cli/src/commands/whoami/index.ts
Normal file
29
packages/cli/src/commands/whoami/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { help } from '../help';
|
||||
import { whoamiCommand } from './command';
|
||||
|
||||
import getScope from '../../util/get-scope';
|
||||
import getArgs from '../../util/get-args';
|
||||
import Client from '../../util/client';
|
||||
|
||||
export default async function whoami(client: Client): Promise<number> {
|
||||
const { output } = client;
|
||||
const argv = getArgs(client.argv.slice(2), {});
|
||||
argv._ = argv._.slice(1);
|
||||
|
||||
if (argv['--help'] || argv._[0] === 'help') {
|
||||
output.print(help(whoamiCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
const { contextName } = await getScope(client, { getTeam: false });
|
||||
|
||||
if (client.stdout.isTTY) {
|
||||
output.log(contextName);
|
||||
} else {
|
||||
// If stdout is not a TTY, then only print the username
|
||||
// to support piping the output to another file / exe
|
||||
client.stdout.write(`${contextName}\n`);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -146,7 +146,8 @@ export default class Client extends EventEmitter implements Stdio {
|
||||
if (!res.ok) {
|
||||
const error = await responseError(res);
|
||||
|
||||
if (isSAMLError(error)) {
|
||||
// we should force reauth only if error has a teamId
|
||||
if (isSAMLError(error) && error.teamId) {
|
||||
try {
|
||||
// A SAML error means the token is expired, or is not
|
||||
// designated for the requested team, so the user needs
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
import stripeFactory from 'stripe';
|
||||
import Now from '.';
|
||||
|
||||
const stripe = stripeFactory('pk_live_alyEi3lN0kSwbdevK0nrGwTw');
|
||||
|
||||
export default class CreditCards extends Now {
|
||||
async ls() {
|
||||
const res = await this._fetch('/stripe/sources/');
|
||||
const body = await res.json();
|
||||
|
||||
if (res.status !== 200) {
|
||||
const e = new Error(body.error.message);
|
||||
e.code = body.error.code;
|
||||
throw e;
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
async setDefault(source) {
|
||||
await this._fetch('/stripe/sources/', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
source,
|
||||
makeDefault: true,
|
||||
},
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async rm(source) {
|
||||
await this._fetch(`/stripe/sources/`, {
|
||||
method: 'DELETE',
|
||||
body: { source },
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async add(card) {
|
||||
if (!card.expDate) {
|
||||
throw new Error(`Please define an expiration date for your card`);
|
||||
}
|
||||
|
||||
const expDateParts = card.expDate.split(' / ');
|
||||
|
||||
card = {
|
||||
name: card.name,
|
||||
number: card.cardNumber,
|
||||
cvc: card.ccv,
|
||||
};
|
||||
|
||||
card.exp_month = expDateParts[0];
|
||||
card.exp_year = expDateParts[1];
|
||||
|
||||
try {
|
||||
const token = (await stripe.tokens.create({ card })).id;
|
||||
|
||||
const res = await this._fetch('/stripe/sources/', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
source: token,
|
||||
},
|
||||
});
|
||||
|
||||
const { source, error } = await res.json();
|
||||
|
||||
if (source && source.id) {
|
||||
return {
|
||||
last4: source.last4,
|
||||
};
|
||||
} else if (error && error.message) {
|
||||
throw new Error(error.message);
|
||||
} else {
|
||||
throw new Error('Unknown error');
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(err.message || 'Unknown error');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,6 +234,10 @@ export default async function processDeployment({
|
||||
return error;
|
||||
}
|
||||
|
||||
if (error.code === 'forbidden') {
|
||||
return error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
Builder,
|
||||
cloneEnv,
|
||||
Env,
|
||||
getNodeBinPath,
|
||||
getNodeBinPaths,
|
||||
StartDevServerResult,
|
||||
FileFsRef,
|
||||
PackageJson,
|
||||
@@ -124,6 +124,7 @@ function sortBuilders(buildA: Builder, buildB: Builder) {
|
||||
|
||||
export default class DevServer {
|
||||
public cwd: string;
|
||||
public repoRoot: string;
|
||||
public output: Output;
|
||||
public proxy: httpProxy;
|
||||
public envConfigs: EnvConfigs;
|
||||
@@ -169,6 +170,7 @@ export default class DevServer {
|
||||
|
||||
constructor(cwd: string, options: DevServerOptions) {
|
||||
this.cwd = cwd;
|
||||
this.repoRoot = options.repoRoot ?? cwd;
|
||||
this.output = options.output;
|
||||
this.envConfigs = { buildEnv: {}, runEnv: {}, allEnv: {} };
|
||||
this.envValues = options.envValues || {};
|
||||
@@ -1412,7 +1414,7 @@ export default class DevServer {
|
||||
files,
|
||||
entrypoint: middleware.entrypoint,
|
||||
workPath,
|
||||
repoRootPath: this.cwd,
|
||||
repoRootPath: this.repoRoot,
|
||||
config: middleware.config || {},
|
||||
meta: {
|
||||
isDev: true,
|
||||
@@ -1849,7 +1851,7 @@ export default class DevServer {
|
||||
entrypoint: match.entrypoint,
|
||||
workPath,
|
||||
config: match.config || {},
|
||||
repoRootPath: this.cwd,
|
||||
repoRootPath: this.repoRoot,
|
||||
meta: {
|
||||
isDev: true,
|
||||
requestPath,
|
||||
@@ -2237,7 +2239,8 @@ export default class DevServer {
|
||||
);
|
||||
|
||||
// add the node_modules/.bin directory to the PATH
|
||||
const nodeBinPath = await getNodeBinPath({ cwd });
|
||||
const nodeBinPaths = getNodeBinPaths({ base: this.repoRoot, start: cwd });
|
||||
const nodeBinPath = nodeBinPaths.join(path.delimiter);
|
||||
env.PATH = `${nodeBinPath}${path.delimiter}${env.PATH}`;
|
||||
|
||||
// This is necesary so that the dev command in the Project
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface DevServerOptions {
|
||||
output: Output;
|
||||
projectSettings?: ProjectSettings;
|
||||
envValues?: Record<string, string>;
|
||||
repoRoot?: string;
|
||||
}
|
||||
|
||||
export interface EnvConfigs {
|
||||
|
||||
5
packages/cli/src/util/git/repo-info-to-url.ts
Normal file
5
packages/cli/src/util/git/repo-info-to-url.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import type { RepoInfo } from './connect-git-provider';
|
||||
|
||||
export function repoInfoToUrl(info: RepoInfo): string {
|
||||
return `https://${info.provider}.com/${info.org}/${info.repo}`;
|
||||
}
|
||||
@@ -61,6 +61,7 @@ export interface ListOptions {
|
||||
version?: number;
|
||||
meta?: Dictionary<string>;
|
||||
nextTimestamp?: number;
|
||||
target?: string;
|
||||
}
|
||||
|
||||
export default class Now extends EventEmitter {
|
||||
@@ -339,7 +340,7 @@ export default class Now extends EventEmitter {
|
||||
|
||||
async list(
|
||||
app?: string,
|
||||
{ version = 4, meta = {}, nextTimestamp }: ListOptions = {},
|
||||
{ version = 4, meta = {}, nextTimestamp, target }: ListOptions = {},
|
||||
prod?: boolean
|
||||
) {
|
||||
const fetchRetry = async (url: string, options: FetchOptions = {}) => {
|
||||
@@ -405,6 +406,8 @@ export default class Now extends EventEmitter {
|
||||
}
|
||||
if (prod) {
|
||||
query.set('target', 'production');
|
||||
} else if (target) {
|
||||
query.set('target', target);
|
||||
}
|
||||
|
||||
const response = await fetchRetry(`/v${version}/now/deployments?${query}`);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import chalk from 'chalk';
|
||||
import inquirer from 'inquirer';
|
||||
import pluralize from 'pluralize';
|
||||
import { homedir } from 'os';
|
||||
import { join, normalize } from 'path';
|
||||
import slugify from '@sindresorhus/slugify';
|
||||
import { basename, join, normalize } from 'path';
|
||||
import { normalizePath, traverseUpDirectories } from '@vercel/build-utils';
|
||||
import { lstat, readJSON, outputJSON } from 'fs-extra';
|
||||
import confirm from '../input/confirm';
|
||||
@@ -13,7 +15,12 @@ import { emoji, prependEmoji } from '../emoji';
|
||||
import selectOrg from '../input/select-org';
|
||||
import { addToGitIgnore } from './add-to-gitignore';
|
||||
import type Client from '../client';
|
||||
import type { Framework } from '@vercel/frameworks';
|
||||
import type { Project } from '@vercel-internals/types';
|
||||
import createProject from '../projects/create-project';
|
||||
import { detectProjects } from '../projects/detect-projects';
|
||||
import { repoInfoToUrl } from '../git/repo-info-to-url';
|
||||
import { connectGitProvider, parseRepoUrl } from '../git/connect-git-provider';
|
||||
|
||||
const home = homedir();
|
||||
|
||||
@@ -35,6 +42,11 @@ export interface RepoLink {
|
||||
repoConfig?: RepoProjectsConfig;
|
||||
}
|
||||
|
||||
export interface EnsureRepoLinkOptions {
|
||||
yes: boolean;
|
||||
overwrite: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a directory path `cwd`, finds the root of the Git repository
|
||||
* and returns the parsed `.vercel/repo.json` file if the repository
|
||||
@@ -62,7 +74,7 @@ export async function getRepoLink(
|
||||
export async function ensureRepoLink(
|
||||
client: Client,
|
||||
cwd: string,
|
||||
yes = false
|
||||
{ yes, overwrite }: EnsureRepoLinkOptions
|
||||
): Promise<RepoLink | undefined> {
|
||||
const { output } = client;
|
||||
|
||||
@@ -74,7 +86,14 @@ export async function ensureRepoLink(
|
||||
}
|
||||
let { rootPath, repoConfig, repoConfigPath } = repoLink;
|
||||
|
||||
if (!repoConfig) {
|
||||
if (overwrite || !repoConfig) {
|
||||
// Detect the projects on the filesystem out of band, so that
|
||||
// they will be ready by the time the projects are listed
|
||||
const detectedProjectsPromise = detectProjects(rootPath).catch(err => {
|
||||
output.debug(`Failed to detect local projects: ${err}`);
|
||||
return new Map<string, Framework[]>();
|
||||
});
|
||||
|
||||
// Not yet linked, so prompt user to begin linking
|
||||
let shouldLink =
|
||||
yes ||
|
||||
@@ -111,41 +130,36 @@ export async function ensureRepoLink(
|
||||
remoteName = remoteNames[0];
|
||||
} else {
|
||||
// Prompt user to select which remote to use
|
||||
const originIndex = remoteNames.indexOf('origin');
|
||||
const answer = await client.prompt({
|
||||
type: 'list',
|
||||
name: 'value',
|
||||
message: 'Which Git remote should be used?',
|
||||
choices: remoteNames.map(name => {
|
||||
choices: remoteNames.sort().map(name => {
|
||||
return { name: name, value: name };
|
||||
}),
|
||||
default: originIndex === -1 ? 0 : originIndex,
|
||||
default: remoteNames.includes('origin') ? 'origin' : undefined,
|
||||
});
|
||||
remoteName = answer.value;
|
||||
}
|
||||
const repoUrl = remoteUrls[remoteName];
|
||||
const parsedRepoUrl = parseRepoUrl(repoUrl);
|
||||
if (!parsedRepoUrl) {
|
||||
throw new Error(`Failed to parse Git URL: ${repoUrl}`);
|
||||
}
|
||||
const repoUrlLink = output.link(repoUrl, repoInfoToUrl(parsedRepoUrl), {
|
||||
fallback: () => link(repoUrl),
|
||||
});
|
||||
output.spinner(
|
||||
`Fetching Projects for ${link(repoUrl)} under ${chalk.bold(org.slug)}…`
|
||||
`Fetching Projects for ${repoUrlLink} under ${chalk.bold(org.slug)}…`
|
||||
);
|
||||
let projects: Project[] = [];
|
||||
const query = new URLSearchParams({ repoUrl });
|
||||
const projectsIterator = client.fetchPaginated<{
|
||||
projects: Project[];
|
||||
}>(`/v9/projects?${query}`);
|
||||
let printedFound = false;
|
||||
const detectedProjects = await detectedProjectsPromise;
|
||||
for await (const chunk of projectsIterator) {
|
||||
projects = projects.concat(chunk.projects);
|
||||
if (!printedFound && projects.length > 0) {
|
||||
output.log(
|
||||
`${pluralize('Project', chunk.projects.length)} linked to ${link(
|
||||
repoUrl
|
||||
)} under ${chalk.bold(org.slug)}:`
|
||||
);
|
||||
printedFound = true;
|
||||
}
|
||||
for (const project of chunk.projects) {
|
||||
output.print(` * ${chalk.cyan(`${org.slug}/${project.name}\n`)}`);
|
||||
}
|
||||
if (chunk.pagination.next) {
|
||||
output.spinner(`Found ${chalk.bold(projects.length)} Projects…`, 0);
|
||||
}
|
||||
@@ -153,36 +167,126 @@ export async function ensureRepoLink(
|
||||
|
||||
if (projects.length === 0) {
|
||||
output.log(
|
||||
`No Projects are linked to ${link(repoUrl)} under ${chalk.bold(
|
||||
`No Projects are linked to ${repoUrlLink} under ${chalk.bold(
|
||||
org.slug
|
||||
)}.`
|
||||
);
|
||||
// TODO: run detection logic to find potential projects.
|
||||
// then prompt user to select valid projects.
|
||||
// then create new Projects
|
||||
} else {
|
||||
output.log(
|
||||
`Found ${pluralize(
|
||||
'Project',
|
||||
projects.length,
|
||||
true
|
||||
)} linked to ${repoUrlLink} under ${chalk.bold(org.slug)}`
|
||||
);
|
||||
}
|
||||
|
||||
shouldLink =
|
||||
yes ||
|
||||
(await confirm(
|
||||
client,
|
||||
`Link to ${
|
||||
projects.length === 1
|
||||
? 'this Project'
|
||||
: `these ${chalk.bold(projects.length)} Projects`
|
||||
}?`,
|
||||
true
|
||||
));
|
||||
// For any projects that already exists on Vercel, remove them from the
|
||||
// locally detected directories. Any remaining ones will be prompted to
|
||||
// create new Projects for.
|
||||
for (const project of projects) {
|
||||
detectedProjects.delete(project.rootDirectory ?? '');
|
||||
}
|
||||
|
||||
if (!shouldLink) {
|
||||
output.print(`Canceled. Repository not linked.\n`);
|
||||
const detectedProjectsCount = Array.from(detectedProjects.values()).reduce(
|
||||
(o, f) => o + f.length,
|
||||
0
|
||||
);
|
||||
if (detectedProjectsCount > 0) {
|
||||
output.log(
|
||||
`Detected ${pluralize(
|
||||
'new Project',
|
||||
detectedProjectsCount,
|
||||
true
|
||||
)} that may be created.`
|
||||
);
|
||||
}
|
||||
|
||||
const addSeparators = projects.length > 0 && detectedProjectsCount > 0;
|
||||
const { selected } = await client.prompt({
|
||||
type: 'checkbox',
|
||||
name: 'selected',
|
||||
message: `Which Projects should be ${
|
||||
projects.length ? 'linked to' : 'created'
|
||||
}?`,
|
||||
choices: [
|
||||
...(addSeparators
|
||||
? [new inquirer.Separator('----- Existing Projects -----')]
|
||||
: []),
|
||||
...projects.map(project => {
|
||||
return {
|
||||
name: `${org.slug}/${project.name}`,
|
||||
value: project,
|
||||
checked: true,
|
||||
};
|
||||
}),
|
||||
...(addSeparators
|
||||
? [new inquirer.Separator('----- New Projects to be created -----')]
|
||||
: []),
|
||||
...Array.from(detectedProjects.entries()).flatMap(
|
||||
([rootDirectory, frameworks]) =>
|
||||
frameworks.map((framework, i) => {
|
||||
const name = slugify(
|
||||
[
|
||||
basename(rootPath),
|
||||
basename(rootDirectory),
|
||||
i > 0 ? framework.slug : '',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('-')
|
||||
);
|
||||
return {
|
||||
name: `${org.slug}/${name} (${framework.name})`,
|
||||
value: {
|
||||
newProject: true,
|
||||
rootDirectory,
|
||||
name,
|
||||
framework,
|
||||
},
|
||||
// Checked by default when there are no other existing Projects
|
||||
checked: projects.length === 0,
|
||||
};
|
||||
})
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
if (selected.length === 0) {
|
||||
output.print(`No Projects were selected. Repository not linked.\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < selected.length; i++) {
|
||||
const selection = selected[i];
|
||||
if (!selection.newProject) continue;
|
||||
const orgAndName = `${org.slug}/${selection.name}`;
|
||||
output.spinner(`Creating new Project: ${orgAndName}`);
|
||||
delete selection.newProject;
|
||||
if (!selection.rootDirectory) delete selection.rootDirectory;
|
||||
selected[i] = await createProject(client, {
|
||||
...selection,
|
||||
framework: selection.framework.slug,
|
||||
});
|
||||
await connectGitProvider(
|
||||
client,
|
||||
org,
|
||||
selected[i].id,
|
||||
parsedRepoUrl.provider,
|
||||
`${parsedRepoUrl.org}/${parsedRepoUrl.repo}`
|
||||
);
|
||||
output.log(
|
||||
`Created new Project: ${output.link(
|
||||
orgAndName,
|
||||
`https://vercel.com/${orgAndName}`,
|
||||
{ fallback: false }
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
repoConfig = {
|
||||
orgId: org.id,
|
||||
remoteName,
|
||||
projects: projects.map(project => {
|
||||
projects: selected.map((project: Project) => {
|
||||
return {
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
@@ -199,9 +303,11 @@ export async function ensureRepoLink(
|
||||
|
||||
output.print(
|
||||
prependEmoji(
|
||||
`Linked to ${link(repoUrl)} under ${chalk.bold(
|
||||
org.slug
|
||||
)} (created ${VERCEL_DIR}${
|
||||
`Linked to ${pluralize(
|
||||
'Project',
|
||||
selected.length,
|
||||
true
|
||||
)} under ${chalk.bold(org.slug)} (created ${VERCEL_DIR}${
|
||||
isGitIgnoreUpdated ? ' and added it to .gitignore' : ''
|
||||
})`,
|
||||
emoji('link')
|
||||
|
||||
38
packages/cli/src/util/projects/detect-projects.ts
Normal file
38
packages/cli/src/util/projects/detect-projects.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { join } from 'path';
|
||||
import frameworkList, { Framework } from '@vercel/frameworks';
|
||||
import {
|
||||
detectFrameworks,
|
||||
getWorkspacePackagePaths,
|
||||
getWorkspaces,
|
||||
LocalFileSystemDetector,
|
||||
} from '@vercel/fs-detectors';
|
||||
|
||||
export async function detectProjects(cwd: string) {
|
||||
const fs = new LocalFileSystemDetector(cwd);
|
||||
const workspaces = await getWorkspaces({ fs });
|
||||
const detectedProjects = new Map<string, Framework[]>();
|
||||
const packagePaths = (
|
||||
await Promise.all(
|
||||
workspaces.map(workspace =>
|
||||
getWorkspacePackagePaths({
|
||||
fs,
|
||||
workspace,
|
||||
})
|
||||
)
|
||||
)
|
||||
).flat();
|
||||
if (packagePaths.length === 0) {
|
||||
packagePaths.push('/');
|
||||
}
|
||||
await Promise.all(
|
||||
packagePaths.map(async p => {
|
||||
const frameworks = await detectFrameworks({
|
||||
fs: fs.chdir(join('.', p)),
|
||||
frameworkList,
|
||||
});
|
||||
if (frameworks.length === 0) return;
|
||||
detectedProjects.set(p.slice(1), frameworks);
|
||||
})
|
||||
);
|
||||
return detectedProjects;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
const { createGzip } = require('zlib');
|
||||
|
||||
module.exports = (_req, resp) => {
|
||||
resp.setHeader('content-encoding', 'gzip');
|
||||
|
||||
const gzip = createGzip();
|
||||
gzip.pipe(resp);
|
||||
gzip.end('Hello World!');
|
||||
};
|
||||
@@ -143,6 +143,13 @@ test(
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] 43-compress-encoding',
|
||||
testFixtureStdio('43-compress-encoding', async (testPath: any) => {
|
||||
await testPath(200, '/api', 'Hello World!');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Middleware that returns a 200 response',
|
||||
testFixtureStdio('middleware-response', async (testPath: any) => {
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
"orgId": ".",
|
||||
"projectId": ".",
|
||||
"settings": {
|
||||
"framework": "storybook",
|
||||
"buildCommand": "npm run build-storybook",
|
||||
"outputDirectory": "storybook-static"
|
||||
"framework": "storybook"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +170,14 @@ function setupDeploymentEndpoints(): void {
|
||||
res.json({ builds });
|
||||
});
|
||||
|
||||
client.scenario.get('/:version/deployments/:id/aliases', (req, res) => {
|
||||
const limit = parseInt(req.query.limit);
|
||||
res.json({
|
||||
aliases: [],
|
||||
pagination: { count: limit, total: limit, page: 1, pages: 1 },
|
||||
});
|
||||
});
|
||||
|
||||
function handleGetDeployments(req: Request, res: Response) {
|
||||
const currentDeployments = Array.from(deployments.values()).sort(
|
||||
(a: Deployment, b: Deployment) => {
|
||||
|
||||
@@ -144,7 +144,7 @@ export const defaultProject: Project = {
|
||||
*/
|
||||
export function useUnknownProject() {
|
||||
let project: Project;
|
||||
client.scenario.get(`/v8/projects/:projectNameOrId`, (_req, res) => {
|
||||
client.scenario.get(`/:version/projects/:projectNameOrId`, (_req, res) => {
|
||||
res.status(404).send();
|
||||
});
|
||||
client.scenario.post(`/:version/projects`, (req, res) => {
|
||||
|
||||
@@ -1,106 +1,164 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`help command help output snapshots column width 40 1`] = `
|
||||
"▲ [1mvercel[22m [1mdeploy[22m [project-path] [options]
|
||||
"
|
||||
▲ [1mvercel[22m [1mdeploy[22m [project-path] [options]
|
||||
|
||||
Deploy your project to Vercel. The \`deploy\` command is the default command for the Vercel CLI, and can be omitted (\`vc deploy my-app\` equals \`vc my-app\`).
|
||||
Deploy your project to Vercel. The \`deploy\` command is the default command for the Vercel CLI, and can be omitted (\`vc deploy my-app\` equals \`vc my-app\`).
|
||||
|
||||
[2mOptions:[22m
|
||||
[2mOptions[22m:
|
||||
|
||||
--archive Compress
|
||||
the
|
||||
deployment
|
||||
code into
|
||||
a file
|
||||
before
|
||||
uploading
|
||||
it
|
||||
--archive
|
||||
Compress
|
||||
the
|
||||
deployment
|
||||
code
|
||||
into
|
||||
a
|
||||
file
|
||||
before
|
||||
uploading
|
||||
it
|
||||
-b, --build-env <key=value>
|
||||
Specify
|
||||
environment
|
||||
variables
|
||||
during
|
||||
build-time
|
||||
(e.g.
|
||||
\`-b
|
||||
KEY1=value1
|
||||
-b
|
||||
KEY2=value2\`)
|
||||
-e, --env <key=value>
|
||||
Specify
|
||||
environment
|
||||
variables
|
||||
during
|
||||
run-time
|
||||
(e.g.
|
||||
\`-e
|
||||
KEY1=value1
|
||||
-e
|
||||
KEY2=value2\`)
|
||||
-f, --force
|
||||
Force
|
||||
a new
|
||||
deployment
|
||||
even
|
||||
if
|
||||
nothing
|
||||
has
|
||||
changed
|
||||
-m, --meta <key=value>
|
||||
Specify
|
||||
metadata
|
||||
for
|
||||
the
|
||||
deployment
|
||||
(e.g.
|
||||
\`-m
|
||||
KEY1=value1
|
||||
-m
|
||||
KEY2=value2\`)
|
||||
--no-wait
|
||||
Don't
|
||||
wait
|
||||
for
|
||||
the
|
||||
deployment
|
||||
to
|
||||
finish
|
||||
--prebuilt Use
|
||||
in
|
||||
combination
|
||||
with
|
||||
\`vc
|
||||
build\`.
|
||||
Deploy
|
||||
an
|
||||
existing
|
||||
build
|
||||
--prod
|
||||
Create
|
||||
a
|
||||
production
|
||||
deployment
|
||||
-p, --public
|
||||
Deployment
|
||||
is
|
||||
public
|
||||
(\`/_src\`)
|
||||
is
|
||||
exposed)
|
||||
--regions Set
|
||||
default
|
||||
regions
|
||||
to
|
||||
enable
|
||||
the
|
||||
deployment
|
||||
on
|
||||
--with-cache
|
||||
Retain
|
||||
build
|
||||
cache
|
||||
when
|
||||
using
|
||||
"--force"
|
||||
-y, --yes Use
|
||||
default
|
||||
options
|
||||
to
|
||||
skip
|
||||
all
|
||||
prompts
|
||||
|
||||
-b, --build-env <key=value> Specify
|
||||
environment
|
||||
variables
|
||||
during
|
||||
build-time
|
||||
(e.g. \`-b
|
||||
KEY1=value1
|
||||
-b
|
||||
KEY2=value2\`)
|
||||
[2mGlobal Options[22m:
|
||||
|
||||
-e, --env <key=value> Specify
|
||||
environment
|
||||
variables
|
||||
during
|
||||
run-time
|
||||
(e.g. \`-e
|
||||
KEY1=value1
|
||||
-e
|
||||
KEY2=value2\`)
|
||||
--cwd <DIR> Sets
|
||||
the
|
||||
current
|
||||
working
|
||||
directory
|
||||
for a
|
||||
single
|
||||
run of
|
||||
a
|
||||
command
|
||||
-d, --debug Debug
|
||||
mode
|
||||
(default
|
||||
off)
|
||||
-Q, --global-config <DIR> Path to
|
||||
the
|
||||
global
|
||||
\`.vercel\`
|
||||
directory
|
||||
-h, --help Output
|
||||
usage
|
||||
information
|
||||
-A, --local-config <FILE> Path to
|
||||
the
|
||||
local
|
||||
\`vercel.json\`
|
||||
file
|
||||
--no-color No
|
||||
color
|
||||
mode
|
||||
(default
|
||||
off)
|
||||
-S, --scope Set a
|
||||
custom
|
||||
scope
|
||||
-t, --token <TOKEN> Login
|
||||
token
|
||||
-v, --version Output
|
||||
the
|
||||
version
|
||||
number
|
||||
|
||||
-f, --force Force a
|
||||
new
|
||||
deployment
|
||||
even if
|
||||
nothing
|
||||
has
|
||||
changed
|
||||
|
||||
-m, --meta <key=value> Specify
|
||||
metadata
|
||||
for the
|
||||
deployment
|
||||
(e.g. \`-m
|
||||
KEY1=value1
|
||||
-m
|
||||
KEY2=value2\`)
|
||||
|
||||
--no-wait Don't
|
||||
wait for
|
||||
the
|
||||
deployment
|
||||
to finish
|
||||
|
||||
--prebuilt Use in
|
||||
combination
|
||||
with \`vc
|
||||
build\`.
|
||||
Deploy an
|
||||
existing
|
||||
build
|
||||
|
||||
--prod Create a
|
||||
production
|
||||
deployment
|
||||
|
||||
-p, --public
|
||||
Deployment
|
||||
is public
|
||||
(\`/_src\`)
|
||||
is
|
||||
exposed)
|
||||
|
||||
--regions Set
|
||||
default
|
||||
regions
|
||||
to enable
|
||||
the
|
||||
deployment
|
||||
on
|
||||
|
||||
--with-cache Retain
|
||||
build
|
||||
cache
|
||||
when
|
||||
using
|
||||
"--force"
|
||||
|
||||
-y, --yes Use
|
||||
default
|
||||
options
|
||||
to skip
|
||||
all
|
||||
prompts
|
||||
|
||||
[2mExamples:[22m
|
||||
[2mExamples:[22m
|
||||
|
||||
[90m-[39m Deploy the current directory
|
||||
|
||||
@@ -122,47 +180,53 @@ Deploy your project to Vercel. The \`deploy\` command is the default command for
|
||||
[90m-[39m Write Deployment URL to a file
|
||||
|
||||
[36m$ vercel > deployment-url.txt[39m
|
||||
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`help command help output snapshots column width 80 1`] = `
|
||||
"▲ [1mvercel[22m [1mdeploy[22m [project-path] [options]
|
||||
"
|
||||
▲ [1mvercel[22m [1mdeploy[22m [project-path] [options]
|
||||
|
||||
Deploy your project to Vercel. The \`deploy\` command is the default command for the Vercel CLI, and can be omitted (\`vc deploy my-app\` equals \`vc my-app\`).
|
||||
Deploy your project to Vercel. The \`deploy\` command is the default command for the Vercel CLI, and can be omitted (\`vc deploy my-app\` equals \`vc my-app\`).
|
||||
|
||||
[2mOptions:[22m
|
||||
[2mOptions[22m:
|
||||
|
||||
--archive Compress the deployment code into a file before
|
||||
uploading it
|
||||
--archive Compress the deployment code into a file
|
||||
before uploading it
|
||||
-b, --build-env <key=value> Specify environment variables during
|
||||
build-time (e.g. \`-b KEY1=value1 -b
|
||||
KEY2=value2\`)
|
||||
-e, --env <key=value> Specify environment variables during run-time
|
||||
(e.g. \`-e KEY1=value1 -e KEY2=value2\`)
|
||||
-f, --force Force a new deployment even if nothing has
|
||||
changed
|
||||
-m, --meta <key=value> Specify metadata for the deployment (e.g. \`-m
|
||||
KEY1=value1 -m KEY2=value2\`)
|
||||
--no-wait Don't wait for the deployment to finish
|
||||
--prebuilt Use in combination with \`vc build\`. Deploy an
|
||||
existing build
|
||||
--prod Create a production deployment
|
||||
-p, --public Deployment is public (\`/_src\`) is exposed)
|
||||
--regions Set default regions to enable the deployment
|
||||
on
|
||||
--with-cache Retain build cache when using "--force"
|
||||
-y, --yes Use default options to skip all prompts
|
||||
|
||||
-b, --build-env <key=value> Specify environment variables during build-time
|
||||
(e.g. \`-b KEY1=value1 -b KEY2=value2\`)
|
||||
[2mGlobal Options[22m:
|
||||
|
||||
-e, --env <key=value> Specify environment variables during run-time
|
||||
(e.g. \`-e KEY1=value1 -e KEY2=value2\`)
|
||||
--cwd <DIR> Sets the current working directory for a single
|
||||
run of a command
|
||||
-d, --debug Debug mode (default off)
|
||||
-Q, --global-config <DIR> Path to the global \`.vercel\` directory
|
||||
-h, --help Output usage information
|
||||
-A, --local-config <FILE> Path to the local \`vercel.json\` file
|
||||
--no-color No color mode (default off)
|
||||
-S, --scope Set a custom scope
|
||||
-t, --token <TOKEN> Login token
|
||||
-v, --version Output the version number
|
||||
|
||||
-f, --force Force a new deployment even if nothing has
|
||||
changed
|
||||
|
||||
-m, --meta <key=value> Specify metadata for the deployment (e.g. \`-m
|
||||
KEY1=value1 -m KEY2=value2\`)
|
||||
|
||||
--no-wait Don't wait for the deployment to finish
|
||||
|
||||
--prebuilt Use in combination with \`vc build\`. Deploy an
|
||||
existing build
|
||||
|
||||
--prod Create a production deployment
|
||||
|
||||
-p, --public Deployment is public (\`/_src\`) is exposed)
|
||||
|
||||
--regions Set default regions to enable the deployment on
|
||||
|
||||
--with-cache Retain build cache when using "--force"
|
||||
|
||||
-y, --yes Use default options to skip all prompts
|
||||
|
||||
[2mExamples:[22m
|
||||
[2mExamples:[22m
|
||||
|
||||
[90m-[39m Deploy the current directory
|
||||
|
||||
@@ -184,41 +248,45 @@ Deploy your project to Vercel. The \`deploy\` command is the default command for
|
||||
[90m-[39m Write Deployment URL to a file
|
||||
|
||||
[36m$ vercel > deployment-url.txt[39m
|
||||
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`help command help output snapshots column width 120 1`] = `
|
||||
"▲ [1mvercel[22m [1mdeploy[22m [project-path] [options]
|
||||
"
|
||||
▲ [1mvercel[22m [1mdeploy[22m [project-path] [options]
|
||||
|
||||
Deploy your project to Vercel. The \`deploy\` command is the default command for the Vercel CLI, and can be omitted (\`vc deploy my-app\` equals \`vc my-app\`).
|
||||
Deploy your project to Vercel. The \`deploy\` command is the default command for the Vercel CLI, and can be omitted (\`vc deploy my-app\` equals \`vc my-app\`).
|
||||
|
||||
[2mOptions:[22m
|
||||
[2mOptions[22m:
|
||||
|
||||
--archive Compress the deployment code into a file before uploading it
|
||||
--archive Compress the deployment code into a file before uploading it
|
||||
-b, --build-env <key=value> Specify environment variables during build-time (e.g. \`-b KEY1=value1 -b
|
||||
KEY2=value2\`)
|
||||
-e, --env <key=value> Specify environment variables during run-time (e.g. \`-e KEY1=value1 -e KEY2=value2\`)
|
||||
-f, --force Force a new deployment even if nothing has changed
|
||||
-m, --meta <key=value> Specify metadata for the deployment (e.g. \`-m KEY1=value1 -m KEY2=value2\`)
|
||||
--no-wait Don't wait for the deployment to finish
|
||||
--prebuilt Use in combination with \`vc build\`. Deploy an existing build
|
||||
--prod Create a production deployment
|
||||
-p, --public Deployment is public (\`/_src\`) is exposed)
|
||||
--regions Set default regions to enable the deployment on
|
||||
--with-cache Retain build cache when using "--force"
|
||||
-y, --yes Use default options to skip all prompts
|
||||
|
||||
-b, --build-env <key=value> Specify environment variables during build-time (e.g. \`-b KEY1=value1 -b KEY2=value2\`)
|
||||
[2mGlobal Options[22m:
|
||||
|
||||
-e, --env <key=value> Specify environment variables during run-time (e.g. \`-e KEY1=value1 -e KEY2=value2\`)
|
||||
--cwd <DIR> Sets the current working directory for a single run of a command
|
||||
-d, --debug Debug mode (default off)
|
||||
-Q, --global-config <DIR> Path to the global \`.vercel\` directory
|
||||
-h, --help Output usage information
|
||||
-A, --local-config <FILE> Path to the local \`vercel.json\` file
|
||||
--no-color No color mode (default off)
|
||||
-S, --scope Set a custom scope
|
||||
-t, --token <TOKEN> Login token
|
||||
-v, --version Output the version number
|
||||
|
||||
-f, --force Force a new deployment even if nothing has changed
|
||||
|
||||
-m, --meta <key=value> Specify metadata for the deployment (e.g. \`-m KEY1=value1 -m KEY2=value2\`)
|
||||
|
||||
--no-wait Don't wait for the deployment to finish
|
||||
|
||||
--prebuilt Use in combination with \`vc build\`. Deploy an existing build
|
||||
|
||||
--prod Create a production deployment
|
||||
|
||||
-p, --public Deployment is public (\`/_src\`) is exposed)
|
||||
|
||||
--regions Set default regions to enable the deployment on
|
||||
|
||||
--with-cache Retain build cache when using "--force"
|
||||
|
||||
-y, --yes Use default options to skip all prompts
|
||||
|
||||
[2mExamples:[22m
|
||||
[2mExamples:[22m
|
||||
|
||||
[90m-[39m Deploy the current directory
|
||||
|
||||
@@ -240,5 +308,6 @@ Deploy your project to Vercel. The \`deploy\` command is the default command for
|
||||
[90m-[39m Write Deployment URL to a file
|
||||
|
||||
[36m$ vercel > deployment-url.txt[39m
|
||||
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -58,6 +58,7 @@ describe('list', () => {
|
||||
'Age',
|
||||
'Deployment',
|
||||
'Status',
|
||||
'Environment',
|
||||
'Duration',
|
||||
'Username',
|
||||
]);
|
||||
@@ -68,6 +69,7 @@ describe('list', () => {
|
||||
expect(data).toEqual([
|
||||
`https://${deployment.url}`,
|
||||
stateString(deployment.state || ''),
|
||||
deployment.target === 'production' ? 'Production' : 'Preview',
|
||||
getDeploymentDuration(deployment),
|
||||
user.username,
|
||||
]);
|
||||
@@ -107,7 +109,13 @@ describe('list', () => {
|
||||
|
||||
line = await lines.next();
|
||||
const header = parseSpacedTableRow(line.value!);
|
||||
expect(header).toEqual(['Age', 'Deployment', 'Status', 'Duration']);
|
||||
expect(header).toEqual([
|
||||
'Age',
|
||||
'Deployment',
|
||||
'Status',
|
||||
'Environment',
|
||||
'Duration',
|
||||
]);
|
||||
|
||||
line = await lines.next();
|
||||
const data = parseSpacedTableRow(line.value!);
|
||||
@@ -116,6 +124,7 @@ describe('list', () => {
|
||||
expect(data).toEqual([
|
||||
'https://' + deployment.url,
|
||||
stateString(deployment.state || ''),
|
||||
deployment.target === 'production' ? 'Production' : 'Preview',
|
||||
getDeploymentDuration(deployment),
|
||||
]);
|
||||
});
|
||||
@@ -160,6 +169,7 @@ describe('list', () => {
|
||||
'Age',
|
||||
'Deployment',
|
||||
'Status',
|
||||
'Environment',
|
||||
'Duration',
|
||||
'Username',
|
||||
]);
|
||||
@@ -170,8 +180,50 @@ describe('list', () => {
|
||||
expect(data).toEqual([
|
||||
`https://${deployment.url}`,
|
||||
stateString(deployment.state || ''),
|
||||
deployment.target === 'production' ? 'Production' : 'Preview',
|
||||
getDeploymentDuration(deployment),
|
||||
user.username,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should output deployment URLs to stdout', async () => {
|
||||
const user = useUser();
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'with-team',
|
||||
name: 'with-team',
|
||||
});
|
||||
const prodDeployment = useDeployment({
|
||||
creator: user,
|
||||
createdAt: Date.now() - 1000,
|
||||
target: 'production',
|
||||
});
|
||||
const previewDeployment = useDeployment({
|
||||
creator: user,
|
||||
createdAt: Date.now(),
|
||||
target: undefined,
|
||||
});
|
||||
|
||||
client.stdout.isTTY = false;
|
||||
client.cwd = fixture('with-team');
|
||||
|
||||
// run with all deployments
|
||||
let prom = list(client);
|
||||
await expect(client.stdout).toOutput(
|
||||
`https://${previewDeployment.url}\nhttps://${prodDeployment.url}`
|
||||
);
|
||||
await prom;
|
||||
|
||||
// run again with preview deployments only
|
||||
client.setArgv('--environment', 'preview');
|
||||
prom = list(client);
|
||||
await expect(client.stdout).toOutput(`https://${previewDeployment.url}`);
|
||||
await prom;
|
||||
|
||||
// run again with production deployments only
|
||||
client.setArgv('--environment', 'production');
|
||||
prom = list(client);
|
||||
await expect(client.stdout).toOutput(`https://${prodDeployment.url}`);
|
||||
await prom;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import { setupUnitFixture } from '../../helpers/setup-unit-fixture';
|
||||
import { useDeployment } from '../../mocks/deployment';
|
||||
import { useTeams } from '../../mocks/team';
|
||||
import { useUser } from '../../mocks/user';
|
||||
import { Deployment } from '@vercel-internals/types';
|
||||
|
||||
describe('redeploy', () => {
|
||||
it('should error if missing deployment url', async () => {
|
||||
@@ -77,9 +78,20 @@ describe('redeploy', () => {
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
});
|
||||
|
||||
it('should redeploy to preview', async () => {
|
||||
const { fromDeployment } = initRedeployTest({ target: null });
|
||||
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('Preview');
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
function initRedeployTest() {
|
||||
function initRedeployTest({ target }: { target?: Deployment['target'] } = {}) {
|
||||
setupUnitFixture('commands/redeploy/simple-static');
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -88,10 +100,18 @@ function initRedeployTest() {
|
||||
id: 'vercel-redeploy',
|
||||
name: 'vercel-redeploy',
|
||||
});
|
||||
const fromDeployment = useDeployment({ creator: user });
|
||||
const toDeployment = useDeployment({ creator: user });
|
||||
const fromDeployment = useDeployment({ creator: user, target });
|
||||
const toDeployment = useDeployment({ creator: user, target });
|
||||
|
||||
client.scenario.post(`/v13/deployments`, (req, res) => {
|
||||
const { target } = req.body;
|
||||
if (target !== undefined && typeof target !== 'string') {
|
||||
res.status(400).json({
|
||||
message: 'Invalid request: `target` should be string',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.json(toDeployment);
|
||||
});
|
||||
|
||||
|
||||
59
packages/cli/test/unit/commands/remove.test.ts
Normal file
59
packages/cli/test/unit/commands/remove.test.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { client } from '../../mocks/client';
|
||||
import {
|
||||
defaultProject,
|
||||
useProject,
|
||||
useUnknownProject,
|
||||
} from '../../mocks/project';
|
||||
import remove from '../../../src/commands/remove';
|
||||
import { useDeployment } from '../../mocks/deployment';
|
||||
import { useUser } from '../../mocks/user';
|
||||
|
||||
describe('remove', () => {
|
||||
it('should error if missing deployment url', async () => {
|
||||
client.setArgv('remove');
|
||||
const exitCodePromise = remove(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error: `vercel rm` expects at least one argument'
|
||||
);
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should error without calling API for invalid names', async () => {
|
||||
const badDeployName = '/#';
|
||||
client.setArgv('remove', badDeployName);
|
||||
const exitCodePromise = remove(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Error: The provided argument "${badDeployName}" is not a valid deployment or project`
|
||||
);
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('calls API to delete a project ', async () => {
|
||||
let deleteAPIWasCalled = false;
|
||||
const user = useUser();
|
||||
|
||||
const project = useProject({
|
||||
...defaultProject,
|
||||
id: '123',
|
||||
});
|
||||
|
||||
useUnknownProject();
|
||||
|
||||
const deployment = useDeployment({
|
||||
creator: user,
|
||||
project,
|
||||
});
|
||||
|
||||
client.scenario.delete('/now/deployments/:id', (req, res) => {
|
||||
deleteAPIWasCalled = true;
|
||||
res.json({});
|
||||
});
|
||||
|
||||
client.setArgv('remove', deployment.url, '--yes');
|
||||
await remove(client);
|
||||
|
||||
expect(deleteAPIWasCalled);
|
||||
});
|
||||
});
|
||||
34
packages/cli/test/unit/util/git/repo-info-to-url.test.ts
Normal file
34
packages/cli/test/unit/util/git/repo-info-to-url.test.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { repoInfoToUrl } from '../../../../src/util/git/repo-info-to-url';
|
||||
import type { RepoInfo } from '../../../../src/util/git/connect-git-provider';
|
||||
|
||||
describe('repoInfoToUrl()', () => {
|
||||
it('should support "github" URL', () => {
|
||||
const info: RepoInfo = {
|
||||
provider: 'github',
|
||||
org: 'vercel',
|
||||
repo: 'foo',
|
||||
url: 'git@github.com:vercel/foo.git',
|
||||
};
|
||||
expect(repoInfoToUrl(info)).toEqual('https://github.com/vercel/foo');
|
||||
});
|
||||
|
||||
it('should support "gitlab" URL', () => {
|
||||
const info: RepoInfo = {
|
||||
provider: 'gitlab',
|
||||
org: 'vercel',
|
||||
repo: 'foo',
|
||||
url: 'git@gitlab.com:vercel/foo.git',
|
||||
};
|
||||
expect(repoInfoToUrl(info)).toEqual('https://gitlab.com/vercel/foo');
|
||||
});
|
||||
|
||||
it('should support "bitbucket" URL', () => {
|
||||
const info: RepoInfo = {
|
||||
provider: 'bitbucket',
|
||||
org: 'vercel',
|
||||
repo: 'foo',
|
||||
url: 'git@bitbucket.com:vercel/foo.git',
|
||||
};
|
||||
expect(repoInfoToUrl(info)).toEqual('https://bitbucket.com/vercel/foo');
|
||||
});
|
||||
});
|
||||
43
packages/cli/test/unit/util/projects/detect-projects.test.ts
Normal file
43
packages/cli/test/unit/util/projects/detect-projects.test.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { join } from 'path';
|
||||
import type { Framework } from '@vercel/frameworks';
|
||||
import { detectProjects } from '../../../../src/util/projects/detect-projects';
|
||||
|
||||
const REPO_ROOT = join(__dirname, '../../../../../..');
|
||||
const EXAMPLES_DIR = join(REPO_ROOT, 'examples');
|
||||
const FS_DETECTORS_FIXTURES = join(
|
||||
REPO_ROOT,
|
||||
'packages/fs-detectors/test/fixtures'
|
||||
);
|
||||
|
||||
function mapDetected(
|
||||
detected: Map<string, Framework[]>
|
||||
): Array<[string, string[]]> {
|
||||
return [...detected.entries()]
|
||||
.sort((a, b) => a[0].localeCompare(b[0]))
|
||||
.map(([dir, frameworks]) => [dir, frameworks.map(f => f.slug as string)]);
|
||||
}
|
||||
|
||||
describe('detectProjects()', () => {
|
||||
it('should match 1 Project in "nextjs" example', async () => {
|
||||
const dir = join(EXAMPLES_DIR, 'nextjs');
|
||||
const detected = await detectProjects(dir);
|
||||
expect(mapDetected(detected)).toEqual([['', ['nextjs']]]);
|
||||
});
|
||||
|
||||
it('should match 2 Projects in "storybook" example', async () => {
|
||||
const dir = join(EXAMPLES_DIR, 'storybook');
|
||||
const detected = await detectProjects(dir);
|
||||
expect(mapDetected(detected)).toEqual([['', ['nextjs', 'storybook']]]);
|
||||
});
|
||||
|
||||
it('should match "30-double-nested-workspaces"', async () => {
|
||||
const dir = join(FS_DETECTORS_FIXTURES, '30-double-nested-workspaces');
|
||||
const detected = await detectProjects(dir);
|
||||
expect(mapDetected(detected)).toEqual([
|
||||
['packages/backend/c', ['remix']],
|
||||
['packages/backend/d', ['nextjs']],
|
||||
['packages/frontend/a', ['hexo']],
|
||||
['packages/frontend/b', ['ember']],
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
declare module 'supports-hyperlinks' {
|
||||
import { Writable } from 'stream';
|
||||
export function supportsHyperlink(stream: Writable): boolean;
|
||||
}
|
||||
@@ -1,5 +1,26 @@
|
||||
# @vercel/client
|
||||
|
||||
## 12.6.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`a8ecf40d6`](https://github.com/vercel/vercel/commit/a8ecf40d6f50e2fc8b13b02c8ef50b3dcafad3a6)]:
|
||||
- @vercel/build-utils@6.8.3
|
||||
|
||||
## 12.6.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`0750517af`](https://github.com/vercel/vercel/commit/0750517af99aea41410d4f1f772ce427699554e7)]:
|
||||
- @vercel/build-utils@6.8.2
|
||||
|
||||
## 12.6.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`7021279b2`](https://github.com/vercel/vercel/commit/7021279b284f314a4d1bdbb4306b4c22291efa08)]:
|
||||
- @vercel/build-utils@6.8.1
|
||||
|
||||
## 12.6.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "12.6.3",
|
||||
"version": "12.6.6",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -35,7 +35,7 @@
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "6.8.0",
|
||||
"@vercel/build-utils": "6.8.3",
|
||||
"@vercel/routing-utils": "2.2.1",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @vercel/frameworks
|
||||
|
||||
## 1.5.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Add `ignorePackageJsonScript` configuration for Framework command settings to ignore the `package.json` script. ([#10228](https://github.com/vercel/vercel/pull/10228))
|
||||
|
||||
Enable this mode for Storybook's `buildCommand`, since it should not invoke the "build" script, which is most likely designated for the frontend app build.
|
||||
|
||||
## 1.4.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "1.4.3",
|
||||
"version": "1.5.0",
|
||||
"main": "./dist/frameworks.js",
|
||||
"types": "./dist/frameworks.d.ts",
|
||||
"files": [
|
||||
|
||||
@@ -1955,6 +1955,7 @@ export const frameworks = [
|
||||
},
|
||||
buildCommand: {
|
||||
value: 'storybook build',
|
||||
ignorePackageJsonScript: true,
|
||||
},
|
||||
devCommand: {
|
||||
value: `storybook dev -p $PORT`,
|
||||
|
||||
@@ -32,11 +32,24 @@ export interface SettingPlaceholder {
|
||||
|
||||
export interface SettingValue {
|
||||
/**
|
||||
* A predefined setting for the detected framework
|
||||
* A predefined setting for the detected framework.
|
||||
* @example "next dev --port $PORT"
|
||||
*/
|
||||
value: string | null;
|
||||
/**
|
||||
* Placeholder text that may be shown in the UI when
|
||||
* the user is configuring this setting value.
|
||||
* @example "`npm run build` or `next build`"
|
||||
*/
|
||||
placeholder?: string;
|
||||
/**
|
||||
* When set to `true`, then the builder will not
|
||||
* invoke the equivalent script in `package.json`,
|
||||
* and instead will invoke the command specified in
|
||||
* configuration setting directly. When this
|
||||
* configuration is enabled, `value` must be a string.
|
||||
*/
|
||||
ignorePackageJsonScript?: boolean;
|
||||
}
|
||||
|
||||
export type Setting = SettingValue | SettingPlaceholder;
|
||||
|
||||
16
packages/frameworks/test/frameworks.unit.test.ts
vendored
16
packages/frameworks/test/frameworks.unit.test.ts
vendored
@@ -49,6 +49,22 @@ const SchemaSettings = {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
required: ['value', 'ignorePackageJsonScript'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
value: {
|
||||
type: 'string',
|
||||
},
|
||||
placeholder: {
|
||||
type: 'string',
|
||||
},
|
||||
ignorePackageJsonScript: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
required: ['placeholder'],
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @vercel/fs-detectors
|
||||
|
||||
## 4.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`ce4633fe4`](https://github.com/vercel/vercel/commit/ce4633fe4d00cb5c251cdabbfab08f39ec3f3b5f)]:
|
||||
- @vercel/frameworks@1.5.0
|
||||
|
||||
## 4.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Add `detectFrameworks()` function ([#10195](https://github.com/vercel/vercel/pull/10195))
|
||||
|
||||
## 4.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/fs-detectors",
|
||||
"version": "4.0.1",
|
||||
"version": "4.1.1",
|
||||
"description": "Vercel filesystem detectors",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -21,7 +21,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/error-utils": "1.0.10",
|
||||
"@vercel/frameworks": "1.4.3",
|
||||
"@vercel/frameworks": "1.5.0",
|
||||
"@vercel/routing-utils": "2.2.1",
|
||||
"glob": "8.0.3",
|
||||
"js-yaml": "4.1.0",
|
||||
@@ -36,7 +36,7 @@
|
||||
"@types/minimatch": "3.0.5",
|
||||
"@types/node": "14.18.33",
|
||||
"@types/semver": "7.3.10",
|
||||
"@vercel/build-utils": "6.8.0",
|
||||
"@vercel/build-utils": "6.8.3",
|
||||
"typescript": "4.9.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +151,24 @@ export async function detectFramework({
|
||||
return result.find(res => res !== null) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects all matching Frameworks based on the given virtual filesystem.
|
||||
*/
|
||||
export async function detectFrameworks({
|
||||
fs,
|
||||
frameworkList,
|
||||
}: DetectFrameworkRecordOptions): Promise<Framework[]> {
|
||||
const result = await Promise.all(
|
||||
frameworkList.map(async frameworkMatch => {
|
||||
if (await matches(fs, frameworkMatch)) {
|
||||
return frameworkMatch;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
);
|
||||
return result.filter(res => res !== null) as Framework[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Framework with a `detectedVersion` specifying the version
|
||||
* or version range of the relevant package
|
||||
|
||||
@@ -7,6 +7,7 @@ export {
|
||||
export { detectFileSystemAPI } from './detect-file-system-api';
|
||||
export {
|
||||
detectFramework,
|
||||
detectFrameworks,
|
||||
detectFrameworkRecord,
|
||||
detectFrameworkVersion,
|
||||
} from './detect-framework';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "backend-c30",
|
||||
"license": "MIT",
|
||||
"version": "0.1.0"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "backend-d30",
|
||||
"license": "MIT",
|
||||
"version": "0.1.0",
|
||||
"devDependencies": {
|
||||
"next": "*",
|
||||
"once": "1.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "frontend-a30",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
@@ -10,6 +11,7 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.2"
|
||||
"debug": "^4.3.2",
|
||||
"hexo": "*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "frontend-b30",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
@@ -10,6 +11,7 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"cowsay": "^1.5.0"
|
||||
"cowsay": "^1.5.0",
|
||||
"ember-cli": "*"
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user