mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-12 21:07:46 +00:00
Compare commits
163 Commits
@now/pytho
...
@now/pytho
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
372a674625 | ||
|
|
fafeadb7ba | ||
|
|
966b1e763f | ||
|
|
bb60e1a5fe | ||
|
|
cac9f807cc | ||
|
|
a0b1254820 | ||
|
|
0faff4132b | ||
|
|
1793a1287d | ||
|
|
5b572239c1 | ||
|
|
f6a66d937e | ||
|
|
2cf9a2f489 | ||
|
|
454f4dcc61 | ||
|
|
6e1065fde2 | ||
|
|
80ce06b20c | ||
|
|
99f3ab8b64 | ||
|
|
ca4f6d2491 | ||
|
|
2ceb2a78aa | ||
|
|
d97da21afc | ||
|
|
03b5a0c0bf | ||
|
|
f76abe3372 | ||
|
|
cdd43b74ae | ||
|
|
fa633d0e02 | ||
|
|
9b46e60c09 | ||
|
|
58eef7f394 | ||
|
|
e97e0fbb64 | ||
|
|
b82876fd82 | ||
|
|
02ad32ec22 | ||
|
|
433fe35c93 | ||
|
|
90c59d6ae2 | ||
|
|
33672c3d78 | ||
|
|
59ae7a989a | ||
|
|
5767e9e8c2 | ||
|
|
e62b9e8ed9 | ||
|
|
59597ccd17 | ||
|
|
7be49c66ef | ||
|
|
1380e25ef3 | ||
|
|
e825ce746f | ||
|
|
4e58951808 | ||
|
|
fbd805aad7 | ||
|
|
2a2705c6e3 | ||
|
|
986c957183 | ||
|
|
c5d063e876 | ||
|
|
500c36f5d4 | ||
|
|
69dbbeac44 | ||
|
|
69486c3adb | ||
|
|
e6692bb79b | ||
|
|
94fba1d7af | ||
|
|
223d8f4774 | ||
|
|
42e7a7e4e3 | ||
|
|
6716fdd49b | ||
|
|
3b69092fd8 | ||
|
|
aa8eaedbc8 | ||
|
|
f519ed373f | ||
|
|
851dff4b03 | ||
|
|
c5ebaa11ea | ||
|
|
934fbc8992 | ||
|
|
72cb5515fd | ||
|
|
c7f0770d53 | ||
|
|
7ea49e8ada | ||
|
|
cae6ce96b3 | ||
|
|
3699dfd756 | ||
|
|
6dca96d877 | ||
|
|
88c14b27a2 | ||
|
|
0d2a9539f6 | ||
|
|
bae160bd7c | ||
|
|
92852ecff2 | ||
|
|
ac0c841cb8 | ||
|
|
53e4b71f89 | ||
|
|
017a2692ca | ||
|
|
311f89eecb | ||
|
|
40d2bc4743 | ||
|
|
37160cbc8b | ||
|
|
3807a2b018 | ||
|
|
b6697dd432 | ||
|
|
6c33496e8a | ||
|
|
89f32625ed | ||
|
|
8253e76ec0 | ||
|
|
e0b3e9606a | ||
|
|
dc75a303f7 | ||
|
|
c1eb8ec78c | ||
|
|
12435f25fd | ||
|
|
d4dc5222cf | ||
|
|
bf1e59b2d3 | ||
|
|
3657e4a36e | ||
|
|
09efc1d865 | ||
|
|
22bded50b6 | ||
|
|
b5b02be3c2 | ||
|
|
776f372eb3 | ||
|
|
81279fd40b | ||
|
|
3342485d29 | ||
|
|
028ee848f5 | ||
|
|
7e64c3b8a9 | ||
|
|
704031f7b2 | ||
|
|
5e3c184735 | ||
|
|
88a8022787 | ||
|
|
96844dc4a5 | ||
|
|
a09acd6969 | ||
|
|
4e232f78de | ||
|
|
b146a04772 | ||
|
|
eaaa50e616 | ||
|
|
c893eaeb7a | ||
|
|
5bf7d7fd07 | ||
|
|
ca8fc92b94 | ||
|
|
9956e85f12 | ||
|
|
7fa4739c78 | ||
|
|
0ef2e2a7ec | ||
|
|
8fd1752acf | ||
|
|
14a1446faf | ||
|
|
0c2c8c5ae5 | ||
|
|
511b27ad39 | ||
|
|
e22ce7da0a | ||
|
|
d9a4ce06bc | ||
|
|
77fb14cc60 | ||
|
|
17c397211e | ||
|
|
6ca83644bc | ||
|
|
d1946ea9b6 | ||
|
|
cc9eae3b71 | ||
|
|
7bbc17df4b | ||
|
|
df6b2be482 | ||
|
|
5ff6263fb7 | ||
|
|
04dc8aaf73 | ||
|
|
5435805e58 | ||
|
|
903f819c5d | ||
|
|
5d927b2d25 | ||
|
|
b7a260cc6d | ||
|
|
e8ba8fb97b | ||
|
|
dd1d9d856b | ||
|
|
eef4c65e5f | ||
|
|
3f64594a22 | ||
|
|
3f5f71f8ab | ||
|
|
2a44179898 | ||
|
|
4a6ddf8b1d | ||
|
|
eeb1b2442c | ||
|
|
129f234aaa | ||
|
|
331c352e2b | ||
|
|
2ccbaea9dd | ||
|
|
069eca3c62 | ||
|
|
305e364f8b | ||
|
|
4068805ae0 | ||
|
|
9a1e7a4a7a | ||
|
|
bbad3d1b96 | ||
|
|
c62116d9a4 | ||
|
|
c94086ff21 | ||
|
|
e99caa7b97 | ||
|
|
658b9e9007 | ||
|
|
3ef27ae45c | ||
|
|
e669cd1152 | ||
|
|
b22aa7c0cf | ||
|
|
582cbb61fb | ||
|
|
7f4197cf43 | ||
|
|
30889db487 | ||
|
|
2d08d5d23e | ||
|
|
59e7367e03 | ||
|
|
84af278e86 | ||
|
|
67d9ee39e2 | ||
|
|
6c67bb81f7 | ||
|
|
aba19701c0 | ||
|
|
bb79402999 | ||
|
|
420bc4b244 | ||
|
|
323c3d74cb | ||
|
|
4ecec8a8f6 | ||
|
|
98883f9978 | ||
|
|
37cf3be437 |
@@ -1,6 +1,5 @@
|
|||||||
version: 2
|
version: 2
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
install:
|
install:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:10
|
- image: circleci/node:10
|
||||||
@@ -22,10 +21,11 @@ jobs:
|
|||||||
command: sudo apt-get install golang-go
|
command: sudo apt-get install golang-go
|
||||||
- run:
|
- run:
|
||||||
name: Installing Dependencies
|
name: Installing Dependencies
|
||||||
command: yarn install
|
command: yarn install --check-files --frozen-lockfile
|
||||||
- save_cache:
|
- save_cache:
|
||||||
paths:
|
paths:
|
||||||
- node_modules
|
- node_modules
|
||||||
|
- packages/gatsby-plugin-now/node_modules
|
||||||
- packages/now-build-utils/node_modules
|
- packages/now-build-utils/node_modules
|
||||||
- packages/now-cgi/node_modules
|
- packages/now-cgi/node_modules
|
||||||
- packages/now-cli/node_modules
|
- packages/now-cli/node_modules
|
||||||
@@ -43,6 +43,7 @@ jobs:
|
|||||||
root: .
|
root: .
|
||||||
paths:
|
paths:
|
||||||
- node_modules
|
- node_modules
|
||||||
|
- packages/gatsby-plugin-now/node_modules
|
||||||
- packages/now-build-utils/node_modules
|
- packages/now-build-utils/node_modules
|
||||||
- packages/now-cgi/node_modules
|
- packages/now-cgi/node_modules
|
||||||
- packages/now-cli/node_modules
|
- packages/now-cli/node_modules
|
||||||
@@ -67,6 +68,9 @@ jobs:
|
|||||||
command: sudo apt install -y rsync
|
command: sudo apt install -y rsync
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: .
|
at: .
|
||||||
|
- run:
|
||||||
|
name: Linking dependencies
|
||||||
|
command: yarn bootstrap
|
||||||
- run:
|
- run:
|
||||||
name: Building
|
name: Building
|
||||||
command: yarn build
|
command: yarn build
|
||||||
@@ -75,6 +79,7 @@ jobs:
|
|||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: .
|
root: .
|
||||||
paths:
|
paths:
|
||||||
|
- packages/gatsby-plugin-now/test/fixtures
|
||||||
- packages/now-build-utils/dist
|
- packages/now-build-utils/dist
|
||||||
- packages/now-cgi/dist
|
- packages/now-cgi/dist
|
||||||
- packages/now-cli/dist
|
- packages/now-cli/dist
|
||||||
@@ -91,6 +96,7 @@ jobs:
|
|||||||
- packages/now-routing-utils/dist
|
- packages/now-routing-utils/dist
|
||||||
- packages/now-ruby/dist
|
- packages/now-ruby/dist
|
||||||
- packages/now-static-build/dist
|
- packages/now-static-build/dist
|
||||||
|
- packages/now-static-build/test/fixtures/10a-gatsby-redirects/plugins
|
||||||
|
|
||||||
test-lint:
|
test-lint:
|
||||||
docker:
|
docker:
|
||||||
@@ -107,36 +113,14 @@ jobs:
|
|||||||
name: Linting Code
|
name: Linting Code
|
||||||
command: yarn test-lint
|
command: yarn test-lint
|
||||||
|
|
||||||
# test-unit:
|
|
||||||
# docker:
|
|
||||||
# - image: circleci/node:10
|
|
||||||
# working_directory: ~/repo
|
|
||||||
# steps:
|
|
||||||
# - checkout
|
|
||||||
# - attach_workspace:
|
|
||||||
# at: .
|
|
||||||
# - run:
|
|
||||||
# name: Compiling `now dev` HTML error templates
|
|
||||||
# command: node packages/now-cli/scripts/compile-templates.js
|
|
||||||
# - run:
|
|
||||||
# name: Running Unit Tests
|
|
||||||
# command: yarn test-unit --clean false
|
|
||||||
# - persist_to_workspace:
|
|
||||||
# root: .
|
|
||||||
# paths:
|
|
||||||
# - packages/now-cli/.nyc_output
|
|
||||||
|
|
||||||
test-integration-macos-node-8:
|
test-integration-macos-node-8:
|
||||||
macos:
|
macos:
|
||||||
xcode: '9.2.0'
|
xcode: '9.0.1'
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: .
|
at: .
|
||||||
- run:
|
|
||||||
name: Update Node.js
|
|
||||||
command: curl -sfLS install-node.now.sh/8.11 | sh -s -- --yes
|
|
||||||
- run:
|
- run:
|
||||||
name: Output version
|
name: Output version
|
||||||
command: node --version
|
command: node --version
|
||||||
@@ -221,15 +205,12 @@ jobs:
|
|||||||
|
|
||||||
test-integration-macos-now-dev-node-8:
|
test-integration-macos-now-dev-node-8:
|
||||||
macos:
|
macos:
|
||||||
xcode: '9.2.0'
|
xcode: '9.0.1'
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: .
|
at: .
|
||||||
- run:
|
|
||||||
name: Update Node.js
|
|
||||||
command: curl -sfLS install-node.now.sh/8.11 | sh -s -- --yes
|
|
||||||
- run:
|
- run:
|
||||||
name: Output version
|
name: Output version
|
||||||
command: node --version
|
command: node --version
|
||||||
@@ -345,6 +326,24 @@ jobs:
|
|||||||
name: Running Integration Tests Once
|
name: Running Integration Tests Once
|
||||||
command: yarn test-integration-once --clean false
|
command: yarn test-integration-once --clean false
|
||||||
|
|
||||||
|
test-unit:
|
||||||
|
docker:
|
||||||
|
- image: circleci/node:10
|
||||||
|
working_directory: ~/repo
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: .
|
||||||
|
- run:
|
||||||
|
name: Compiling `now dev` HTML error templates
|
||||||
|
command: node packages/now-cli/scripts/compile-templates.js
|
||||||
|
- run:
|
||||||
|
name: Output version
|
||||||
|
command: node --version
|
||||||
|
- run:
|
||||||
|
name: Running Unit Tests
|
||||||
|
command: yarn test-unit --clean false
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:10
|
- image: circleci/node:10
|
||||||
@@ -384,36 +383,6 @@ jobs:
|
|||||||
name: Finalize Sentry Release
|
name: Finalize Sentry Release
|
||||||
command: sentry-cli releases finalize now-cli@`git describe --tags`
|
command: sentry-cli releases finalize now-cli@`git describe --tags`
|
||||||
|
|
||||||
publish-stable:
|
|
||||||
docker:
|
|
||||||
- image: circleci/node:10
|
|
||||||
working_directory: ~/repo
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- attach_workspace:
|
|
||||||
at: .
|
|
||||||
- run:
|
|
||||||
name: Saving Authentication Information
|
|
||||||
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
|
|
||||||
- run:
|
|
||||||
name: Publishing to Stable Channel
|
|
||||||
command: npm publish --tag latest
|
|
||||||
|
|
||||||
publish-canary:
|
|
||||||
docker:
|
|
||||||
- image: circleci/node:10
|
|
||||||
working_directory: ~/repo
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- attach_workspace:
|
|
||||||
at: .
|
|
||||||
- run:
|
|
||||||
name: Saving Authentication Information
|
|
||||||
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
|
|
||||||
- run:
|
|
||||||
name: Publishing to Canary Channel
|
|
||||||
command: npm publish --tag canary
|
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
unscheduled:
|
unscheduled:
|
||||||
@@ -434,12 +403,6 @@ workflows:
|
|||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only: /.*/
|
only: /.*/
|
||||||
# - test-unit:
|
|
||||||
# requires:
|
|
||||||
# - build
|
|
||||||
# filters:
|
|
||||||
# tags:
|
|
||||||
# only: /.*/
|
|
||||||
- test-integration-macos-node-8:
|
- test-integration-macos-node-8:
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
@@ -515,12 +478,14 @@ workflows:
|
|||||||
- test-integration-once:
|
- test-integration-once:
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
|
- test-unit:
|
||||||
|
requires:
|
||||||
|
- build
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only: /.*/
|
only: /.*/
|
||||||
- coverage:
|
- coverage:
|
||||||
requires:
|
requires:
|
||||||
#- test-unit
|
|
||||||
- test-integration-macos-node-8
|
- test-integration-macos-node-8
|
||||||
- test-integration-macos-node-10
|
- test-integration-macos-node-10
|
||||||
- test-integration-macos-node-12
|
- test-integration-macos-node-12
|
||||||
@@ -534,23 +499,8 @@ workflows:
|
|||||||
- test-integration-linux-now-dev-node-10
|
- test-integration-linux-now-dev-node-10
|
||||||
- test-integration-linux-now-dev-node-12
|
- test-integration-linux-now-dev-node-12
|
||||||
- test-integration-once
|
- test-integration-once
|
||||||
|
- test-unit
|
||||||
- test-lint
|
- test-lint
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only: /.*/
|
only: /.*/
|
||||||
- publish-canary:
|
|
||||||
requires:
|
|
||||||
- coverage
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /^.*canary.*($|\b)/
|
|
||||||
branches:
|
|
||||||
ignore: /.*/
|
|
||||||
- publish-stable:
|
|
||||||
requires:
|
|
||||||
- coverage
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /^(\d+\.)?(\d+\.)?(\*|\d+)$/
|
|
||||||
branches:
|
|
||||||
ignore: /.*/
|
|
||||||
|
|||||||
32
.eslintignore
Normal file
32
.eslintignore
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
|
||||||
|
# gatsby-plugin-now
|
||||||
|
packages/gatsby-plugin-now/test/fixtures
|
||||||
|
|
||||||
|
# now-cli
|
||||||
|
packages/now-cli/@types
|
||||||
|
packages/now-cli/download
|
||||||
|
packages/now-cli/dist
|
||||||
|
packages/now-cli/test/fixtures
|
||||||
|
packages/now-cli/test/dev/fixtures
|
||||||
|
packages/now-cli/bin
|
||||||
|
packages/now-cli/link
|
||||||
|
packages/now-cli/src/util/dev/templates/*.ts
|
||||||
|
|
||||||
|
# now-client
|
||||||
|
packages/now-client/tests/fixtures
|
||||||
|
packages/now-client/lib
|
||||||
|
|
||||||
|
# now-next
|
||||||
|
packages/now-next/test/fixtures
|
||||||
|
|
||||||
|
# now-node
|
||||||
|
packages/now-node/src/bridge.ts
|
||||||
|
packages/now-node/test/fixtures
|
||||||
|
|
||||||
|
# now-node-bridge
|
||||||
|
packages/now-node-bridge/bridge.*
|
||||||
|
|
||||||
|
# now-static-build
|
||||||
|
packages/now-static-build/test/fixtures
|
||||||
64
.eslintrc.json
Normal file
64
.eslintrc.json
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2018,
|
||||||
|
"sourceType": "module",
|
||||||
|
"modules": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"prettier",
|
||||||
|
"prettier/@typescript-eslint"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"jest": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"require-atomic-updates": 0,
|
||||||
|
"@typescript-eslint/ban-ts-ignore": 0,
|
||||||
|
"@typescript-eslint/camelcase": 0,
|
||||||
|
"@typescript-eslint/explicit-function-return-type": 0,
|
||||||
|
"@typescript-eslint/no-empty-function": 0,
|
||||||
|
"@typescript-eslint/no-use-before-define": 0
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["**/*.js"],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-var-requires": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["packages/now-cli/**/*"],
|
||||||
|
"rules": {
|
||||||
|
"lines-between-class-members": 0,
|
||||||
|
"no-async-promise-executor": 0,
|
||||||
|
"no-control-regex": 0,
|
||||||
|
"no-empty": 0,
|
||||||
|
"prefer-const": 0,
|
||||||
|
"prefer-destructuring": 0,
|
||||||
|
"@typescript-eslint/ban-types": 0,
|
||||||
|
"@typescript-eslint/consistent-type-assertions": 0,
|
||||||
|
"@typescript-eslint/member-delimiter-style": 0,
|
||||||
|
"@typescript-eslint/no-empty-function": 0,
|
||||||
|
"@typescript-eslint/no-explicit-any": 0,
|
||||||
|
"@typescript-eslint/no-inferrable-types": 0,
|
||||||
|
"@typescript-eslint/no-var-requires": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["packages/now-client/**/*"],
|
||||||
|
"rules": {
|
||||||
|
"prefer-const": 0,
|
||||||
|
"require-atomic-updates": 0,
|
||||||
|
"@typescript-eslint/ban-ts-ignore": 0,
|
||||||
|
"@typescript-eslint/no-explicit-any": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Ignore test fixtures in GitHub Languages
|
||||||
|
# See https://github.com/github/linguist#vendored-code
|
||||||
|
packages/*/test/* linguist-vendored
|
||||||
4
.github/workflows/publish.yml
vendored
4
.github/workflows/publish.yml
vendored
@@ -9,7 +9,7 @@ on:
|
|||||||
- '!*'
|
- '!*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
Publish:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- name: Install
|
- name: Install
|
||||||
run: yarn install --pure-lockfile
|
run: yarn install --check-files --frozen-lockfile
|
||||||
- name: Build
|
- name: Build
|
||||||
run: yarn build
|
run: yarn build
|
||||||
- name: Publish
|
- name: Publish
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -12,7 +12,9 @@ packages/now-cli/.builders
|
|||||||
packages/now-cli/assets
|
packages/now-cli/assets
|
||||||
packages/now-cli/src/util/dev/templates/*.ts
|
packages/now-cli/src/util/dev/templates/*.ts
|
||||||
packages/now-cli/test/**/yarn.lock
|
packages/now-cli/test/**/yarn.lock
|
||||||
|
!packages/now-cli/test/dev/**/yarn.lock
|
||||||
packages/now-cli/test/**/node_modules
|
packages/now-cli/test/**/node_modules
|
||||||
packages/now-cli/test/dev/fixtures/08-hugo/hugo
|
packages/now-cli/test/dev/fixtures/08-hugo/hugo
|
||||||
packages/now-cli/test/dev/fixtures/**/dist
|
packages/now-cli/test/dev/fixtures/**/dist
|
||||||
packages/now-cli/test/dev/fixtures/**/public
|
packages/now-cli/test/dev/fixtures/**/public
|
||||||
|
packages/now-cli/test/fixtures/integration
|
||||||
|
|||||||
@@ -12,5 +12,6 @@ optimistic_updates = true
|
|||||||
|
|
||||||
[merge.message]
|
[merge.message]
|
||||||
title = "pull_request_title"
|
title = "pull_request_title"
|
||||||
|
body = "pull_request_body"
|
||||||
include_pr_number = true
|
include_pr_number = true
|
||||||
body_type = "markdown"
|
body_type = "markdown"
|
||||||
|
|||||||
4
.prettierrc
Normal file
4
.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "es5"
|
||||||
|
}
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Publishing to npm
|
|
||||||
|
|
||||||
Always publish to the Canary Channel as soon as a PR is merged into the `canary` branch.
|
|
||||||
|
|
||||||
```
|
|
||||||
yarn publish-canary
|
|
||||||
```
|
|
||||||
|
|
||||||
Publish the Stable Channel weekly.
|
|
||||||
|
|
||||||
- Cherry pick each commit from `canary` to `master` branch
|
|
||||||
- Verify that you are _in-sync_ with canary (with the exception of the `version` line in `package.json`)
|
|
||||||
- Deploy the modified Builders
|
|
||||||
|
|
||||||
```
|
|
||||||
# View differences excluding "Publish" commits
|
|
||||||
git checkout canary && git pull
|
|
||||||
git log --pretty=format:"$ad- %s [%an]" | grep -v Publish > ~/Desktop/canary.txt
|
|
||||||
git checkout master && git pull
|
|
||||||
git log --pretty=format:"$ad- %s [%an]" | grep -v Publish > ~/Desktop/master.txt
|
|
||||||
diff ~/Desktop/canary.txt ~/Desktop/master.txt
|
|
||||||
|
|
||||||
# Cherry pick all PRs from canary into master ...
|
|
||||||
git cherry-pick <PR501_COMMIT_SHA>
|
|
||||||
git cherry-pick <PR502_COMMIT_SHA>
|
|
||||||
git cherry-pick <PR503_COMMIT_SHA>
|
|
||||||
git cherry-pick <PR504_COMMIT_SHA>
|
|
||||||
|
|
||||||
# Verify the only difference is "version" in package.json
|
|
||||||
git diff origin/canary
|
|
||||||
|
|
||||||
# Ship it
|
|
||||||
yarn publish-stable
|
|
||||||
```
|
|
||||||
|
|
||||||
After running this publish step, GitHub Actions will take care of publishing the modified Builder packages to npm.
|
|
||||||
|
|
||||||
If for some reason GitHub Actions fails to publish the npm package, you may do so
|
|
||||||
manually by running `npm publish` from the package directory. Make sure to
|
|
||||||
use `npm publish --tag canary` if you are publishing a canary release!
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||

|

|
||||||
|
|
||||||
[](https://circleci.com/gh/zeit/workflows/now)
|
[](https://circleci.com/gh/zeit/workflows/now/tree/master)
|
||||||
[](https://spectrum.chat/zeit)
|
[](https://spectrum.chat/zeit)
|
||||||
|
|
||||||
**Note**: The [canary](https://github.com/zeit/now/tree/canary) branch is under heavy development – the stable release branch is [master](https://github.com/zeit/now/tree/master).
|
**Note**: The [canary](https://github.com/zeit/now/tree/canary) branch is under heavy development – the stable release branch is [master](https://github.com/zeit/now/tree/master).
|
||||||
|
|||||||
18
changelog.js
Normal file
18
changelog.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const { execSync } = require('child_process');
|
||||||
|
|
||||||
|
const commit = execSync('git log --pretty=format:"%s %H"')
|
||||||
|
.toString()
|
||||||
|
.trim()
|
||||||
|
.split('\n')
|
||||||
|
.find(line => line.startsWith('Publish '))
|
||||||
|
.split(' ')
|
||||||
|
.pop();
|
||||||
|
|
||||||
|
if (!commit) {
|
||||||
|
throw new Error('Unable to find last publish commit');
|
||||||
|
}
|
||||||
|
|
||||||
|
const log = execSync(`git log --pretty=format:"- %s [%an]" ${commit}...HEAD`).toString().trim();
|
||||||
|
|
||||||
|
console.log(`Changes since the last publish commit ${commit}:`);
|
||||||
|
console.log(`\n${log}\n`);
|
||||||
36
diff.js
Normal file
36
diff.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
const { execSync } = require('child_process');
|
||||||
|
const { join } = require('path');
|
||||||
|
const { tmpdir } = require('os');
|
||||||
|
const { mkdirSync, writeFileSync } = require('fs');
|
||||||
|
|
||||||
|
function getCommits(count) {
|
||||||
|
return execSync('git log --pretty=format:"%s [%an]"')
|
||||||
|
.toString()
|
||||||
|
.trim()
|
||||||
|
.split('\n')
|
||||||
|
.slice(0, count)
|
||||||
|
.filter(line => !line.startsWith('Publish '))
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function main(count = '100') {
|
||||||
|
console.log(`Generating diff using last ${count} commits...`);
|
||||||
|
const randomTmpId = Math.random().toString().slice(2);
|
||||||
|
const dir = join(tmpdir(), 'now-diff' + randomTmpId);
|
||||||
|
mkdirSync(dir);
|
||||||
|
|
||||||
|
execSync('git checkout canary && git pull');
|
||||||
|
const canary = getCommits(count);
|
||||||
|
execSync('git checkout master && git pull');
|
||||||
|
const master = getCommits(count);
|
||||||
|
|
||||||
|
writeFileSync(join(dir, 'log.txt'), '# canary\n' + canary);
|
||||||
|
execSync('git init && git add -A && git commit -m "init"', { cwd: dir });
|
||||||
|
writeFileSync(join(dir, 'log.txt'), '# master\n' + master);
|
||||||
|
|
||||||
|
console.log(`Done generating diff. Run the following:`);
|
||||||
|
console.log(`cd ${dir}`);
|
||||||
|
console.log('Then use `git diff` or `git difftool` to view the differences.');
|
||||||
|
}
|
||||||
|
|
||||||
|
main(process.argv[2]);
|
||||||
41
package.json
41
package.json
@@ -3,17 +3,29 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": {
|
||||||
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
|
"nohoist": [
|
||||||
|
"**/@types/**"
|
||||||
|
]
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lerna": "3.16.4"
|
"lerna": "3.16.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@typescript-eslint/eslint-plugin": "2.0.0",
|
||||||
|
"@typescript-eslint/parser": "2.0.0",
|
||||||
"@zeit/ncc": "0.20.4",
|
"@zeit/ncc": "0.20.4",
|
||||||
"async-retry": "1.2.3",
|
"async-retry": "1.2.3",
|
||||||
"buffer-replace": "1.0.0",
|
"buffer-replace": "1.0.0",
|
||||||
"node-fetch": "2.6.0"
|
"eslint": "6.2.2",
|
||||||
|
"eslint-config-prettier": "6.1.0",
|
||||||
|
"husky": "3.0.4",
|
||||||
|
"lint-staged": "9.2.5",
|
||||||
|
"node-fetch": "2.6.0",
|
||||||
|
"prettier": "1.18.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lerna": "lerna",
|
"lerna": "lerna",
|
||||||
@@ -21,12 +33,33 @@
|
|||||||
"publish-stable": "git checkout master && git pull && lerna version --exact",
|
"publish-stable": "git checkout master && git pull && lerna version --exact",
|
||||||
"publish-canary": "git checkout canary && git pull && lerna version prerelease --preid canary --exact",
|
"publish-canary": "git checkout canary && git pull && lerna version prerelease --preid canary --exact",
|
||||||
"publish-from-github": "./.circleci/publish.sh",
|
"publish-from-github": "./.circleci/publish.sh",
|
||||||
|
"diff": "node diff.js",
|
||||||
|
"changelog": "node changelog.js",
|
||||||
"build": "node run.js build all",
|
"build": "node run.js build all",
|
||||||
"test-lint": "node run.js test-lint",
|
"test-lint": "node run.js test-lint",
|
||||||
"test-unit": "node run.js test-unit",
|
"test-unit": "node run.js test-unit",
|
||||||
"test-integration": "node run.js test-integration",
|
"test-integration": "node run.js test-integration",
|
||||||
"test-integration-once": "node run.js test-integration-once",
|
"test-integration-once": "node run.js test-integration-once",
|
||||||
"test-integration-now-dev": "node run.js test-integration-now-dev"
|
"test-integration-now-dev": "node run.js test-integration-now-dev",
|
||||||
|
"lint": "eslint . --ext .ts,.js"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,ts}": [
|
||||||
|
"prettier --write",
|
||||||
|
"eslint",
|
||||||
|
"git add"
|
||||||
|
],
|
||||||
|
"*.{json,md}": [
|
||||||
|
"prettier --write",
|
||||||
|
"git add"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "lint-staged"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"signal-exit": "TooTallNate/signal-exit#update/sighub-to-sigint-on-windows"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
6
packages/gatsby-plugin-now/build.sh
Executable file
6
packages/gatsby-plugin-now/build.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# build fixtures for tests
|
||||||
|
yarn --cwd test/fixtures install
|
||||||
|
yarn --cwd test/fixtures run build
|
||||||
33
packages/gatsby-plugin-now/gatsby-node.js
Normal file
33
packages/gatsby-plugin-now/gatsby-node.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const writeFile = require('util').promisify(require('fs').writeFile);
|
||||||
|
|
||||||
|
const REDIRECT_FILE_NAME = '__now_routes_g4t5bY.json';
|
||||||
|
|
||||||
|
exports.onPostBuild = async ({ store }) => {
|
||||||
|
const { redirects, program } = store.getState();
|
||||||
|
|
||||||
|
if (!redirects.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const routes = [{ handle: 'filesystem' }];
|
||||||
|
|
||||||
|
for (const redirect of redirects) {
|
||||||
|
const route = {
|
||||||
|
src: redirect.fromPath,
|
||||||
|
status: redirect.statusCode || (redirect.isPermanent ? 301 : 302),
|
||||||
|
headers: { Location: redirect.toPath }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (redirect.force) {
|
||||||
|
routes.unshift(route);
|
||||||
|
} else {
|
||||||
|
routes.push(route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await writeFile(
|
||||||
|
path.join(program.directory, 'public', REDIRECT_FILE_NAME),
|
||||||
|
JSON.stringify(routes)
|
||||||
|
);
|
||||||
|
};
|
||||||
1
packages/gatsby-plugin-now/index.js
Normal file
1
packages/gatsby-plugin-now/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
// noop
|
||||||
36
packages/gatsby-plugin-now/package.json
Normal file
36
packages/gatsby-plugin-now/package.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "gatsby-plugin-now",
|
||||||
|
"version": "1.2.2",
|
||||||
|
"main": "index.js",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://github.com/zeit/now/tree/canary/packages/gatsby-plugin-now#readme",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/zeit/now.git",
|
||||||
|
"directory": "packages/gatsby-plugin-now"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"gatsby",
|
||||||
|
"gatsby-plugin"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "./build.sh",
|
||||||
|
"test-integration-once": "jest --verbose"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"gatsby-node.js"
|
||||||
|
],
|
||||||
|
"peerDependencies": {
|
||||||
|
"gatsby": ">=2.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"jest": "24.9.0"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"testPathIgnorePatterns": [
|
||||||
|
"/node_modules/",
|
||||||
|
"<rootDir>/test/fixtures/"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
24
packages/gatsby-plugin-now/readme.md
Normal file
24
packages/gatsby-plugin-now/readme.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# gatsby-plugin-now
|
||||||
|
|
||||||
|
This plugin generates [Now Routes](https://zeit.co/docs/v2/advanced/routes) for [redirects](https://www.gatsbyjs.org/docs/actions/#createRedirect) you configured for to your Gatsby project.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
1. Install the plugin:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install gatsby-plugin-now --save-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Add it to the configuration file:
|
||||||
|
|
||||||
|
```
|
||||||
|
// gatsby-config.js
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
'gatsby-plugin-now'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. [Deploy your project to ZEIT Now](https://www.gatsbyjs.org/docs/deploying-to-zeit-now/)
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`test generated now routes 1`] = `
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"headers": Object {
|
||||||
|
"Location": "/",
|
||||||
|
},
|
||||||
|
"src": "/my-special-redirect",
|
||||||
|
"status": 302,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"handle": "filesystem",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"headers": Object {
|
||||||
|
"Location": "/page-2",
|
||||||
|
},
|
||||||
|
"src": "/page2",
|
||||||
|
"status": 301,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"headers": Object {
|
||||||
|
"Location": "/page-2/",
|
||||||
|
},
|
||||||
|
"src": "/page2/",
|
||||||
|
"status": 301,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"headers": Object {
|
||||||
|
"Location": "/",
|
||||||
|
},
|
||||||
|
"src": "/orange",
|
||||||
|
"status": 302,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"headers": Object {
|
||||||
|
"Location": "/",
|
||||||
|
},
|
||||||
|
"src": "/grape",
|
||||||
|
"status": 302,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"headers": Object {
|
||||||
|
"Location": "/page-2/",
|
||||||
|
},
|
||||||
|
"src": "/blue",
|
||||||
|
"status": 302,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"headers": Object {
|
||||||
|
"Location": "/page-2/",
|
||||||
|
},
|
||||||
|
"src": "/randirect",
|
||||||
|
"status": 302,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"headers": Object {
|
||||||
|
"Location": "/",
|
||||||
|
},
|
||||||
|
"src": "/juice",
|
||||||
|
"status": 302,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"headers": Object {
|
||||||
|
"Location": "/",
|
||||||
|
},
|
||||||
|
"src": "/soda",
|
||||||
|
"status": 302,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"headers": Object {
|
||||||
|
"Location": "/page-2/",
|
||||||
|
},
|
||||||
|
"src": "/donut",
|
||||||
|
"status": 302,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"headers": Object {
|
||||||
|
"Location": "/page-2/",
|
||||||
|
},
|
||||||
|
"src": "/randorect",
|
||||||
|
"status": 302,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
4
packages/gatsby-plugin-now/test/fixtures/.gitignore
vendored
Normal file
4
packages/gatsby-plugin-now/test/fixtures/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
public
|
||||||
|
node_modules
|
||||||
|
.cache
|
||||||
|
yarn.lock
|
||||||
3
packages/gatsby-plugin-now/test/fixtures/gatsby-config.js
vendored
Normal file
3
packages/gatsby-plugin-now/test/fixtures/gatsby-config.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: [{ resolve: require.resolve('../../') }]
|
||||||
|
};
|
||||||
105
packages/gatsby-plugin-now/test/fixtures/gatsby-node.js
vendored
Normal file
105
packages/gatsby-plugin-now/test/fixtures/gatsby-node.js
vendored
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Implement the Gatsby API “createPages”. This is called once the
|
||||||
|
// data layer is bootstrapped to let plugins create pages from data.
|
||||||
|
exports.createPages = ({ actions }) => {
|
||||||
|
// need createRedirect action in actions collection
|
||||||
|
// to make the redirection magic happen.
|
||||||
|
// https://www.gatsbyjs.org/docs/bound-action-creators/
|
||||||
|
const { createRedirect } = actions;
|
||||||
|
|
||||||
|
// Let’s set up some string consts to use thoroughout the following.
|
||||||
|
// MDN > JavaScript reference > Statements and declarations
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
|
||||||
|
// Maybe we usually redirect to page 2, with trailing slash.
|
||||||
|
const page2Path = `/page-2/`;
|
||||||
|
// But sometimes to homepage.
|
||||||
|
const homePath = `/`;
|
||||||
|
|
||||||
|
// One-off redirect, note trailing slash missing on fromPath and
|
||||||
|
// toPath here.
|
||||||
|
createRedirect({
|
||||||
|
fromPath: `/page2`,
|
||||||
|
isPermanent: true,
|
||||||
|
redirectInBrowser: true,
|
||||||
|
toPath: `/page-2`
|
||||||
|
});
|
||||||
|
|
||||||
|
// Another one-off redirect, note trailing slash on toPath here.
|
||||||
|
// This time we want trailing slash on toPath so we use
|
||||||
|
// page2Path. Need to handle trailing-slashed and non-trailing-
|
||||||
|
// slashed fromPaths separately, Gatsby serves page components
|
||||||
|
// at either version by default, but we need to explicitly redirect
|
||||||
|
// both versions independently, more on page components:
|
||||||
|
// Docs > Building with Components
|
||||||
|
// https://www.gatsbyjs.org/docs/building-with-components/
|
||||||
|
// and handling trailing slashes:
|
||||||
|
// Docs > Creating and modifying pages > Removing trailing slashes
|
||||||
|
// https://www.gatsbyjs.org/docs/creating-and-modifying-pages/#removing-trailing-slashes
|
||||||
|
createRedirect({
|
||||||
|
fromPath: `/page2/`,
|
||||||
|
isPermanent: true,
|
||||||
|
redirectInBrowser: true,
|
||||||
|
toPath: page2Path
|
||||||
|
});
|
||||||
|
|
||||||
|
// One approach to handle several redirects at once is to create an
|
||||||
|
// array of from/to path pairs.
|
||||||
|
let redirectBatch1 = [
|
||||||
|
{ f: `/orange`, t: `/` },
|
||||||
|
// We could use homePath and page2Path directly here.
|
||||||
|
{ f: `/grape`, t: homePath },
|
||||||
|
{ f: `/blue`, t: page2Path },
|
||||||
|
// or leave to empty and swap for page2Path later on.
|
||||||
|
{ f: `/randirect`, t: `` }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Then we can loop through the array of object literals to create
|
||||||
|
// each redirect. A for loop would do the trick
|
||||||
|
for (var { f: f, t: t } of redirectBatch1) {
|
||||||
|
// Here we swap any empty toPath values for trusty page 2 via
|
||||||
|
// page2Path.
|
||||||
|
if (t === ``) {
|
||||||
|
t = page2Path;
|
||||||
|
}
|
||||||
|
createRedirect({
|
||||||
|
fromPath: f,
|
||||||
|
redirectInBrowser: true,
|
||||||
|
toPath: t
|
||||||
|
});
|
||||||
|
// Uncomment next line to see loop in action during build
|
||||||
|
// console.log('\nRedirecting:\n' + f + '\nTo:\n' + t + '\n');
|
||||||
|
// or check .cache/redirects.json post-compile.
|
||||||
|
}
|
||||||
|
|
||||||
|
// A more modern approach might use forEach rather than for...of
|
||||||
|
// Compare
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration#for...of_statement
|
||||||
|
// and
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
|
||||||
|
let redirectBatch2 = [
|
||||||
|
{ f: `/juice`, t: `/` },
|
||||||
|
{ f: `/soda`, t: `/` },
|
||||||
|
{ f: `/donut`, t: page2Path },
|
||||||
|
{ f: `/randorect`, t: `` }
|
||||||
|
];
|
||||||
|
|
||||||
|
redirectBatch2.forEach(({ f, t }) => {
|
||||||
|
if (t === ``) {
|
||||||
|
t = page2Path;
|
||||||
|
}
|
||||||
|
createRedirect({
|
||||||
|
fromPath: f,
|
||||||
|
redirectInBrowser: true,
|
||||||
|
toPath: t
|
||||||
|
});
|
||||||
|
// Uncomment next line to see forEach in action during build
|
||||||
|
// console.log('\nRedirecting:\n' + f + '\nTo:\n' + t + '\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
createRedirect({
|
||||||
|
fromPath: '/my-special-redirect',
|
||||||
|
toPath: homePath,
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
};
|
||||||
11
packages/gatsby-plugin-now/test/fixtures/package.json
vendored
Normal file
11
packages/gatsby-plugin-now/test/fixtures/package.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "fixtures",
|
||||||
|
"dependencies": {
|
||||||
|
"gatsby": "2.14.0",
|
||||||
|
"react": "16.9.0",
|
||||||
|
"react-dom": "16.9.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "gatsby build"
|
||||||
|
}
|
||||||
|
}
|
||||||
9
packages/gatsby-plugin-now/test/fixtures/src/pages/index.js
vendored
Normal file
9
packages/gatsby-plugin-now/test/fixtures/src/pages/index.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const IndexPage = () => (
|
||||||
|
<div>
|
||||||
|
<h1>Hi people</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default IndexPage;
|
||||||
5
packages/gatsby-plugin-now/test/index.test.js
vendored
Normal file
5
packages/gatsby-plugin-now/test/index.test.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
test('test generated now routes', async () => {
|
||||||
|
const nowRoutes = require('./fixtures/public/__now_routes_g4t5bY.json');
|
||||||
|
|
||||||
|
expect(nowRoutes).toMatchSnapshot();
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@now/build-utils",
|
"name": "@now/build-utils",
|
||||||
"version": "0.9.14",
|
"version": "0.11.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.js",
|
"types": "./dist/index.d.js",
|
||||||
@@ -12,7 +12,8 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "./build.sh",
|
"build": "./build.sh",
|
||||||
"test-integration-once": "jest --env node --verbose --runInBand",
|
"test-unit": "jest --env node --verbose --runInBand test/unit.test.js",
|
||||||
|
"test-integration-once": "jest --env node --verbose --runInBand test/integration.test.js",
|
||||||
"prepublishOnly": "./build.sh"
|
"prepublishOnly": "./build.sh"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
export default function debug(message: string, ...additional: any[]) {
|
export default function debug(message: string, ...additional: any[]) {
|
||||||
if (process.env.NOW_BUILDER_DEBUG) {
|
if (process.env.NOW_BUILDER_DEBUG) {
|
||||||
console.log(message, ...additional);
|
console.log(message, ...additional);
|
||||||
} else if (process.env.NOW_BUILDER_ANNOTATE) {
|
|
||||||
console.log(`[now-builder-debug] ${message}`, ...additional);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ interface Options {
|
|||||||
tag?: 'canary' | 'latest' | string;
|
tag?: 'canary' | 'latest' | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const src: string = 'package.json';
|
const src = 'package.json';
|
||||||
const config: Config = { zeroConfig: true };
|
const config: Config = { zeroConfig: true };
|
||||||
|
|
||||||
const MISSING_BUILD_SCRIPT_ERROR: ErrorResponse = {
|
const MISSING_BUILD_SCRIPT_ERROR: ErrorResponse = {
|
||||||
@@ -96,8 +96,8 @@ async function detectApiBuilders(files: string[]): Promise<Builder[]> {
|
|||||||
.sort(sortFiles)
|
.sort(sortFiles)
|
||||||
.filter(ignoreApiFilter)
|
.filter(ignoreApiFilter)
|
||||||
.map(file => {
|
.map(file => {
|
||||||
const result = getApiBuilders().find(
|
const result = getApiBuilders().find(({ src }): boolean =>
|
||||||
({ src }): boolean => minimatch(file, src)
|
minimatch(file, src)
|
||||||
);
|
);
|
||||||
|
|
||||||
return result ? { ...result, src: file } : null;
|
return result ? { ...result, src: file } : null;
|
||||||
@@ -107,6 +107,31 @@ async function detectApiBuilders(files: string[]): Promise<Builder[]> {
|
|||||||
return finishedBuilds as Builder[];
|
return finishedBuilds as Builder[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When a package has files that conflict with `/api` routes
|
||||||
|
// e.g. Next.js pages/api we'll check it here and return an error.
|
||||||
|
async function checkConflictingFiles(
|
||||||
|
files: string[],
|
||||||
|
builders: Builder[]
|
||||||
|
): Promise<ErrorResponse | null> {
|
||||||
|
// For Next.js
|
||||||
|
if (builders.some(builder => builder.use.startsWith('@now/next'))) {
|
||||||
|
const hasApiPages = files.some(file => file.startsWith('pages/api/'));
|
||||||
|
const hasApiBuilders = builders.some(builder =>
|
||||||
|
builder.src.startsWith('api/')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasApiPages && hasApiBuilders) {
|
||||||
|
return {
|
||||||
|
code: 'conflicting_files',
|
||||||
|
message:
|
||||||
|
'It is not possible to use `api` and `pages/api` at the same time, please only use one option',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// When zero config is used we can call this function
|
// When zero config is used we can call this function
|
||||||
// to determine what builders to use
|
// to determine what builders to use
|
||||||
export async function detectBuilders(
|
export async function detectBuilders(
|
||||||
@@ -116,20 +141,28 @@ export async function detectBuilders(
|
|||||||
): Promise<{
|
): Promise<{
|
||||||
builders: Builder[] | null;
|
builders: Builder[] | null;
|
||||||
errors: ErrorResponse[] | null;
|
errors: ErrorResponse[] | null;
|
||||||
|
warnings: ErrorResponse[];
|
||||||
}> {
|
}> {
|
||||||
const errors: ErrorResponse[] = [];
|
const errors: ErrorResponse[] = [];
|
||||||
|
const warnings: ErrorResponse[] = [];
|
||||||
|
|
||||||
// Detect all builders for the `api` directory before anything else
|
// Detect all builders for the `api` directory before anything else
|
||||||
let builders = await detectApiBuilders(files);
|
let builders = await detectApiBuilders(files);
|
||||||
|
|
||||||
if (pkg && hasBuildScript(pkg)) {
|
if (pkg && hasBuildScript(pkg)) {
|
||||||
builders.push(await detectBuilder(pkg));
|
builders.push(await detectBuilder(pkg));
|
||||||
|
|
||||||
|
const conflictError = await checkConflictingFiles(files, builders);
|
||||||
|
|
||||||
|
if (conflictError) {
|
||||||
|
warnings.push(conflictError);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (pkg && builders.length === 0) {
|
if (pkg && builders.length === 0) {
|
||||||
// We only show this error when there are no api builders
|
// We only show this error when there are no api builders
|
||||||
// since the dependencies of the pkg could be used for those
|
// since the dependencies of the pkg could be used for those
|
||||||
errors.push(MISSING_BUILD_SCRIPT_ERROR);
|
errors.push(MISSING_BUILD_SCRIPT_ERROR);
|
||||||
return { errors, builders: null };
|
return { errors, warnings, builders: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
// We allow a `public` directory
|
// We allow a `public` directory
|
||||||
@@ -140,19 +173,17 @@ export async function detectBuilders(
|
|||||||
src: 'public/**/*',
|
src: 'public/**/*',
|
||||||
config,
|
config,
|
||||||
});
|
});
|
||||||
} else if (builders.length > 0) {
|
} else if (
|
||||||
// We can't use pattern matching, since `!(api)` and `!(api)/**/*`
|
builders.length > 0 &&
|
||||||
// won't give the correct results
|
files.some(f => !f.startsWith('api/') && f !== 'package.json')
|
||||||
builders.push(
|
) {
|
||||||
...files
|
// Everything besides the api directory
|
||||||
.filter(name => !name.startsWith('api/'))
|
// and package.json can be served as static files
|
||||||
.filter(name => !(name === 'package.json'))
|
builders.push({
|
||||||
.map(name => ({
|
|
||||||
use: '@now/static',
|
use: '@now/static',
|
||||||
src: name,
|
src: '!{api/**,package.json}',
|
||||||
config,
|
config,
|
||||||
}))
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,5 +209,6 @@ export async function detectBuilders(
|
|||||||
return {
|
return {
|
||||||
builders: builders.length ? builders : null,
|
builders: builders.length ? builders : null,
|
||||||
errors: errors.length ? errors : null,
|
errors: errors.length ? errors : null,
|
||||||
|
warnings,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,11 +43,10 @@ function getSegmentName(segment: string): string | null {
|
|||||||
function createRouteFromPath(filePath: string): Route {
|
function createRouteFromPath(filePath: string): Route {
|
||||||
const parts = filePath.split('/');
|
const parts = filePath.split('/');
|
||||||
|
|
||||||
let counter: number = 1;
|
let counter = 1;
|
||||||
const query: string[] = [];
|
const query: string[] = [];
|
||||||
|
|
||||||
const srcParts = parts.map(
|
const srcParts = parts.map((segment, index): string => {
|
||||||
(segment, index): string => {
|
|
||||||
const name = getSegmentName(segment);
|
const name = getSegmentName(segment);
|
||||||
const isLast = index === parts.length - 1;
|
const isLast = index === parts.length - 1;
|
||||||
|
|
||||||
@@ -63,7 +62,7 @@ function createRouteFromPath(filePath: string): Route {
|
|||||||
const names = [
|
const names = [
|
||||||
prefix,
|
prefix,
|
||||||
prefix + escapeName(fileName),
|
prefix + escapeName(fileName),
|
||||||
prefix + escapeName(fileName) + escapeName(ext),
|
prefix + escapeName(fileName) + escapeName(ext)
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
// Either filename with extension, filename without extension
|
// Either filename with extension, filename without extension
|
||||||
@@ -72,8 +71,7 @@ function createRouteFromPath(filePath: string): Route {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return segment;
|
return segment;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { name: fileName } = parsePath(filePath);
|
const { name: fileName } = parsePath(filePath);
|
||||||
const isIndex = fileName === 'index';
|
const isIndex = fileName === 'index';
|
||||||
@@ -120,7 +118,7 @@ function partiallyMatches(pathA: string, pathB: string): boolean {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Counts how often a path occurres when all placeholders
|
// Counts how often a path occurs when all placeholders
|
||||||
// got resolved, so we can check if they have conflicts
|
// got resolved, so we can check if they have conflicts
|
||||||
function pathOccurrences(filePath: string, files: string[]): string[] {
|
function pathOccurrences(filePath: string, files: string[]): string[] {
|
||||||
const getAbsolutePath = (unresolvedPath: string): string => {
|
const getAbsolutePath = (unresolvedPath: string): string => {
|
||||||
@@ -228,10 +226,10 @@ async function detectApiRoutes(files: string[]): Promise<RoutesResult> {
|
|||||||
error: {
|
error: {
|
||||||
code: 'conflicting_path_segment',
|
code: 'conflicting_path_segment',
|
||||||
message:
|
message:
|
||||||
`The segment "${conflictingSegment}" occurres more than ` +
|
`The segment "${conflictingSegment}" occurs more than ` +
|
||||||
`one time in your path "${file}". Please make sure that ` +
|
`one time in your path "${file}". Please make sure that ` +
|
||||||
`every segment in a path is unique`,
|
`every segment in a path is unique`
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,8 +249,8 @@ async function detectApiRoutes(files: string[]): Promise<RoutesResult> {
|
|||||||
message:
|
message:
|
||||||
`Two or more files have conflicting paths or names. ` +
|
`Two or more files have conflicting paths or names. ` +
|
||||||
`Please make sure path segments and filenames, without their extension, are unique. ` +
|
`Please make sure path segments and filenames, without their extension, are unique. ` +
|
||||||
`The path "${file}" has conflicts with ${messagePaths}`,
|
`The path "${file}" has conflicts with ${messagePaths}`
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,7 +261,7 @@ async function detectApiRoutes(files: string[]): Promise<RoutesResult> {
|
|||||||
if (defaultRoutes.length) {
|
if (defaultRoutes.length) {
|
||||||
defaultRoutes.push({
|
defaultRoutes.push({
|
||||||
status: 404,
|
status: 404,
|
||||||
src: '/api(\\/.*)?$',
|
src: '/api(\\/.*)?$'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,7 +287,7 @@ export async function detectRoutes(
|
|||||||
if (routesResult.defaultRoutes && hasPublicBuilder(builders)) {
|
if (routesResult.defaultRoutes && hasPublicBuilder(builders)) {
|
||||||
routesResult.defaultRoutes.push({
|
routesResult.defaultRoutes.push({
|
||||||
src: '/(.*)',
|
src: '/(.*)',
|
||||||
dest: '/public/$1',
|
dest: '/public/$1'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import { File } from './types';
|
|||||||
|
|
||||||
interface FileBlobOptions {
|
interface FileBlobOptions {
|
||||||
mode?: number;
|
mode?: number;
|
||||||
|
contentType?: string;
|
||||||
data: string | Buffer;
|
data: string | Buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FromStreamOptions {
|
interface FromStreamOptions {
|
||||||
mode?: number;
|
mode?: number;
|
||||||
|
contentType?: string;
|
||||||
stream: NodeJS.ReadableStream;
|
stream: NodeJS.ReadableStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,16 +18,22 @@ export default class FileBlob implements File {
|
|||||||
public type: 'FileBlob';
|
public type: 'FileBlob';
|
||||||
public mode: number;
|
public mode: number;
|
||||||
public data: string | Buffer;
|
public data: string | Buffer;
|
||||||
|
public contentType: string | undefined;
|
||||||
|
|
||||||
constructor({ mode = 0o100644, data }: FileBlobOptions) {
|
constructor({ mode = 0o100644, contentType, data }: FileBlobOptions) {
|
||||||
assert(typeof mode === 'number');
|
assert(typeof mode === 'number');
|
||||||
assert(typeof data === 'string' || Buffer.isBuffer(data));
|
assert(typeof data === 'string' || Buffer.isBuffer(data));
|
||||||
this.type = 'FileBlob';
|
this.type = 'FileBlob';
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
|
this.contentType = contentType;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async fromStream({ mode = 0o100644, stream }: FromStreamOptions) {
|
static async fromStream({
|
||||||
|
mode = 0o100644,
|
||||||
|
contentType,
|
||||||
|
stream,
|
||||||
|
}: FromStreamOptions) {
|
||||||
assert(typeof mode === 'number');
|
assert(typeof mode === 'number');
|
||||||
assert(typeof stream.pipe === 'function'); // is-stream
|
assert(typeof stream.pipe === 'function'); // is-stream
|
||||||
const chunks: Buffer[] = [];
|
const chunks: Buffer[] = [];
|
||||||
@@ -37,7 +45,7 @@ export default class FileBlob implements File {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const data = Buffer.concat(chunks);
|
const data = Buffer.concat(chunks);
|
||||||
return new FileBlob({ mode, data });
|
return new FileBlob({ mode, contentType, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
toStream(): NodeJS.ReadableStream {
|
toStream(): NodeJS.ReadableStream {
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ const semaToPreventEMFILE = new Sema(20);
|
|||||||
|
|
||||||
interface FileFsRefOptions {
|
interface FileFsRefOptions {
|
||||||
mode?: number;
|
mode?: number;
|
||||||
|
contentType?: string;
|
||||||
fsPath: string;
|
fsPath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FromStreamOptions {
|
interface FromStreamOptions {
|
||||||
mode: number;
|
mode: number;
|
||||||
|
contentType?: string;
|
||||||
stream: NodeJS.ReadableStream;
|
stream: NodeJS.ReadableStream;
|
||||||
fsPath: string;
|
fsPath: string;
|
||||||
}
|
}
|
||||||
@@ -22,17 +24,20 @@ class FileFsRef implements File {
|
|||||||
public type: 'FileFsRef';
|
public type: 'FileFsRef';
|
||||||
public mode: number;
|
public mode: number;
|
||||||
public fsPath: string;
|
public fsPath: string;
|
||||||
|
public contentType: string | undefined;
|
||||||
|
|
||||||
constructor({ mode = 0o100644, fsPath }: FileFsRefOptions) {
|
constructor({ mode = 0o100644, contentType, fsPath }: FileFsRefOptions) {
|
||||||
assert(typeof mode === 'number');
|
assert(typeof mode === 'number');
|
||||||
assert(typeof fsPath === 'string');
|
assert(typeof fsPath === 'string');
|
||||||
this.type = 'FileFsRef';
|
this.type = 'FileFsRef';
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
|
this.contentType = contentType;
|
||||||
this.fsPath = fsPath;
|
this.fsPath = fsPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async fromFsPath({
|
static async fromFsPath({
|
||||||
mode,
|
mode,
|
||||||
|
contentType,
|
||||||
fsPath,
|
fsPath,
|
||||||
}: FileFsRefOptions): Promise<FileFsRef> {
|
}: FileFsRefOptions): Promise<FileFsRef> {
|
||||||
let m = mode;
|
let m = mode;
|
||||||
@@ -40,11 +45,12 @@ class FileFsRef implements File {
|
|||||||
const stat = await fs.lstat(fsPath);
|
const stat = await fs.lstat(fsPath);
|
||||||
m = stat.mode;
|
m = stat.mode;
|
||||||
}
|
}
|
||||||
return new FileFsRef({ mode: m, fsPath });
|
return new FileFsRef({ mode: m, contentType, fsPath });
|
||||||
}
|
}
|
||||||
|
|
||||||
static async fromStream({
|
static async fromStream({
|
||||||
mode = 0o100644,
|
mode = 0o100644,
|
||||||
|
contentType,
|
||||||
stream,
|
stream,
|
||||||
fsPath,
|
fsPath,
|
||||||
}: FromStreamOptions): Promise<FileFsRef> {
|
}: FromStreamOptions): Promise<FileFsRef> {
|
||||||
@@ -63,7 +69,7 @@ class FileFsRef implements File {
|
|||||||
dest.on('error', reject);
|
dest.on('error', reject);
|
||||||
});
|
});
|
||||||
|
|
||||||
return new FileFsRef({ mode, fsPath });
|
return new FileFsRef({ mode, contentType, fsPath });
|
||||||
}
|
}
|
||||||
|
|
||||||
async toStreamAsync(): Promise<NodeJS.ReadableStream> {
|
async toStreamAsync(): Promise<NodeJS.ReadableStream> {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { File } from './types';
|
|||||||
interface FileRefOptions {
|
interface FileRefOptions {
|
||||||
mode?: number;
|
mode?: number;
|
||||||
digest: string;
|
digest: string;
|
||||||
|
contentType?: string;
|
||||||
mutable?: boolean;
|
mutable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,14 +27,21 @@ export default class FileRef implements File {
|
|||||||
public type: 'FileRef';
|
public type: 'FileRef';
|
||||||
public mode: number;
|
public mode: number;
|
||||||
public digest: string;
|
public digest: string;
|
||||||
|
public contentType: string | undefined;
|
||||||
private mutable: boolean;
|
private mutable: boolean;
|
||||||
|
|
||||||
constructor({ mode = 0o100644, digest, mutable = false }: FileRefOptions) {
|
constructor({
|
||||||
|
mode = 0o100644,
|
||||||
|
digest,
|
||||||
|
contentType,
|
||||||
|
mutable = false,
|
||||||
|
}: FileRefOptions) {
|
||||||
assert(typeof mode === 'number');
|
assert(typeof mode === 'number');
|
||||||
assert(typeof digest === 'string');
|
assert(typeof digest === 'string');
|
||||||
this.type = 'FileRef';
|
this.type = 'FileRef';
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
this.digest = digest;
|
this.digest = digest;
|
||||||
|
this.contentType = contentType;
|
||||||
this.mutable = mutable;
|
this.mutable = mutable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { intersects } from 'semver';
|
import { intersects } from 'semver';
|
||||||
import { NodeVersion } from '../types';
|
import { NodeVersion } from '../types';
|
||||||
|
import debug from '../debug';
|
||||||
|
|
||||||
const supportedOptions: NodeVersion[] = [
|
const supportedOptions: NodeVersion[] = [
|
||||||
{ major: 10, range: '10.x', runtime: 'nodejs10.x' },
|
{ major: 10, range: '10.x', runtime: 'nodejs10.x' },
|
||||||
@@ -20,7 +21,7 @@ export async function getSupportedNodeVersion(
|
|||||||
|
|
||||||
if (!engineRange) {
|
if (!engineRange) {
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
console.log(
|
debug(
|
||||||
'missing `engines` in `package.json`, using default range: ' +
|
'missing `engines` in `package.json`, using default range: ' +
|
||||||
selection.range
|
selection.range
|
||||||
);
|
);
|
||||||
@@ -34,7 +35,7 @@ export async function getSupportedNodeVersion(
|
|||||||
});
|
});
|
||||||
if (found) {
|
if (found) {
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
console.log(
|
debug(
|
||||||
'Found `engines` in `package.json`, selecting range: ' +
|
'Found `engines` in `package.json`, selecting range: ' +
|
||||||
selection.range
|
selection.range
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import debug from '../debug';
|
||||||
import spawn from 'cross-spawn';
|
import spawn from 'cross-spawn';
|
||||||
import { SpawnOptions } from 'child_process';
|
import { SpawnOptions } from 'child_process';
|
||||||
import { deprecate } from 'util';
|
import { deprecate } from 'util';
|
||||||
|
import { cpus } from 'os';
|
||||||
import { Meta, PackageJson, NodeVersion, Config } from '../types';
|
import { Meta, PackageJson, NodeVersion, Config } from '../types';
|
||||||
import { getSupportedNodeVersion } from './node-version';
|
import { getSupportedNodeVersion } from './node-version';
|
||||||
|
|
||||||
function spawnAsync(
|
export function spawnAsync(
|
||||||
command: string,
|
command: string,
|
||||||
args: string[],
|
args: string[],
|
||||||
cwd: string,
|
|
||||||
opts: SpawnOptions = {}
|
opts: SpawnOptions = {}
|
||||||
) {
|
) {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const stderrLogs: Buffer[] = [];
|
const stderrLogs: Buffer[] = [];
|
||||||
opts = { stdio: 'inherit', cwd, ...opts };
|
opts = { stdio: 'inherit', ...opts };
|
||||||
const child = spawn(command, args, opts);
|
const child = spawn(command, args, opts);
|
||||||
|
|
||||||
if (opts.stdio === 'pipe' && child.stderr) {
|
if (opts.stdio === 'pipe' && child.stderr) {
|
||||||
@@ -54,7 +55,10 @@ export async function runShellScript(
|
|||||||
assert(path.isAbsolute(fsPath));
|
assert(path.isAbsolute(fsPath));
|
||||||
const destPath = path.dirname(fsPath);
|
const destPath = path.dirname(fsPath);
|
||||||
await chmodPlusX(fsPath);
|
await chmodPlusX(fsPath);
|
||||||
await spawnAsync(`./${path.basename(fsPath)}`, args, destPath, spawnOpts);
|
await spawnAsync(`./${path.basename(fsPath)}`, args, {
|
||||||
|
cwd: destPath,
|
||||||
|
...spawnOpts,
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,38 +132,100 @@ async function scanParentDirs(destPath: string, readPackageJson = false) {
|
|||||||
export async function runNpmInstall(
|
export async function runNpmInstall(
|
||||||
destPath: string,
|
destPath: string,
|
||||||
args: string[] = [],
|
args: string[] = [],
|
||||||
spawnOpts?: SpawnOptions
|
spawnOpts?: SpawnOptions,
|
||||||
|
meta?: Meta
|
||||||
) {
|
) {
|
||||||
|
if (meta && meta.isDev) {
|
||||||
|
debug('Skipping dependency installation because dev mode is enabled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
assert(path.isAbsolute(destPath));
|
assert(path.isAbsolute(destPath));
|
||||||
|
|
||||||
let commandArgs = args;
|
let commandArgs = args;
|
||||||
console.log(`installing to ${destPath}`);
|
debug(`Installing to ${destPath}`);
|
||||||
const { hasPackageLockJson } = await scanParentDirs(destPath);
|
const { hasPackageLockJson } = await scanParentDirs(destPath);
|
||||||
|
|
||||||
const opts = spawnOpts || { env: process.env };
|
const opts = { cwd: destPath, ...spawnOpts } || {
|
||||||
|
cwd: destPath,
|
||||||
|
env: process.env,
|
||||||
|
};
|
||||||
|
|
||||||
if (hasPackageLockJson) {
|
if (hasPackageLockJson) {
|
||||||
commandArgs = args.filter(a => a !== '--prefer-offline');
|
commandArgs = args.filter(a => a !== '--prefer-offline');
|
||||||
await spawnAsync(
|
await spawnAsync(
|
||||||
'npm',
|
'npm',
|
||||||
commandArgs.concat(['install', '--unsafe-perm']),
|
commandArgs.concat(['install', '--unsafe-perm']),
|
||||||
destPath,
|
|
||||||
opts
|
opts
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await spawnAsync(
|
await spawnAsync(
|
||||||
'yarn',
|
'yarn',
|
||||||
commandArgs.concat(['--ignore-engines', '--cwd', destPath]),
|
commandArgs.concat(['--ignore-engines', '--cwd', destPath]),
|
||||||
destPath,
|
|
||||||
opts
|
opts
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function runBundleInstall(
|
||||||
|
destPath: string,
|
||||||
|
args: string[] = [],
|
||||||
|
spawnOpts?: SpawnOptions,
|
||||||
|
meta?: Meta
|
||||||
|
) {
|
||||||
|
if (meta && meta.isDev) {
|
||||||
|
debug('Skipping dependency installation because dev mode is enabled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(path.isAbsolute(destPath));
|
||||||
|
const opts = { cwd: destPath, ...spawnOpts } || {
|
||||||
|
cwd: destPath,
|
||||||
|
env: process.env,
|
||||||
|
};
|
||||||
|
|
||||||
|
await spawnAsync(
|
||||||
|
'bundle',
|
||||||
|
args.concat([
|
||||||
|
'install',
|
||||||
|
'--no-prune',
|
||||||
|
'--retry',
|
||||||
|
'3',
|
||||||
|
'--jobs',
|
||||||
|
String(cpus().length || 1),
|
||||||
|
]),
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runPipInstall(
|
||||||
|
destPath: string,
|
||||||
|
args: string[] = [],
|
||||||
|
spawnOpts?: SpawnOptions,
|
||||||
|
meta?: Meta
|
||||||
|
) {
|
||||||
|
if (meta && meta.isDev) {
|
||||||
|
debug('Skipping dependency installation because dev mode is enabled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(path.isAbsolute(destPath));
|
||||||
|
const opts = { cwd: destPath, ...spawnOpts } || {
|
||||||
|
cwd: destPath,
|
||||||
|
env: process.env,
|
||||||
|
};
|
||||||
|
|
||||||
|
await spawnAsync(
|
||||||
|
'pip3',
|
||||||
|
['install', '--disable-pip-version-check', ...args],
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export async function runPackageJsonScript(
|
export async function runPackageJsonScript(
|
||||||
destPath: string,
|
destPath: string,
|
||||||
scriptName: string,
|
scriptName: string,
|
||||||
opts?: SpawnOptions
|
spawnOpts?: SpawnOptions
|
||||||
) {
|
) {
|
||||||
assert(path.isAbsolute(destPath));
|
assert(path.isAbsolute(destPath));
|
||||||
const { packageJson, hasPackageLockJson } = await scanParentDirs(
|
const { packageJson, hasPackageLockJson } = await scanParentDirs(
|
||||||
@@ -174,17 +240,14 @@ export async function runPackageJsonScript(
|
|||||||
);
|
);
|
||||||
if (!hasScript) return false;
|
if (!hasScript) return false;
|
||||||
|
|
||||||
|
const opts = { cwd: destPath, ...spawnOpts };
|
||||||
|
|
||||||
if (hasPackageLockJson) {
|
if (hasPackageLockJson) {
|
||||||
console.log(`running "npm run ${scriptName}"`);
|
console.log(`Running "npm run ${scriptName}"`);
|
||||||
await spawnAsync('npm', ['run', scriptName], destPath, opts);
|
await spawnAsync('npm', ['run', scriptName], opts);
|
||||||
} else {
|
} else {
|
||||||
console.log(`running "yarn run ${scriptName}"`);
|
console.log(`Running "yarn run ${scriptName}"`);
|
||||||
await spawnAsync(
|
await spawnAsync('yarn', ['--cwd', destPath, 'run', scriptName], opts);
|
||||||
'yarn',
|
|
||||||
['--cwd', destPath, 'run', scriptName],
|
|
||||||
destPath,
|
|
||||||
opts
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -2,14 +2,18 @@ import FileBlob from './file-blob';
|
|||||||
import FileFsRef from './file-fs-ref';
|
import FileFsRef from './file-fs-ref';
|
||||||
import FileRef from './file-ref';
|
import FileRef from './file-ref';
|
||||||
import { Lambda, createLambda } from './lambda';
|
import { Lambda, createLambda } from './lambda';
|
||||||
|
import { Prerender } from './prerender';
|
||||||
import download, { DownloadedFiles } from './fs/download';
|
import download, { DownloadedFiles } from './fs/download';
|
||||||
import getWriteableDirectory from './fs/get-writable-directory';
|
import getWriteableDirectory from './fs/get-writable-directory';
|
||||||
import glob from './fs/glob';
|
import glob from './fs/glob';
|
||||||
import rename from './fs/rename';
|
import rename from './fs/rename';
|
||||||
import {
|
import {
|
||||||
|
spawnAsync,
|
||||||
installDependencies,
|
installDependencies,
|
||||||
runPackageJsonScript,
|
runPackageJsonScript,
|
||||||
runNpmInstall,
|
runNpmInstall,
|
||||||
|
runBundleInstall,
|
||||||
|
runPipInstall,
|
||||||
runShellScript,
|
runShellScript,
|
||||||
getNodeVersion,
|
getNodeVersion,
|
||||||
getSpawnOptions,
|
getSpawnOptions,
|
||||||
@@ -26,14 +30,18 @@ export {
|
|||||||
FileRef,
|
FileRef,
|
||||||
Lambda,
|
Lambda,
|
||||||
createLambda,
|
createLambda,
|
||||||
|
Prerender,
|
||||||
download,
|
download,
|
||||||
DownloadedFiles,
|
DownloadedFiles,
|
||||||
getWriteableDirectory,
|
getWriteableDirectory,
|
||||||
glob,
|
glob,
|
||||||
rename,
|
rename,
|
||||||
|
spawnAsync,
|
||||||
installDependencies,
|
installDependencies,
|
||||||
runPackageJsonScript,
|
runPackageJsonScript,
|
||||||
runNpmInstall,
|
runNpmInstall,
|
||||||
|
runBundleInstall,
|
||||||
|
runPipInstall,
|
||||||
runShellScript,
|
runShellScript,
|
||||||
getNodeVersion,
|
getNodeVersion,
|
||||||
getSpawnOptions,
|
getSpawnOptions,
|
||||||
|
|||||||
42
packages/now-build-utils/src/prerender.ts
Normal file
42
packages/now-build-utils/src/prerender.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import FileBlob from './file-blob';
|
||||||
|
import FileFsRef from './file-fs-ref';
|
||||||
|
import FileRef from './file-ref';
|
||||||
|
import { Lambda } from './lambda';
|
||||||
|
|
||||||
|
interface PrerenderOptions {
|
||||||
|
expiration: number;
|
||||||
|
lambda: Lambda;
|
||||||
|
fallback: FileBlob | FileFsRef | FileRef | null;
|
||||||
|
group?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Prerender {
|
||||||
|
public type: 'Prerender';
|
||||||
|
public expiration: number;
|
||||||
|
public lambda: Lambda;
|
||||||
|
public fallback: FileBlob | FileFsRef | FileRef | null;
|
||||||
|
public group?: number;
|
||||||
|
|
||||||
|
constructor({ expiration, lambda, fallback, group }: PrerenderOptions) {
|
||||||
|
this.type = 'Prerender';
|
||||||
|
this.expiration = expiration;
|
||||||
|
this.lambda = lambda;
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof group !== 'undefined' &&
|
||||||
|
(group <= 0 || !Number.isInteger(group))
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
'The `group` argument for `Prerender` needs to be a natural number.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.group = group;
|
||||||
|
|
||||||
|
if (typeof fallback === 'undefined') {
|
||||||
|
throw new Error(
|
||||||
|
'The `fallback` argument for `Prerender` needs to be a `FileBlob`, `FileFsRef`, `FileRef`, or null.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.fallback = fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
import FileRef from './file-ref';
|
import FileRef from './file-ref';
|
||||||
import FileFsRef from './file-fs-ref';
|
import FileFsRef from './file-fs-ref';
|
||||||
|
|
||||||
|
export interface Env {
|
||||||
|
[name: string]: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export interface File {
|
export interface File {
|
||||||
type: string;
|
type: string;
|
||||||
mode: number;
|
mode: number;
|
||||||
|
contentType?: string;
|
||||||
toStream: () => NodeJS.ReadableStream;
|
toStream: () => NodeJS.ReadableStream;
|
||||||
/**
|
/**
|
||||||
* The absolute path to the file in the filesystem
|
* The absolute path to the file in the filesystem
|
||||||
@@ -52,6 +57,8 @@ export interface Meta {
|
|||||||
requestPath?: string;
|
requestPath?: string;
|
||||||
filesChanged?: string[];
|
filesChanged?: string[];
|
||||||
filesRemoved?: string[];
|
filesRemoved?: string[];
|
||||||
|
env?: Env;
|
||||||
|
buildEnv?: Env;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AnalyzeOptions {
|
export interface AnalyzeOptions {
|
||||||
@@ -184,23 +191,110 @@ export interface ShouldServeOptions {
|
|||||||
config: Config;
|
config: Config;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PackageJson {
|
/**
|
||||||
|
* Credit to Iain Reid, MIT license.
|
||||||
|
* Source: https://gist.github.com/iainreid820/5c1cc527fe6b5b7dba41fec7fe54bf6e
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
namespace PackageJson {
|
||||||
|
/**
|
||||||
|
* An author or contributor
|
||||||
|
*/
|
||||||
|
export interface Author {
|
||||||
name: string;
|
name: string;
|
||||||
version: string;
|
email?: string;
|
||||||
engines?: {
|
homepage?: string;
|
||||||
[key: string]: string;
|
}
|
||||||
node: string;
|
|
||||||
npm: string;
|
/**
|
||||||
};
|
* A map of exposed bin commands
|
||||||
scripts?: {
|
*/
|
||||||
[key: string]: string;
|
export interface BinMap {
|
||||||
};
|
[commandName: string]: string;
|
||||||
dependencies?: {
|
}
|
||||||
[key: string]: string;
|
|
||||||
};
|
/**
|
||||||
devDependencies?: {
|
* A bugs link
|
||||||
[key: string]: string;
|
*/
|
||||||
};
|
export interface Bugs {
|
||||||
|
email: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Config {
|
||||||
|
name?: string;
|
||||||
|
config?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of dependencies
|
||||||
|
*/
|
||||||
|
export interface DependencyMap {
|
||||||
|
[dependencyName: string]: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CommonJS package structure
|
||||||
|
*/
|
||||||
|
export interface Directories {
|
||||||
|
lib?: string;
|
||||||
|
bin?: string;
|
||||||
|
man?: string;
|
||||||
|
doc?: string;
|
||||||
|
example?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Engines {
|
||||||
|
node?: string;
|
||||||
|
npm?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PublishConfig {
|
||||||
|
registry?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A project repository
|
||||||
|
*/
|
||||||
|
export interface Repository {
|
||||||
|
type: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScriptsMap {
|
||||||
|
[scriptName: string]: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PackageJson {
|
||||||
|
readonly name?: string;
|
||||||
|
readonly version?: string;
|
||||||
|
readonly description?: string;
|
||||||
|
readonly keywords?: string[];
|
||||||
|
readonly homepage?: string;
|
||||||
|
readonly bugs?: string | PackageJson.Bugs;
|
||||||
|
readonly license?: string;
|
||||||
|
readonly author?: string | PackageJson.Author;
|
||||||
|
readonly contributors?: string[] | PackageJson.Author[];
|
||||||
|
readonly files?: string[];
|
||||||
|
readonly main?: string;
|
||||||
|
readonly bin?: string | PackageJson.BinMap;
|
||||||
|
readonly man?: string | string[];
|
||||||
|
readonly directories?: PackageJson.Directories;
|
||||||
|
readonly repository?: string | PackageJson.Repository;
|
||||||
|
readonly scripts?: PackageJson.ScriptsMap;
|
||||||
|
readonly config?: PackageJson.Config;
|
||||||
|
readonly dependencies?: PackageJson.DependencyMap;
|
||||||
|
readonly devDependencies?: PackageJson.DependencyMap;
|
||||||
|
readonly peerDependencies?: PackageJson.DependencyMap;
|
||||||
|
readonly optionalDependencies?: PackageJson.DependencyMap;
|
||||||
|
readonly bundledDependencies?: string[];
|
||||||
|
readonly engines?: PackageJson.Engines;
|
||||||
|
readonly os?: string[];
|
||||||
|
readonly cpu?: string[];
|
||||||
|
readonly preferGlobal?: boolean;
|
||||||
|
readonly private?: boolean;
|
||||||
|
readonly publishConfig?: PackageJson.PublishConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeVersion {
|
export interface NodeVersion {
|
||||||
|
|||||||
209
packages/now-build-utils/test/integration.test.js
vendored
Normal file
209
packages/now-build-utils/test/integration.test.js
vendored
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const {
|
||||||
|
packAndDeploy,
|
||||||
|
testDeployment,
|
||||||
|
} = require('../../../test/lib/deployment/test-deployment');
|
||||||
|
const { glob, detectBuilders, detectRoutes } = require('../');
|
||||||
|
|
||||||
|
jest.setTimeout(4 * 60 * 1000);
|
||||||
|
|
||||||
|
const builderUrl = '@canary';
|
||||||
|
let buildUtilsUrl;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const buildUtilsPath = path.resolve(__dirname, '..');
|
||||||
|
buildUtilsUrl = await packAndDeploy(buildUtilsPath);
|
||||||
|
console.log('buildUtilsUrl', buildUtilsUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
const fixturesPath = path.resolve(__dirname, 'fixtures');
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const fixture of fs.readdirSync(fixturesPath)) {
|
||||||
|
if (fixture.includes('zero-config')) {
|
||||||
|
// Those have separate tests
|
||||||
|
continue; // eslint-disable-line no-continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-loop-func
|
||||||
|
it(`should build ${fixture}`, async () => {
|
||||||
|
await expect(
|
||||||
|
testDeployment(
|
||||||
|
{ builderUrl, buildUtilsUrl },
|
||||||
|
path.join(fixturesPath, fixture)
|
||||||
|
)
|
||||||
|
).resolves.toBeDefined();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// few foreign tests
|
||||||
|
|
||||||
|
const buildersToTestWith = ['now-next', 'now-node', 'now-static-build'];
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const builder of buildersToTestWith) {
|
||||||
|
const fixturesPath2 = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
`../../${builder}/test/fixtures`
|
||||||
|
);
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const fixture of fs.readdirSync(fixturesPath2)) {
|
||||||
|
// don't run all foreign fixtures, just some
|
||||||
|
if (['01-cowsay', '01-cache-headers', '03-env-vars'].includes(fixture)) {
|
||||||
|
// eslint-disable-next-line no-loop-func
|
||||||
|
it(`should build ${builder}/${fixture}`, async () => {
|
||||||
|
await expect(
|
||||||
|
testDeployment(
|
||||||
|
{ builderUrl, buildUtilsUrl },
|
||||||
|
path.join(fixturesPath2, fixture)
|
||||||
|
)
|
||||||
|
).resolves.toBeDefined();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it('Test `detectBuilders` and `detectRoutes`', async () => {
|
||||||
|
const fixture = path.join(__dirname, 'fixtures', '01-zero-config-api');
|
||||||
|
const pkg = await fs.readJSON(path.join(fixture, 'package.json'));
|
||||||
|
const fileList = await glob('**', fixture);
|
||||||
|
const files = Object.keys(fileList);
|
||||||
|
|
||||||
|
const probes = [
|
||||||
|
{
|
||||||
|
path: '/api/my-endpoint',
|
||||||
|
mustContain: 'my-endpoint',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/other-endpoint',
|
||||||
|
mustContain: 'other-endpoint',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/team/zeit',
|
||||||
|
mustContain: 'team/zeit',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/user/myself',
|
||||||
|
mustContain: 'user/myself',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/not-okay/',
|
||||||
|
status: 404,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api',
|
||||||
|
status: 404,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/',
|
||||||
|
status: 404,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
mustContain: 'hello from index.txt',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files, pkg);
|
||||||
|
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||||
|
|
||||||
|
const nowConfig = { builds: builders, routes: defaultRoutes, probes };
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(fixture, 'now.json'),
|
||||||
|
JSON.stringify(nowConfig, null, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
const deployment = await testDeployment(
|
||||||
|
{ builderUrl, buildUtilsUrl },
|
||||||
|
fixture
|
||||||
|
);
|
||||||
|
expect(deployment).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Test `detectBuilders` and `detectRoutes` with `index` files', async () => {
|
||||||
|
const fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
|
||||||
|
const pkg = await fs.readJSON(path.join(fixture, 'package.json'));
|
||||||
|
const fileList = await glob('**', fixture);
|
||||||
|
const files = Object.keys(fileList);
|
||||||
|
|
||||||
|
const probes = [
|
||||||
|
{
|
||||||
|
path: '/api/not-okay',
|
||||||
|
status: 404,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api',
|
||||||
|
mustContain: 'hello from api/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/',
|
||||||
|
mustContain: 'hello from api/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/index',
|
||||||
|
mustContain: 'hello from api/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/index.js',
|
||||||
|
mustContain: 'hello from api/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/date.js',
|
||||||
|
mustContain: 'hello from api/date.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Someone might expect this to be `date.js`,
|
||||||
|
// but I doubt that there is any case were both
|
||||||
|
// `date/index.js` and `date.js` exists,
|
||||||
|
// so it is not special cased
|
||||||
|
path: '/api/date',
|
||||||
|
mustContain: 'hello from api/date/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/date/',
|
||||||
|
mustContain: 'hello from api/date/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/date/index',
|
||||||
|
mustContain: 'hello from api/date/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/api/date/index.js',
|
||||||
|
mustContain: 'hello from api/date/index.js',
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
mustContain: 'hello from index.txt',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files, pkg);
|
||||||
|
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||||
|
|
||||||
|
const nowConfig = { builds: builders, routes: defaultRoutes, probes };
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(fixture, 'now.json'),
|
||||||
|
JSON.stringify(nowConfig, null, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
const deployment = await testDeployment(
|
||||||
|
{ builderUrl, buildUtilsUrl },
|
||||||
|
fixture
|
||||||
|
);
|
||||||
|
expect(deployment).toBeDefined();
|
||||||
|
});
|
||||||
@@ -1,34 +1,13 @@
|
|||||||
/* global beforeAll, expect, it, jest */
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
||||||
const execa = require('execa');
|
const execa = require('execa');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const { createZip } = require('../dist/lambda');
|
const { createZip } = require('../dist/lambda');
|
||||||
const {
|
const { glob, download, detectBuilders, detectRoutes } = require('../');
|
||||||
glob, download, detectBuilders, detectRoutes,
|
|
||||||
} = require('../');
|
|
||||||
const {
|
const {
|
||||||
getSupportedNodeVersion,
|
getSupportedNodeVersion,
|
||||||
defaultSelection,
|
defaultSelection,
|
||||||
} = require('../dist/fs/node-version');
|
} = require('../dist/fs/node-version');
|
||||||
const {
|
|
||||||
packAndDeploy,
|
|
||||||
testDeployment,
|
|
||||||
} = require('../../../test/lib/deployment/test-deployment');
|
|
||||||
|
|
||||||
jest.setTimeout(4 * 60 * 1000);
|
|
||||||
|
|
||||||
const builderUrl = '@canary';
|
|
||||||
let buildUtilsUrl;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
const buildUtilsPath = path.resolve(__dirname, '..');
|
|
||||||
buildUtilsUrl = await packAndDeploy(buildUtilsPath);
|
|
||||||
console.log('buildUtilsUrl', buildUtilsUrl);
|
|
||||||
});
|
|
||||||
|
|
||||||
// unit tests
|
|
||||||
|
|
||||||
it('should re-create symlinks properly', async () => {
|
it('should re-create symlinks properly', async () => {
|
||||||
const files = await glob('**', path.join(__dirname, 'symlinks'));
|
const files = await glob('**', path.join(__dirname, 'symlinks'));
|
||||||
@@ -86,33 +65,33 @@ it('should match all semver ranges', () => {
|
|||||||
// See https://docs.npmjs.com/files/package.json#engines
|
// See https://docs.npmjs.com/files/package.json#engines
|
||||||
expect(getSupportedNodeVersion('10.0.0')).resolves.toHaveProperty(
|
expect(getSupportedNodeVersion('10.0.0')).resolves.toHaveProperty(
|
||||||
'major',
|
'major',
|
||||||
10,
|
10
|
||||||
);
|
);
|
||||||
expect(getSupportedNodeVersion('10.x')).resolves.toHaveProperty('major', 10);
|
expect(getSupportedNodeVersion('10.x')).resolves.toHaveProperty('major', 10);
|
||||||
expect(getSupportedNodeVersion('>=10')).resolves.toHaveProperty('major', 10);
|
expect(getSupportedNodeVersion('>=10')).resolves.toHaveProperty('major', 10);
|
||||||
expect(getSupportedNodeVersion('>=10.3.0')).resolves.toHaveProperty(
|
expect(getSupportedNodeVersion('>=10.3.0')).resolves.toHaveProperty(
|
||||||
'major',
|
'major',
|
||||||
10,
|
10
|
||||||
);
|
);
|
||||||
expect(getSupportedNodeVersion('8.5.0 - 10.5.0')).resolves.toHaveProperty(
|
expect(getSupportedNodeVersion('8.5.0 - 10.5.0')).resolves.toHaveProperty(
|
||||||
'major',
|
'major',
|
||||||
10,
|
10
|
||||||
);
|
);
|
||||||
expect(getSupportedNodeVersion('>=9.0.0')).resolves.toHaveProperty(
|
expect(getSupportedNodeVersion('>=9.0.0')).resolves.toHaveProperty(
|
||||||
'major',
|
'major',
|
||||||
10,
|
10
|
||||||
);
|
);
|
||||||
expect(getSupportedNodeVersion('>=9.5.0 <=10.5.0')).resolves.toHaveProperty(
|
expect(getSupportedNodeVersion('>=9.5.0 <=10.5.0')).resolves.toHaveProperty(
|
||||||
'major',
|
'major',
|
||||||
10,
|
10
|
||||||
);
|
);
|
||||||
expect(getSupportedNodeVersion('~10.5.0')).resolves.toHaveProperty(
|
expect(getSupportedNodeVersion('~10.5.0')).resolves.toHaveProperty(
|
||||||
'major',
|
'major',
|
||||||
10,
|
10
|
||||||
);
|
);
|
||||||
expect(getSupportedNodeVersion('^10.5.0')).resolves.toHaveProperty(
|
expect(getSupportedNodeVersion('^10.5.0')).resolves.toHaveProperty(
|
||||||
'major',
|
'major',
|
||||||
10,
|
10
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -146,56 +125,6 @@ it('should support require by path for legacy builders', () => {
|
|||||||
expect(Lambda2).toBe(index.Lambda);
|
expect(Lambda2).toBe(index.Lambda);
|
||||||
});
|
});
|
||||||
|
|
||||||
// own fixtures
|
|
||||||
|
|
||||||
const fixturesPath = path.resolve(__dirname, 'fixtures');
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
|
||||||
for (const fixture of fs.readdirSync(fixturesPath)) {
|
|
||||||
if (fixture.includes('zero-config')) {
|
|
||||||
// Those have separate tests
|
|
||||||
continue; // eslint-disable-line no-continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-loop-func
|
|
||||||
it(`should build ${fixture}`, async () => {
|
|
||||||
await expect(
|
|
||||||
testDeployment(
|
|
||||||
{ builderUrl, buildUtilsUrl },
|
|
||||||
path.join(fixturesPath, fixture),
|
|
||||||
),
|
|
||||||
).resolves.toBeDefined();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// few foreign tests
|
|
||||||
|
|
||||||
const buildersToTestWith = ['now-next', 'now-node', 'now-static-build'];
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
|
||||||
for (const builder of buildersToTestWith) {
|
|
||||||
const fixturesPath2 = path.resolve(
|
|
||||||
__dirname,
|
|
||||||
`../../${builder}/test/fixtures`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
|
||||||
for (const fixture of fs.readdirSync(fixturesPath2)) {
|
|
||||||
// don't run all foreign fixtures, just some
|
|
||||||
if (['01-cowsay', '01-cache-headers', '03-env-vars'].includes(fixture)) {
|
|
||||||
// eslint-disable-next-line no-loop-func
|
|
||||||
it(`should build ${builder}/${fixture}`, async () => {
|
|
||||||
await expect(
|
|
||||||
testDeployment(
|
|
||||||
{ builderUrl, buildUtilsUrl },
|
|
||||||
path.join(fixturesPath2, fixture),
|
|
||||||
),
|
|
||||||
).resolves.toBeDefined();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
it('Test `detectBuilders`', async () => {
|
it('Test `detectBuilders`', async () => {
|
||||||
{
|
{
|
||||||
// package.json + no build
|
// package.json + no build
|
||||||
@@ -262,7 +191,7 @@ it('Test `detectBuilders`', async () => {
|
|||||||
expect(builders[0].use).toBe('@now/node');
|
expect(builders[0].use).toBe('@now/node');
|
||||||
expect(builders[0].src).toBe('api/users.js');
|
expect(builders[0].src).toBe('api/users.js');
|
||||||
expect(builders[1].use).toBe('@now/static');
|
expect(builders[1].use).toBe('@now/static');
|
||||||
expect(builders[1].src).toBe('index.html');
|
expect(builders[1].src).toBe('!{api/**,package.json}');
|
||||||
expect(builders.length).toBe(2);
|
expect(builders.length).toBe(2);
|
||||||
expect(errors).toBe(null);
|
expect(errors).toBe(null);
|
||||||
}
|
}
|
||||||
@@ -274,10 +203,8 @@ it('Test `detectBuilders`', async () => {
|
|||||||
expect(builders[0].use).toBe('@now/node');
|
expect(builders[0].use).toBe('@now/node');
|
||||||
expect(builders[0].src).toBe('api/[endpoint].js');
|
expect(builders[0].src).toBe('api/[endpoint].js');
|
||||||
expect(builders[1].use).toBe('@now/static');
|
expect(builders[1].use).toBe('@now/static');
|
||||||
expect(builders[1].src).toBe('index.html');
|
expect(builders[1].src).toBe('!{api/**,package.json}');
|
||||||
expect(builders[2].use).toBe('@now/static');
|
expect(builders.length).toBe(2);
|
||||||
expect(builders[2].src).toBe('static/image.png');
|
|
||||||
expect(builders.length).toBe(3);
|
|
||||||
expect(errors).toBe(null);
|
expect(errors).toBe(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,10 +262,8 @@ it('Test `detectBuilders`', async () => {
|
|||||||
expect(builders[0].use).toBe('@now/node');
|
expect(builders[0].use).toBe('@now/node');
|
||||||
expect(builders[0].src).toBe('api/endpoint.js');
|
expect(builders[0].src).toBe('api/endpoint.js');
|
||||||
expect(builders[1].use).toBe('@now/static');
|
expect(builders[1].use).toBe('@now/static');
|
||||||
expect(builders[1].src).toBe('favicon.ico');
|
expect(builders[1].src).toBe('!{api/**,package.json}');
|
||||||
expect(builders[2].use).toBe('@now/static');
|
expect(builders.length).toBe(2);
|
||||||
expect(builders[2].src).toBe('index.html');
|
|
||||||
expect(builders.length).toBe(3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -489,6 +414,36 @@ it('Test `detectBuilders`', async () => {
|
|||||||
expect(builders[2].use).toBe('@now/next@haha');
|
expect(builders[2].use).toBe('@now/next@haha');
|
||||||
expect(builders.length).toBe(3);
|
expect(builders.length).toBe(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// next.js pages/api + api
|
||||||
|
const pkg = {
|
||||||
|
scripts: { build: 'next build' },
|
||||||
|
dependencies: { next: '9.0.0' },
|
||||||
|
};
|
||||||
|
const files = ['api/user.js', 'pages/api/user.js'];
|
||||||
|
|
||||||
|
const { warnings, errors, builders } = await detectBuilders(files, pkg);
|
||||||
|
expect(errors).toBe(null);
|
||||||
|
expect(warnings[0].code).toBe('conflicting_files');
|
||||||
|
expect(builders).toBeDefined();
|
||||||
|
expect(builders.length).toBe(2);
|
||||||
|
expect(builders[0].use).toBe('@now/node');
|
||||||
|
expect(builders[1].use).toBe('@now/next');
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// many static files + one api file
|
||||||
|
const files = Array.from({ length: 5000 }).map((_, i) => `file${i}.html`);
|
||||||
|
files.push('api/index.ts');
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
|
||||||
|
expect(builders.length).toBe(2);
|
||||||
|
expect(builders[0].use).toBe('@now/node');
|
||||||
|
expect(builders[0].src).toBe('api/index.ts');
|
||||||
|
expect(builders[1].use).toBe('@now/static');
|
||||||
|
expect(builders[1].src).toBe('!{api/**,package.json}');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Test `detectRoutes`', async () => {
|
it('Test `detectRoutes`', async () => {
|
||||||
@@ -592,7 +547,7 @@ it('Test `detectRoutes`', async () => {
|
|||||||
|
|
||||||
expect(defaultRoutes.length).toBe(3);
|
expect(defaultRoutes.length).toBe(3);
|
||||||
expect(defaultRoutes[0].src).toBe(
|
expect(defaultRoutes[0].src).toBe(
|
||||||
'^/api/date(\\/|\\/index|\\/index\\.js)?$',
|
'^/api/date(\\/|\\/index|\\/index\\.js)?$'
|
||||||
);
|
);
|
||||||
expect(defaultRoutes[0].dest).toBe('/api/date/index.js');
|
expect(defaultRoutes[0].dest).toBe('/api/date/index.js');
|
||||||
expect(defaultRoutes[1].src).toBe('^/api/(date|date\\.js)$');
|
expect(defaultRoutes[1].src).toBe('^/api/(date|date\\.js)$');
|
||||||
@@ -607,7 +562,7 @@ it('Test `detectRoutes`', async () => {
|
|||||||
|
|
||||||
expect(defaultRoutes.length).toBe(3);
|
expect(defaultRoutes.length).toBe(3);
|
||||||
expect(defaultRoutes[0].src).toBe(
|
expect(defaultRoutes[0].src).toBe(
|
||||||
'^/api/([^\\/]+)(\\/|\\/index|\\/index\\.js)?$',
|
'^/api/([^\\/]+)(\\/|\\/index|\\/index\\.js)?$'
|
||||||
);
|
);
|
||||||
expect(defaultRoutes[0].dest).toBe('/api/[date]/index.js?date=$1');
|
expect(defaultRoutes[0].dest).toBe('/api/[date]/index.js?date=$1');
|
||||||
expect(defaultRoutes[1].src).toBe('^/api/(date|date\\.js)$');
|
expect(defaultRoutes[1].src).toBe('^/api/(date|date\\.js)$');
|
||||||
@@ -634,146 +589,3 @@ it('Test `detectRoutes`', async () => {
|
|||||||
expect(defaultRoutes.length).toBe(5);
|
expect(defaultRoutes.length).toBe(5);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Test `detectBuilders` and `detectRoutes`', async () => {
|
|
||||||
const fixture = path.join(__dirname, 'fixtures', '01-zero-config-api');
|
|
||||||
const pkg = await fs.readJSON(path.join(fixture, 'package.json'));
|
|
||||||
const fileList = await glob('**', fixture);
|
|
||||||
const files = Object.keys(fileList);
|
|
||||||
|
|
||||||
const probes = [
|
|
||||||
{
|
|
||||||
path: '/api/my-endpoint',
|
|
||||||
mustContain: 'my-endpoint',
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api/other-endpoint',
|
|
||||||
mustContain: 'other-endpoint',
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api/team/zeit',
|
|
||||||
mustContain: 'team/zeit',
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api/user/myself',
|
|
||||||
mustContain: 'user/myself',
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api/not-okay/',
|
|
||||||
status: 404,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api',
|
|
||||||
status: 404,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api/',
|
|
||||||
status: 404,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
mustContain: 'hello from index.txt',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files, pkg);
|
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders);
|
|
||||||
|
|
||||||
const nowConfig = { builds: builders, routes: defaultRoutes, probes };
|
|
||||||
await fs.writeFile(
|
|
||||||
path.join(fixture, 'now.json'),
|
|
||||||
JSON.stringify(nowConfig, null, 2),
|
|
||||||
);
|
|
||||||
|
|
||||||
const deployment = await testDeployment(
|
|
||||||
{ builderUrl, buildUtilsUrl },
|
|
||||||
fixture,
|
|
||||||
);
|
|
||||||
expect(deployment).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Test `detectBuilders` and `detectRoutes` with `index` files', async () => {
|
|
||||||
const fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
|
|
||||||
const pkg = await fs.readJSON(path.join(fixture, 'package.json'));
|
|
||||||
const fileList = await glob('**', fixture);
|
|
||||||
const files = Object.keys(fileList);
|
|
||||||
|
|
||||||
const probes = [
|
|
||||||
{
|
|
||||||
path: '/api/not-okay',
|
|
||||||
status: 404,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api',
|
|
||||||
mustContain: 'hello from api/index.js',
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api/',
|
|
||||||
mustContain: 'hello from api/index.js',
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api/index',
|
|
||||||
mustContain: 'hello from api/index.js',
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api/index.js',
|
|
||||||
mustContain: 'hello from api/index.js',
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api/date.js',
|
|
||||||
mustContain: 'hello from api/date.js',
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Someone might expect this to be `date.js`,
|
|
||||||
// but I doubt that there is any case were both
|
|
||||||
// `date/index.js` and `date.js` exists,
|
|
||||||
// so it is not special cased
|
|
||||||
path: '/api/date',
|
|
||||||
mustContain: 'hello from api/date/index.js',
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api/date/',
|
|
||||||
mustContain: 'hello from api/date/index.js',
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api/date/index',
|
|
||||||
mustContain: 'hello from api/date/index.js',
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/api/date/index.js',
|
|
||||||
mustContain: 'hello from api/date/index.js',
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
mustContain: 'hello from index.txt',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files, pkg);
|
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders);
|
|
||||||
|
|
||||||
const nowConfig = { builds: builders, routes: defaultRoutes, probes };
|
|
||||||
await fs.writeFile(
|
|
||||||
path.join(fixture, 'now.json'),
|
|
||||||
JSON.stringify(nowConfig, null, 2),
|
|
||||||
);
|
|
||||||
|
|
||||||
const deployment = await testDeployment(
|
|
||||||
{ builderUrl, buildUtilsUrl },
|
|
||||||
fixture,
|
|
||||||
);
|
|
||||||
expect(deployment).toBeDefined();
|
|
||||||
});
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
indent_style = tab
|
|
||||||
indent_size = 4
|
|
||||||
tab_width = 4
|
|
||||||
end_of_line = lf
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
|
|
||||||
[{*.json,*.json.example,*.gyp,*.yml}]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[*.py]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
|
|
||||||
# Ideal settings - some plugins might support these.
|
|
||||||
[*.js]
|
|
||||||
quote_type = single
|
|
||||||
|
|
||||||
[{*.c,*.cc,*.h,*.hh,*.cpp,*.hpp,*.m,*.mm,*.mpp,*.js,*.java,*.go,*.rs,*.php,*.ng,*.jsx,*.ts,*.d,*.cs,*.swift}]
|
|
||||||
curly_bracket_next_line = false
|
|
||||||
spaces_around_operators = true
|
|
||||||
spaces_around_brackets = outside
|
|
||||||
# close enough to 1TB
|
|
||||||
indent_brace_style = K&R
|
|
||||||
1
packages/now-cgi/.gitignore
vendored
1
packages/now-cgi/.gitignore
vendored
@@ -1,2 +1 @@
|
|||||||
node_modules
|
|
||||||
handler
|
handler
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { mkdirp, copyFile } = require('fs-extra');
|
const { mkdirp, copyFile } = require('fs-extra');
|
||||||
|
|
||||||
const glob = require('@now/build-utils/fs/glob'); // eslint-disable-line import/no-extraneous-dependencies
|
const glob = require('@now/build-utils/fs/glob');
|
||||||
const download = require('@now/build-utils/fs/download'); // eslint-disable-line import/no-extraneous-dependencies
|
const download = require('@now/build-utils/fs/download');
|
||||||
const { createLambda } = require('@now/build-utils/lambda'); // eslint-disable-line import/no-extraneous-dependencies
|
const { createLambda } = require('@now/build-utils/lambda');
|
||||||
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory'); // eslint-disable-line import/no-extraneous-dependencies
|
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory');
|
||||||
const { shouldServe } = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
|
const { shouldServe } = require('@now/build-utils');
|
||||||
|
|
||||||
exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;
|
exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;
|
||||||
|
|
||||||
exports.build = async ({
|
exports.build = async ({ workPath, files, entrypoint, meta }) => {
|
||||||
workPath, files, entrypoint, meta,
|
|
||||||
}) => {
|
|
||||||
console.log('downloading files...');
|
console.log('downloading files...');
|
||||||
const outDir = await getWritableDirectory();
|
const outDir = await getWritableDirectory();
|
||||||
|
|
||||||
@@ -26,7 +24,7 @@ exports.build = async ({
|
|||||||
// For now only the entrypoint file is copied into the lambda
|
// For now only the entrypoint file is copied into the lambda
|
||||||
await copyFile(
|
await copyFile(
|
||||||
path.join(workPath, entrypoint),
|
path.join(workPath, entrypoint),
|
||||||
path.join(outDir, entrypoint),
|
path.join(outDir, entrypoint)
|
||||||
);
|
);
|
||||||
|
|
||||||
const lambda = await createLambda({
|
const lambda = await createLambda({
|
||||||
@@ -34,12 +32,12 @@ exports.build = async ({
|
|||||||
handler: 'handler',
|
handler: 'handler',
|
||||||
runtime: 'go1.x',
|
runtime: 'go1.x',
|
||||||
environment: {
|
environment: {
|
||||||
SCRIPT_FILENAME: entrypoint,
|
SCRIPT_FILENAME: entrypoint
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[entrypoint]: lambda,
|
[entrypoint]: lambda
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@now/cgi",
|
"name": "@now/cgi",
|
||||||
"version": "0.1.5",
|
"version": "0.1.6",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
@types
|
|
||||||
download
|
|
||||||
dist
|
|
||||||
test/fixtures
|
|
||||||
test/dev/fixtures
|
|
||||||
bin
|
|
||||||
link
|
|
||||||
src/util/dev/templates/*.ts
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
'extends': [
|
|
||||||
'airbnb',
|
|
||||||
'prettier'
|
|
||||||
],
|
|
||||||
'parser': '@typescript-eslint/parser',
|
|
||||||
'parserOptions': {
|
|
||||||
'ecmaVersion': 2018,
|
|
||||||
'sourceType': 'module',
|
|
||||||
'modules': true
|
|
||||||
},
|
|
||||||
'plugins': [
|
|
||||||
'@typescript-eslint'
|
|
||||||
],
|
|
||||||
'settings': {
|
|
||||||
'import/resolver': {
|
|
||||||
'typescript': {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'rules': {
|
|
||||||
'quotes': [
|
|
||||||
2,
|
|
||||||
'single',
|
|
||||||
{
|
|
||||||
'allowTemplateLiterals': true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'class-methods-use-this': 0,
|
|
||||||
'consistent-return': 0,
|
|
||||||
'func-names': 0,
|
|
||||||
'global-require': 0,
|
|
||||||
'guard-for-in': 0,
|
|
||||||
'import/no-duplicates': 0,
|
|
||||||
'import/no-dynamic-require': 0,
|
|
||||||
'import/no-extraneous-dependencies': 0,
|
|
||||||
'import/prefer-default-export': 0,
|
|
||||||
'lines-between-class-members': 0,
|
|
||||||
'no-await-in-loop': 0,
|
|
||||||
'no-bitwise': 0,
|
|
||||||
'no-console': 0,
|
|
||||||
'no-continue': 0,
|
|
||||||
'no-control-regex': 0,
|
|
||||||
'no-empty': 0,
|
|
||||||
'no-loop-func': 0,
|
|
||||||
'no-nested-ternary': 0,
|
|
||||||
'no-param-reassign': 0,
|
|
||||||
'no-plusplus': 0,
|
|
||||||
'no-restricted-globals': 0,
|
|
||||||
'no-restricted-syntax': 0,
|
|
||||||
'no-shadow': 0,
|
|
||||||
'no-underscore-dangle': 0,
|
|
||||||
'no-use-before-define': 0,
|
|
||||||
'prefer-const': 0,
|
|
||||||
'prefer-destructuring': 0,
|
|
||||||
'camelcase': 0,
|
|
||||||
'no-unused-vars': 0, // in favor of '@typescript-eslint/no-unused-vars'
|
|
||||||
// 'indent': 0 // in favor of '@typescript-eslint/indent'
|
|
||||||
'@typescript-eslint/no-unused-vars': 'warn',
|
|
||||||
// '@typescript-eslint/indent': ['error', 2] // this might conflict with a lot ongoing changes
|
|
||||||
'@typescript-eslint/no-array-constructor': 'error',
|
|
||||||
'@typescript-eslint/adjacent-overload-signatures': 'error',
|
|
||||||
'@typescript-eslint/class-name-casing': 'error',
|
|
||||||
'@typescript-eslint/interface-name-prefix': 'error',
|
|
||||||
'@typescript-eslint/no-empty-interface': 'error',
|
|
||||||
'@typescript-eslint/no-inferrable-types': 'error',
|
|
||||||
'@typescript-eslint/no-misused-new': 'error',
|
|
||||||
'@typescript-eslint/no-namespace': 'error',
|
|
||||||
'@typescript-eslint/no-non-null-assertion': 'error',
|
|
||||||
'@typescript-eslint/no-parameter-properties': 'error',
|
|
||||||
'@typescript-eslint/no-triple-slash-reference': 'error',
|
|
||||||
'@typescript-eslint/prefer-namespace-keyword': 'error',
|
|
||||||
'@typescript-eslint/type-annotation-spacing': 'error',
|
|
||||||
// '@typescript-eslint/array-type': 'error',
|
|
||||||
// '@typescript-eslint/ban-types': 'error',
|
|
||||||
// '@typescript-eslint/explicit-function-return-type': 'warn',
|
|
||||||
// '@typescript-eslint/explicit-member-accessibility': 'error',
|
|
||||||
// '@typescript-eslint/member-delimiter-style': 'error',
|
|
||||||
// '@typescript-eslint/no-angle-bracket-type-assertion': 'error',
|
|
||||||
// '@typescript-eslint/no-explicit-any': 'warn',
|
|
||||||
// '@typescript-eslint/no-object-literal-type-assertion': 'error',
|
|
||||||
// '@typescript-eslint/no-use-before-define': 'error',
|
|
||||||
// '@typescript-eslint/no-var-requires': 'error',
|
|
||||||
// '@typescript-eslint/prefer-interface': 'error'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
declare module 'cache-or-tmp-directory' {
|
|
||||||
export default function (appName: string) : string | null
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
declare module 'pcre-to-regexp' {
|
|
||||||
export default function (pattern: string, keys?: string[]): RegExp
|
|
||||||
}
|
|
||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
[](https://spectrum.chat/zeit)
|
[](https://spectrum.chat/zeit)
|
||||||
|
|
||||||
## Usage
|
## Usages
|
||||||
|
|
||||||
To install the latest version of Now CLI, visit [zeit.co/download](https://zeit.co/download) or run this command:
|
To install the latest version of Now CLI, visit [zeit.co/download](https://zeit.co/download) or run this command:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
npm i -g now
|
npm i -g now
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "now",
|
"name": "now",
|
||||||
"version": "16.1.3",
|
"version": "16.4.1",
|
||||||
"preferGlobal": true,
|
"preferGlobal": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"description": "The command-line interface for Now",
|
"description": "The command-line interface for Now",
|
||||||
@@ -11,17 +11,15 @@
|
|||||||
"directory": "packages/now-cli"
|
"directory": "packages/now-cli"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "yarn test-lint",
|
|
||||||
"preinstall": "node ./scripts/preinstall.js",
|
"preinstall": "node ./scripts/preinstall.js",
|
||||||
"test-unit": "nyc ava test/*unit.js --serial --fail-fast --verbose",
|
"test-unit": "nyc ava test/*unit.js --serial --fail-fast --verbose",
|
||||||
"test-integration": "ava test/integration.js --serial --fail-fast",
|
"test-integration": "ava test/integration.js --serial --fail-fast",
|
||||||
"test-integration-now-dev": "ava test/dev/integration.js --serial --fail-fast --verbose",
|
"test-integration-now-dev": "ava test/dev/integration.js --serial --fail-fast --verbose",
|
||||||
"test-lint": "eslint . --ext .js,.ts",
|
|
||||||
"prepublishOnly": "yarn build",
|
"prepublishOnly": "yarn build",
|
||||||
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
||||||
"build": "ts-node ./scripts/build.ts",
|
"build": "ts-node ./scripts/build.ts",
|
||||||
"build-dev": "ts-node ./scripts/build.ts --dev",
|
"build-dev": "ts-node ./scripts/build.ts --dev",
|
||||||
"format-modified": "prettier --parser typescript --write --single-quote `git diff --name-only HEAD * | grep -e \".*\\.ts$\" -e \".*\\.js$\" | xargs echo`"
|
"test-lint": "eslint . --ext .ts,.js --ignore-path ../../.eslintignore"
|
||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
"include": [
|
"include": [
|
||||||
@@ -42,12 +40,6 @@
|
|||||||
"instrument": true,
|
"instrument": true,
|
||||||
"all": true
|
"all": true
|
||||||
},
|
},
|
||||||
"git": {
|
|
||||||
"pre-commit": [
|
|
||||||
"test-lint",
|
|
||||||
"format-modified"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"now": "./dist/index.js"
|
"now": "./dist/index.js"
|
||||||
},
|
},
|
||||||
@@ -66,16 +58,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8.11"
|
"node": ">= 8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@now/build-utils": "0.9.14",
|
|
||||||
"@now/go": "latest",
|
|
||||||
"@now/next": "latest",
|
|
||||||
"@now/node": "latest",
|
|
||||||
"@now/php": "latest",
|
|
||||||
"@now/routing-utils": "1.2.3",
|
|
||||||
"@now/static-build": "latest",
|
|
||||||
"@sentry/node": "5.5.0",
|
"@sentry/node": "5.5.0",
|
||||||
"@types/ansi-escapes": "3.0.0",
|
"@types/ansi-escapes": "3.0.0",
|
||||||
"@types/ansi-regex": "4.0.0",
|
"@types/ansi-regex": "4.0.0",
|
||||||
@@ -106,11 +91,8 @@
|
|||||||
"@types/universal-analytics": "0.4.2",
|
"@types/universal-analytics": "0.4.2",
|
||||||
"@types/which": "1.3.1",
|
"@types/which": "1.3.1",
|
||||||
"@types/write-json-file": "2.2.1",
|
"@types/write-json-file": "2.2.1",
|
||||||
"@typescript-eslint/eslint-plugin": "1.6.0",
|
|
||||||
"@typescript-eslint/parser": "1.1.0",
|
|
||||||
"@zeit/dockerignore": "0.0.5",
|
"@zeit/dockerignore": "0.0.5",
|
||||||
"@zeit/fun": "0.9.1",
|
"@zeit/fun": "0.10.2",
|
||||||
"@zeit/git-hooks": "0.1.4",
|
|
||||||
"@zeit/ncc": "0.18.5",
|
"@zeit/ncc": "0.18.5",
|
||||||
"@zeit/source-map-support": "0.6.2",
|
"@zeit/source-map-support": "0.6.2",
|
||||||
"ajv": "6.10.2",
|
"ajv": "6.10.2",
|
||||||
@@ -123,7 +105,6 @@
|
|||||||
"async-sema": "2.1.4",
|
"async-sema": "2.1.4",
|
||||||
"ava": "2.2.0",
|
"ava": "2.2.0",
|
||||||
"bytes": "3.0.0",
|
"bytes": "3.0.0",
|
||||||
"cache-or-tmp-directory": "1.0.0",
|
|
||||||
"chalk": "2.4.2",
|
"chalk": "2.4.2",
|
||||||
"chokidar": "2.1.6",
|
"chokidar": "2.1.6",
|
||||||
"clipboardy": "2.1.0",
|
"clipboardy": "2.1.0",
|
||||||
@@ -142,19 +123,12 @@
|
|||||||
"email-validator": "1.1.1",
|
"email-validator": "1.1.1",
|
||||||
"epipebomb": "1.0.0",
|
"epipebomb": "1.0.0",
|
||||||
"escape-html": "1.0.3",
|
"escape-html": "1.0.3",
|
||||||
"eslint": "5.16.0",
|
|
||||||
"eslint-config-airbnb": "17.1.0",
|
|
||||||
"eslint-config-prettier": "4.1.0",
|
|
||||||
"eslint-import-resolver-typescript": "1.1.1",
|
|
||||||
"eslint-plugin-import": "2.16.0",
|
|
||||||
"eslint-plugin-jsx-a11y": "6.2.1",
|
|
||||||
"eslint-plugin-react": "7.12.4",
|
|
||||||
"esm": "3.1.4",
|
"esm": "3.1.4",
|
||||||
"execa": "1.0.0",
|
"execa": "1.0.0",
|
||||||
"fetch-h2": "2.0.3",
|
|
||||||
"fs-extra": "7.0.1",
|
"fs-extra": "7.0.1",
|
||||||
"glob": "7.1.2",
|
"glob": "7.1.2",
|
||||||
"http-proxy": "1.17.0",
|
"http-proxy": "1.17.0",
|
||||||
|
"ignore": "4.0.6",
|
||||||
"ini": "1.3.4",
|
"ini": "1.3.4",
|
||||||
"inquirer": "3.3.0",
|
"inquirer": "3.3.0",
|
||||||
"is-url": "1.2.2",
|
"is-url": "1.2.2",
|
||||||
@@ -165,15 +139,15 @@
|
|||||||
"mime-types": "2.1.24",
|
"mime-types": "2.1.24",
|
||||||
"minimatch": "3.0.4",
|
"minimatch": "3.0.4",
|
||||||
"mri": "1.1.0",
|
"mri": "1.1.0",
|
||||||
"ms": "2.1.1",
|
"ms": "2.1.2",
|
||||||
"node-fetch": "1.7.3",
|
"node-fetch": "1.7.3",
|
||||||
|
"now-client": "./packages/now-client",
|
||||||
"npm-package-arg": "6.1.0",
|
"npm-package-arg": "6.1.0",
|
||||||
"nyc": "13.2.0",
|
"nyc": "13.2.0",
|
||||||
"ora": "3.4.0",
|
"ora": "3.4.0",
|
||||||
"pcre-to-regexp": "0.0.5",
|
"pcre-to-regexp": "1.0.0",
|
||||||
"pluralize": "7.0.0",
|
"pluralize": "7.0.0",
|
||||||
"pre-commit": "1.2.2",
|
"pre-commit": "1.2.2",
|
||||||
"prettier": "1.16.2",
|
|
||||||
"printf": "0.2.5",
|
"printf": "0.2.5",
|
||||||
"progress": "2.0.3",
|
"progress": "2.0.3",
|
||||||
"promisepipe": "3.0.0",
|
"promisepipe": "3.0.0",
|
||||||
@@ -194,6 +168,7 @@
|
|||||||
"through2": "2.0.3",
|
"through2": "2.0.3",
|
||||||
"title": "3.4.1",
|
"title": "3.4.1",
|
||||||
"tmp-promise": "1.0.3",
|
"tmp-promise": "1.0.3",
|
||||||
|
"tree-kill": "1.2.1",
|
||||||
"ts-node": "8.3.0",
|
"ts-node": "8.3.0",
|
||||||
"typescript": "3.2.4",
|
"typescript": "3.2.4",
|
||||||
"universal-analytics": "0.4.20",
|
"universal-analytics": "0.4.20",
|
||||||
|
|||||||
@@ -8,16 +8,13 @@ import { createWriteStream, mkdirp, remove, writeJSON } from 'fs-extra';
|
|||||||
|
|
||||||
import { getDistTag } from '../src/util/get-dist-tag';
|
import { getDistTag } from '../src/util/get-dist-tag';
|
||||||
import pkg from '../package.json';
|
import pkg from '../package.json';
|
||||||
|
import { getBundledBuilders } from '../src/util/dev/get-bundled-builders';
|
||||||
|
|
||||||
const dirRoot = join(__dirname, '..');
|
const dirRoot = join(__dirname, '..');
|
||||||
|
|
||||||
const bundledBuilders = Object.keys(pkg.devDependencies).filter(d =>
|
|
||||||
d.startsWith('@now/')
|
|
||||||
);
|
|
||||||
|
|
||||||
async function createBuildersTarball() {
|
async function createBuildersTarball() {
|
||||||
const distTag = getDistTag(pkg.version);
|
const distTag = getDistTag(pkg.version);
|
||||||
const builders = Array.from(bundledBuilders).map(b => `${b}@${distTag}`);
|
const builders = Array.from(getBundledBuilders()).map(b => `${b}@${distTag}`);
|
||||||
console.log(`Creating builders tarball with: ${builders.join(', ')}`);
|
console.log(`Creating builders tarball with: ${builders.join(', ')}`);
|
||||||
|
|
||||||
const buildersDir = join(dirRoot, '.builders');
|
const buildersDir = join(dirRoot, '.builders');
|
||||||
@@ -39,7 +36,7 @@ async function createBuildersTarball() {
|
|||||||
const yarn = join(dirRoot, '../../node_modules/yarn/bin/yarn.js');
|
const yarn = join(dirRoot, '../../node_modules/yarn/bin/yarn.js');
|
||||||
await execa(process.execPath, [yarn, 'add', '--no-lockfile', ...builders], {
|
await execa(process.execPath, [yarn, 'add', '--no-lockfile', ...builders], {
|
||||||
cwd: buildersDir,
|
cwd: buildersDir,
|
||||||
stdio: 'inherit'
|
stdio: 'inherit',
|
||||||
});
|
});
|
||||||
|
|
||||||
const packer = tar.pack(buildersDir);
|
const packer = tar.pack(buildersDir);
|
||||||
@@ -66,7 +63,7 @@ async function main() {
|
|||||||
// Compile the `doT.js` template files for `now dev`
|
// Compile the `doT.js` template files for `now dev`
|
||||||
console.log();
|
console.log();
|
||||||
await execa(process.execPath, [join(__dirname, 'compile-templates.js')], {
|
await execa(process.execPath, [join(__dirname, 'compile-templates.js')], {
|
||||||
stdio: 'inherit'
|
stdio: 'inherit',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Do the initial `ncc` build
|
// Do the initial `ncc` build
|
||||||
@@ -92,20 +89,22 @@ async function main() {
|
|||||||
// get compiled into the final ncc bundle file, however, we want them to be
|
// get compiled into the final ncc bundle file, however, we want them to be
|
||||||
// present in the npm package because the contents of those files are involved
|
// present in the npm package because the contents of those files are involved
|
||||||
// with `fun`'s cache invalidation mechanism and they need to be shasum'd.
|
// with `fun`'s cache invalidation mechanism and they need to be shasum'd.
|
||||||
const runtimes = join(dirRoot, '../../node_modules/@zeit/fun/dist/src/runtimes');
|
const runtimes = join(
|
||||||
|
dirRoot,
|
||||||
|
'../../node_modules/@zeit/fun/dist/src/runtimes'
|
||||||
|
);
|
||||||
const dest = join(dirRoot, 'dist/runtimes');
|
const dest = join(dirRoot, 'dist/runtimes');
|
||||||
await cpy('**/*', dest, { parents: true, cwd: runtimes });
|
await cpy('**/*', dest, { parents: true, cwd: runtimes });
|
||||||
|
|
||||||
console.log('Finished building `now-cli`');
|
console.log('Finished building `now-cli`');
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on('unhandledRejection', (err: any) => {
|
process.on('unhandledRejection', (reason: any, promise: Promise<any>) => {
|
||||||
console.error('Unhandled Rejection:');
|
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
||||||
console.error(err);
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('uncaughtException', (err: any) => {
|
process.on('uncaughtException', err => {
|
||||||
console.error('Uncaught Exception:');
|
console.error('Uncaught Exception:');
|
||||||
console.error(err);
|
console.error(err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ import strlen from '../../util/strlen.ts';
|
|||||||
import wait from '../../util/output/wait';
|
import wait from '../../util/output/wait';
|
||||||
|
|
||||||
export default async function ls(ctx, opts, args, output) {
|
export default async function ls(ctx, opts, args, output) {
|
||||||
const { authConfig: { token }, config } = ctx;
|
const {
|
||||||
|
authConfig: { token },
|
||||||
|
config
|
||||||
|
} = ctx;
|
||||||
const { currentTeam } = config;
|
const { currentTeam } = config;
|
||||||
const { apiUrl } = ctx;
|
const { apiUrl } = ctx;
|
||||||
const { '--debug': debugEnabled } = opts;
|
const { '--debug': debugEnabled } = opts;
|
||||||
@@ -48,7 +51,6 @@ export default async function ls(ctx, opts, args, output) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opts['--json']) {
|
|
||||||
cancelWait = wait(
|
cancelWait = wait(
|
||||||
args[0]
|
args[0]
|
||||||
? `Fetching alias details for "${args[0]}" under ${chalk.bold(
|
? `Fetching alias details for "${args[0]}" under ${chalk.bold(
|
||||||
@@ -56,7 +58,6 @@ export default async function ls(ctx, opts, args, output) {
|
|||||||
)}`
|
)}`
|
||||||
: `Fetching aliases under ${chalk.bold(contextName)}`
|
: `Fetching aliases under ${chalk.bold(contextName)}`
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const aliases = await getAliases(now);
|
const aliases = await getAliases(now);
|
||||||
if (cancelWait) cancelWait();
|
if (cancelWait) cancelWait();
|
||||||
@@ -72,7 +73,7 @@ export default async function ls(ctx, opts, args, output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opts['--json']) {
|
if (opts['--json']) {
|
||||||
output.print(JSON.stringify({ rules: alias.rules }, null, 2));
|
console.log(JSON.stringify({ rules: alias.rules }, null, 2));
|
||||||
} else {
|
} else {
|
||||||
const rules = alias.rules || [];
|
const rules = alias.rules || [];
|
||||||
output.log(
|
output.log(
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default async function set(
|
|||||||
const {
|
const {
|
||||||
authConfig: { token },
|
authConfig: { token },
|
||||||
config,
|
config,
|
||||||
localConfig
|
localConfig,
|
||||||
} = ctx;
|
} = ctx;
|
||||||
|
|
||||||
const { currentTeam } = config;
|
const { currentTeam } = config;
|
||||||
@@ -48,14 +48,14 @@ export default async function set(
|
|||||||
const {
|
const {
|
||||||
'--debug': debugEnabled,
|
'--debug': debugEnabled,
|
||||||
'--no-verify': noVerify,
|
'--no-verify': noVerify,
|
||||||
'--rules': rulesPath
|
'--rules': rulesPath,
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
apiUrl,
|
apiUrl,
|
||||||
token,
|
token,
|
||||||
currentTeam,
|
currentTeam,
|
||||||
debug: debugEnabled
|
debug: debugEnabled,
|
||||||
});
|
});
|
||||||
let contextName = null;
|
let contextName = null;
|
||||||
let user = null;
|
let user = null;
|
||||||
@@ -79,12 +79,14 @@ export default async function set(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidName(args[0])) {
|
if (args.length >= 1 && !isValidName(args[0])) {
|
||||||
output.error(`The provided argument "${args[0]}" is not a valid deployment`);
|
output.error(
|
||||||
|
`The provided argument "${args[0]}" is not a valid deployment`
|
||||||
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidName(args[1])) {
|
if (args.length >= 2 && !isValidName(args[1])) {
|
||||||
output.error(`The provided argument "${args[1]}" is not a valid domain`);
|
output.error(`The provided argument "${args[1]}" is not a valid domain`);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -212,6 +214,7 @@ export default async function set(
|
|||||||
for (const target of targets) {
|
for (const target of targets) {
|
||||||
output.log(`Assigning alias ${target} to deployment ${deployment.url}`);
|
output.log(`Assigning alias ${target} to deployment ${deployment.url}`);
|
||||||
|
|
||||||
|
const isWildcard = isWildcardAlias(target);
|
||||||
const record = await assignAlias(
|
const record = await assignAlias(
|
||||||
output,
|
output,
|
||||||
client,
|
client,
|
||||||
@@ -222,13 +225,14 @@ export default async function set(
|
|||||||
);
|
);
|
||||||
const handleResult = handleSetupDomainError(
|
const handleResult = handleSetupDomainError(
|
||||||
output,
|
output,
|
||||||
handleCreateAliasError(output, record)
|
handleCreateAliasError(output, record),
|
||||||
|
isWildcard
|
||||||
);
|
);
|
||||||
if (handleResult === 1) {
|
if (handleResult === 1) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const prefix = isWildcardAlias(handleResult.alias) ? '' : 'https://';
|
const prefix = isWildcard ? '' : 'https://';
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`${chalk.cyan('> Success!')} ${chalk.bold(
|
`${chalk.cyan('> Success!')} ${chalk.bold(
|
||||||
@@ -246,10 +250,15 @@ type SetupDomainError = Exclude<SetupDomainResolve, Domain>;
|
|||||||
|
|
||||||
function handleSetupDomainError<T>(
|
function handleSetupDomainError<T>(
|
||||||
output: Output,
|
output: Output,
|
||||||
error: SetupDomainError | T
|
error: SetupDomainError | T,
|
||||||
|
isWildcard: boolean = false
|
||||||
): T | 1 {
|
): T | 1 {
|
||||||
if (error instanceof ERRORS.DomainVerificationFailed) {
|
if (
|
||||||
const { nsVerification, txtVerification, domain } = error.meta;
|
error instanceof ERRORS.DomainVerificationFailed ||
|
||||||
|
error instanceof ERRORS.DomainNsNotVerifiedForWildcard
|
||||||
|
) {
|
||||||
|
const { nsVerification, domain } = error.meta;
|
||||||
|
|
||||||
output.error(
|
output.error(
|
||||||
`We could not alias since the domain ${domain} could not be verified due to the following reasons:\n`
|
`We could not alias since the domain ${domain} could not be verified due to the following reasons:\n`
|
||||||
);
|
);
|
||||||
@@ -265,6 +274,8 @@ function handleSetupDomainError<T>(
|
|||||||
{ extraSpace: ' ' }
|
{ extraSpace: ' ' }
|
||||||
)}\n\n`
|
)}\n\n`
|
||||||
);
|
);
|
||||||
|
if (error instanceof ERRORS.DomainVerificationFailed && !isWildcard) {
|
||||||
|
const { txtVerification } = error.meta;
|
||||||
output.print(
|
output.print(
|
||||||
` ${chalk.gray(
|
` ${chalk.gray(
|
||||||
'b)'
|
'b)'
|
||||||
@@ -284,6 +295,13 @@ function handleSetupDomainError<T>(
|
|||||||
output.print(
|
output.print(
|
||||||
` We will also periodically run a verification check for you and you will receive an email once your domain is verified.\n`
|
` We will also periodically run a verification check for you and you will receive an email once your domain is verified.\n`
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
output.print(
|
||||||
|
` Once your domain uses the nameservers from above, run again ${cmd(
|
||||||
|
'now domains verify <domain>'
|
||||||
|
)}.\n`
|
||||||
|
);
|
||||||
|
}
|
||||||
output.print(' Read more: https://err.sh/now/domain-verification\n');
|
output.print(' Read more: https://err.sh/now/domain-verification\n');
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -349,9 +367,7 @@ function handleSetupDomainError<T>(
|
|||||||
|
|
||||||
if (error instanceof ERRORS.DomainPurchasePending) {
|
if (error instanceof ERRORS.DomainPurchasePending) {
|
||||||
output.error(
|
output.error(
|
||||||
`The domain ${
|
`The domain ${error.meta.domain} is processing and will be available once the order is completed.`
|
||||||
error.meta.domain
|
|
||||||
} is processing and will be available once the order is completed.`
|
|
||||||
);
|
);
|
||||||
output.print(
|
output.print(
|
||||||
` An email will be sent upon completion so you can alias to your new domain.\n`
|
` An email will be sent upon completion so you can alias to your new domain.\n`
|
||||||
@@ -467,9 +483,7 @@ function handleCreateAliasError<T>(
|
|||||||
}
|
}
|
||||||
if (error instanceof ERRORS.ForbiddenScaleMinInstances) {
|
if (error instanceof ERRORS.ForbiddenScaleMinInstances) {
|
||||||
output.error(
|
output.error(
|
||||||
`You can't scale to more than ${
|
`You can't scale to more than ${error.meta.max} min instances with your current plan.`
|
||||||
error.meta.max
|
|
||||||
} min instances with your current plan.`
|
|
||||||
);
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -490,9 +504,7 @@ function handleCreateAliasError<T>(
|
|||||||
|
|
||||||
if (error instanceof ERRORS.CertMissing) {
|
if (error instanceof ERRORS.CertMissing) {
|
||||||
output.error(
|
output.error(
|
||||||
`There is no certificate for the domain ${
|
`There is no certificate for the domain ${error.meta.domain} and it could not be created.`
|
||||||
error.meta.domain
|
|
||||||
} and it could not be created.`
|
|
||||||
);
|
);
|
||||||
output.log(
|
output.log(
|
||||||
`Please generate a new certificate manually with ${cmd(
|
`Please generate a new certificate manually with ${cmd(
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
import Now from '../../util';
|
import Now from '../../util';
|
||||||
import Client from '../../util/client.ts';
|
import Client from '../../util/client';
|
||||||
import getScope from '../../util/get-scope.ts';
|
import getScope from '../../util/get-scope';
|
||||||
import stamp from '../../util/output/stamp.ts';
|
import stamp from '../../util/output/stamp';
|
||||||
import wait from '../../util/output/wait';
|
import wait from '../../util/output/wait';
|
||||||
import createCertFromFile from '../../util/certs/create-cert-from-file';
|
import createCertFromFile from '../../util/certs/create-cert-from-file';
|
||||||
import createCertForCns from '../../util/certs/create-cert-for-cns';
|
import createCertForCns from '../../util/certs/create-cert-for-cns';
|
||||||
|
import { NowContext } from '../../types';
|
||||||
|
import { Output } from '../../util/output';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DomainPermissionDenied,
|
DomainPermissionDenied,
|
||||||
@@ -14,7 +17,20 @@ import {
|
|||||||
} from '../../util/errors-ts';
|
} from '../../util/errors-ts';
|
||||||
import handleCertError from '../../util/certs/handle-cert-error';
|
import handleCertError from '../../util/certs/handle-cert-error';
|
||||||
|
|
||||||
async function add(ctx, opts, args, output) {
|
interface Options {
|
||||||
|
'--overwrite'?: boolean;
|
||||||
|
'--debug'?: boolean;
|
||||||
|
'--crt'?: string;
|
||||||
|
'--key'?: string;
|
||||||
|
'--ca'?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function add(
|
||||||
|
ctx: NowContext,
|
||||||
|
opts: Options,
|
||||||
|
args: string[],
|
||||||
|
output: Output
|
||||||
|
): Promise<number> {
|
||||||
const {
|
const {
|
||||||
authConfig: { token },
|
authConfig: { token },
|
||||||
config
|
config
|
||||||
@@ -77,10 +93,12 @@ async function add(ctx, opts, args, output) {
|
|||||||
|
|
||||||
// Create a custom certificate from the given file paths
|
// Create a custom certificate from the given file paths
|
||||||
cert = await createCertFromFile(now, keyPath, crtPath, caPath, contextName);
|
cert = await createCertFromFile(now, keyPath, crtPath, caPath, contextName);
|
||||||
|
|
||||||
if (cert instanceof InvalidCert) {
|
if (cert instanceof InvalidCert) {
|
||||||
output.error(`The provided certificate is not valid and can't be added.`);
|
output.error(`The provided certificate is not valid and can't be added.`);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cert instanceof DomainPermissionDenied) {
|
if (cert instanceof DomainPermissionDenied) {
|
||||||
output.error(
|
output.error(
|
||||||
`You don't have permissions over domain ${chalk.underline(
|
`You don't have permissions over domain ${chalk.underline(
|
||||||
@@ -97,6 +115,7 @@ async function add(ctx, opts, args, output) {
|
|||||||
'now certs issue <cn> <cns>'
|
'now certs issue <cn> <cns>'
|
||||||
)} instead`
|
)} instead`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (args.length < 1) {
|
if (args.length < 1) {
|
||||||
output.error(
|
output.error(
|
||||||
`Invalid number of arguments to create a custom certificate entry. Usage:`
|
`Invalid number of arguments to create a custom certificate entry. Usage:`
|
||||||
@@ -107,16 +126,19 @@ async function add(ctx, opts, args, output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the certificate from the given array of CNs
|
// Create the certificate from the given array of CNs
|
||||||
const cns = args.reduce((res, item) => [...res, ...item.split(',')], []);
|
const cns = args.reduce<string[]>((res, item) => res.concat(item.split(',')), []);
|
||||||
const cancelWait = wait(
|
const cancelWait = wait(
|
||||||
`Generating a certificate for ${chalk.bold(cns.join(', '))}`
|
`Generating a certificate for ${chalk.bold(cns.join(', '))}`
|
||||||
);
|
);
|
||||||
|
|
||||||
cert = await createCertForCns(now, cns, contextName);
|
cert = await createCertForCns(now, cns, contextName);
|
||||||
cancelWait();
|
cancelWait();
|
||||||
|
}
|
||||||
|
|
||||||
const result = handleCertError(output, cert);
|
const result = handleCertError(output, cert);
|
||||||
|
|
||||||
if (result === 1) {
|
if (result === 1) {
|
||||||
return result
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cert instanceof DomainPermissionDenied) {
|
if (cert instanceof DomainPermissionDenied) {
|
||||||
@@ -127,14 +149,20 @@ async function add(ctx, opts, args, output) {
|
|||||||
);
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if (cert instanceof Error) {
|
||||||
|
// All cert errors are handled above,
|
||||||
|
// so this is only for typescript
|
||||||
|
throw cert;
|
||||||
|
} else {
|
||||||
// Print success message
|
// Print success message
|
||||||
output.success(
|
output.success(
|
||||||
`Certificate entry for ${chalk.bold(
|
`Certificate entry for ${chalk.bold(
|
||||||
cert.cns.join(', ')
|
cert.cns.join(', ')
|
||||||
)} created ${addStamp()}`
|
)} created ${addStamp()}`
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
//
|
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
import { handleError } from '../../util/error';
|
import { handleError } from '../../util/error';
|
||||||
|
|
||||||
import createOutput from '../../util/output';
|
import createOutput from '../../util/output';
|
||||||
@@ -12,6 +12,7 @@ import add from './add';
|
|||||||
import issue from './issue';
|
import issue from './issue';
|
||||||
import ls from './ls';
|
import ls from './ls';
|
||||||
import rm from './rm';
|
import rm from './rm';
|
||||||
|
import { NowContext } from '../../types';
|
||||||
|
|
||||||
const help = () => {
|
const help = () => {
|
||||||
console.log(`
|
console.log(`
|
||||||
@@ -71,7 +72,7 @@ const COMMAND_CONFIG = {
|
|||||||
rm: ['rm', 'remove']
|
rm: ['rm', 'remove']
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function main(ctx) {
|
export default async function main(ctx: NowContext) {
|
||||||
let argv;
|
let argv;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -3,16 +3,29 @@ import ms from 'ms';
|
|||||||
import plural from 'pluralize';
|
import plural from 'pluralize';
|
||||||
import psl from 'psl';
|
import psl from 'psl';
|
||||||
import table from 'text-table';
|
import table from 'text-table';
|
||||||
|
// @ts-ignore
|
||||||
import Now from '../../util';
|
import Now from '../../util';
|
||||||
import cmd from '../../util/output/cmd';
|
import cmd from '../../util/output/cmd';
|
||||||
import Client from '../../util/client.ts';
|
import Client from '../../util/client';
|
||||||
import getScope from '../../util/get-scope.ts';
|
import getScope from '../../util/get-scope';
|
||||||
import stamp from '../../util/output/stamp.ts';
|
import stamp from '../../util/output/stamp';
|
||||||
import getCerts from '../../util/certs/get-certs';
|
import getCerts from '../../util/certs/get-certs';
|
||||||
import { CertNotFound } from '../../util/errors-ts';
|
import { CertNotFound } from '../../util/errors-ts';
|
||||||
import strlen from '../../util/strlen.ts';
|
import strlen from '../../util/strlen';
|
||||||
|
import { Output } from '../../util/output';
|
||||||
|
import { NowContext, Cert } from '../../types';
|
||||||
|
|
||||||
async function ls(ctx, opts, args, output) {
|
interface Options {
|
||||||
|
'--debug'?: boolean;
|
||||||
|
'--after'?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ls(
|
||||||
|
ctx: NowContext,
|
||||||
|
opts: Options,
|
||||||
|
args: string[],
|
||||||
|
output: Output
|
||||||
|
): Promise<number> {
|
||||||
const { authConfig: { token }, config } = ctx;
|
const { authConfig: { token }, config } = ctx;
|
||||||
const { currentTeam } = config;
|
const { currentTeam } = config;
|
||||||
const { apiUrl } = ctx;
|
const { apiUrl } = ctx;
|
||||||
@@ -32,7 +45,6 @@ async function ls(ctx, opts, args, output) {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// $FlowFixMe
|
|
||||||
const now = new Now({ apiUrl, token, debug, currentTeam });
|
const now = new Now({ apiUrl, token, debug, currentTeam });
|
||||||
const lsStamp = stamp();
|
const lsStamp = stamp();
|
||||||
|
|
||||||
@@ -55,7 +67,6 @@ async function ls(ctx, opts, args, output) {
|
|||||||
throw certificates;
|
throw certificates;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { uid: lastCert } = certificates[certificates.length - 1];
|
|
||||||
const certs = sortByCn(certificates);
|
const certs = sortByCn(certificates);
|
||||||
|
|
||||||
output.log(
|
output.log(
|
||||||
@@ -65,7 +76,8 @@ async function ls(ctx, opts, args, output) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (certs.length >= 100) {
|
if (certs.length >= 100) {
|
||||||
output.note(`There may be more certificates that can be retrieved with ${cmd(`now ${process.argv.slice(2).join(' ')} --after=${lastCert}`)}.`);
|
const { uid: lastCert } = certificates[certificates.length - 1];
|
||||||
|
output.note(`There may be more certificates that can be retrieved with ${cmd(`now ${process.argv.slice(2).join(' ')} --after=${lastCert}`)}.\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (certs.length > 0) {
|
if (certs.length > 0) {
|
||||||
@@ -75,7 +87,7 @@ async function ls(ctx, opts, args, output) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCertsTable(certsList) {
|
function formatCertsTable(certsList: Cert[]) {
|
||||||
return `${table(
|
return `${table(
|
||||||
[formatCertsTableHead(), ...formatCertsTableBody(certsList)],
|
[formatCertsTableHead(), ...formatCertsTableBody(certsList)],
|
||||||
{
|
{
|
||||||
@@ -86,7 +98,7 @@ function formatCertsTable(certsList) {
|
|||||||
).replace(/^(.*)/gm, ' $1')}\n`;
|
).replace(/^(.*)/gm, ' $1')}\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCertsTableHead() {
|
function formatCertsTableHead(): string[] {
|
||||||
return [
|
return [
|
||||||
chalk.dim('id'),
|
chalk.dim('id'),
|
||||||
chalk.dim('cns'),
|
chalk.dim('cns'),
|
||||||
@@ -96,15 +108,12 @@ function formatCertsTableHead() {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCertsTableBody(certsList) {
|
function formatCertsTableBody(certsList: Cert[]) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
return certsList.reduce(
|
return certsList.reduce<string[][]>((result, cert) => result.concat(formatCert(now, cert)), []);
|
||||||
(result, cert) => [...result, ...formatCert(now, cert)],
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCert(time, cert) {
|
function formatCert(time: Date, cert: Cert) {
|
||||||
return cert.cns.map(
|
return cert.cns.map(
|
||||||
(cn, idx) =>
|
(cn, idx) =>
|
||||||
idx === 0
|
idx === 0
|
||||||
@@ -113,26 +122,26 @@ function formatCert(time, cert) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCertNonFirstCn(cn, multiple) {
|
function formatCertNonFirstCn(cn: string, multiple: boolean): string[] {
|
||||||
return ['', formatCertCn(cn, multiple), '', '', ''];
|
return ['', formatCertCn(cn, multiple), '', '', ''];
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCertCn(cn, multiple) {
|
function formatCertCn(cn: string, multiple: boolean) {
|
||||||
return multiple ? `${chalk.gray('-')} ${chalk.bold(cn)}` : chalk.bold(cn);
|
return multiple ? `${chalk.gray('-')} ${chalk.bold(cn)}` : chalk.bold(cn);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCertFirstCn(time, cert, cn, multiple) {
|
function formatCertFirstCn(time: Date, cert: Cert, cn: string, multiple: boolean): string[] {
|
||||||
return [
|
return [
|
||||||
cert.uid,
|
cert.uid,
|
||||||
formatCertCn(cn, multiple),
|
formatCertCn(cn, multiple),
|
||||||
formatExpirationDate(new Date(cert.expiration)),
|
formatExpirationDate(new Date(cert.expiration)),
|
||||||
cert.autoRenew ? 'yes' : 'no',
|
cert.autoRenew ? 'yes' : 'no',
|
||||||
chalk.gray(ms(time - new Date(cert.created)))
|
chalk.gray(ms(time.getTime() - new Date(cert.created).getTime()))
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatExpirationDate(date) {
|
function formatExpirationDate(date: Date) {
|
||||||
const diff = date - Date.now();
|
const diff = date.getTime() - Date.now();
|
||||||
return diff < 0
|
return diff < 0
|
||||||
? chalk.gray(`${ms(-diff)} ago`)
|
? chalk.gray(`${ms(-diff)} ago`)
|
||||||
: chalk.gray(`in ${ms(diff)}`);
|
: chalk.gray(`in ${ms(diff)}`);
|
||||||
@@ -143,8 +152,8 @@ function formatExpirationDate(date) {
|
|||||||
* to 'wildcard' since that will allow psl get the root domain
|
* to 'wildcard' since that will allow psl get the root domain
|
||||||
* properly to make the comparison.
|
* properly to make the comparison.
|
||||||
*/
|
*/
|
||||||
function sortByCn(certsList) {
|
function sortByCn(certsList: Cert[]) {
|
||||||
return certsList.concat().sort((a, b) => {
|
return certsList.concat().sort((a: Cert, b: Cert) => {
|
||||||
const domainA = psl.get(a.cns[0].replace('*', 'wildcard'));
|
const domainA = psl.get(a.cns[0].replace('*', 'wildcard'));
|
||||||
const domainB = psl.get(b.cns[0].replace('*', 'wildcard'));
|
const domainB = psl.get(b.cns[0].replace('*', 'wildcard'));
|
||||||
if (!domainA || !domainB) return 0;
|
if (!domainA || !domainB) return 0;
|
||||||
@@ -10,7 +10,9 @@ export const latestHelp = () => `
|
|||||||
|
|
||||||
${chalk.dim('Basic')}
|
${chalk.dim('Basic')}
|
||||||
|
|
||||||
deploy [path] Performs a deployment ${chalk.bold('(default)')}
|
deploy [path] Performs a deployment ${chalk.bold(
|
||||||
|
'(default)'
|
||||||
|
)}
|
||||||
dev Start a local development server
|
dev Start a local development server
|
||||||
init [example] Initialize an example project
|
init [example] Initialize an example project
|
||||||
ls | list [app] Lists deployments
|
ls | list [app] Lists deployments
|
||||||
@@ -18,7 +20,6 @@ export const latestHelp = () => `
|
|||||||
login [email] Logs into your account or creates a new one
|
login [email] Logs into your account or creates a new one
|
||||||
logout Logs out of your account
|
logout Logs out of your account
|
||||||
switch [scope] Switches between teams and your personal account
|
switch [scope] Switches between teams and your personal account
|
||||||
update Updates Now CLI to the latest version
|
|
||||||
help [cmd] Displays complete help for [cmd]
|
help [cmd] Displays complete help for [cmd]
|
||||||
|
|
||||||
${chalk.dim('Advanced')}
|
${chalk.dim('Advanced')}
|
||||||
@@ -29,7 +30,6 @@ export const latestHelp = () => `
|
|||||||
certs [cmd] Manages your SSL certificates
|
certs [cmd] Manages your SSL certificates
|
||||||
secrets [name] Manages your secret environment variables
|
secrets [name] Manages your secret environment variables
|
||||||
logs [url] Displays the logs for a deployment
|
logs [url] Displays the logs for a deployment
|
||||||
scale [args] Scales the instance count of a deployment
|
|
||||||
teams Manages your teams
|
teams Manages your teams
|
||||||
whoami Shows the username of the currently logged in user
|
whoami Shows the username of the currently logged in user
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ export const latestArgs = {
|
|||||||
'-e': '--env',
|
'-e': '--env',
|
||||||
'-b': '--build-env',
|
'-b': '--build-env',
|
||||||
'-C': '--no-clipboard',
|
'-C': '--no-clipboard',
|
||||||
'-m': '--meta'
|
'-m': '--meta',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const legacyArgsMri = {
|
export const legacyArgsMri = {
|
||||||
@@ -126,7 +126,8 @@ export const legacyArgsMri = {
|
|||||||
'meta',
|
'meta',
|
||||||
'session-affinity',
|
'session-affinity',
|
||||||
'regions',
|
'regions',
|
||||||
'dotenv'
|
'dotenv',
|
||||||
|
'target',
|
||||||
],
|
],
|
||||||
boolean: [
|
boolean: [
|
||||||
'help',
|
'help',
|
||||||
@@ -143,11 +144,12 @@ export const legacyArgsMri = {
|
|||||||
'public',
|
'public',
|
||||||
'no-scale',
|
'no-scale',
|
||||||
'no-verify',
|
'no-verify',
|
||||||
'dotenv'
|
'dotenv',
|
||||||
|
'prod',
|
||||||
],
|
],
|
||||||
default: {
|
default: {
|
||||||
C: false,
|
C: false,
|
||||||
clipboard: true
|
clipboard: true,
|
||||||
},
|
},
|
||||||
alias: {
|
alias: {
|
||||||
env: 'e',
|
env: 'e',
|
||||||
@@ -164,8 +166,8 @@ export const legacyArgsMri = {
|
|||||||
'session-affinity': 'S',
|
'session-affinity': 'S',
|
||||||
name: 'n',
|
name: 'n',
|
||||||
project: 'P',
|
project: 'P',
|
||||||
alias: 'a'
|
alias: 'a',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// The following arg parsing is simply to make it compatible
|
// The following arg parsing is simply to make it compatible
|
||||||
|
|||||||
@@ -3,18 +3,14 @@ import bytes from 'bytes';
|
|||||||
import { write as copy } from 'clipboardy';
|
import { write as copy } from 'clipboardy';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import title from 'title';
|
import title from 'title';
|
||||||
import Progress from 'progress';
|
|
||||||
import Client from '../../util/client';
|
import Client from '../../util/client';
|
||||||
import wait from '../../util/output/wait';
|
|
||||||
import { handleError } from '../../util/error';
|
import { handleError } from '../../util/error';
|
||||||
import getArgs from '../../util/get-args';
|
import getArgs from '../../util/get-args';
|
||||||
import toHumanPath from '../../util/humanize-path';
|
import toHumanPath from '../../util/humanize-path';
|
||||||
import Now from '../../util';
|
import Now from '../../util';
|
||||||
import stamp from '../../util/output/stamp.ts';
|
import stamp from '../../util/output/stamp.ts';
|
||||||
import { isReady, isDone, isFailed } from '../../util/build-state';
|
|
||||||
import createDeploy from '../../util/deploy/create-deploy';
|
import createDeploy from '../../util/deploy/create-deploy';
|
||||||
import getDeploymentByIdOrHost from '../../util/deploy/get-deployment-by-id-or-host';
|
import getDeploymentByIdOrHost from '../../util/deploy/get-deployment-by-id-or-host';
|
||||||
import sleep from '../../util/sleep';
|
|
||||||
import parseMeta from '../../util/parse-meta';
|
import parseMeta from '../../util/parse-meta';
|
||||||
import code from '../../util/output/code';
|
import code from '../../util/output/code';
|
||||||
import param from '../../util/output/param';
|
import param from '../../util/output/param';
|
||||||
@@ -36,12 +32,15 @@ import {
|
|||||||
AliasDomainConfigured,
|
AliasDomainConfigured,
|
||||||
MissingBuildScript,
|
MissingBuildScript,
|
||||||
ConflictingFilePath,
|
ConflictingFilePath,
|
||||||
ConflictingPathSegment
|
ConflictingPathSegment,
|
||||||
|
BuildError,
|
||||||
|
NotDomainOwner,
|
||||||
} from '../../util/errors-ts';
|
} from '../../util/errors-ts';
|
||||||
import { SchemaValidationFailed } from '../../util/errors';
|
import { SchemaValidationFailed } from '../../util/errors';
|
||||||
import purchaseDomainIfAvailable from '../../util/domains/purchase-domain-if-available';
|
import purchaseDomainIfAvailable from '../../util/domains/purchase-domain-if-available';
|
||||||
import handleCertError from '../../util/certs/handle-cert-error';
|
import handleCertError from '../../util/certs/handle-cert-error';
|
||||||
import isWildcardAlias from '../../util/alias/is-wildcard-alias';
|
import isWildcardAlias from '../../util/alias/is-wildcard-alias';
|
||||||
|
import shouldDeployDir from '../../util/deploy/should-deploy-dir';
|
||||||
|
|
||||||
const addProcessEnv = async (log, env) => {
|
const addProcessEnv = async (log, env) => {
|
||||||
let val;
|
let val;
|
||||||
@@ -72,11 +71,12 @@ const addProcessEnv = async (log, env) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deploymentErrorMsg = `Your deployment failed. Please retry later. More: https://err.sh/now/deployment-error`;
|
const deploymentErrorMsg = `Your deployment failed. Please retry later. More: https://err.sh/now/deployment-error`;
|
||||||
const prepareAlias = input => isWildcardAlias(input) ? input : `https://${input}`;
|
const prepareAlias = input =>
|
||||||
|
isWildcardAlias(input) ? input : `https://${input}`;
|
||||||
|
|
||||||
const printDeploymentStatus = async (
|
const printDeploymentStatus = async (
|
||||||
output,
|
output,
|
||||||
{ url, readyState, alias: aliasList, aliasError },
|
{ readyState, alias: aliasList, aliasError },
|
||||||
deployStamp,
|
deployStamp,
|
||||||
clipboardEnabled,
|
clipboardEnabled,
|
||||||
localConfig,
|
localConfig,
|
||||||
@@ -94,10 +94,18 @@ const printDeploymentStatus = async (
|
|||||||
const preparedAlias = prepareAlias(firstAlias);
|
const preparedAlias = prepareAlias(firstAlias);
|
||||||
try {
|
try {
|
||||||
await copy(`https://${firstAlias}`);
|
await copy(`https://${firstAlias}`);
|
||||||
output.ready(`Deployed to ${chalk.bold(chalk.cyan(preparedAlias))} ${chalk.gray('[in clipboard]')} ${deployStamp()}`);
|
output.ready(
|
||||||
|
`Deployed to ${chalk.bold(
|
||||||
|
chalk.cyan(preparedAlias)
|
||||||
|
)} ${chalk.gray('[in clipboard]')} ${deployStamp()}`
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
output.debug(`Error copying to clipboard: ${err}`);
|
output.debug(`Error copying to clipboard: ${err}`);
|
||||||
output.ready(`Deployed to ${chalk.bold(chalk.cyan(preparedAlias))} ${deployStamp()}`);
|
output.ready(
|
||||||
|
`Deployed to ${chalk.bold(
|
||||||
|
chalk.cyan(preparedAlias)
|
||||||
|
)} ${deployStamp()}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -109,13 +117,17 @@ const printDeploymentStatus = async (
|
|||||||
|
|
||||||
for (const alias of aliasList) {
|
for (const alias of aliasList) {
|
||||||
const index = aliasList.indexOf(alias);
|
const index = aliasList.indexOf(alias);
|
||||||
const isLast = index === (aliasList.length - 1);
|
const isLast = index === aliasList.length - 1;
|
||||||
const shouldCopy = matching ? alias === matching : isLast;
|
const shouldCopy = matching ? alias === matching : isLast;
|
||||||
|
|
||||||
if (shouldCopy && clipboardEnabled) {
|
if (shouldCopy && clipboardEnabled) {
|
||||||
try {
|
try {
|
||||||
await copy(`https://${alias}`);
|
await copy(`https://${alias}`);
|
||||||
output.print(`- ${chalk.bold(chalk.cyan(prepareAlias(alias)))} ${chalk.gray('[in clipboard]')}\n`);
|
output.print(
|
||||||
|
`- ${chalk.bold(chalk.cyan(prepareAlias(alias)))} ${chalk.gray(
|
||||||
|
'[in clipboard]'
|
||||||
|
)}\n`
|
||||||
|
);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -138,20 +150,6 @@ const printDeploymentStatus = async (
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const failedBuilds = builds.filter(isFailed);
|
|
||||||
const amount = failedBuilds.length;
|
|
||||||
|
|
||||||
if (amount > 0) {
|
|
||||||
output.error('Build failed');
|
|
||||||
output.error(
|
|
||||||
`Check your logs at https://${url}/_logs or run ${code(
|
|
||||||
`now logs ${url}`
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.error(deploymentErrorMsg);
|
output.error(deploymentErrorMsg);
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
@@ -206,7 +204,15 @@ export default async function main(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { apiUrl, authConfig: { token }, config: { currentTeam } } = ctx;
|
if (!(await shouldDeployDir(argv._[0], output))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
apiUrl,
|
||||||
|
authConfig: { token },
|
||||||
|
config: { currentTeam },
|
||||||
|
} = ctx;
|
||||||
const { log, debug, error, warn } = output;
|
const { log, debug, error, warn } = output;
|
||||||
const paths = Object.keys(stats);
|
const paths = Object.keys(stats);
|
||||||
const debugEnabled = argv['--debug'];
|
const debugEnabled = argv['--debug'];
|
||||||
@@ -236,7 +242,6 @@ export default async function main(
|
|||||||
parseMeta(argv['--meta'])
|
parseMeta(argv['--meta'])
|
||||||
);
|
);
|
||||||
|
|
||||||
let syncCount;
|
|
||||||
let deployStamp = stamp();
|
let deployStamp = stamp();
|
||||||
let deployment = null;
|
let deployment = null;
|
||||||
|
|
||||||
@@ -289,11 +294,15 @@ export default async function main(
|
|||||||
parseEnv(argv['--env'])
|
parseEnv(argv['--env'])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Enable debug mode for builders
|
||||||
|
const buildDebugEnv = debugEnabled ? { NOW_BUILDER_DEBUG: '1' } : {};
|
||||||
|
|
||||||
// Merge build env out of `build.env` from now.json, and `--build-env` args
|
// Merge build env out of `build.env` from now.json, and `--build-env` args
|
||||||
const deploymentBuildEnv = Object.assign(
|
const deploymentBuildEnv = Object.assign(
|
||||||
{},
|
{},
|
||||||
parseEnv(localConfig.build && localConfig.build.env),
|
parseEnv(localConfig.build && localConfig.build.env),
|
||||||
parseEnv(argv['--build-env'])
|
parseEnv(argv['--build-env']),
|
||||||
|
buildDebugEnv
|
||||||
);
|
);
|
||||||
|
|
||||||
// If there's any undefined values, then inherit them from this process
|
// If there's any undefined values, then inherit them from this process
|
||||||
@@ -313,7 +322,12 @@ export default async function main(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
const project = getProjectName({argv, nowConfig: localConfig, isFile, paths});
|
const project = getProjectName({
|
||||||
|
argv,
|
||||||
|
nowConfig: localConfig,
|
||||||
|
isFile,
|
||||||
|
paths,
|
||||||
|
});
|
||||||
log(`Using project ${chalk.bold(project)}`);
|
log(`Using project ${chalk.bold(project)}`);
|
||||||
|
|
||||||
const createArgs = {
|
const createArgs = {
|
||||||
@@ -327,19 +341,26 @@ export default async function main(
|
|||||||
type: null,
|
type: null,
|
||||||
nowConfig: localConfig,
|
nowConfig: localConfig,
|
||||||
regions,
|
regions,
|
||||||
meta
|
meta,
|
||||||
|
deployStamp,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (argv['--target']) {
|
if (argv['--target']) {
|
||||||
const deprecatedTarget = argv['--target'];
|
const deprecatedTarget = argv['--target'];
|
||||||
|
|
||||||
if (!['staging', 'production'].includes(deprecatedTarget)) {
|
if (!['staging', 'production'].includes(deprecatedTarget)) {
|
||||||
error(`The specified ${param('--target')} ${code(deprecatedTarget)} is not valid`);
|
error(
|
||||||
|
`The specified ${param('--target')} ${code(
|
||||||
|
deprecatedTarget
|
||||||
|
)} is not valid`
|
||||||
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deprecatedTarget === 'production') {
|
if (deprecatedTarget === 'production') {
|
||||||
warn('We recommend using the much shorter `--prod` option instead of `--target production` (deprecated)');
|
warn(
|
||||||
|
'We recommend using the much shorter `--prod` option instead of `--target production` (deprecated)'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
output.debug(`Setting target to ${deprecatedTarget}`);
|
output.debug(`Setting target to ${deprecatedTarget}`);
|
||||||
@@ -351,7 +372,7 @@ export default async function main(
|
|||||||
|
|
||||||
deployStamp = stamp();
|
deployStamp = stamp();
|
||||||
|
|
||||||
const firstDeployCall = await createDeploy(
|
deployment = await createDeploy(
|
||||||
output,
|
output,
|
||||||
now,
|
now,
|
||||||
contextName,
|
contextName,
|
||||||
@@ -360,13 +381,49 @@ export default async function main(
|
|||||||
ctx
|
ctx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (deployment instanceof NotDomainOwner) {
|
||||||
|
output.error(deployment);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deploymentResponse = handleCertError(
|
||||||
|
output,
|
||||||
|
await getDeploymentByIdOrHost(now, contextName, deployment.id, 'v10')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (deploymentResponse === 1) {
|
||||||
|
return deploymentResponse;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
firstDeployCall instanceof DomainNotFound &&
|
deploymentResponse instanceof DeploymentNotFound ||
|
||||||
firstDeployCall.meta && firstDeployCall.meta.domain
|
deploymentResponse instanceof DeploymentPermissionDenied ||
|
||||||
|
deploymentResponse instanceof InvalidDeploymentId
|
||||||
) {
|
) {
|
||||||
output.debug(`The domain ${
|
output.error(deploymentResponse.message);
|
||||||
firstDeployCall.meta.domain
|
return 1;
|
||||||
} was not found, trying to purchase it`);
|
}
|
||||||
|
|
||||||
|
if (handleCertError(output, deployment) === 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deployment === null) {
|
||||||
|
error('Uploading failed. Please try again.');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
debug(`Error: ${err}\n${err.stack}`);
|
||||||
|
|
||||||
|
if (err instanceof NotDomainOwner) {
|
||||||
|
output.error(err.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err instanceof DomainNotFound && err.meta && err.meta.domain) {
|
||||||
|
output.debug(
|
||||||
|
`The domain ${err.meta.domain} was not found, trying to purchase it`
|
||||||
|
);
|
||||||
|
|
||||||
const purchase = await purchaseDomainIfAvailable(
|
const purchase = await purchaseDomainIfAvailable(
|
||||||
output,
|
output,
|
||||||
@@ -374,16 +431,14 @@ export default async function main(
|
|||||||
apiUrl: ctx.apiUrl,
|
apiUrl: ctx.apiUrl,
|
||||||
token: ctx.authConfig.token,
|
token: ctx.authConfig.token,
|
||||||
currentTeam: ctx.config.currentTeam,
|
currentTeam: ctx.config.currentTeam,
|
||||||
debug: debugEnabled
|
debug: debugEnabled,
|
||||||
}),
|
}),
|
||||||
firstDeployCall.meta.domain,
|
err.meta.domain,
|
||||||
contextName
|
contextName
|
||||||
);
|
);
|
||||||
|
|
||||||
if (purchase === true) {
|
if (purchase === true) {
|
||||||
output.success(`Successfully purchased the domain ${
|
output.success(`Successfully purchased the domain ${err.meta.domain}!`);
|
||||||
firstDeployCall.meta.domain
|
|
||||||
}!`);
|
|
||||||
|
|
||||||
// We exit if the purchase is completed since
|
// We exit if the purchase is completed since
|
||||||
// the domain verification can take some time
|
// the domain verification can take some time
|
||||||
@@ -391,7 +446,7 @@ export default async function main(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (purchase === false || purchase instanceof UserAborted) {
|
if (purchase === false || purchase instanceof UserAborted) {
|
||||||
handleCreateDeployError(output, firstDeployCall);
|
handleCreateDeployError(output, deployment);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,121 +454,37 @@ export default async function main(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handleCertError(output, firstDeployCall) === 1) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
firstDeployCall instanceof DomainNotFound ||
|
err instanceof DomainNotFound ||
|
||||||
firstDeployCall instanceof DomainNotVerified ||
|
err instanceof DomainNotVerified ||
|
||||||
firstDeployCall instanceof DomainPermissionDenied ||
|
err instanceof NotDomainOwner ||
|
||||||
firstDeployCall instanceof DomainVerificationFailed ||
|
err instanceof DomainPermissionDenied ||
|
||||||
firstDeployCall instanceof SchemaValidationFailed ||
|
err instanceof DomainVerificationFailed ||
|
||||||
firstDeployCall instanceof InvalidDomain ||
|
err instanceof SchemaValidationFailed ||
|
||||||
firstDeployCall instanceof DeploymentNotFound ||
|
err instanceof InvalidDomain ||
|
||||||
firstDeployCall instanceof BuildsRateLimited ||
|
err instanceof DeploymentNotFound ||
|
||||||
firstDeployCall instanceof DeploymentsRateLimited ||
|
err instanceof BuildsRateLimited ||
|
||||||
firstDeployCall instanceof AliasDomainConfigured ||
|
err instanceof DeploymentsRateLimited ||
|
||||||
firstDeployCall instanceof MissingBuildScript ||
|
err instanceof AliasDomainConfigured ||
|
||||||
firstDeployCall instanceof ConflictingFilePath ||
|
err instanceof MissingBuildScript ||
|
||||||
firstDeployCall instanceof ConflictingPathSegment
|
err instanceof ConflictingFilePath ||
|
||||||
|
err instanceof ConflictingPathSegment
|
||||||
) {
|
) {
|
||||||
handleCreateDeployError(output, firstDeployCall);
|
handleCreateDeployError(output, err);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
deployment = firstDeployCall;
|
if (err instanceof BuildError) {
|
||||||
|
output.error('Build failed');
|
||||||
if (now.syncFileCount > 0) {
|
output.error(
|
||||||
const uploadStamp = stamp();
|
`Check your logs at ${now.url}/_logs or run ${code(
|
||||||
|
`now logs ${now.url}`
|
||||||
await new Promise((resolve, reject) => {
|
)}`
|
||||||
if (now.syncFileCount !== now.fileCount) {
|
|
||||||
debug(`Total files ${now.fileCount}, ${now.syncFileCount} changed`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const size = bytes(now.syncAmount);
|
|
||||||
syncCount = `${now.syncFileCount} file${now.syncFileCount > 1
|
|
||||||
? 's'
|
|
||||||
: ''}`;
|
|
||||||
const bar = new Progress(
|
|
||||||
`${chalk.gray(
|
|
||||||
'>'
|
|
||||||
)} Upload [:bar] :percent :etas (${size}) [${syncCount}]`,
|
|
||||||
{
|
|
||||||
width: 20,
|
|
||||||
complete: '=',
|
|
||||||
incomplete: '',
|
|
||||||
total: now.syncAmount,
|
|
||||||
clear: true
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
now.upload({ scale: {} });
|
|
||||||
|
|
||||||
now.on('upload', ({ names, data }) => {
|
|
||||||
debug(`Uploaded: ${names.join(' ')} (${bytes(data.length)})`);
|
|
||||||
});
|
|
||||||
|
|
||||||
now.on('uploadProgress', progress => {
|
|
||||||
bar.tick(progress);
|
|
||||||
});
|
|
||||||
|
|
||||||
now.on('complete', resolve);
|
|
||||||
|
|
||||||
now.on('error', err => {
|
|
||||||
error('Upload failed');
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!quiet && syncCount) {
|
|
||||||
log(`Synced ${syncCount} (${bytes(now.syncAmount)}) ${uploadStamp()}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < 4; i += 1) {
|
|
||||||
deployStamp = stamp();
|
|
||||||
const secondDeployCall = await createDeploy(
|
|
||||||
output,
|
|
||||||
now,
|
|
||||||
contextName,
|
|
||||||
paths,
|
|
||||||
createArgs
|
|
||||||
);
|
|
||||||
|
|
||||||
if (handleCertError(output, secondDeployCall) === 1) {
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
secondDeployCall instanceof DomainPermissionDenied ||
|
|
||||||
secondDeployCall instanceof DomainVerificationFailed ||
|
|
||||||
secondDeployCall instanceof SchemaValidationFailed ||
|
|
||||||
secondDeployCall instanceof DeploymentNotFound ||
|
|
||||||
secondDeployCall instanceof DeploymentsRateLimited ||
|
|
||||||
secondDeployCall instanceof AliasDomainConfigured ||
|
|
||||||
secondDeployCall instanceof MissingBuildScript ||
|
|
||||||
secondDeployCall instanceof ConflictingFilePath ||
|
|
||||||
secondDeployCall instanceof ConflictingPathSegment
|
|
||||||
) {
|
|
||||||
handleCreateDeployError(output, secondDeployCall);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (now.syncFileCount === 0) {
|
|
||||||
deployment = secondDeployCall;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deployment === null) {
|
|
||||||
error('Uploading failed. Please try again.');
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
debug(`Error: ${err}\n${err.stack}`);
|
|
||||||
|
|
||||||
if (err.keyword === 'additionalProperties' && err.dataPath === '.scale') {
|
if (err.keyword === 'additionalProperties' && err.dataPath === '.scale') {
|
||||||
const { additionalProperty = '' } = err.params || {};
|
const { additionalProperty = '' } = err.params || {};
|
||||||
const message = `Invalid DC name for the scale option: ${additionalProperty}`;
|
const message = `Invalid DC name for the scale option: ${additionalProperty}`;
|
||||||
@@ -531,115 +502,15 @@ export default async function main(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { url } = now;
|
return printDeploymentStatus(
|
||||||
|
|
||||||
if (isTTY) {
|
|
||||||
log(`${url} ${chalk.gray(`[v2]`)} ${deployStamp()}`);
|
|
||||||
} else {
|
|
||||||
process.stdout.write(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If an error occurred, we want to let it fall down to rendering
|
|
||||||
// builds so the user can see in which build the error occurred.
|
|
||||||
if (isReady(deployment)) {
|
|
||||||
return printDeploymentStatus(output, deployment, deployStamp, !argv['--no-clipboard'], localConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
const sleepingTime = ms('1.5s');
|
|
||||||
const allBuildsTime = stamp();
|
|
||||||
const times = {};
|
|
||||||
const buildsUrl = `/v1/now/deployments/${deployment.id}/builds`;
|
|
||||||
|
|
||||||
let builds = [];
|
|
||||||
let buildsCompleted = false;
|
|
||||||
let buildSpinner = null;
|
|
||||||
|
|
||||||
let deploymentSpinner = null;
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-constant-condition
|
|
||||||
while (true) {
|
|
||||||
if (!buildsCompleted) {
|
|
||||||
const { builds: freshBuilds } = await now.fetch(buildsUrl);
|
|
||||||
|
|
||||||
// If there are no builds, we need to exit.
|
|
||||||
if (freshBuilds.length === 0 || freshBuilds.every(isDone)) {
|
|
||||||
builds = freshBuilds;
|
|
||||||
buildsCompleted = true;
|
|
||||||
} else {
|
|
||||||
for (const build of freshBuilds) {
|
|
||||||
const id = build.id;
|
|
||||||
const done = isDone(build);
|
|
||||||
|
|
||||||
if (times[id]) {
|
|
||||||
if (done && typeof times[id] === 'function') {
|
|
||||||
times[id] = times[id]();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
times[id] = done ? allBuildsTime() : stamp();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON.stringify(builds) !== JSON.stringify(freshBuilds)) {
|
|
||||||
builds = freshBuilds;
|
|
||||||
|
|
||||||
if (buildSpinner === null) {
|
|
||||||
buildSpinner = wait('Building...');
|
|
||||||
}
|
|
||||||
|
|
||||||
buildsCompleted = builds.every(isDone);
|
|
||||||
|
|
||||||
if (builds.some(isFailed)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const deploymentResponse = handleCertError(
|
|
||||||
output,
|
output,
|
||||||
await getDeploymentByIdOrHost(now, contextName, deployment.id, 'v9')
|
deployment,
|
||||||
)
|
deployStamp,
|
||||||
|
!argv['--no-clipboard'],
|
||||||
if (deploymentResponse === 1) {
|
localConfig
|
||||||
return deploymentResponse;
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
deploymentResponse instanceof DeploymentNotFound ||
|
|
||||||
deploymentResponse instanceof DeploymentPermissionDenied ||
|
|
||||||
deploymentResponse instanceof InvalidDeploymentId
|
|
||||||
) {
|
|
||||||
output.error(deploymentResponse.message);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isReady(deploymentResponse) || isFailed(deploymentResponse)) {
|
|
||||||
deployment = deploymentResponse;
|
|
||||||
|
|
||||||
if (typeof deploymentSpinner === 'function') {
|
|
||||||
// This stops it
|
|
||||||
deploymentSpinner();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
} else if (!deploymentSpinner) {
|
|
||||||
if (typeof buildSpinner === 'function') {
|
|
||||||
buildSpinner();
|
|
||||||
}
|
|
||||||
|
|
||||||
deploymentSpinner = wait('Finalizing...');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await sleep(sleepingTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof buildSpinner === 'function') {
|
|
||||||
buildSpinner();
|
|
||||||
}
|
|
||||||
|
|
||||||
return printDeploymentStatus(output, deployment, deployStamp, !argv['--no-clipboard'], localConfig, builds);
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleCreateDeployError(output, error) {
|
function handleCreateDeployError(output, error) {
|
||||||
if (error instanceof InvalidDomain) {
|
if (error instanceof InvalidDomain) {
|
||||||
output.error(`The domain ${error.meta.domain} is not valid`);
|
output.error(`The domain ${error.meta.domain} is not valid`);
|
||||||
@@ -708,18 +579,20 @@ function handleCreateDeployError(output, error) {
|
|||||||
}
|
}
|
||||||
if (error instanceof TooManyRequests) {
|
if (error instanceof TooManyRequests) {
|
||||||
output.error(
|
output.error(
|
||||||
`Too many requests detected for ${error.meta
|
`Too many requests detected for ${error.meta.api} API. Try again in ${ms(
|
||||||
.api} API. Try again in ${ms(error.meta.retryAfter * 1000, {
|
error.meta.retryAfter * 1000,
|
||||||
long: true
|
{
|
||||||
})}.`
|
long: true,
|
||||||
|
}
|
||||||
|
)}.`
|
||||||
);
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (error instanceof DomainNotVerified) {
|
if (error instanceof DomainNotVerified) {
|
||||||
output.error(
|
output.error(
|
||||||
`The domain used as an alias ${
|
`The domain used as an alias ${chalk.underline(
|
||||||
chalk.underline(error.meta.domain)
|
error.meta.domain
|
||||||
} is not verified yet. Please verify it.`
|
)} is not verified yet. Please verify it.`
|
||||||
);
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -730,6 +603,7 @@ function handleCreateDeployError(output, error) {
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
error instanceof DeploymentNotFound ||
|
error instanceof DeploymentNotFound ||
|
||||||
|
error instanceof NotDomainOwner ||
|
||||||
error instanceof DeploymentsRateLimited ||
|
error instanceof DeploymentsRateLimited ||
|
||||||
error instanceof AliasDomainConfigured ||
|
error instanceof AliasDomainConfigured ||
|
||||||
error instanceof MissingBuildScript ||
|
error instanceof MissingBuildScript ||
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { resolve, basename, join } from 'path';
|
|||||||
import { eraseLines } from 'ansi-escapes';
|
import { eraseLines } from 'ansi-escapes';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { write as copy } from 'clipboardy';
|
import { write as copy } from 'clipboardy';
|
||||||
import bytes from 'bytes';
|
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
@@ -13,7 +12,6 @@ import ms from 'ms';
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import title from 'title';
|
import title from 'title';
|
||||||
import plural from 'pluralize';
|
import plural from 'pluralize';
|
||||||
import Progress from 'progress';
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { handleError } from '../../util/error';
|
import { handleError } from '../../util/error';
|
||||||
import chars from '../../util/output/chars';
|
import chars from '../../util/output/chars';
|
||||||
@@ -34,19 +32,16 @@ import promptOptions from '../../util/prompt-options';
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import readMetaData from '../../util/read-metadata';
|
import readMetaData from '../../util/read-metadata';
|
||||||
import toHumanPath from '../../util/humanize-path';
|
import toHumanPath from '../../util/humanize-path';
|
||||||
import combineAsyncGenerators from '../../util/combine-async-generators';
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import createDeploy from '../../util/deploy/create-deploy';
|
import createDeploy from '../../util/deploy/create-deploy';
|
||||||
import eventListenerToGenerator from '../../util/event-listener-to-generator';
|
import eventListenerToGenerator from '../../util/event-listener-to-generator';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import formatLogCmd from '../../util/output/format-log-cmd';
|
|
||||||
// @ts-ignore
|
|
||||||
import formatLogOutput from '../../util/output/format-log-output';
|
import formatLogOutput from '../../util/output/format-log-output';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import getEventsStream from '../../util/deploy/get-events-stream';
|
import getEventsStream from '../../util/deploy/get-events-stream';
|
||||||
|
import shouldDeployDir from '../../util/deploy/should-deploy-dir';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import getInstanceIndex from '../../util/deploy/get-instance-index';
|
import getInstanceIndex from '../../util/deploy/get-instance-index';
|
||||||
import getStateChangeFromPolling from '../../util/deploy/get-state-change-from-polling';
|
|
||||||
import joinWords from '../../util/output/join-words';
|
import joinWords from '../../util/output/join-words';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import normalizeRegionsList from '../../util/scale/normalize-regions-list';
|
import normalizeRegionsList from '../../util/scale/normalize-regions-list';
|
||||||
@@ -67,11 +62,12 @@ import {
|
|||||||
DomainVerificationFailed,
|
DomainVerificationFailed,
|
||||||
TooManyRequests,
|
TooManyRequests,
|
||||||
VerifyScaleTimeout,
|
VerifyScaleTimeout,
|
||||||
DeploymentsRateLimited
|
DeploymentsRateLimited,
|
||||||
|
NotDomainOwner,
|
||||||
} from '../../util/errors-ts';
|
} from '../../util/errors-ts';
|
||||||
import {
|
import {
|
||||||
InvalidAllForScale,
|
InvalidAllForScale,
|
||||||
InvalidRegionOrDCForScale
|
InvalidRegionOrDCForScale,
|
||||||
} from '../../util/errors';
|
} from '../../util/errors';
|
||||||
import { SchemaValidationFailed } from '../../util/errors';
|
import { SchemaValidationFailed } from '../../util/errors';
|
||||||
import handleCertError from '../../util/certs/handle-cert-error';
|
import handleCertError from '../../util/certs/handle-cert-error';
|
||||||
@@ -198,11 +194,10 @@ const promptForEnvFields = async (list: string[]) => {
|
|||||||
for (const field of list) {
|
for (const field of list) {
|
||||||
questions.push({
|
questions.push({
|
||||||
name: field,
|
name: field,
|
||||||
message: field
|
message: field,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-unassigned-import
|
|
||||||
require('../../util/input/patch-inquirer');
|
require('../../util/input/patch-inquirer');
|
||||||
|
|
||||||
log('Please enter values for the following environment variables:');
|
log('Please enter values for the following environment variables:');
|
||||||
@@ -221,7 +216,7 @@ const promptForEnvFields = async (list: string[]) => {
|
|||||||
|
|
||||||
async function canUseZeroConfig(cwd: string): Promise<boolean> {
|
async function canUseZeroConfig(cwd: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const pkg = (await readPackage(join(cwd, 'package.json')));
|
const pkg = await readPackage(join(cwd, 'package.json'));
|
||||||
|
|
||||||
if (!pkg || pkg instanceof Error) {
|
if (!pkg || pkg instanceof Error) {
|
||||||
return false;
|
return false;
|
||||||
@@ -276,6 +271,10 @@ export default async function main(
|
|||||||
paths = [process.cwd()];
|
paths = [process.cwd()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(await shouldDeployDir(argv._[0], output))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
forceNew = argv.force;
|
forceNew = argv.force;
|
||||||
deploymentName = argv.name;
|
deploymentName = argv.name;
|
||||||
@@ -297,15 +296,29 @@ export default async function main(
|
|||||||
quiet = !isTTY;
|
quiet = !isTTY;
|
||||||
({ log, error, note, debug, warn } = output);
|
({ log, error, note, debug, warn } = output);
|
||||||
|
|
||||||
const infoUrl = await canUseZeroConfig(paths[0])
|
const infoUrl = (await canUseZeroConfig(paths[0]))
|
||||||
? 'https://zeit.co/guides/migrate-to-zeit-now'
|
? 'https://zeit.co/guides/migrate-to-zeit-now'
|
||||||
: 'https://zeit.co/docs/v2/advanced/platform/changes-in-now-2-0'
|
: 'https://zeit.co/docs/v2/advanced/platform/changes-in-now-2-0';
|
||||||
|
|
||||||
warn(`You are using an old version of the Now Platform. More: ${link(infoUrl)}`);
|
warn(
|
||||||
|
`You are using an old version of the Now Platform. More: ${link(infoUrl)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (argv.prod || argv.target) {
|
||||||
|
error(
|
||||||
|
`The option ${cmd(
|
||||||
|
argv.prod ? '--prod' : '--target'
|
||||||
|
)} is not supported for Now 1.0 deployments. To manually alias a deployment, use ${cmd(
|
||||||
|
'now alias'
|
||||||
|
)} instead.`
|
||||||
|
);
|
||||||
|
await exit(1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
authConfig: { token },
|
authConfig: { token },
|
||||||
config
|
config,
|
||||||
} = ctx;
|
} = ctx;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -315,7 +328,7 @@ export default async function main(
|
|||||||
token,
|
token,
|
||||||
config,
|
config,
|
||||||
firstRun: true,
|
firstRun: true,
|
||||||
deploymentType: undefined
|
deploymentType: undefined,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await stopDeployment(err);
|
await stopDeployment(err);
|
||||||
@@ -328,7 +341,7 @@ async function sync({
|
|||||||
token,
|
token,
|
||||||
config: { currentTeam },
|
config: { currentTeam },
|
||||||
firstRun,
|
firstRun,
|
||||||
deploymentType
|
deploymentType,
|
||||||
}: SyncOptions): Promise<void> {
|
}: SyncOptions): Promise<void> {
|
||||||
return new Promise(async (_resolve, reject) => {
|
return new Promise(async (_resolve, reject) => {
|
||||||
let deployStamp = stamp();
|
let deployStamp = stamp();
|
||||||
@@ -477,7 +490,7 @@ async function sync({
|
|||||||
|
|
||||||
// XXX: legacy
|
// XXX: legacy
|
||||||
deploymentType,
|
deploymentType,
|
||||||
sessionAffinity
|
sessionAffinity,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,7 +500,7 @@ async function sync({
|
|||||||
meta,
|
meta,
|
||||||
deploymentName,
|
deploymentName,
|
||||||
deploymentType,
|
deploymentType,
|
||||||
sessionAffinity
|
sessionAffinity,
|
||||||
} = await readMeta(
|
} = await readMeta(
|
||||||
paths[0],
|
paths[0],
|
||||||
deploymentName,
|
deploymentName,
|
||||||
@@ -500,7 +513,7 @@ async function sync({
|
|||||||
'dockerfile_missing',
|
'dockerfile_missing',
|
||||||
'no_dockerfile_commands',
|
'no_dockerfile_commands',
|
||||||
'unsupported_deployment_type',
|
'unsupported_deployment_type',
|
||||||
'multiple_manifests'
|
'multiple_manifests',
|
||||||
];
|
];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -538,7 +551,7 @@ async function sync({
|
|||||||
// Read scale and fail if we have both regions and scale
|
// Read scale and fail if we have both regions and scale
|
||||||
if (regions.length > 0 && Object.keys(scaleFromConfig).length > 0) {
|
if (regions.length > 0 && Object.keys(scaleFromConfig).length > 0) {
|
||||||
error(
|
error(
|
||||||
'Can\'t set both `regions` and `scale` options simultaneously',
|
"Can't set both `regions` and `scale` options simultaneously",
|
||||||
'regions-and-scale-at-once'
|
'regions-and-scale-at-once'
|
||||||
);
|
);
|
||||||
await exit(1);
|
await exit(1);
|
||||||
@@ -549,9 +562,7 @@ async function sync({
|
|||||||
dcIds = normalizeRegionsList(regions);
|
dcIds = normalizeRegionsList(regions);
|
||||||
if (dcIds instanceof InvalidRegionOrDCForScale) {
|
if (dcIds instanceof InvalidRegionOrDCForScale) {
|
||||||
error(
|
error(
|
||||||
`The value "${
|
`The value "${dcIds.meta.regionOrDC}" is not a valid region or DC identifier`
|
||||||
dcIds.meta.regionOrDC
|
|
||||||
}" is not a valid region or DC identifier`
|
|
||||||
);
|
);
|
||||||
await exit(1);
|
await exit(1);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -566,7 +577,7 @@ async function sync({
|
|||||||
scale = dcIds.reduce(
|
scale = dcIds.reduce(
|
||||||
(result: DcScale, dcId: string) => ({
|
(result: DcScale, dcId: string) => ({
|
||||||
...result,
|
...result,
|
||||||
[dcId]: { min: 0, max: 1 }
|
[dcId]: { min: 0, max: 1 },
|
||||||
}),
|
}),
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
@@ -662,8 +673,9 @@ async function sync({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hasSecrets = Object.keys(deploymentEnv).some(key =>
|
const hasSecrets = Object.keys(deploymentEnv).some(key =>
|
||||||
deploymentEnv[key].startsWith('@')
|
(deploymentEnv[key] || '').startsWith('@')
|
||||||
);
|
);
|
||||||
|
|
||||||
const secretsPromise = hasSecrets ? now.listSecrets() : null;
|
const secretsPromise = hasSecrets ? now.listSecrets() : null;
|
||||||
|
|
||||||
const findSecret = async (uidOrName: string) => {
|
const findSecret = async (uidOrName: string) => {
|
||||||
@@ -755,15 +767,13 @@ async function sync({
|
|||||||
parseMeta(argv.meta)
|
parseMeta(argv.meta)
|
||||||
);
|
);
|
||||||
|
|
||||||
let syncCount;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
meta.name = getProjectName({
|
meta.name = getProjectName({
|
||||||
argv,
|
argv,
|
||||||
nowConfig,
|
nowConfig,
|
||||||
isFile,
|
isFile,
|
||||||
paths,
|
paths,
|
||||||
pre: meta.name
|
pre: meta.name,
|
||||||
});
|
});
|
||||||
log(`Using project ${chalk.bold(meta.name)}`);
|
log(`Using project ${chalk.bold(meta.name)}`);
|
||||||
const createArgs = Object.assign(
|
const createArgs = Object.assign(
|
||||||
@@ -777,13 +787,15 @@ async function sync({
|
|||||||
scale,
|
scale,
|
||||||
wantsPublic,
|
wantsPublic,
|
||||||
sessionAffinity,
|
sessionAffinity,
|
||||||
isFile
|
isFile,
|
||||||
|
nowConfig,
|
||||||
|
deployStamp,
|
||||||
},
|
},
|
||||||
meta
|
meta
|
||||||
);
|
);
|
||||||
|
|
||||||
deployStamp = stamp();
|
deployStamp = stamp();
|
||||||
const firstDeployCall = await createDeploy(
|
deployment = await createDeploy(
|
||||||
output,
|
output,
|
||||||
now,
|
now,
|
||||||
contextName,
|
contextName,
|
||||||
@@ -791,118 +803,24 @@ async function sync({
|
|||||||
createArgs
|
createArgs
|
||||||
);
|
);
|
||||||
|
|
||||||
const handledResult = handleCertError(output, firstDeployCall);
|
const handledResult = handleCertError(output, deployment);
|
||||||
if (handledResult === 1) {
|
if (handledResult === 1) {
|
||||||
return handledResult;
|
return handledResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
firstDeployCall instanceof DomainNotFound ||
|
deployment instanceof DomainNotFound ||
|
||||||
firstDeployCall instanceof DomainPermissionDenied ||
|
deployment instanceof NotDomainOwner ||
|
||||||
firstDeployCall instanceof DomainVerificationFailed ||
|
deployment instanceof DomainPermissionDenied ||
|
||||||
firstDeployCall instanceof SchemaValidationFailed ||
|
deployment instanceof DomainVerificationFailed ||
|
||||||
firstDeployCall instanceof DeploymentNotFound ||
|
deployment instanceof SchemaValidationFailed ||
|
||||||
firstDeployCall instanceof DeploymentsRateLimited
|
deployment instanceof DeploymentNotFound ||
|
||||||
|
deployment instanceof DeploymentsRateLimited
|
||||||
) {
|
) {
|
||||||
handleCreateDeployError(output, firstDeployCall);
|
handleCreateDeployError(output, deployment);
|
||||||
await exit(1);
|
await exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
deployment = firstDeployCall;
|
|
||||||
|
|
||||||
if (now.syncFileCount > 0) {
|
|
||||||
const uploadStamp = stamp();
|
|
||||||
await new Promise(resolve => {
|
|
||||||
if (now.syncFileCount !== now.fileCount) {
|
|
||||||
debug(`Total files ${now.fileCount}, ${now.syncFileCount} changed`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const size = bytes(now.syncAmount);
|
|
||||||
syncCount = `${now.syncFileCount} file${
|
|
||||||
now.syncFileCount > 1 ? 's' : ''
|
|
||||||
}`;
|
|
||||||
const bar = new Progress(
|
|
||||||
`${chalk.gray(
|
|
||||||
'>'
|
|
||||||
)} Upload [:bar] :percent :etas (${size}) [${syncCount}]`,
|
|
||||||
{
|
|
||||||
width: 20,
|
|
||||||
complete: '=',
|
|
||||||
incomplete: '',
|
|
||||||
total: now.syncAmount,
|
|
||||||
clear: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
now.upload({ scale });
|
|
||||||
|
|
||||||
now.on(
|
|
||||||
'upload',
|
|
||||||
({ names, data }: { names: string[]; data: Buffer }) => {
|
|
||||||
debug(`Uploaded: ${names.join(' ')} (${bytes(data.length)})`);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
now.on('uploadProgress', (progress: number) => {
|
|
||||||
bar.tick(progress);
|
|
||||||
});
|
|
||||||
|
|
||||||
now.on('complete', resolve);
|
|
||||||
|
|
||||||
now.on('error', (err: Error) => {
|
|
||||||
error('Upload failed');
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!quiet && syncCount) {
|
|
||||||
log(
|
|
||||||
`Synced ${syncCount} (${bytes(now.syncAmount)}) ${uploadStamp()}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < 4; i += 1) {
|
|
||||||
deployStamp = stamp();
|
|
||||||
const secondDeployCall = await createDeploy(
|
|
||||||
output,
|
|
||||||
now,
|
|
||||||
contextName,
|
|
||||||
paths,
|
|
||||||
createArgs
|
|
||||||
);
|
|
||||||
|
|
||||||
const handledResult = handleCertError(output, secondDeployCall);
|
|
||||||
if (handledResult === 1) {
|
|
||||||
return handledResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
secondDeployCall instanceof DomainNotFound ||
|
|
||||||
secondDeployCall instanceof DomainPermissionDenied ||
|
|
||||||
secondDeployCall instanceof DomainVerificationFailed ||
|
|
||||||
secondDeployCall instanceof SchemaValidationFailed ||
|
|
||||||
secondDeployCall instanceof TooManyRequests ||
|
|
||||||
secondDeployCall instanceof DeploymentNotFound ||
|
|
||||||
secondDeployCall instanceof DeploymentsRateLimited
|
|
||||||
) {
|
|
||||||
handleCreateDeployError(output, secondDeployCall);
|
|
||||||
await exit(1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (now.syncFileCount === 0) {
|
|
||||||
deployment = secondDeployCall;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deployment === null) {
|
|
||||||
error('Uploading failed. Please try again.');
|
|
||||||
await exit(1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code === 'plan_requires_public') {
|
if (err.code === 'plan_requires_public') {
|
||||||
if (!wantsPublic) {
|
if (!wantsPublic) {
|
||||||
@@ -915,7 +833,7 @@ async function sync({
|
|||||||
|
|
||||||
if (isTTY) {
|
if (isTTY) {
|
||||||
proceed = await promptBool('Are you sure you want to proceed?', {
|
proceed = await promptBool('Are you sure you want to proceed?', {
|
||||||
trailing: eraseLines(1)
|
trailing: eraseLines(1),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -955,10 +873,10 @@ async function sync({
|
|||||||
output,
|
output,
|
||||||
token,
|
token,
|
||||||
config: {
|
config: {
|
||||||
currentTeam
|
currentTeam,
|
||||||
},
|
},
|
||||||
firstRun: false,
|
firstRun: false,
|
||||||
deploymentType
|
deploymentType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1003,8 +921,6 @@ async function sync({
|
|||||||
} else {
|
} else {
|
||||||
log(`${chalk.bold(chalk.cyan(url))}${dcs} ${deployStamp()}`);
|
log(`${chalk.bold(chalk.cyan(url))}${dcs} ${deployStamp()}`);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
process.stdout.write(url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deploymentType === 'static') {
|
if (deploymentType === 'static') {
|
||||||
@@ -1023,49 +939,8 @@ async function sync({
|
|||||||
// Show build logs
|
// Show build logs
|
||||||
// (We have to add this check for flow but it will never happen)
|
// (We have to add this check for flow but it will never happen)
|
||||||
if (deployment !== null) {
|
if (deployment !== null) {
|
||||||
// If the created deployment is ready it was a deduping and we should exit
|
|
||||||
if (deployment.readyState !== 'READY') {
|
|
||||||
require('assert')(deployment); // mute linter
|
|
||||||
const instanceIndex = getInstanceIndex();
|
const instanceIndex = getInstanceIndex();
|
||||||
const eventsStream = await maybeGetEventsStream(now, deployment);
|
const eventsStream = await maybeGetEventsStream(now, deployment);
|
||||||
const eventsGenerator = getEventsGenerator(
|
|
||||||
now,
|
|
||||||
contextName,
|
|
||||||
deployment,
|
|
||||||
eventsStream
|
|
||||||
);
|
|
||||||
|
|
||||||
for await (const _event of eventsGenerator) {
|
|
||||||
const event = _event as any;
|
|
||||||
// Stop when the deployment is ready
|
|
||||||
if (
|
|
||||||
event.type === 'state-change' &&
|
|
||||||
event.payload.value === 'READY'
|
|
||||||
) {
|
|
||||||
output.log(`Build completed`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop then there is an error state
|
|
||||||
if (
|
|
||||||
event.type === 'state-change' &&
|
|
||||||
event.payload.value === 'ERROR'
|
|
||||||
) {
|
|
||||||
output.error(`Build failed`);
|
|
||||||
await exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For any relevant event we receive, print the result
|
|
||||||
if (event.type === 'build-start') {
|
|
||||||
output.log('Building…');
|
|
||||||
} else if (event.type === 'command') {
|
|
||||||
output.log(formatLogCmd(event.payload.text));
|
|
||||||
} else if (event.type === 'stdout' || event.type === 'stderr') {
|
|
||||||
formatLogOutput(event.payload.text).forEach((msg: string) =>
|
|
||||||
output.log(msg)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!noVerify) {
|
if (!noVerify) {
|
||||||
output.log(
|
output.log(
|
||||||
@@ -1085,12 +960,10 @@ async function sync({
|
|||||||
const dcOrEvent = _dcOrEvent as any;
|
const dcOrEvent = _dcOrEvent as any;
|
||||||
if (dcOrEvent instanceof VerifyScaleTimeout) {
|
if (dcOrEvent instanceof VerifyScaleTimeout) {
|
||||||
output.error(
|
output.error(
|
||||||
`Instance verification timed out (${ms(
|
`Instance verification timed out (${ms(dcOrEvent.meta.timeout)})`
|
||||||
dcOrEvent.meta.timeout
|
|
||||||
)})`
|
|
||||||
);
|
);
|
||||||
output.log(
|
output.log(
|
||||||
'Read more: https://err.sh/now/verification-timeout'
|
'Read more: https://err.sh/now-cli/verification-timeout'
|
||||||
);
|
);
|
||||||
await exit(1);
|
await exit(1);
|
||||||
} else if (Array.isArray(dcOrEvent)) {
|
} else if (Array.isArray(dcOrEvent)) {
|
||||||
@@ -1115,7 +988,6 @@ async function sync({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
output.success(`Deployment ready`);
|
output.success(`Deployment ready`);
|
||||||
await exit(0);
|
await exit(0);
|
||||||
@@ -1134,7 +1006,7 @@ async function readMeta(
|
|||||||
deploymentType,
|
deploymentType,
|
||||||
deploymentName: _deploymentName,
|
deploymentName: _deploymentName,
|
||||||
quiet: true,
|
quiet: true,
|
||||||
sessionAffinity: _sessionAffinity
|
sessionAffinity: _sessionAffinity,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!deploymentType) {
|
if (!deploymentType) {
|
||||||
@@ -1151,7 +1023,7 @@ async function readMeta(
|
|||||||
meta,
|
meta,
|
||||||
deploymentName: _deploymentName,
|
deploymentName: _deploymentName,
|
||||||
deploymentType,
|
deploymentType,
|
||||||
sessionAffinity: _sessionAffinity
|
sessionAffinity: _sessionAffinity,
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isTTY && err.code === 'multiple_manifests') {
|
if (isTTY && err.code === 'multiple_manifests') {
|
||||||
@@ -1165,7 +1037,7 @@ async function readMeta(
|
|||||||
try {
|
try {
|
||||||
deploymentType = await promptOptions([
|
deploymentType = await promptOptions([
|
||||||
['npm', `${chalk.bold('package.json')}\t${chalk.gray(' --npm')} `],
|
['npm', `${chalk.bold('package.json')}\t${chalk.gray(' --npm')} `],
|
||||||
['docker', `${chalk.bold('Dockerfile')}\t${chalk.gray('--docker')} `]
|
['docker', `${chalk.bold('Dockerfile')}\t${chalk.gray('--docker')} `],
|
||||||
]);
|
]);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -1191,35 +1063,13 @@ async function maybeGetEventsStream(now: Now, deployment: any) {
|
|||||||
try {
|
try {
|
||||||
return await getEventsStream(now, deployment.deploymentId, {
|
return await getEventsStream(now, deployment.deploymentId, {
|
||||||
direction: 'forward',
|
direction: 'forward',
|
||||||
follow: true
|
follow: true,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEventsGenerator(
|
|
||||||
now: Now,
|
|
||||||
contextName: string,
|
|
||||||
deployment: any,
|
|
||||||
eventsStream: any
|
|
||||||
) {
|
|
||||||
const stateChangeFromPollingGenerator = getStateChangeFromPolling(
|
|
||||||
now,
|
|
||||||
contextName,
|
|
||||||
deployment.deploymentId,
|
|
||||||
deployment.readyState
|
|
||||||
);
|
|
||||||
if (eventsStream !== null) {
|
|
||||||
return combineAsyncGenerators(
|
|
||||||
eventListenerToGenerator('data', eventsStream),
|
|
||||||
stateChangeFromPollingGenerator
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return stateChangeFromPollingGenerator;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getVerifyDCsGenerator(
|
function getVerifyDCsGenerator(
|
||||||
output: Output,
|
output: Output,
|
||||||
now: Now,
|
now: Now,
|
||||||
@@ -1229,7 +1079,7 @@ function getVerifyDCsGenerator(
|
|||||||
const verifyDeployment = verifyDeploymentScale(
|
const verifyDeployment = verifyDeploymentScale(
|
||||||
output,
|
output,
|
||||||
now,
|
now,
|
||||||
deployment.deploymentId,
|
deployment.deploymentId || deployment.uid,
|
||||||
deployment.scale
|
deployment.scale
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1296,9 +1146,9 @@ function handleCreateDeployError(output: Output, error: Error) {
|
|||||||
output.error(
|
output.error(
|
||||||
`Failed to validate ${highlight(
|
`Failed to validate ${highlight(
|
||||||
'now.json'
|
'now.json'
|
||||||
)}: ${message}\nDocumentation: ${
|
)}: ${message}\nDocumentation: ${link(
|
||||||
link('https://zeit.co/docs/v2/advanced/configuration')
|
'https://zeit.co/docs/v2/advanced/configuration'
|
||||||
}`
|
)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -1308,7 +1158,7 @@ function handleCreateDeployError(output: Output, error: Error) {
|
|||||||
`Too many requests detected for ${error.meta.api} API. Try again in ${ms(
|
`Too many requests detected for ${error.meta.api} API. Try again in ${ms(
|
||||||
error.meta.retryAfter * 1000,
|
error.meta.retryAfter * 1000,
|
||||||
{
|
{
|
||||||
long: true
|
long: true,
|
||||||
}
|
}
|
||||||
)}.`
|
)}.`
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
import { PackageJson } from '@now/build-utils';
|
||||||
|
|
||||||
import getArgs from '../../util/get-args';
|
import getArgs from '../../util/get-args';
|
||||||
import getSubcommand from '../../util/get-subcommand';
|
import getSubcommand from '../../util/get-subcommand';
|
||||||
@@ -11,11 +12,10 @@ import logo from '../../util/output/logo';
|
|||||||
import cmd from '../../util/output/cmd';
|
import cmd from '../../util/output/cmd';
|
||||||
import dev from './dev';
|
import dev from './dev';
|
||||||
import readPackage from '../../util/read-package';
|
import readPackage from '../../util/read-package';
|
||||||
import { Package } from '../../util/dev/types';
|
|
||||||
import readConfig from '../../util/config/read-config';
|
import readConfig from '../../util/config/read-config';
|
||||||
|
|
||||||
const COMMAND_CONFIG = {
|
const COMMAND_CONFIG = {
|
||||||
dev: ['dev']
|
dev: ['dev'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const help = () => {
|
const help = () => {
|
||||||
@@ -54,18 +54,12 @@ export default async function main(ctx: NowContext) {
|
|||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
'--port': Number,
|
'--port': Number,
|
||||||
'-p': '--port'
|
'-p': '--port',
|
||||||
});
|
});
|
||||||
const debug = argv['--debug'];
|
const debug = argv['--debug'];
|
||||||
args = getSubcommand(argv._.slice(1), COMMAND_CONFIG).args;
|
args = getSubcommand(argv._.slice(1), COMMAND_CONFIG).args;
|
||||||
output = createOutput({ debug });
|
output = createOutput({ debug });
|
||||||
|
|
||||||
// Builders won't show debug logs by default
|
|
||||||
// the `NOW_BUILDER_DEBUG` env variable will enable them
|
|
||||||
if (debug) {
|
|
||||||
process.env.NOW_BUILDER_DEBUG = '1';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('--port' in argv) {
|
if ('--port' in argv) {
|
||||||
output.warn('`--port` is deprecated, please use `--listen` instead');
|
output.warn('`--port` is deprecated, please use `--listen` instead');
|
||||||
argv['--listen'] = String(argv['--port']);
|
argv['--listen'] = String(argv['--port']);
|
||||||
@@ -90,7 +84,7 @@ export default async function main(ctx: NowContext) {
|
|||||||
const pkg = await readPackage(path.join(dir, 'package.json'));
|
const pkg = await readPackage(path.join(dir, 'package.json'));
|
||||||
|
|
||||||
if (pkg) {
|
if (pkg) {
|
||||||
const { scripts } = pkg as Package;
|
const { scripts } = pkg as PackageJson;
|
||||||
|
|
||||||
if (scripts && scripts.dev && /\bnow\b\W+\bdev\b/.test(scripts.dev)) {
|
if (scripts && scripts.dev && /\bnow\b\W+\bdev\b/.test(scripts.dev)) {
|
||||||
output.error(
|
output.error(
|
||||||
@@ -98,9 +92,7 @@ export default async function main(ctx: NowContext) {
|
|||||||
'package.json'
|
'package.json'
|
||||||
)} must not contain ${cmd('now dev')}`
|
)} must not contain ${cmd('now dev')}`
|
||||||
);
|
);
|
||||||
output.error(
|
output.error(`More details: http://err.sh/now/now-dev-as-dev-script`);
|
||||||
`More details: http://err.sh/now/now-dev-as-dev-script`
|
|
||||||
);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default async function verify(
|
|||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
authConfig: { token },
|
authConfig: { token },
|
||||||
config
|
config,
|
||||||
} = ctx;
|
} = ctx;
|
||||||
const { currentTeam } = config;
|
const { currentTeam } = config;
|
||||||
const { apiUrl } = ctx;
|
const { apiUrl } = ctx;
|
||||||
@@ -122,7 +122,15 @@ export default async function verify(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.txtVerifiedAt) {
|
if (result.nsVerifiedAt) {
|
||||||
|
console.log(
|
||||||
|
`${chalk.cyan('> Success!')} Domain ${chalk.bold(
|
||||||
|
domain.name
|
||||||
|
)} was verified using nameservers. ${verifyStamp()}`
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`${chalk.cyan('> Success!')} Domain ${chalk.bold(
|
`${chalk.cyan('> Success!')} Domain ${chalk.bold(
|
||||||
domain.name
|
domain.name
|
||||||
@@ -135,11 +143,3 @@ export default async function verify(
|
|||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
|
||||||
`${chalk.cyan('> Success!')} Domain ${chalk.bold(
|
|
||||||
domain.name
|
|
||||||
)} was verified using nameservers. ${verifyStamp()}`
|
|
||||||
);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default async function main(ctx) {
|
|||||||
'--all': Boolean,
|
'--all': Boolean,
|
||||||
'--meta': [String],
|
'--meta': [String],
|
||||||
'-a': '--all',
|
'-a': '--all',
|
||||||
'-m': '--meta'
|
'-m': '--meta',
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
handleError(err);
|
handleError(err);
|
||||||
@@ -84,7 +84,7 @@ export default async function main(ctx) {
|
|||||||
const debugEnabled = argv['--debug'];
|
const debugEnabled = argv['--debug'];
|
||||||
|
|
||||||
const { print, log, error, note, debug } = createOutput({
|
const { print, log, error, note, debug } = createOutput({
|
||||||
debug: debugEnabled
|
debug: debugEnabled,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (argv._.length > 2) {
|
if (argv._.length > 2) {
|
||||||
@@ -103,13 +103,16 @@ export default async function main(ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const meta = parseMeta(argv['--meta']);
|
const meta = parseMeta(argv['--meta']);
|
||||||
const { authConfig: { token }, config } = ctx;
|
const {
|
||||||
|
authConfig: { token },
|
||||||
|
config,
|
||||||
|
} = ctx;
|
||||||
const { currentTeam, includeScheme } = config;
|
const { currentTeam, includeScheme } = config;
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
apiUrl,
|
apiUrl,
|
||||||
token,
|
token,
|
||||||
currentTeam,
|
currentTeam,
|
||||||
debug: debugEnabled
|
debug: debugEnabled,
|
||||||
});
|
});
|
||||||
let contextName = null;
|
let contextName = null;
|
||||||
|
|
||||||
@@ -202,7 +205,16 @@ export default async function main(ctx) {
|
|||||||
const item = aliases.find(e => e.uid === app || e.alias === app);
|
const item = aliases.find(e => e.uid === app || e.alias === app);
|
||||||
|
|
||||||
if (item) {
|
if (item) {
|
||||||
debug('Found alias that matches app name');
|
debug(`Found alias that matches app name: ${item.alias}`);
|
||||||
|
|
||||||
|
if (Array.isArray(item.rules)) {
|
||||||
|
now.close();
|
||||||
|
stopSpinner();
|
||||||
|
log(`Found matching path alias: ${chalk.cyan(item.alias)}`);
|
||||||
|
log(`Please run ${cmd(`now alias ls ${item.alias}`)} instead`);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const match = await now.findDeployment(item.deploymentId);
|
const match = await now.findDeployment(item.deploymentId);
|
||||||
const instances = await getDeploymentInstances(
|
const instances = await getDeploymentInstances(
|
||||||
now,
|
now,
|
||||||
@@ -250,7 +262,9 @@ export default async function main(ctx) {
|
|||||||
|
|
||||||
// information to help the user find other deployments or instances
|
// information to help the user find other deployments or instances
|
||||||
if (app == null) {
|
if (app == null) {
|
||||||
log(`To list more deployments for a project run ${cmd('now ls [project]')}`);
|
log(
|
||||||
|
`To list more deployments for a project run ${cmd('now ls [project]')}`
|
||||||
|
);
|
||||||
} else if (!argv['--all']) {
|
} else if (!argv['--all']) {
|
||||||
log(`To list deployment instances run ${cmd('now ls --all [project]')}`);
|
log(`To list deployment instances run ${cmd('now ls --all [project]')}`);
|
||||||
}
|
}
|
||||||
@@ -260,7 +274,9 @@ export default async function main(ctx) {
|
|||||||
console.log(
|
console.log(
|
||||||
`${table(
|
`${table(
|
||||||
[
|
[
|
||||||
['project', 'latest deployment', 'inst #', 'type', 'state', 'age'].map(s => chalk.dim(s)),
|
['project', 'latest deployment', 'inst #', 'type', 'state', 'age'].map(
|
||||||
|
s => chalk.dim(s)
|
||||||
|
),
|
||||||
...deployments
|
...deployments
|
||||||
.sort(sortRecent())
|
.sort(sortRecent())
|
||||||
.map(dep => [
|
.map(dep => [
|
||||||
@@ -272,7 +288,7 @@ export default async function main(ctx) {
|
|||||||
: dep.instanceCount,
|
: dep.instanceCount,
|
||||||
dep.type === 'LAMBDAS' ? chalk.gray('-') : dep.type,
|
dep.type === 'LAMBDAS' ? chalk.gray('-') : dep.type,
|
||||||
stateString(dep.state),
|
stateString(dep.state),
|
||||||
chalk.gray(ms(Date.now() - new Date(dep.created)))
|
chalk.gray(ms(Date.now() - new Date(dep.created))),
|
||||||
],
|
],
|
||||||
...(argv['--all']
|
...(argv['--all']
|
||||||
? dep.instances.map(i => [
|
? dep.instances.map(i => [
|
||||||
@@ -280,9 +296,9 @@ export default async function main(ctx) {
|
|||||||
` ${chalk.gray('-')} ${i.url} `,
|
` ${chalk.gray('-')} ${i.url} `,
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
''
|
'',
|
||||||
])
|
])
|
||||||
: [])
|
: []),
|
||||||
])
|
])
|
||||||
// flatten since the previous step returns a nested
|
// flatten since the previous step returns a nested
|
||||||
// array of the deployment and (optionally) its instances
|
// array of the deployment and (optionally) its instances
|
||||||
@@ -293,12 +309,12 @@ export default async function main(ctx) {
|
|||||||
// we only want to render one deployment per app
|
// we only want to render one deployment per app
|
||||||
filterUniqueApps()
|
filterUniqueApps()
|
||||||
: () => true
|
: () => true
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
align: ['l', 'l', 'r', 'l', 'b'],
|
align: ['l', 'l', 'r', 'l', 'b'],
|
||||||
hsep: ' '.repeat(4),
|
hsep: ' '.repeat(4),
|
||||||
stringLength: strlen
|
stringLength: strlen,
|
||||||
}
|
}
|
||||||
).replace(/^/gm, ' ')}\n\n`
|
).replace(/^/gm, ' ')}\n\n`
|
||||||
);
|
);
|
||||||
@@ -310,7 +326,7 @@ function getProjectName(d) {
|
|||||||
return 'files';
|
return 'files';
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.name
|
return d.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// renders the state string
|
// renders the state string
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ export default async function main(ctx) {
|
|||||||
debug: 'd',
|
debug: 'd',
|
||||||
query: 'q',
|
query: 'q',
|
||||||
follow: 'f',
|
follow: 'f',
|
||||||
output: 'o'
|
output: 'o',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
argv._ = argv._.slice(1);
|
argv._ = argv._.slice(1);
|
||||||
@@ -136,14 +136,17 @@ export default async function main(ctx) {
|
|||||||
types = argv.all ? [] : ['command', 'stdout', 'stderr', 'exit'];
|
types = argv.all ? [] : ['command', 'stdout', 'stderr', 'exit'];
|
||||||
outputMode = argv.output in logPrinters ? argv.output : 'short';
|
outputMode = argv.output in logPrinters ? argv.output : 'short';
|
||||||
|
|
||||||
const { authConfig: { token }, config } = ctx;
|
const {
|
||||||
|
authConfig: { token },
|
||||||
|
config,
|
||||||
|
} = ctx;
|
||||||
const { currentTeam } = config;
|
const { currentTeam } = config;
|
||||||
const now = new Now({ apiUrl, token, debug, currentTeam });
|
const now = new Now({ apiUrl, token, debug, currentTeam });
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
apiUrl,
|
apiUrl,
|
||||||
token,
|
token,
|
||||||
currentTeam,
|
currentTeam,
|
||||||
debug: debugEnabled
|
debug: debugEnabled,
|
||||||
});
|
});
|
||||||
let contextName = null;
|
let contextName = null;
|
||||||
|
|
||||||
@@ -206,7 +209,7 @@ export default async function main(ctx) {
|
|||||||
types,
|
types,
|
||||||
instanceId,
|
instanceId,
|
||||||
since,
|
since,
|
||||||
until
|
until,
|
||||||
}; // no follow
|
}; // no follow
|
||||||
const storage = [];
|
const storage = [];
|
||||||
const storeEvent = event => storage.push(event);
|
const storeEvent = event => storage.push(event);
|
||||||
@@ -216,7 +219,7 @@ export default async function main(ctx) {
|
|||||||
onEvent: storeEvent,
|
onEvent: storeEvent,
|
||||||
quiet: false,
|
quiet: false,
|
||||||
debug,
|
debug,
|
||||||
findOpts: findOpts1
|
findOpts: findOpts1,
|
||||||
});
|
});
|
||||||
|
|
||||||
const printedEventIds = new Set();
|
const printedEventIds = new Set();
|
||||||
@@ -238,14 +241,14 @@ export default async function main(ctx) {
|
|||||||
types,
|
types,
|
||||||
instanceId,
|
instanceId,
|
||||||
since: since2,
|
since: since2,
|
||||||
follow: true
|
follow: true,
|
||||||
};
|
};
|
||||||
await printEvents(now, deployment.uid || deployment.id, currentTeam, {
|
await printEvents(now, deployment.uid || deployment.id, currentTeam, {
|
||||||
mode: 'logs',
|
mode: 'logs',
|
||||||
onEvent: printEvent,
|
onEvent: printEvent,
|
||||||
quiet: false,
|
quiet: false,
|
||||||
debug,
|
debug,
|
||||||
findOpts: findOpts2
|
findOpts: findOpts2,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,25 +283,53 @@ function printLogShort(log) {
|
|||||||
` ${obj.status} ${obj.bodyBytesSent}`;
|
` ${obj.status} ${obj.bodyBytesSent}`;
|
||||||
} else if (log.type === 'event') {
|
} else if (log.type === 'event') {
|
||||||
data = `EVENT ${log.event} ${JSON.stringify(log.payload)}`;
|
data = `EVENT ${log.event} ${JSON.stringify(log.payload)}`;
|
||||||
|
} else if (obj) {
|
||||||
|
data = JSON.stringify(obj, null, 2);
|
||||||
} else {
|
} else {
|
||||||
data = obj
|
data = (log.text || '')
|
||||||
? JSON.stringify(obj, null, 2)
|
|
||||||
: (log.text || '')
|
|
||||||
.replace(/\n$/, '')
|
.replace(/\n$/, '')
|
||||||
.replace(/^\n/, '')
|
.replace(/^\n/, '')
|
||||||
// eslint-disable-next-line no-control-regex
|
// eslint-disable-next-line no-control-regex
|
||||||
.replace(/\x1b\[1000D/g, '')
|
.replace(/\x1b\[1000D/g, '')
|
||||||
.replace(/\x1b\[0K/g, '')
|
.replace(/\x1b\[0K/g, '')
|
||||||
.replace(/\x1b\[1A/g, '');
|
.replace(/\x1b\[1A/g, '');
|
||||||
|
if (/warning/i.test(data)) {
|
||||||
|
data = chalk.yellow(data);
|
||||||
|
} else if (log.type === 'stderr') {
|
||||||
|
data = chalk.red(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const date = new Date(log.created).toISOString();
|
const date = new Date(log.created).toISOString();
|
||||||
|
|
||||||
data.split('\n').forEach((line, i) => {
|
data.split('\n').forEach((line, i) => {
|
||||||
|
if (
|
||||||
|
line.includes('START RequestId:') ||
|
||||||
|
line.includes('END RequestId:') ||
|
||||||
|
line.includes('XRAY TraceId:')
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.includes('REPORT RequestId:')) {
|
||||||
|
line = line.substring(line.indexOf('Duration:'), line.length);
|
||||||
|
|
||||||
|
if (line.includes('Init Duration:')) {
|
||||||
|
line = line.substring(0, line.indexOf('Init Duration:'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
console.log(`${chalk.dim(date)} ${line}`);
|
console.log(
|
||||||
|
`${chalk.dim(date)} ${line.replace('[now-builder-debug] ', '')}`
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
console.log(`${' '.repeat(date.length)} ${line}`);
|
console.log(
|
||||||
|
`${' '.repeat(date.length)} ${line.replace(
|
||||||
|
'[now-builder-debug] ',
|
||||||
|
''
|
||||||
|
)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -327,7 +358,7 @@ function printLogRaw(log) {
|
|||||||
|
|
||||||
const logPrinters = {
|
const logPrinters = {
|
||||||
short: printLogShort,
|
short: printLogShort,
|
||||||
raw: printLogRaw
|
raw: printLogRaw,
|
||||||
};
|
};
|
||||||
|
|
||||||
function toTimestamp(datestr) {
|
function toTimestamp(datestr) {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import Client from '../util/client.ts';
|
|||||||
import logo from '../util/output/logo';
|
import logo from '../util/output/logo';
|
||||||
import getScope from '../util/get-scope';
|
import getScope from '../util/get-scope';
|
||||||
|
|
||||||
const e = encodeURIComponent
|
const e = encodeURIComponent;
|
||||||
|
|
||||||
const help = () => {
|
const help = () => {
|
||||||
console.log(`
|
console.log(`
|
||||||
@@ -48,8 +48,8 @@ const main = async ctx => {
|
|||||||
argv = mri(ctx.argv.slice(2), {
|
argv = mri(ctx.argv.slice(2), {
|
||||||
boolean: ['help'],
|
boolean: ['help'],
|
||||||
alias: {
|
alias: {
|
||||||
help: 'h'
|
help: 'h',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
argv._ = argv._.slice(1);
|
argv._ = argv._.slice(1);
|
||||||
@@ -63,7 +63,10 @@ const main = async ctx => {
|
|||||||
await exit(0);
|
await exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { authConfig: { token }, config: { currentTeam }} = ctx;
|
const {
|
||||||
|
authConfig: { token },
|
||||||
|
config: { currentTeam },
|
||||||
|
} = ctx;
|
||||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
const client = new Client({ apiUrl, token, currentTeam, debug });
|
||||||
|
|
||||||
const { contextName } = await getScope(client);
|
const { contextName } = await getScope(client);
|
||||||
@@ -93,17 +96,21 @@ async function run({ client, contextName }) {
|
|||||||
if (args.length !== 0) {
|
if (args.length !== 0) {
|
||||||
console.error(
|
console.error(
|
||||||
error(
|
error(
|
||||||
`Invalid number of arguments. Usage: ${chalk.cyan('`now projects ls`')}`
|
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||||
|
'`now projects ls`'
|
||||||
|
)}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return exit(1);
|
return exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const list = await client.fetch('/projects/list', {method: 'GET'});
|
const list = await client.fetch('/v2/projects/', { method: 'GET' });
|
||||||
const elapsed = ms(new Date() - start);
|
const elapsed = ms(new Date() - start);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`> ${plural('project', list.length, true)} found under ${chalk.bold(contextName)} ${chalk.gray(`[${elapsed}]`)}`
|
`> ${plural('project', list.length, true)} found under ${chalk.bold(
|
||||||
|
contextName
|
||||||
|
)} ${chalk.gray(`[${elapsed}]`)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (list.length > 0) {
|
if (list.length > 0) {
|
||||||
@@ -115,13 +122,13 @@ async function run({ client, contextName }) {
|
|||||||
list.map(secret => [
|
list.map(secret => [
|
||||||
'',
|
'',
|
||||||
chalk.bold(secret.name),
|
chalk.bold(secret.name),
|
||||||
chalk.gray(`${ms(cur - new Date(secret.updatedAt)) } ago`)
|
chalk.gray(`${ms(cur - new Date(secret.updatedAt))} ago`),
|
||||||
])
|
])
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
align: ['l', 'l', 'l'],
|
align: ['l', 'l', 'l'],
|
||||||
hsep: ' '.repeat(2),
|
hsep: ' '.repeat(2),
|
||||||
stringLength: strlen
|
stringLength: strlen,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -148,11 +155,11 @@ async function run({ client, contextName }) {
|
|||||||
|
|
||||||
// Check the existence of the project
|
// Check the existence of the project
|
||||||
try {
|
try {
|
||||||
await client.fetch(`/projects/info/${e(name)}`)
|
await client.fetch(`/projects/info/${e(name)}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.status === 404) {
|
if (err.status === 404) {
|
||||||
console.error(error('No such project exists'))
|
console.error(error('No such project exists'));
|
||||||
return exit(1)
|
return exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +169,9 @@ async function run({ client, contextName }) {
|
|||||||
return exit(0);
|
return exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.fetch('/projects/remove', {method: 'DELETE', body: {name}});
|
await client.fetch(`/v2/projects/${name}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
const elapsed = ms(new Date() - start);
|
const elapsed = ms(new Date() - start);
|
||||||
console.log(
|
console.log(
|
||||||
`${chalk.cyan('> Success!')} Project ${chalk.bold(
|
`${chalk.cyan('> Success!')} Project ${chalk.bold(
|
||||||
@@ -193,7 +202,10 @@ async function run({ client, contextName }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const [name] = args;
|
const [name] = args;
|
||||||
await client.fetch('/projects/ensure-project', {method: 'POST', body: {name}});
|
await client.fetch('/projects/ensure-project', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { name },
|
||||||
|
});
|
||||||
const elapsed = ms(new Date() - start);
|
const elapsed = ms(new Date() - start);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
@@ -204,9 +216,7 @@ async function run({ client, contextName }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(
|
console.error(error('Please specify a valid subcommand: ls | add | rm'));
|
||||||
error('Please specify a valid subcommand: ls | add | rm')
|
|
||||||
);
|
|
||||||
help();
|
help();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,11 +70,12 @@ let subcommand;
|
|||||||
|
|
||||||
const main = async ctx => {
|
const main = async ctx => {
|
||||||
argv = mri(ctx.argv.slice(2), {
|
argv = mri(ctx.argv.slice(2), {
|
||||||
boolean: ['help', 'debug'],
|
boolean: ['help', 'debug', 'yes'],
|
||||||
alias: {
|
alias: {
|
||||||
help: 'h',
|
help: 'h',
|
||||||
debug: 'd'
|
debug: 'd',
|
||||||
}
|
yes: 'y',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
argv._ = argv._.slice(1);
|
argv._ = argv._.slice(1);
|
||||||
@@ -88,7 +89,10 @@ const main = async ctx => {
|
|||||||
await exit(0);
|
await exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { authConfig: { token }, config: { currentTeam } } = ctx;
|
const {
|
||||||
|
authConfig: { token },
|
||||||
|
config: { currentTeam },
|
||||||
|
} = ctx;
|
||||||
const output = createOutput({ debug });
|
const output = createOutput({ debug });
|
||||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
const client = new Client({ apiUrl, token, currentTeam, debug });
|
||||||
let contextName = null;
|
let contextName = null;
|
||||||
@@ -105,7 +109,7 @@ const main = async ctx => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await run({ token, contextName, currentTeam });
|
await run({ output, token, contextName, currentTeam });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
handleError(err);
|
handleError(err);
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -121,7 +125,7 @@ export default async ctx => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async function run({ token, contextName, currentTeam }) {
|
async function run({ output, token, contextName, currentTeam }) {
|
||||||
const secrets = new NowSecrets({ apiUrl, token, debug, currentTeam });
|
const secrets = new NowSecrets({ apiUrl, token, debug, currentTeam });
|
||||||
const args = argv._.slice(1);
|
const args = argv._.slice(1);
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
@@ -153,13 +157,13 @@ async function run({ token, contextName, currentTeam }) {
|
|||||||
list.map(secret => [
|
list.map(secret => [
|
||||||
'',
|
'',
|
||||||
chalk.bold(secret.name),
|
chalk.bold(secret.name),
|
||||||
chalk.gray(`${ms(cur - new Date(secret.created))} ago`)
|
chalk.gray(`${ms(cur - new Date(secret.created))} ago`),
|
||||||
])
|
])
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
align: ['l', 'l', 'l'],
|
align: ['l', 'l', 'l'],
|
||||||
hsep: ' '.repeat(2),
|
hsep: ' '.repeat(2),
|
||||||
stringLength: strlen
|
stringLength: strlen,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -185,7 +189,7 @@ async function run({ token, contextName, currentTeam }) {
|
|||||||
const theSecret = list.find(secret => secret.name === args[0]);
|
const theSecret = list.find(secret => secret.name === args[0]);
|
||||||
|
|
||||||
if (theSecret) {
|
if (theSecret) {
|
||||||
const yes = await readConfirmation(theSecret);
|
const yes = argv.yes || (await readConfirmation(theSecret));
|
||||||
if (!yes) {
|
if (!yes) {
|
||||||
console.error(error('User abort'));
|
console.error(error('User abort'));
|
||||||
return exit(0);
|
return exit(0);
|
||||||
@@ -250,6 +254,10 @@ async function run({ token, contextName, currentTeam }) {
|
|||||||
await secrets.add(name, value);
|
await secrets.add(name, value);
|
||||||
const elapsed = ms(new Date() - start);
|
const elapsed = ms(new Date() - start);
|
||||||
|
|
||||||
|
if (name !== name.toLowerCase()) {
|
||||||
|
output.warn(`Your secret name was converted to lower-case`);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`${chalk.cyan('> Success!')} Secret ${chalk.bold(
|
`${chalk.cyan('> Success!')} Secret ${chalk.bold(
|
||||||
name.toLowerCase()
|
name.toLowerCase()
|
||||||
@@ -275,7 +283,7 @@ function readConfirmation(secret) {
|
|||||||
const time = chalk.gray(`${ms(new Date() - new Date(secret.created))} ago`);
|
const time = chalk.gray(`${ms(new Date() - new Date(secret.created))} ago`);
|
||||||
const tbl = table([[chalk.bold(secret.name), time]], {
|
const tbl = table([[chalk.bold(secret.name), time]], {
|
||||||
align: ['r', 'l'],
|
align: ['r', 'l'],
|
||||||
hsep: ' '.repeat(6)
|
hsep: ' '.repeat(6),
|
||||||
});
|
});
|
||||||
|
|
||||||
process.stdout.write(
|
process.stdout.write(
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import info from './util/output/info';
|
|||||||
import getNowDir from './util/config/global-path';
|
import getNowDir from './util/config/global-path';
|
||||||
import {
|
import {
|
||||||
getDefaultConfig,
|
getDefaultConfig,
|
||||||
getDefaultAuthConfig
|
getDefaultAuthConfig,
|
||||||
} from './util/config/get-default';
|
} from './util/config/get-default';
|
||||||
import hp from './util/humanize-path';
|
import hp from './util/humanize-path';
|
||||||
import commands from './commands/index.ts';
|
import commands from './commands/index.ts';
|
||||||
@@ -53,7 +53,7 @@ sourceMap.install();
|
|||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: SENTRY_DSN,
|
dsn: SENTRY_DSN,
|
||||||
release: `now-cli@${pkg.version}`,
|
release: `now-cli@${pkg.version}`,
|
||||||
environment: pkg.version.includes('canary') ? 'canary' : 'stable'
|
environment: pkg.version.includes('canary') ? 'canary' : 'stable',
|
||||||
});
|
});
|
||||||
|
|
||||||
let debug = () => {};
|
let debug = () => {};
|
||||||
@@ -71,7 +71,7 @@ const main = async argv_ => {
|
|||||||
'--version': Boolean,
|
'--version': Boolean,
|
||||||
'-v': '--version',
|
'-v': '--version',
|
||||||
'--debug': Boolean,
|
'--debug': Boolean,
|
||||||
'-d': '--debug'
|
'-d': '--debug',
|
||||||
},
|
},
|
||||||
{ permissive: true }
|
{ permissive: true }
|
||||||
);
|
);
|
||||||
@@ -102,7 +102,10 @@ const main = async argv_ => {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localConfig instanceof NowError && !(localConfig instanceof ERRORS.CantFindConfig)) {
|
if (
|
||||||
|
localConfig instanceof NowError &&
|
||||||
|
!(localConfig instanceof ERRORS.CantFindConfig)
|
||||||
|
) {
|
||||||
output.error(`Failed to load local config file: ${localConfig.message}`);
|
output.error(`Failed to load local config file: ${localConfig.message}`);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -118,7 +121,7 @@ const main = async argv_ => {
|
|||||||
if (targetOrSubcommand !== 'update') {
|
if (targetOrSubcommand !== 'update') {
|
||||||
update = await checkForUpdate(pkg, {
|
update = await checkForUpdate(pkg, {
|
||||||
interval: ms('1d'),
|
interval: ms('1d'),
|
||||||
distTag: pkg.version.includes('canary') ? 'canary' : 'latest'
|
distTag: pkg.version.includes('canary') ? 'canary' : 'latest',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -135,7 +138,15 @@ const main = async argv_ => {
|
|||||||
console.log(
|
console.log(
|
||||||
info(
|
info(
|
||||||
`${chalk.bgRed('UPDATE AVAILABLE')} ` +
|
`${chalk.bgRed('UPDATE AVAILABLE')} ` +
|
||||||
`Run ${cmd(await getUpdateCommand())} to install Now CLI ${update.latest}`
|
`Run ${cmd(await getUpdateCommand())} to install Now CLI ${
|
||||||
|
update.latest
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
info(
|
||||||
|
`Changelog: https://github.com/zeit/now/releases/tag/now@${update.latest}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -307,9 +318,9 @@ const main = async argv_ => {
|
|||||||
console.error(
|
console.error(
|
||||||
error(
|
error(
|
||||||
`${'An unexpected error occurred while trying to write the ' +
|
`${'An unexpected error occurred while trying to write the ' +
|
||||||
`default now config to "${hp(
|
`default now config to "${hp(NOW_AUTH_CONFIG_PATH)}" `}${
|
||||||
NOW_AUTH_CONFIG_PATH
|
err.message
|
||||||
)}" `}${err.message}`
|
}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -329,7 +340,7 @@ const main = async argv_ => {
|
|||||||
config,
|
config,
|
||||||
authConfig,
|
authConfig,
|
||||||
localConfig,
|
localConfig,
|
||||||
argv: argv_
|
argv: argv_,
|
||||||
};
|
};
|
||||||
|
|
||||||
let subcommand;
|
let subcommand;
|
||||||
@@ -339,7 +350,8 @@ const main = async argv_ => {
|
|||||||
const targetPath = join(process.cwd(), targetOrSubcommand);
|
const targetPath = join(process.cwd(), targetOrSubcommand);
|
||||||
const targetPathExists = existsSync(targetPath);
|
const targetPathExists = existsSync(targetPath);
|
||||||
const subcommandExists =
|
const subcommandExists =
|
||||||
GLOBAL_COMMANDS.has(targetOrSubcommand) || commands.has(targetOrSubcommand);
|
GLOBAL_COMMANDS.has(targetOrSubcommand) ||
|
||||||
|
commands.has(targetOrSubcommand);
|
||||||
|
|
||||||
if (targetPathExists && subcommandExists) {
|
if (targetPathExists && subcommandExists) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -412,7 +424,7 @@ const main = async argv_ => {
|
|||||||
message:
|
message:
|
||||||
'No existing credentials found. Please run ' +
|
'No existing credentials found. Please run ' +
|
||||||
`${param('now login')} or pass ${param('--token')}`,
|
`${param('now login')} or pass ${param('--token')}`,
|
||||||
slug: 'no-credentials-found'
|
slug: 'no-credentials-found',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -426,7 +438,7 @@ const main = async argv_ => {
|
|||||||
message: `This command doesn't work with ${param(
|
message: `This command doesn't work with ${param(
|
||||||
'--token'
|
'--token'
|
||||||
)}. Please use ${param('--scope')}.`,
|
)}. Please use ${param('--scope')}.`,
|
||||||
slug: 'no-token-allowed'
|
slug: 'no-token-allowed',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -440,7 +452,7 @@ const main = async argv_ => {
|
|||||||
console.error(
|
console.error(
|
||||||
error({
|
error({
|
||||||
message: `You defined ${param('--token')}, but it's missing a value`,
|
message: `You defined ${param('--token')}, but it's missing a value`,
|
||||||
slug: 'missing-token-value'
|
slug: 'missing-token-value',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -459,11 +471,22 @@ const main = async argv_ => {
|
|||||||
const targetCommand = commands.get(subcommand);
|
const targetCommand = commands.get(subcommand);
|
||||||
|
|
||||||
if (argv['--team']) {
|
if (argv['--team']) {
|
||||||
output.warn(`The ${param('--team')} flag is deprecated. Please use ${param('--scope')} instead.`);
|
output.warn(
|
||||||
|
`The ${param('--team')} flag is deprecated. Please use ${param(
|
||||||
|
'--scope'
|
||||||
|
)} instead.`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof scope === 'string' && targetCommand !== 'login' && targetCommand !== 'dev' && !(targetCommand === 'teams' && argv._[3] !== 'invite')) {
|
if (
|
||||||
const { authConfig: { token } } = ctx;
|
typeof scope === 'string' &&
|
||||||
|
targetCommand !== 'login' &&
|
||||||
|
targetCommand !== 'dev' &&
|
||||||
|
!(targetCommand === 'teams' && argv._[3] !== 'invite')
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
authConfig: { token },
|
||||||
|
} = ctx;
|
||||||
const client = new Client({ apiUrl, token });
|
const client = new Client({ apiUrl, token });
|
||||||
|
|
||||||
let user = null;
|
let user = null;
|
||||||
@@ -475,7 +498,7 @@ const main = async argv_ => {
|
|||||||
console.error(
|
console.error(
|
||||||
error({
|
error({
|
||||||
message: `You do not have access to the specified account`,
|
message: `You do not have access to the specified account`,
|
||||||
slug: 'scope-not-accessible'
|
slug: 'scope-not-accessible',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -499,7 +522,7 @@ const main = async argv_ => {
|
|||||||
console.error(
|
console.error(
|
||||||
error({
|
error({
|
||||||
message: `You do not have access to the specified team`,
|
message: `You do not have access to the specified team`,
|
||||||
slug: 'scope-not-accessible'
|
slug: 'scope-not-accessible',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -517,7 +540,7 @@ const main = async argv_ => {
|
|||||||
console.error(
|
console.error(
|
||||||
error({
|
error({
|
||||||
message: 'The specified scope does not exist',
|
message: 'The specified scope does not exist',
|
||||||
slug: 'scope-not-existent'
|
slug: 'scope-not-existent',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -577,7 +600,8 @@ const main = async argv_ => {
|
|||||||
if (shouldCollectMetrics) {
|
if (shouldCollectMetrics) {
|
||||||
metric
|
metric
|
||||||
.event(eventCategory, '1', pkg.version)
|
.event(eventCategory, '1', pkg.version)
|
||||||
.exception(err.message).send();
|
.exception(err.message)
|
||||||
|
.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -586,7 +610,8 @@ const main = async argv_ => {
|
|||||||
if (shouldCollectMetrics) {
|
if (shouldCollectMetrics) {
|
||||||
metric
|
metric
|
||||||
.event(eventCategory, '1', pkg.version)
|
.event(eventCategory, '1', pkg.version)
|
||||||
.exception(err.message).send();
|
.exception(err.message)
|
||||||
|
.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise it is an unexpected error and we should show the trace
|
// Otherwise it is an unexpected error and we should show the trace
|
||||||
@@ -647,9 +672,7 @@ process.on('uncaughtException', handleUnexpected);
|
|||||||
// subcommands waiting for further data won't work (like `logs` and `logout`)!
|
// subcommands waiting for further data won't work (like `logs` and `logout`)!
|
||||||
main(process.argv)
|
main(process.argv)
|
||||||
.then(exitCode => {
|
.then(exitCode => {
|
||||||
|
process.exitCode = exitCode;
|
||||||
process.emit('nowExit');
|
process.emit('nowExit');
|
||||||
process.on('beforeExit', () => {
|
|
||||||
process.exit(exitCode);
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
.catch(handleUnexpected);
|
.catch(handleUnexpected);
|
||||||
|
|||||||
@@ -195,28 +195,31 @@ export type DNSRecord = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type SRVRecordData = {
|
type SRVRecordData = {
|
||||||
name: string,
|
name: string;
|
||||||
type: 'SRV',
|
type: 'SRV';
|
||||||
srv: {
|
srv: {
|
||||||
port: number,
|
port: number;
|
||||||
priority: number,
|
priority: number;
|
||||||
target: string,
|
target: string;
|
||||||
weight: number,
|
weight: number;
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
type MXRecordData = {
|
|
||||||
name: string,
|
|
||||||
type: 'MX',
|
|
||||||
value: string,
|
|
||||||
mxPriority: number,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DNSRecordData = {
|
type MXRecordData = {
|
||||||
name: string,
|
name: string;
|
||||||
type: string,
|
type: 'MX';
|
||||||
value: string,
|
value: string;
|
||||||
} | SRVRecordData | MXRecordData;
|
mxPriority: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DNSRecordData =
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
| SRVRecordData
|
||||||
|
| MXRecordData;
|
||||||
|
|
||||||
export interface Project {
|
export interface Project {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
import { JsonBody, StreamBody, context } from 'fetch-h2';
|
|
||||||
|
|
||||||
// Packages
|
|
||||||
import { parse } from 'url';
|
|
||||||
import Sema from 'async-sema';
|
|
||||||
import createOutput, { Output } from './output/create-output';
|
|
||||||
|
|
||||||
const MAX_REQUESTS_PER_CONNECTION = 1000;
|
|
||||||
|
|
||||||
type CurrentContext = ReturnType<typeof context> & {
|
|
||||||
fetchesMade: number;
|
|
||||||
ongoingFetches: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface AgentFetchOptions {
|
|
||||||
method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
|
|
||||||
body?: NodeJS.ReadableStream | string;
|
|
||||||
headers: { [key: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a `fetch` version with a similar API to the browser's configured with a
|
|
||||||
* HTTP2 agent. It encodes `body` automatically as JSON.
|
|
||||||
*
|
|
||||||
* @param {String} host
|
|
||||||
* @return {Function} fetch
|
|
||||||
*/
|
|
||||||
export default class NowAgent {
|
|
||||||
_contexts: ReturnType<typeof context>[];
|
|
||||||
_currContext: CurrentContext;
|
|
||||||
_output: Output;
|
|
||||||
_protocol?: string;
|
|
||||||
_sema: Sema;
|
|
||||||
_url: string;
|
|
||||||
|
|
||||||
constructor(url: string, { debug = false } = {}) {
|
|
||||||
// We use multiple contexts because each context represent one connection
|
|
||||||
// With nginx, we're limited to 1000 requests before a connection is closed
|
|
||||||
// http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_requests
|
|
||||||
// To get arround this, we keep track of requests made on a connection. when we're about to hit 1000
|
|
||||||
// we start up a new connection, and re-route all future traffic through the new connection
|
|
||||||
// and when the final request from the old connection resolves, we auto-close the old connection
|
|
||||||
this._contexts = [context()];
|
|
||||||
this._currContext = {
|
|
||||||
...this._contexts[0],
|
|
||||||
fetchesMade: 0,
|
|
||||||
ongoingFetches: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const parsed = parse(url);
|
|
||||||
this._url = url;
|
|
||||||
this._protocol = parsed.protocol;
|
|
||||||
this._sema = new Sema(20);
|
|
||||||
this._output = createOutput({ debug });
|
|
||||||
}
|
|
||||||
|
|
||||||
setConcurrency({
|
|
||||||
maxStreams,
|
|
||||||
capacity
|
|
||||||
}: {
|
|
||||||
maxStreams: number;
|
|
||||||
capacity: number;
|
|
||||||
}) {
|
|
||||||
this._sema = new Sema(maxStreams || 20, { capacity });
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetch(path: string, opts: AgentFetchOptions) {
|
|
||||||
const { debug } = this._output;
|
|
||||||
await this._sema.acquire();
|
|
||||||
let currentContext: CurrentContext;
|
|
||||||
this._currContext.fetchesMade++;
|
|
||||||
if (this._currContext.fetchesMade >= MAX_REQUESTS_PER_CONNECTION) {
|
|
||||||
const ctx = { ...context(), fetchesMade: 1, ongoingFetches: 0 };
|
|
||||||
this._contexts.push(ctx);
|
|
||||||
this._currContext = ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're changing contexts, we don't want to record the ongoingFetch on the old context
|
|
||||||
// That'll cause an off-by-one error when trying to close the old socket later
|
|
||||||
this._currContext.ongoingFetches++;
|
|
||||||
currentContext = this._currContext;
|
|
||||||
|
|
||||||
debug(
|
|
||||||
`Total requests made on socket #${this._contexts.length}: ${this
|
|
||||||
._currContext.fetchesMade}`
|
|
||||||
);
|
|
||||||
debug(
|
|
||||||
`Concurrent requests on socket #${this._contexts.length}: ${this
|
|
||||||
._currContext.ongoingFetches}`
|
|
||||||
);
|
|
||||||
|
|
||||||
let body: JsonBody | StreamBody | string | undefined;
|
|
||||||
if (opts.body && typeof opts.body === 'object') {
|
|
||||||
if (typeof (<NodeJS.ReadableStream>opts.body).pipe === 'function') {
|
|
||||||
body = new StreamBody(<NodeJS.ReadableStream>opts.body);
|
|
||||||
} else {
|
|
||||||
opts.headers['Content-Type'] = 'application/json';
|
|
||||||
body = new JsonBody(opts.body);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
body = opts.body;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { host, protocol } = parse(path);
|
|
||||||
const url = host ? `${protocol}//${host}` : this._url;
|
|
||||||
const handleCompleted = async <T>(res: T) => {
|
|
||||||
currentContext.ongoingFetches--;
|
|
||||||
if (
|
|
||||||
(currentContext !== this._currContext || host) &&
|
|
||||||
currentContext.ongoingFetches <= 0
|
|
||||||
) {
|
|
||||||
// We've completely moved on to a new socket
|
|
||||||
// close the old one
|
|
||||||
|
|
||||||
// TODO: Fix race condition:
|
|
||||||
// If the response is a stream, and the server is still streaming data
|
|
||||||
// we should check if the stream has closed before disconnecting
|
|
||||||
// hasCompleted CAN technically be called before the res body stream is closed
|
|
||||||
debug('Closing old socket');
|
|
||||||
currentContext.disconnect(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._sema.release();
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
return currentContext
|
|
||||||
.fetch((host ? '' : this._url) + path, { ...opts, body })
|
|
||||||
.then(res => handleCompleted(res))
|
|
||||||
.catch((err: Error) => {
|
|
||||||
handleCompleted(null);
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
const { debug } = this._output;
|
|
||||||
debug('Closing agent');
|
|
||||||
|
|
||||||
this._currContext.disconnect(this._url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -34,6 +34,11 @@ export default async function getDeploymentForAlias(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const appName = await getAppName(output, localConfig, localConfigPath);
|
const appName = await getAppName(output, localConfig, localConfigPath);
|
||||||
|
|
||||||
|
if (!appName) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const deployment = await getAppLastDeployment(
|
const deployment = await getAppLastDeployment(
|
||||||
output,
|
output,
|
||||||
client,
|
client,
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ export default async function getInferredTargets(
|
|||||||
output: Output,
|
output: Output,
|
||||||
config: Config
|
config: Config
|
||||||
) {
|
) {
|
||||||
output.warn(`The ${cmd('now alias')} command (no arguments) was deprecated in favour of ${cmd('now --prod')}.`);
|
output.warn(
|
||||||
|
`The ${cmd(
|
||||||
|
'now alias'
|
||||||
|
)} command (no arguments) was deprecated in favor of ${cmd('now --prod')}.`
|
||||||
|
);
|
||||||
|
|
||||||
// This field is deprecated, warn about it
|
// This field is deprecated, warn about it
|
||||||
if (config.aliases) {
|
if (config.aliases) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import qs from 'querystring';
|
import qs from 'querystring';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { parse as parseUrl } from 'url';
|
import { parse as parseUrl } from 'url';
|
||||||
|
import fetch, { RequestInit } from 'node-fetch';
|
||||||
import retry, { RetryFunction, Options as RetryOptions } from 'async-retry';
|
import retry, { RetryFunction, Options as RetryOptions } from 'async-retry';
|
||||||
import createOutput, { Output } from './output/create-output';
|
import createOutput, { Output } from './output/create-output';
|
||||||
import Agent, { AgentFetchOptions } from './agent';
|
|
||||||
import responseError from './response-error';
|
import responseError from './response-error';
|
||||||
import ua from './ua';
|
import ua from './ua';
|
||||||
|
|
||||||
@@ -17,7 +17,6 @@ export type FetchOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default class Client extends EventEmitter {
|
export default class Client extends EventEmitter {
|
||||||
_agent: Agent;
|
|
||||||
_apiUrl: string;
|
_apiUrl: string;
|
||||||
_debug: boolean;
|
_debug: boolean;
|
||||||
_forceNew: boolean;
|
_forceNew: boolean;
|
||||||
@@ -30,7 +29,7 @@ export default class Client extends EventEmitter {
|
|||||||
token,
|
token,
|
||||||
currentTeam,
|
currentTeam,
|
||||||
forceNew = false,
|
forceNew = false,
|
||||||
debug = false
|
debug = false,
|
||||||
}: {
|
}: {
|
||||||
apiUrl: string;
|
apiUrl: string;
|
||||||
token: string;
|
token: string;
|
||||||
@@ -44,30 +43,23 @@ export default class Client extends EventEmitter {
|
|||||||
this._forceNew = forceNew;
|
this._forceNew = forceNew;
|
||||||
this._output = createOutput({ debug });
|
this._output = createOutput({ debug });
|
||||||
this._apiUrl = apiUrl;
|
this._apiUrl = apiUrl;
|
||||||
this._agent = new Agent(apiUrl, { debug });
|
|
||||||
this._onRetry = this._onRetry.bind(this);
|
this._onRetry = this._onRetry.bind(this);
|
||||||
this.currentTeam = currentTeam;
|
this.currentTeam = currentTeam;
|
||||||
|
|
||||||
const closeAgent = () => {
|
|
||||||
this._agent.close();
|
|
||||||
process.removeListener('nowExit', closeAgent);
|
|
||||||
};
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
process.on('nowExit', closeAgent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retry<T>(fn: RetryFunction<T>, { retries = 3, maxTimeout = Infinity } = {}) {
|
retry<T>(fn: RetryFunction<T>, { retries = 3, maxTimeout = Infinity } = {}) {
|
||||||
return retry(fn, {
|
return retry(fn, {
|
||||||
retries,
|
retries,
|
||||||
maxTimeout,
|
maxTimeout,
|
||||||
onRetry: this._onRetry
|
onRetry: this._onRetry,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_fetch(_url: string, opts: FetchOptions = {}) {
|
_fetch(_url: string, opts: FetchOptions = {}) {
|
||||||
const parsedUrl = parseUrl(_url, true);
|
const parsedUrl = parseUrl(_url, true);
|
||||||
const apiUrl = parsedUrl.host ? `${parsedUrl.protocol}//${parsedUrl.host}` : '';
|
const apiUrl = parsedUrl.host
|
||||||
|
? `${parsedUrl.protocol}//${parsedUrl.host}`
|
||||||
|
: '';
|
||||||
|
|
||||||
if (opts.useCurrentTeam !== false && this.currentTeam) {
|
if (opts.useCurrentTeam !== false && this.currentTeam) {
|
||||||
const query = parsedUrl.query;
|
const query = parsedUrl.query;
|
||||||
@@ -80,20 +72,19 @@ export default class Client extends EventEmitter {
|
|||||||
Object.assign(opts, {
|
Object.assign(opts, {
|
||||||
body: JSON.stringify(opts.body),
|
body: JSON.stringify(opts.body),
|
||||||
headers: Object.assign({}, opts.headers, {
|
headers: Object.assign({}, opts.headers, {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.headers = opts.headers || {};
|
opts.headers = opts.headers || {};
|
||||||
opts.headers.authorization = `Bearer ${this._token}`;
|
opts.headers.Authorization = `Bearer ${this._token}`;
|
||||||
opts.headers['user-agent'] = ua;
|
opts.headers['user-agent'] = ua;
|
||||||
|
|
||||||
|
const url = `${apiUrl ? '' : this._apiUrl}${_url}`;
|
||||||
return this._output.time(
|
return this._output.time(
|
||||||
`${opts.method || 'GET'} ${apiUrl ? '' : this._apiUrl}${_url} ${JSON.stringify(
|
`${opts.method || 'GET'} ${url} ${JSON.stringify(opts.body) || ''}`,
|
||||||
opts.body
|
fetch(url, opts as RequestInit)
|
||||||
) || ''}`,
|
|
||||||
this._agent.fetch(_url, opts as AgentFetchOptions)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +117,5 @@ export default class Client extends EventEmitter {
|
|||||||
this._output.debug(`Retrying: ${error}\n${error.stack}`);
|
this._output.debug(`Retrying: ${error}\n${error.stack}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {}
|
||||||
this._agent.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,40 +15,45 @@ export default async function createDeploy(
|
|||||||
return await now.create(paths, createArgs);
|
return await now.create(paths, createArgs);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === 'rate_limited') {
|
if (error.code === 'rate_limited') {
|
||||||
return new ERRORS_TS.DeploymentsRateLimited(error.message);
|
throw new ERRORS_TS.DeploymentsRateLimited(error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Means that the domain used as a suffix no longer exists
|
// Means that the domain used as a suffix no longer exists
|
||||||
if (error.code === 'domain_missing') {
|
if (error.code === 'domain_missing') {
|
||||||
return new ERRORS_TS.DomainNotFound(error.value);
|
throw new ERRORS_TS.DomainNotFound(error.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.code === 'domain_not_found' && error.domain) {
|
if (error.code === 'domain_not_found' && error.domain) {
|
||||||
return new ERRORS_TS.DomainNotFound(error.domain);
|
throw new ERRORS_TS.DomainNotFound(error.domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This error occures when a domain used in the `alias`
|
// This error occures when a domain used in the `alias`
|
||||||
// is not yet verified
|
// is not yet verified
|
||||||
if (error.code === 'domain_not_verified' && error.domain) {
|
if (error.code === 'domain_not_verified' && error.domain) {
|
||||||
return new ERRORS_TS.DomainNotVerified(error.domain);
|
throw new ERRORS_TS.DomainNotVerified(error.domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the domain used as a suffix is not verified, we fail
|
// If the domain used as a suffix is not verified, we fail
|
||||||
if (error.code === 'domain_not_verified' && error.value) {
|
if (error.code === 'domain_not_verified' && error.value) {
|
||||||
return new ERRORS_TS.DomainVerificationFailed(error.value);
|
throw new ERRORS_TS.DomainVerificationFailed(error.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the domain isn't owned by the user
|
||||||
|
if (error.code === 'not_domain_owner') {
|
||||||
|
throw new ERRORS_TS.NotDomainOwner(error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.code === 'builds_rate_limited') {
|
if (error.code === 'builds_rate_limited') {
|
||||||
return new ERRORS_TS.BuildsRateLimited(error.message);
|
throw new ERRORS_TS.BuildsRateLimited(error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user doesn't have permissions over the domain used as a suffix we fail
|
// If the user doesn't have permissions over the domain used as a suffix we fail
|
||||||
if (error.code === 'forbidden') {
|
if (error.code === 'forbidden') {
|
||||||
return new ERRORS_TS.DomainPermissionDenied(error.value, contextName);
|
throw new ERRORS_TS.DomainPermissionDenied(error.value, contextName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.code === 'bad_request' && error.keyword) {
|
if (error.code === 'bad_request' && error.keyword) {
|
||||||
return new ERRORS.SchemaValidationFailed(
|
throw new ERRORS.SchemaValidationFailed(
|
||||||
error.message,
|
error.message,
|
||||||
error.keyword,
|
error.keyword,
|
||||||
error.dataPath,
|
error.dataPath,
|
||||||
@@ -57,19 +62,19 @@ export default async function createDeploy(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (error.code === 'domain_configured') {
|
if (error.code === 'domain_configured') {
|
||||||
return new ERRORS_TS.AliasDomainConfigured(error);
|
throw new ERRORS_TS.AliasDomainConfigured(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.code === 'missing_build_script') {
|
if (error.code === 'missing_build_script') {
|
||||||
return new ERRORS_TS.MissingBuildScript(error);
|
throw new ERRORS_TS.MissingBuildScript(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.code === 'conflicting_file_path') {
|
if (error.code === 'conflicting_file_path') {
|
||||||
return new ERRORS_TS.ConflictingFilePath(error);
|
throw new ERRORS_TS.ConflictingFilePath(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.code === 'conflicting_path_segment') {
|
if (error.code === 'conflicting_path_segment') {
|
||||||
return new ERRORS_TS.ConflictingPathSegment(error);
|
throw new ERRORS_TS.ConflictingPathSegment(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the cert is missing we try to generate a new one and the retry
|
// If the cert is missing we try to generate a new one and the retry
|
||||||
@@ -87,10 +92,10 @@ export default async function createDeploy(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (error.code === 'not_found') {
|
if (error.code === 'not_found') {
|
||||||
return new ERRORS_TS.DeploymentNotFound({ context: contextName });
|
throw new ERRORS_TS.DeploymentNotFound({ context: contextName });
|
||||||
}
|
}
|
||||||
|
|
||||||
const certError = mapCertError(error)
|
const certError = mapCertError(error);
|
||||||
if (certError) {
|
if (certError) {
|
||||||
return certError;
|
return certError;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import { Deployment } from '../../types';
|
|||||||
import {
|
import {
|
||||||
DeploymentNotFound,
|
DeploymentNotFound,
|
||||||
DeploymentPermissionDenied,
|
DeploymentPermissionDenied,
|
||||||
InvalidDeploymentId
|
InvalidDeploymentId,
|
||||||
} from '../errors-ts';
|
} from '../errors-ts';
|
||||||
import mapCertError from '../certs/map-cert-error';
|
import mapCertError from '../certs/map-cert-error';
|
||||||
|
|
||||||
type APIVersion = 'v5' | 'v9';
|
type APIVersion = 'v5' | 'v10';
|
||||||
|
|
||||||
export default async function getDeploymentByIdOrHost(
|
export default async function getDeploymentByIdOrHost(
|
||||||
client: Client,
|
client: Client,
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
import sleep from '../sleep';
|
|
||||||
|
|
||||||
import createPollingFn from '../create-polling-fn';
|
|
||||||
|
|
||||||
import getDeploymentByIdOrThrow from './get-deployment-by-id-or-throw';
|
|
||||||
|
|
||||||
const POLLING_INTERVAL = 5000;
|
|
||||||
|
|
||||||
async function* getStatusChangeFromPolling(
|
|
||||||
now: any,
|
|
||||||
contextName: string,
|
|
||||||
idOrHost: string,
|
|
||||||
initialState: string
|
|
||||||
) {
|
|
||||||
const pollDeployment = createPollingFn(
|
|
||||||
getDeploymentByIdOrThrow,
|
|
||||||
POLLING_INTERVAL
|
|
||||||
);
|
|
||||||
let prevState = initialState;
|
|
||||||
for await (const deployment of pollDeployment(now, contextName, idOrHost)) {
|
|
||||||
if (prevState !== deployment.state) {
|
|
||||||
await sleep(5000);
|
|
||||||
yield {
|
|
||||||
type: 'state-change',
|
|
||||||
created: Date.now(),
|
|
||||||
payload: { value: deployment.state }
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
prevState = deployment.state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default getStatusChangeFromPolling;
|
|
||||||
223
packages/now-cli/src/util/deploy/process-deployment.ts
Normal file
223
packages/now-cli/src/util/deploy/process-deployment.ts
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
import bytes from 'bytes';
|
||||||
|
import Progress from 'progress';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import pluralize from 'pluralize';
|
||||||
|
import {
|
||||||
|
createDeployment,
|
||||||
|
createLegacyDeployment,
|
||||||
|
DeploymentOptions,
|
||||||
|
} from '../../../../now-client';
|
||||||
|
import wait from '../output/wait';
|
||||||
|
import { Output } from '../output';
|
||||||
|
// @ts-ignore
|
||||||
|
import Now from '../../util';
|
||||||
|
import { NowConfig } from '../dev/types';
|
||||||
|
|
||||||
|
export default async function processDeployment({
|
||||||
|
now,
|
||||||
|
output,
|
||||||
|
hashes,
|
||||||
|
paths,
|
||||||
|
requestBody,
|
||||||
|
uploadStamp,
|
||||||
|
deployStamp,
|
||||||
|
legacy,
|
||||||
|
env,
|
||||||
|
quiet,
|
||||||
|
nowConfig,
|
||||||
|
}: {
|
||||||
|
now: Now;
|
||||||
|
output: Output;
|
||||||
|
hashes: { [key: string]: any };
|
||||||
|
paths: string[];
|
||||||
|
requestBody: DeploymentOptions;
|
||||||
|
uploadStamp: () => number;
|
||||||
|
deployStamp: () => number;
|
||||||
|
legacy: boolean;
|
||||||
|
env: any;
|
||||||
|
quiet: boolean;
|
||||||
|
nowConfig?: NowConfig;
|
||||||
|
}) {
|
||||||
|
const { warn, log, debug, note } = output;
|
||||||
|
let bar: Progress | null = null;
|
||||||
|
|
||||||
|
const path0 = paths[0];
|
||||||
|
const opts: DeploymentOptions = {
|
||||||
|
...requestBody,
|
||||||
|
debug: now._debug,
|
||||||
|
apiUrl: now._apiUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!legacy) {
|
||||||
|
let buildSpinner = null;
|
||||||
|
let deploySpinner = null;
|
||||||
|
|
||||||
|
for await (const event of createDeployment(path0, opts, nowConfig)) {
|
||||||
|
if (event.type === 'hashes-calculated') {
|
||||||
|
hashes = event.payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'warning') {
|
||||||
|
warn(event.payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'notice') {
|
||||||
|
note(event.payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'file_count') {
|
||||||
|
debug(
|
||||||
|
`Total files ${event.payload.total.size}, ${event.payload.missing.length} changed`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!quiet) {
|
||||||
|
log(
|
||||||
|
`Synced ${pluralize(
|
||||||
|
'file',
|
||||||
|
event.payload.missing.length,
|
||||||
|
true
|
||||||
|
)} ${uploadStamp()}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const missingSize = event.payload.missing
|
||||||
|
.map((sha: string) => event.payload.total.get(sha).data.length)
|
||||||
|
.reduce((a: number, b: number) => a + b, 0);
|
||||||
|
|
||||||
|
bar = new Progress(`${chalk.gray('>')} Upload [:bar] :percent :etas`, {
|
||||||
|
width: 20,
|
||||||
|
complete: '=',
|
||||||
|
incomplete: '',
|
||||||
|
total: missingSize,
|
||||||
|
clear: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'file-uploaded') {
|
||||||
|
debug(
|
||||||
|
`Uploaded: ${event.payload.file.names.join(' ')} (${bytes(
|
||||||
|
event.payload.file.data.length
|
||||||
|
)})`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (bar) {
|
||||||
|
bar.tick(event.payload.file.data.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'created') {
|
||||||
|
now._host = event.payload.url;
|
||||||
|
|
||||||
|
if (!quiet) {
|
||||||
|
const version = legacy ? `${chalk.grey('[v1]')} ` : '';
|
||||||
|
log(`https://${event.payload.url} ${version}${deployStamp()}`);
|
||||||
|
} else {
|
||||||
|
process.stdout.write(`https://${event.payload.url}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'build-state-changed') {
|
||||||
|
if (buildSpinner === null) {
|
||||||
|
buildSpinner = wait('Building...');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'all-builds-completed') {
|
||||||
|
if (buildSpinner) {
|
||||||
|
buildSpinner();
|
||||||
|
}
|
||||||
|
|
||||||
|
deploySpinner = wait('Finalizing...');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle error events
|
||||||
|
if (event.type === 'error') {
|
||||||
|
if (buildSpinner) {
|
||||||
|
buildSpinner();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deploySpinner) {
|
||||||
|
deploySpinner();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw await now.handleDeploymentError(event.payload, { hashes, env });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle ready event
|
||||||
|
if (event.type === 'alias-assigned') {
|
||||||
|
if (deploySpinner) {
|
||||||
|
deploySpinner();
|
||||||
|
}
|
||||||
|
|
||||||
|
return event.payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for await (const event of createLegacyDeployment(path0, opts, nowConfig)) {
|
||||||
|
if (event.type === 'hashes-calculated') {
|
||||||
|
hashes = event.payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'file_count') {
|
||||||
|
debug(
|
||||||
|
`Total files ${event.payload.total.size}, ${event.payload.missing.length} changed`
|
||||||
|
);
|
||||||
|
if (!quiet) {
|
||||||
|
log(
|
||||||
|
`Synced ${pluralize(
|
||||||
|
'file',
|
||||||
|
event.payload.missing.length,
|
||||||
|
true
|
||||||
|
)} ${uploadStamp()}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const missingSize = event.payload.missing
|
||||||
|
.map((sha: string) => event.payload.total.get(sha).data.length)
|
||||||
|
.reduce((a: number, b: number) => a + b, 0);
|
||||||
|
|
||||||
|
bar = new Progress(`${chalk.gray('>')} Upload [:bar] :percent :etas`, {
|
||||||
|
width: 20,
|
||||||
|
complete: '=',
|
||||||
|
incomplete: '',
|
||||||
|
total: missingSize,
|
||||||
|
clear: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'file-uploaded') {
|
||||||
|
debug(
|
||||||
|
`Uploaded: ${event.payload.file.names.join(' ')} (${bytes(
|
||||||
|
event.payload.file.data.length
|
||||||
|
)})`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (bar) {
|
||||||
|
bar.tick(event.payload.file.data.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'created') {
|
||||||
|
now._host = event.payload.url;
|
||||||
|
|
||||||
|
if (!quiet) {
|
||||||
|
const version = legacy ? `${chalk.grey('[v1]')} ` : '';
|
||||||
|
log(`${event.payload.url} ${version}${deployStamp()}`);
|
||||||
|
} else {
|
||||||
|
process.stdout.write(`https://${event.payload.url}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle error events
|
||||||
|
if (event.type === 'error') {
|
||||||
|
throw await now.handleDeploymentError(event.payload, { hashes, env });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle ready event
|
||||||
|
if (event.type === 'ready') {
|
||||||
|
log(`Build completed`);
|
||||||
|
return event.payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
packages/now-cli/src/util/deploy/should-deploy-dir.ts
Normal file
18
packages/now-cli/src/util/deploy/should-deploy-dir.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { homedir } from 'os';
|
||||||
|
import promptBool from '../input/prompt-bool';
|
||||||
|
import { Output } from '../output';
|
||||||
|
|
||||||
|
export default async function shouldDeployDir(argv0: string, output: Output) {
|
||||||
|
let yes = true;
|
||||||
|
if (argv0 === homedir()) {
|
||||||
|
if (
|
||||||
|
!(await promptBool(
|
||||||
|
'You are deploying your home directory. Do you want to continue?'
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
output.log('Aborted');
|
||||||
|
yes = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return yes;
|
||||||
|
}
|
||||||
@@ -8,14 +8,15 @@ import { createHash } from 'crypto';
|
|||||||
import { createGunzip } from 'zlib';
|
import { createGunzip } from 'zlib';
|
||||||
import { join, resolve } from 'path';
|
import { join, resolve } from 'path';
|
||||||
import { funCacheDir } from '@zeit/fun';
|
import { funCacheDir } from '@zeit/fun';
|
||||||
import cacheDirectory from 'cache-or-tmp-directory';
|
import { PackageJson } from '@now/build-utils';
|
||||||
|
import XDGAppPaths from 'xdg-app-paths';
|
||||||
import {
|
import {
|
||||||
createReadStream,
|
createReadStream,
|
||||||
mkdirp,
|
mkdirp,
|
||||||
readFile,
|
readFile,
|
||||||
readJSON,
|
readJSON,
|
||||||
writeFile,
|
writeFile,
|
||||||
remove
|
remove,
|
||||||
} from 'fs-extra';
|
} from 'fs-extra';
|
||||||
import pkg from '../../../package.json';
|
import pkg from '../../../package.json';
|
||||||
|
|
||||||
@@ -23,10 +24,10 @@ import { NoBuilderCacheError, BuilderCacheCleanError } from '../errors-ts';
|
|||||||
import wait from '../output/wait';
|
import wait from '../output/wait';
|
||||||
import { Output } from '../output';
|
import { Output } from '../output';
|
||||||
import { getDistTag } from '../get-dist-tag';
|
import { getDistTag } from '../get-dist-tag';
|
||||||
import { devDependencies } from '../../../package.json';
|
|
||||||
|
|
||||||
import * as staticBuilder from './static-builder';
|
import * as staticBuilder from './static-builder';
|
||||||
import { BuilderWithPackage, Package } from './types';
|
import { BuilderWithPackage } from './types';
|
||||||
|
import { getBundledBuilders } from './get-bundled-builders';
|
||||||
|
|
||||||
const registryTypes = new Set(['version', 'tag', 'range']);
|
const registryTypes = new Set(['version', 'tag', 'range']);
|
||||||
|
|
||||||
@@ -34,14 +35,10 @@ const localBuilders: { [key: string]: BuilderWithPackage } = {
|
|||||||
'@now/static': {
|
'@now/static': {
|
||||||
runInProcess: true,
|
runInProcess: true,
|
||||||
builder: Object.freeze(staticBuilder),
|
builder: Object.freeze(staticBuilder),
|
||||||
package: Object.freeze({ name: '@now/static', version: '' })
|
package: Object.freeze({ name: '@now/static', version: '' }),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const bundledBuilders = Object.keys(devDependencies).filter(d =>
|
|
||||||
d.startsWith('@now/')
|
|
||||||
);
|
|
||||||
|
|
||||||
const distTag = getDistTag(pkg.version);
|
const distTag = getDistTag(pkg.version);
|
||||||
|
|
||||||
export const cacheDirPromise = prepareCacheDir();
|
export const cacheDirPromise = prepareCacheDir();
|
||||||
@@ -80,7 +77,7 @@ export async function prepareCacheDir() {
|
|||||||
const { NOW_BUILDER_CACHE_DIR } = process.env;
|
const { NOW_BUILDER_CACHE_DIR } = process.env;
|
||||||
const designated = NOW_BUILDER_CACHE_DIR
|
const designated = NOW_BUILDER_CACHE_DIR
|
||||||
? resolve(NOW_BUILDER_CACHE_DIR)
|
? resolve(NOW_BUILDER_CACHE_DIR)
|
||||||
: cacheDirectory('co.zeit.now');
|
: XDGAppPaths('co.zeit.now').cache();
|
||||||
|
|
||||||
if (!designated) {
|
if (!designated) {
|
||||||
throw new NoBuilderCacheError();
|
throw new NoBuilderCacheError();
|
||||||
@@ -117,7 +114,7 @@ export async function prepareBuilderDir() {
|
|||||||
export async function prepareBuilderModulePath() {
|
export async function prepareBuilderModulePath() {
|
||||||
const [builderDir, builderContents] = await Promise.all([
|
const [builderDir, builderContents] = await Promise.all([
|
||||||
builderDirPromise,
|
builderDirPromise,
|
||||||
readFile(join(__dirname, 'builder-worker.js'))
|
readFile(join(__dirname, 'builder-worker.js')),
|
||||||
]);
|
]);
|
||||||
let needsWrite = false;
|
let needsWrite = false;
|
||||||
const builderSha = getSha(builderContents);
|
const builderSha = getSha(builderContents);
|
||||||
@@ -179,7 +176,7 @@ export function getBuildUtils(packages: string[]): string {
|
|||||||
export function filterPackage(
|
export function filterPackage(
|
||||||
builderSpec: string,
|
builderSpec: string,
|
||||||
distTag: string,
|
distTag: string,
|
||||||
buildersPkg: Package
|
buildersPkg: PackageJson
|
||||||
) {
|
) {
|
||||||
if (builderSpec in localBuilders) return false;
|
if (builderSpec in localBuilders) return false;
|
||||||
const parsed = npa(builderSpec);
|
const parsed = npa(builderSpec);
|
||||||
@@ -187,7 +184,7 @@ export function filterPackage(
|
|||||||
parsed.name &&
|
parsed.name &&
|
||||||
parsed.type === 'tag' &&
|
parsed.type === 'tag' &&
|
||||||
parsed.fetchSpec === distTag &&
|
parsed.fetchSpec === distTag &&
|
||||||
bundledBuilders.includes(parsed.name) &&
|
getBundledBuilders().includes(parsed.name) &&
|
||||||
buildersPkg.dependencies
|
buildersPkg.dependencies
|
||||||
) {
|
) {
|
||||||
const parsedInstalled = npa(
|
const parsedInstalled = npa(
|
||||||
@@ -259,10 +256,10 @@ export async function installBuilders(
|
|||||||
'--exact',
|
'--exact',
|
||||||
'--no-lockfile',
|
'--no-lockfile',
|
||||||
'--non-interactive',
|
'--non-interactive',
|
||||||
...packagesToInstall
|
...packagesToInstall,
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: builderDir
|
cwd: builderDir,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -294,10 +291,10 @@ export async function updateBuilders(
|
|||||||
'--exact',
|
'--exact',
|
||||||
'--no-lockfile',
|
'--no-lockfile',
|
||||||
'--non-interactive',
|
'--non-interactive',
|
||||||
...packages.filter(p => p !== '@now/static')
|
...packages.filter(p => p !== '@now/static'),
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: builderDir
|
cwd: builderDir,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -336,7 +333,7 @@ export async function getBuilder(
|
|||||||
const pkg = require(join(dest, 'package.json'));
|
const pkg = require(join(dest, 'package.json'));
|
||||||
builderWithPkg = {
|
builderWithPkg = {
|
||||||
builder: Object.freeze(mod),
|
builder: Object.freeze(mod),
|
||||||
package: Object.freeze(pkg)
|
package: Object.freeze(pkg),
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code === 'MODULE_NOT_FOUND') {
|
if (err.code === 'MODULE_NOT_FOUND') {
|
||||||
@@ -357,7 +354,7 @@ export async function getBuilder(
|
|||||||
|
|
||||||
function getPackageName(
|
function getPackageName(
|
||||||
parsed: npa.Result,
|
parsed: npa.Result,
|
||||||
buildersPkg: Package
|
buildersPkg: PackageJson
|
||||||
): string | null {
|
): string | null {
|
||||||
if (registryTypes.has(parsed.type)) {
|
if (registryTypes.has(parsed.type)) {
|
||||||
return parsed.name;
|
return parsed.name;
|
||||||
@@ -378,7 +375,7 @@ function getSha(buffer: Buffer): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function hasBundledBuilders(dependencies: { [name: string]: string }): boolean {
|
function hasBundledBuilders(dependencies: { [name: string]: string }): boolean {
|
||||||
for (const name of bundledBuilders) {
|
for (const name of getBundledBuilders()) {
|
||||||
if (!(name in dependencies)) {
|
if (!(name in dependencies)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,17 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
import bytes from 'bytes';
|
import bytes from 'bytes';
|
||||||
|
import { promisify } from 'util';
|
||||||
import { delimiter, dirname, join } from 'path';
|
import { delimiter, dirname, join } from 'path';
|
||||||
import { fork, ChildProcess } from 'child_process';
|
import { fork, ChildProcess } from 'child_process';
|
||||||
import { createFunction } from '@zeit/fun';
|
import { createFunction } from '@zeit/fun';
|
||||||
import { File, Lambda, FileBlob, FileFsRef } from '@now/build-utils';
|
import { Builder, File, Lambda, FileBlob, FileFsRef } from '@now/build-utils';
|
||||||
import stripAnsi from 'strip-ansi';
|
import stripAnsi from 'strip-ansi';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import which from 'which';
|
import which from 'which';
|
||||||
import plural from 'pluralize';
|
import plural from 'pluralize';
|
||||||
import ora, { Ora } from 'ora';
|
|
||||||
import minimatch from 'minimatch';
|
import minimatch from 'minimatch';
|
||||||
|
import _treeKill from 'tree-kill';
|
||||||
|
|
||||||
import { Output } from '../output';
|
import { Output } from '../output';
|
||||||
import highlight from '../output/highlight';
|
import highlight from '../output/highlight';
|
||||||
@@ -23,12 +24,11 @@ import { builderModulePathPromise, getBuilder } from './builder-cache';
|
|||||||
import {
|
import {
|
||||||
EnvConfig,
|
EnvConfig,
|
||||||
NowConfig,
|
NowConfig,
|
||||||
BuildConfig,
|
|
||||||
BuildMatch,
|
BuildMatch,
|
||||||
BuildResult,
|
BuildResult,
|
||||||
BuilderInputs,
|
BuilderInputs,
|
||||||
BuilderOutput,
|
BuilderOutput,
|
||||||
BuilderOutputs
|
BuilderOutputs,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
interface BuildMessage {
|
interface BuildMessage {
|
||||||
@@ -41,7 +41,7 @@ interface BuildMessageResult extends BuildMessage {
|
|||||||
error?: object;
|
error?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLogging = new WeakSet<ChildProcess>();
|
const treeKill = promisify(_treeKill);
|
||||||
|
|
||||||
let nodeBinPromise: Promise<string>;
|
let nodeBinPromise: Promise<string>;
|
||||||
|
|
||||||
@@ -49,43 +49,48 @@ async function getNodeBin(): Promise<string> {
|
|||||||
return which.sync('node', { nothrow: true }) || process.execPath;
|
return which.sync('node', { nothrow: true }) || process.execPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pipeChildLogging(child: ChildProcess): void {
|
|
||||||
if (!isLogging.has(child)) {
|
|
||||||
child.stdout!.pipe(process.stdout);
|
|
||||||
child.stderr!.pipe(process.stderr);
|
|
||||||
isLogging.add(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createBuildProcess(
|
async function createBuildProcess(
|
||||||
match: BuildMatch,
|
match: BuildMatch,
|
||||||
buildEnv: EnvConfig,
|
buildEnv: EnvConfig,
|
||||||
workPath: string,
|
workPath: string,
|
||||||
output: Output,
|
output: Output,
|
||||||
yarnPath?: string
|
yarnPath?: string,
|
||||||
|
debugEnabled: boolean = false
|
||||||
): Promise<ChildProcess> {
|
): Promise<ChildProcess> {
|
||||||
if (!nodeBinPromise) {
|
if (!nodeBinPromise) {
|
||||||
nodeBinPromise = getNodeBin();
|
nodeBinPromise = getNodeBin();
|
||||||
}
|
}
|
||||||
const [execPath, modulePath] = await Promise.all([
|
const [execPath, modulePath] = await Promise.all([
|
||||||
nodeBinPromise,
|
nodeBinPromise,
|
||||||
builderModulePathPromise
|
builderModulePathPromise,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Ensure that `node` is in the builder's `PATH`
|
||||||
let PATH = `${dirname(execPath)}${delimiter}${process.env.PATH}`;
|
let PATH = `${dirname(execPath)}${delimiter}${process.env.PATH}`;
|
||||||
|
|
||||||
|
// Ensure that `yarn` is in the builder's `PATH`
|
||||||
if (yarnPath) {
|
if (yarnPath) {
|
||||||
PATH = `${yarnPath}${delimiter}${PATH}`;
|
PATH = `${yarnPath}${delimiter}${PATH}`;
|
||||||
}
|
}
|
||||||
const buildProcess = fork(modulePath, [], {
|
|
||||||
cwd: workPath,
|
const env: EnvConfig = {
|
||||||
env: {
|
|
||||||
...process.env,
|
...process.env,
|
||||||
PATH,
|
PATH,
|
||||||
...buildEnv,
|
...buildEnv,
|
||||||
NOW_REGION: 'dev1'
|
NOW_REGION: 'dev1',
|
||||||
},
|
};
|
||||||
|
|
||||||
|
// Builders won't show debug logs by default.
|
||||||
|
// The `NOW_BUILDER_DEBUG` env variable enables them.
|
||||||
|
if (debugEnabled) {
|
||||||
|
env.NOW_BUILDER_DEBUG = '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildProcess = fork(modulePath, [], {
|
||||||
|
cwd: workPath,
|
||||||
|
env,
|
||||||
execPath,
|
execPath,
|
||||||
execArgv: [],
|
execArgv: [],
|
||||||
stdio: ['ignore', 'pipe', 'pipe', 'ipc']
|
|
||||||
});
|
});
|
||||||
match.buildProcess = buildProcess;
|
match.buildProcess = buildProcess;
|
||||||
|
|
||||||
@@ -96,9 +101,6 @@ async function createBuildProcess(
|
|||||||
match.buildProcess = undefined;
|
match.buildProcess = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
buildProcess.stdout!.setEncoding('utf8');
|
|
||||||
buildProcess.stderr!.setEncoding('utf8');
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// The first message that the builder process sends is the `ready` event
|
// The first message that the builder process sends is the `ready` event
|
||||||
buildProcess.once('message', ({ type }) => {
|
buildProcess.once('message', ({ type }) => {
|
||||||
@@ -122,7 +124,7 @@ export async function executeBuild(
|
|||||||
filesRemoved?: string[]
|
filesRemoved?: string[]
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const {
|
const {
|
||||||
builderWithPkg: { runInProcess, builder, package: pkg }
|
builderWithPkg: { runInProcess, builder, package: pkg },
|
||||||
} = match;
|
} = match;
|
||||||
const { src: entrypoint } = match;
|
const { src: entrypoint } = match;
|
||||||
const { env, debug, buildEnv, yarnPath, cwd: workPath } = devServer;
|
const { env, debug, buildEnv, yarnPath, cwd: workPath } = devServer;
|
||||||
@@ -150,7 +152,8 @@ export async function executeBuild(
|
|||||||
buildEnv,
|
buildEnv,
|
||||||
workPath,
|
workPath,
|
||||||
devServer.output,
|
devServer.output,
|
||||||
yarnPath
|
yarnPath,
|
||||||
|
debug
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,45 +168,16 @@ export async function executeBuild(
|
|||||||
filesChanged,
|
filesChanged,
|
||||||
filesRemoved,
|
filesRemoved,
|
||||||
env,
|
env,
|
||||||
buildEnv
|
buildEnv,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let buildResultOrOutputs: BuilderOutputs | BuildResult;
|
let buildResultOrOutputs: BuilderOutputs | BuildResult;
|
||||||
if (buildProcess) {
|
if (buildProcess) {
|
||||||
let spinLogger;
|
|
||||||
let spinner: Ora | undefined;
|
|
||||||
const fullLogs: string[] = [];
|
|
||||||
|
|
||||||
if (isInitialBuild && !debug && process.stdout.isTTY) {
|
|
||||||
const logTitle = `${chalk.bold(
|
|
||||||
`Preparing ${chalk.underline(entrypoint)} for build`
|
|
||||||
)}:`;
|
|
||||||
spinner = ora(logTitle).start();
|
|
||||||
|
|
||||||
spinLogger = (data: Buffer) => {
|
|
||||||
const rawLog = stripAnsi(data.toString());
|
|
||||||
fullLogs.push(rawLog);
|
|
||||||
|
|
||||||
const lines = rawLog.replace(/\s+$/, '').split('\n');
|
|
||||||
const spinText = `${logTitle} ${lines[lines.length - 1]}`;
|
|
||||||
const maxCols = process.stdout.columns || 80;
|
|
||||||
const overflow = stripAnsi(spinText).length + 2 - maxCols;
|
|
||||||
spinner!.text =
|
|
||||||
overflow > 0 ? `${spinText.slice(0, -overflow - 3)}...` : spinText;
|
|
||||||
};
|
|
||||||
|
|
||||||
buildProcess!.stdout!.on('data', spinLogger);
|
|
||||||
buildProcess!.stderr!.on('data', spinLogger);
|
|
||||||
} else {
|
|
||||||
pipeChildLogging(buildProcess!);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
buildProcess.send({
|
buildProcess.send({
|
||||||
type: 'build',
|
type: 'build',
|
||||||
builderName: pkg.name,
|
builderName: pkg.name,
|
||||||
buildParams
|
buildParams,
|
||||||
});
|
});
|
||||||
|
|
||||||
buildResultOrOutputs = await new Promise((resolve, reject) => {
|
buildResultOrOutputs = await new Promise((resolve, reject) => {
|
||||||
@@ -233,23 +207,6 @@ export async function executeBuild(
|
|||||||
buildProcess!.on('exit', onExit);
|
buildProcess!.on('exit', onExit);
|
||||||
buildProcess!.on('message', onMessage);
|
buildProcess!.on('message', onMessage);
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
if (spinner) {
|
|
||||||
spinner.stop();
|
|
||||||
spinner = undefined;
|
|
||||||
console.log(fullLogs.join(''));
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
} finally {
|
|
||||||
if (spinLogger) {
|
|
||||||
buildProcess.stdout!.removeListener('data', spinLogger);
|
|
||||||
buildProcess.stderr!.removeListener('data', spinLogger);
|
|
||||||
}
|
|
||||||
if (spinner) {
|
|
||||||
spinner.stop();
|
|
||||||
}
|
|
||||||
pipeChildLogging(buildProcess!);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
buildResultOrOutputs = await builder.build(buildParams);
|
buildResultOrOutputs = await builder.build(buildParams);
|
||||||
}
|
}
|
||||||
@@ -260,7 +217,11 @@ export async function executeBuild(
|
|||||||
result = {
|
result = {
|
||||||
output: buildResultOrOutputs as BuilderOutputs,
|
output: buildResultOrOutputs as BuilderOutputs,
|
||||||
routes: [],
|
routes: [],
|
||||||
watch: []
|
watch: [],
|
||||||
|
distPath:
|
||||||
|
typeof buildResultOrOutputs.distPath === 'string'
|
||||||
|
? buildResultOrOutputs.distPath
|
||||||
|
: undefined,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
result = buildResultOrOutputs as BuildResult;
|
result = buildResultOrOutputs as BuildResult;
|
||||||
@@ -346,9 +307,9 @@ export async function executeBuild(
|
|||||||
...nowConfig.env,
|
...nowConfig.env,
|
||||||
...asset.environment,
|
...asset.environment,
|
||||||
...env,
|
...env,
|
||||||
NOW_REGION: 'dev1'
|
NOW_REGION: 'dev1',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,7 +343,7 @@ export async function getBuildMatches(
|
|||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
const noMatches: BuildConfig[] = [];
|
const noMatches: Builder[] = [];
|
||||||
const builds = nowConfig.builds || [{ src: '**', use: '@now/static' }];
|
const builds = nowConfig.builds || [{ src: '**', use: '@now/static' }];
|
||||||
|
|
||||||
for (const buildConfig of builds) {
|
for (const buildConfig of builds) {
|
||||||
@@ -420,7 +381,7 @@ export async function getBuildMatches(
|
|||||||
builderWithPkg,
|
builderWithPkg,
|
||||||
buildOutput: {},
|
buildOutput: {},
|
||||||
buildResults: new Map(),
|
buildResults: new Map(),
|
||||||
buildTimestamp: 0
|
buildTimestamp: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,3 +403,35 @@ export async function getBuildMatches(
|
|||||||
|
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function shutdownBuilder(
|
||||||
|
match: BuildMatch,
|
||||||
|
{ debug }: Output
|
||||||
|
): Promise<void> {
|
||||||
|
const ops: Promise<void>[] = [];
|
||||||
|
|
||||||
|
if (match.buildProcess) {
|
||||||
|
const { pid } = match.buildProcess;
|
||||||
|
debug(`Killing builder sub-process with PID ${pid}`);
|
||||||
|
const killPromise = treeKill(pid)
|
||||||
|
.then(() => {
|
||||||
|
debug(`Killed builder with PID ${pid}`);
|
||||||
|
})
|
||||||
|
.catch((err: Error) => {
|
||||||
|
debug(`Failed to kill builder with PID ${pid}: ${err}`);
|
||||||
|
});
|
||||||
|
ops.push(killPromise);
|
||||||
|
delete match.buildProcess;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match.buildOutput) {
|
||||||
|
for (const asset of Object.values(match.buildOutput)) {
|
||||||
|
if (asset.type === 'Lambda' && asset.fn) {
|
||||||
|
debug(`Shutting down Lambda function`);
|
||||||
|
ops.push(asset.fn.destroy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(ops);
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ export const httpStatusDescriptionMap = new Map([
|
|||||||
[502, 'BAD_GATEWAY'],
|
[502, 'BAD_GATEWAY'],
|
||||||
[503, 'SERVICE_UNAVAILABLE'],
|
[503, 'SERVICE_UNAVAILABLE'],
|
||||||
[504, 'GATEWAY_TIMEOUT'],
|
[504, 'GATEWAY_TIMEOUT'],
|
||||||
[508, 'INFINITE_LOOP']
|
[508, 'INFINITE_LOOP'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const errorMessageMap = new Map([
|
export const errorMessageMap = new Map([
|
||||||
[400, 'Bad request'],
|
[400, 'Bad request'],
|
||||||
[402, 'Payment required'],
|
[402, 'Payment required'],
|
||||||
[403, 'You don\'t have the required permissions'],
|
[403, "You don't have the required permissions"],
|
||||||
[404, 'The page could not be found'],
|
[404, 'The page could not be found'],
|
||||||
[405, 'Method not allowed'],
|
[405, 'Method not allowed'],
|
||||||
[410, 'The deployment has been removed'],
|
[410, 'The deployment has been removed'],
|
||||||
@@ -28,7 +28,7 @@ export const errorMessageMap = new Map([
|
|||||||
[501, 'Not implemented'],
|
[501, 'Not implemented'],
|
||||||
[503, 'The deployment is currently unavailable'],
|
[503, 'The deployment is currently unavailable'],
|
||||||
[504, 'An error occurred with your deployment'],
|
[504, 'An error occurred with your deployment'],
|
||||||
[508, 'Infinite loop detected']
|
[508, 'Infinite loop detected'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
interface ErrorMessage {
|
interface ErrorMessage {
|
||||||
@@ -40,20 +40,20 @@ interface ErrorMessage {
|
|||||||
const appError = {
|
const appError = {
|
||||||
title: 'An error occurred with this application.',
|
title: 'An error occurred with this application.',
|
||||||
subtitle: 'This is an error with the application itself, not the platform.',
|
subtitle: 'This is an error with the application itself, not the platform.',
|
||||||
app_error: true
|
app_error: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const infrastructureError = {
|
const infrastructureError = {
|
||||||
title: 'An internal error occurred with ZEIT Now.',
|
title: 'An internal error occurred with ZEIT Now.',
|
||||||
subtitle: 'This is an error with the platform itself, not the application.',
|
subtitle: 'This is an error with the platform itself, not the application.',
|
||||||
app_error: false
|
app_error: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const pageNotFoundError = {
|
const pageNotFoundError = {
|
||||||
title: 'The page could not be found.',
|
title: 'The page could not be found.',
|
||||||
subtitle: 'The page could not be found in the application.',
|
subtitle: 'The page could not be found in the application.',
|
||||||
app_error: true
|
app_error: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function generateErrorMessage(
|
export function generateErrorMessage(
|
||||||
@@ -68,7 +68,7 @@ export function generateErrorMessage(
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
title: errorMessageMap.get(statusCode) || 'Error occurred',
|
title: errorMessageMap.get(statusCode) || 'Error occurred',
|
||||||
app_error: false
|
app_error: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
packages/now-cli/src/util/dev/get-bundled-builders.ts
Normal file
11
packages/now-cli/src/util/dev/get-bundled-builders.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export function getBundledBuilders() {
|
||||||
|
return [
|
||||||
|
'@now/go',
|
||||||
|
'@now/next',
|
||||||
|
'@now/node',
|
||||||
|
'@now/ruby',
|
||||||
|
'@now/python',
|
||||||
|
'@now/static-build',
|
||||||
|
'@now/build-utils',
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -98,7 +98,7 @@ export default async function(
|
|||||||
headers: combinedHeaders,
|
headers: combinedHeaders,
|
||||||
uri_args: query,
|
uri_args: query,
|
||||||
matched_route: routeConfig,
|
matched_route: routeConfig,
|
||||||
matched_route_idx: idx
|
matched_route_idx: idx,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@@ -114,7 +114,7 @@ export default async function(
|
|||||||
headers: combinedHeaders,
|
headers: combinedHeaders,
|
||||||
uri_args: query,
|
uri_args: query,
|
||||||
matched_route: routeConfig,
|
matched_route: routeConfig,
|
||||||
matched_route_idx: idx
|
matched_route_idx: idx,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -127,7 +127,7 @@ export default async function(
|
|||||||
found: false,
|
found: false,
|
||||||
dest: reqPathname,
|
dest: reqPathname,
|
||||||
uri_args: query,
|
uri_args: query,
|
||||||
headers: combinedHeaders
|
headers: combinedHeaders,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,11 @@ import { basename, dirname, extname, join } from 'path';
|
|||||||
import directoryTemplate from 'serve-handler/src/directory';
|
import directoryTemplate from 'serve-handler/src/directory';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
Builder,
|
||||||
FileFsRef,
|
FileFsRef,
|
||||||
PackageJson,
|
PackageJson,
|
||||||
detectBuilders,
|
detectBuilders,
|
||||||
detectRoutes
|
detectRoutes,
|
||||||
} from '@now/build-utils';
|
} from '@now/build-utils';
|
||||||
|
|
||||||
import { once } from '../once';
|
import { once } from '../once';
|
||||||
@@ -33,7 +34,7 @@ import { version as cliVersion } from '../../../package.json';
|
|||||||
import {
|
import {
|
||||||
createIgnore,
|
createIgnore,
|
||||||
staticFiles as getFiles,
|
staticFiles as getFiles,
|
||||||
getAllProjectFiles
|
getAllProjectFiles,
|
||||||
} from '../get-files';
|
} from '../get-files';
|
||||||
import { validateNowConfigBuilds, validateNowConfigRoutes } from './validate';
|
import { validateNowConfigBuilds, validateNowConfigRoutes } from './validate';
|
||||||
|
|
||||||
@@ -41,12 +42,12 @@ import isURL from './is-url';
|
|||||||
import devRouter from './router';
|
import devRouter from './router';
|
||||||
import getMimeType from './mime-type';
|
import getMimeType from './mime-type';
|
||||||
import { getYarnPath } from './yarn-installer';
|
import { getYarnPath } from './yarn-installer';
|
||||||
import { executeBuild, getBuildMatches } from './builder';
|
import { executeBuild, getBuildMatches, shutdownBuilder } from './builder';
|
||||||
import { generateErrorMessage, generateHttpStatusDescription } from './errors';
|
import { generateErrorMessage, generateHttpStatusDescription } from './errors';
|
||||||
import {
|
import {
|
||||||
builderDirPromise,
|
builderDirPromise,
|
||||||
installBuilders,
|
installBuilders,
|
||||||
updateBuilders
|
updateBuilders,
|
||||||
} from './builder-cache';
|
} from './builder-cache';
|
||||||
|
|
||||||
// HTML templates
|
// HTML templates
|
||||||
@@ -60,7 +61,6 @@ import {
|
|||||||
EnvConfig,
|
EnvConfig,
|
||||||
NowConfig,
|
NowConfig,
|
||||||
DevServerOptions,
|
DevServerOptions,
|
||||||
BuildConfig,
|
|
||||||
BuildMatch,
|
BuildMatch,
|
||||||
BuildResult,
|
BuildResult,
|
||||||
BuilderInputs,
|
BuilderInputs,
|
||||||
@@ -70,7 +70,7 @@ import {
|
|||||||
InvokeResult,
|
InvokeResult,
|
||||||
ListenSpec,
|
ListenSpec,
|
||||||
RouteConfig,
|
RouteConfig,
|
||||||
RouteResult
|
RouteResult,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
interface FSEvent {
|
interface FSEvent {
|
||||||
@@ -87,7 +87,7 @@ interface NodeRequire {
|
|||||||
|
|
||||||
declare const __non_webpack_require__: NodeRequire;
|
declare const __non_webpack_require__: NodeRequire;
|
||||||
|
|
||||||
function sortBuilders(buildA: BuildConfig, buildB: BuildConfig) {
|
function sortBuilders(buildA: Builder, buildB: Builder) {
|
||||||
if (buildA && buildA.use && buildA.use.startsWith('@now/static-build')) {
|
if (buildA && buildA.use && buildA.use.startsWith('@now/static-build')) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -182,6 +182,20 @@ export default class DevServer {
|
|||||||
const filesChanged: Set<string> = new Set();
|
const filesChanged: Set<string> = new Set();
|
||||||
const filesRemoved: Set<string> = new Set();
|
const filesRemoved: Set<string> = new Set();
|
||||||
|
|
||||||
|
const distPaths: string[] = [];
|
||||||
|
|
||||||
|
for (const buildMatch of this.buildMatches.values()) {
|
||||||
|
for (const buildResult of buildMatch.buildResults.values()) {
|
||||||
|
if (buildResult.distPath) {
|
||||||
|
distPaths.push(buildResult.distPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
events = events.filter(event =>
|
||||||
|
distPaths.every(distPath => !event.path.startsWith(distPath))
|
||||||
|
);
|
||||||
|
|
||||||
// First, update the `files` mapping of source files
|
// First, update the `files` mapping of source files
|
||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
if (event.type === 'add') {
|
if (event.type === 'add') {
|
||||||
@@ -255,9 +269,7 @@ export default class DevServer {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.output.debug(
|
this.output.debug(
|
||||||
`Not rebuilding because \`shouldServe()\` returned \`false\` for "${
|
`Not rebuilding because \`shouldServe()\` returned \`false\` for "${match.use}" request path "${requestPath}"`
|
||||||
match.use
|
|
||||||
}" request path "${requestPath}"`
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,13 +347,18 @@ export default class DevServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete build matches that no longer exists
|
// Delete build matches that no longer exists
|
||||||
|
const ops: Promise<void>[] = [];
|
||||||
for (const src of this.buildMatches.keys()) {
|
for (const src of this.buildMatches.keys()) {
|
||||||
if (!sources.includes(src)) {
|
if (!sources.includes(src)) {
|
||||||
this.output.debug(`Removing build match for "${src}"`);
|
this.output.debug(`Removing build match for "${src}"`);
|
||||||
// TODO: shutdown lambda functions
|
const match = this.buildMatches.get(src);
|
||||||
|
if (match) {
|
||||||
|
ops.push(shutdownBuilder(match, this.output));
|
||||||
|
}
|
||||||
this.buildMatches.delete(src);
|
this.buildMatches.delete(src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await Promise.all(ops);
|
||||||
|
|
||||||
// Add the new matches to the `buildMatches` map
|
// Add the new matches to the `buildMatches` map
|
||||||
const blockingBuilds: Promise<void>[] = [];
|
const blockingBuilds: Promise<void>[] = [];
|
||||||
@@ -376,7 +393,7 @@ export default class DevServer {
|
|||||||
// Sort build matches to make sure `@now/static-build` is always last
|
// Sort build matches to make sure `@now/static-build` is always last
|
||||||
this.buildMatches = new Map(
|
this.buildMatches = new Map(
|
||||||
[...this.buildMatches.entries()].sort((matchA, matchB) => {
|
[...this.buildMatches.entries()].sort((matchA, matchB) => {
|
||||||
return sortBuilders(matchA[1] as BuildConfig, matchB[1] as BuildConfig);
|
return sortBuilders(matchA[1] as Builder, matchB[1] as Builder);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -413,10 +430,11 @@ export default class DevServer {
|
|||||||
for (const buildMatch of this.buildMatches.values()) {
|
for (const buildMatch of this.buildMatches.values()) {
|
||||||
const {
|
const {
|
||||||
src,
|
src,
|
||||||
builderWithPkg: { package: pkg }
|
builderWithPkg: { package: pkg },
|
||||||
} = buildMatch;
|
} = buildMatch;
|
||||||
if (pkg.name === '@now/static') continue;
|
if (pkg.name === '@now/static') continue;
|
||||||
if (updatedBuilders.includes(pkg.name)) {
|
if (pkg.name && updatedBuilders.includes(pkg.name)) {
|
||||||
|
shutdownBuilder(buildMatch, this.output);
|
||||||
this.buildMatches.delete(src);
|
this.buildMatches.delete(src);
|
||||||
this.output.debug(`Invalidated build match for "${src}"`);
|
this.output.debug(`Invalidated build match for "${src}"`);
|
||||||
}
|
}
|
||||||
@@ -441,7 +459,7 @@ export default class DevServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.validateEnvConfig(fileName, base || {}, env);
|
return this.validateEnvConfig(fileName, base || {}, env);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof MissingDotenvVarsError) {
|
if (err instanceof MissingDotenvVarsError) {
|
||||||
this.output.error(err.message);
|
this.output.error(err.message);
|
||||||
@@ -450,7 +468,7 @@ export default class DevServer {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { ...base, ...env };
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getNowConfig(
|
async getNowConfig(
|
||||||
@@ -516,8 +534,8 @@ export default class DevServer {
|
|||||||
`filtered out ${allFiles.length - files.length} files`
|
`filtered out ${allFiles.length - files.length} files`
|
||||||
);
|
);
|
||||||
|
|
||||||
const { builders, errors } = await detectBuilders(files, pkg, {
|
const { builders, warnings, errors } = await detectBuilders(files, pkg, {
|
||||||
tag: getDistTag(cliVersion) === 'canary' ? 'canary' : 'latest'
|
tag: getDistTag(cliVersion) === 'canary' ? 'canary' : 'latest',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
@@ -525,6 +543,10 @@ export default class DevServer {
|
|||||||
await this.exit();
|
await this.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (warnings && warnings.length > 0) {
|
||||||
|
warnings.forEach(warning => this.output.warn(warning.message));
|
||||||
|
}
|
||||||
|
|
||||||
if (builders) {
|
if (builders) {
|
||||||
const { defaultRoutes, error: routesError } = await detectRoutes(
|
const { defaultRoutes, error: routesError } = await detectRoutes(
|
||||||
files,
|
files,
|
||||||
@@ -608,7 +630,9 @@ export default class DevServer {
|
|||||||
type: string,
|
type: string,
|
||||||
env: EnvConfig = {},
|
env: EnvConfig = {},
|
||||||
localEnv: EnvConfig = {}
|
localEnv: EnvConfig = {}
|
||||||
): void {
|
): EnvConfig {
|
||||||
|
// Validate if there are any missing env vars defined in `now.json`,
|
||||||
|
// but not in the `.env` / `.build.env` file
|
||||||
const missing: string[] = Object.entries(env)
|
const missing: string[] = Object.entries(env)
|
||||||
.filter(
|
.filter(
|
||||||
([name, value]) =>
|
([name, value]) =>
|
||||||
@@ -617,9 +641,36 @@ export default class DevServer {
|
|||||||
!hasOwnProperty(localEnv, name)
|
!hasOwnProperty(localEnv, name)
|
||||||
)
|
)
|
||||||
.map(([name]) => name);
|
.map(([name]) => name);
|
||||||
if (missing.length >= 1) {
|
|
||||||
|
if (missing.length > 0) {
|
||||||
throw new MissingDotenvVarsError(type, missing);
|
throw new MissingDotenvVarsError(type, missing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const merged: EnvConfig = { ...env, ...localEnv };
|
||||||
|
|
||||||
|
// Validate that the env var name matches what AWS Lambda allows:
|
||||||
|
// - https://docs.aws.amazon.com/lambda/latest/dg/env_variables.html
|
||||||
|
let hasInvalidName = false;
|
||||||
|
for (const key of Object.keys(merged)) {
|
||||||
|
if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(key)) {
|
||||||
|
this.output.warn(
|
||||||
|
`Ignoring ${type
|
||||||
|
.split('.')
|
||||||
|
.slice(1)
|
||||||
|
.reverse()
|
||||||
|
.join(' ')} var ${JSON.stringify(key)} because name is invalid`
|
||||||
|
);
|
||||||
|
hasInvalidName = true;
|
||||||
|
delete merged[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasInvalidName) {
|
||||||
|
this.output.log(
|
||||||
|
'Env var names must start with letters, and can only contain alphanumeric characters and underscores'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -652,7 +703,7 @@ export default class DevServer {
|
|||||||
const nowConfigBuild = nowConfig.build || {};
|
const nowConfigBuild = nowConfig.build || {};
|
||||||
const [env, buildEnv] = await Promise.all([
|
const [env, buildEnv] = await Promise.all([
|
||||||
this.getLocalEnv('.env', nowConfig.env),
|
this.getLocalEnv('.env', nowConfig.env),
|
||||||
this.getLocalEnv('.env.build', nowConfigBuild.env)
|
this.getLocalEnv('.env.build', nowConfigBuild.env),
|
||||||
]);
|
]);
|
||||||
Object.assign(process.env, buildEnv);
|
Object.assign(process.env, buildEnv);
|
||||||
this.env = env;
|
this.env = env;
|
||||||
@@ -670,8 +721,8 @@ export default class DevServer {
|
|||||||
|
|
||||||
const builders: Set<string> = new Set(
|
const builders: Set<string> = new Set(
|
||||||
(nowConfig.builds || [])
|
(nowConfig.builds || [])
|
||||||
.filter((b: BuildConfig) => b.use)
|
.filter((b: Builder) => b.use)
|
||||||
.map((b: BuildConfig) => b.use as string)
|
.map((b: Builder) => b.use as string)
|
||||||
);
|
);
|
||||||
|
|
||||||
await installBuilders(builders, this.yarnPath, this.output);
|
await installBuilders(builders, this.yarnPath, this.output);
|
||||||
@@ -684,10 +735,12 @@ export default class DevServer {
|
|||||||
this.yarnPath,
|
this.yarnPath,
|
||||||
this.output
|
this.output
|
||||||
)
|
)
|
||||||
.then(updatedBuilders =>
|
.then(updatedBuilders => {
|
||||||
this.invalidateBuildMatches(nowConfig, updatedBuilders)
|
this.updateBuildersPromise = null;
|
||||||
)
|
this.invalidateBuildMatches(nowConfig, updatedBuilders);
|
||||||
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
this.updateBuildersPromise = null;
|
||||||
this.output.error(`Failed to update builders: ${err.message}`);
|
this.output.error(`Failed to update builders: ${err.message}`);
|
||||||
this.output.debug(err.stack);
|
this.output.debug(err.stack);
|
||||||
});
|
});
|
||||||
@@ -716,7 +769,7 @@ export default class DevServer {
|
|||||||
ignoreInitial: true,
|
ignoreInitial: true,
|
||||||
useFsEvents: false,
|
useFsEvents: false,
|
||||||
usePolling: false,
|
usePolling: false,
|
||||||
persistent: true
|
persistent: true,
|
||||||
});
|
});
|
||||||
this.watcher.on('add', (path: string) => {
|
this.watcher.on('add', (path: string) => {
|
||||||
this.enqueueFsEvent('add', path);
|
this.enqueueFsEvent('add', path);
|
||||||
@@ -786,22 +839,18 @@ export default class DevServer {
|
|||||||
const ops: Promise<void>[] = [];
|
const ops: Promise<void>[] = [];
|
||||||
|
|
||||||
for (const match of this.buildMatches.values()) {
|
for (const match of this.buildMatches.values()) {
|
||||||
if (!match.buildOutput) continue;
|
ops.push(shutdownBuilder(match, this.output));
|
||||||
|
|
||||||
for (const asset of Object.values(match.buildOutput)) {
|
|
||||||
if (asset.type === 'Lambda' && asset.fn) {
|
|
||||||
ops.push(asset.fn.destroy());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ops.push(close(this.server));
|
ops.push(close(this.server));
|
||||||
|
|
||||||
if (this.watcher) {
|
if (this.watcher) {
|
||||||
|
this.output.debug(`Closing file watcher`);
|
||||||
this.watcher.close();
|
this.watcher.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.updateBuildersPromise) {
|
if (this.updateBuildersPromise) {
|
||||||
|
this.output.debug(`Waiting for builders update to complete`);
|
||||||
ops.push(this.updateBuildersPromise);
|
ops.push(this.updateBuildersPromise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -856,8 +905,8 @@ export default class DevServer {
|
|||||||
const json = JSON.stringify({
|
const json = JSON.stringify({
|
||||||
error: {
|
error: {
|
||||||
code: statusCode,
|
code: statusCode,
|
||||||
message: errorMessage.title
|
message: errorMessage.title,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
body = `${json}\n`;
|
body = `${json}\n`;
|
||||||
} else if (accept.includes('html')) {
|
} else if (accept.includes('html')) {
|
||||||
@@ -870,7 +919,7 @@ export default class DevServer {
|
|||||||
http_status_code: statusCode,
|
http_status_code: statusCode,
|
||||||
http_status_description,
|
http_status_description,
|
||||||
error_code,
|
error_code,
|
||||||
now_id: nowRequestId
|
now_id: nowRequestId,
|
||||||
});
|
});
|
||||||
} else if (statusCode === 502) {
|
} else if (statusCode === 502) {
|
||||||
view = errorTemplate502({
|
view = errorTemplate502({
|
||||||
@@ -878,19 +927,19 @@ export default class DevServer {
|
|||||||
http_status_code: statusCode,
|
http_status_code: statusCode,
|
||||||
http_status_description,
|
http_status_description,
|
||||||
error_code,
|
error_code,
|
||||||
now_id: nowRequestId
|
now_id: nowRequestId,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
view = errorTemplate({
|
view = errorTemplate({
|
||||||
http_status_code: statusCode,
|
http_status_code: statusCode,
|
||||||
http_status_description,
|
http_status_description,
|
||||||
now_id: nowRequestId
|
now_id: nowRequestId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
body = errorTemplateBase({
|
body = errorTemplateBase({
|
||||||
http_status_code: statusCode,
|
http_status_code: statusCode,
|
||||||
http_status_description,
|
http_status_description,
|
||||||
view
|
view,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.setHeader('content-type', 'text/plain; charset=utf-8');
|
res.setHeader('content-type', 'text/plain; charset=utf-8');
|
||||||
@@ -917,7 +966,7 @@ export default class DevServer {
|
|||||||
res.setHeader('content-type', 'application/json');
|
res.setHeader('content-type', 'application/json');
|
||||||
const json = JSON.stringify({
|
const json = JSON.stringify({
|
||||||
redirect: location,
|
redirect: location,
|
||||||
status: String(statusCode)
|
status: String(statusCode),
|
||||||
});
|
});
|
||||||
body = `${json}\n`;
|
body = `${json}\n`;
|
||||||
} else if (accept.includes('html')) {
|
} else if (accept.includes('html')) {
|
||||||
@@ -949,7 +998,7 @@ export default class DevServer {
|
|||||||
server: 'now',
|
server: 'now',
|
||||||
'x-now-trace': 'dev1',
|
'x-now-trace': 'dev1',
|
||||||
'x-now-id': nowRequestId,
|
'x-now-id': nowRequestId,
|
||||||
'x-now-cache': 'MISS'
|
'x-now-cache': 'MISS',
|
||||||
};
|
};
|
||||||
for (const [name, value] of Object.entries(allHeaders)) {
|
for (const [name, value] of Object.entries(allHeaders)) {
|
||||||
res.setHeader(name, value);
|
res.setHeader(name, value);
|
||||||
@@ -976,7 +1025,7 @@ export default class DevServer {
|
|||||||
'x-now-deployment-url': host,
|
'x-now-deployment-url': host,
|
||||||
'x-now-id': nowRequestId,
|
'x-now-id': nowRequestId,
|
||||||
'x-now-log-id': nowRequestId.split('-')[2],
|
'x-now-log-id': nowRequestId.split('-')[2],
|
||||||
'x-zeit-co-forwarded-for': ip
|
'x-zeit-co-forwarded-for': ip,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1057,7 +1106,7 @@ export default class DevServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const method = req.method || 'GET';
|
const method = req.method || 'GET';
|
||||||
this.output.log(`${chalk.bold(method)} ${req.url}`);
|
this.output.debug(`${chalk.bold(method)} ${req.url}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const nowConfig = await this.getNowConfig();
|
const nowConfig = await this.getNowConfig();
|
||||||
@@ -1183,9 +1232,7 @@ export default class DevServer {
|
|||||||
Object.assign(origUrl.query, uri_args);
|
Object.assign(origUrl.query, uri_args);
|
||||||
const newUrl = url.format(origUrl);
|
const newUrl = url.format(origUrl);
|
||||||
this.output.debug(
|
this.output.debug(
|
||||||
`Checking build result's ${
|
`Checking build result's ${buildResult.routes.length} \`routes\` to match ${newUrl}`
|
||||||
buildResult.routes.length
|
|
||||||
} \`routes\` to match ${newUrl}`
|
|
||||||
);
|
);
|
||||||
const matchedRoute = await devRouter(
|
const matchedRoute = await devRouter(
|
||||||
newUrl,
|
newUrl,
|
||||||
@@ -1238,17 +1285,17 @@ export default class DevServer {
|
|||||||
headers: [
|
headers: [
|
||||||
{
|
{
|
||||||
key: 'Content-Type',
|
key: 'Content-Type',
|
||||||
value: getMimeType(assetKey)
|
value: getMimeType(assetKey),
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
case 'FileBlob':
|
case 'FileBlob':
|
||||||
const headers: http.OutgoingHttpHeaders = {
|
const headers: http.OutgoingHttpHeaders = {
|
||||||
'Content-Length': asset.data.length,
|
'Content-Length': asset.data.length,
|
||||||
'Content-Type': getMimeType(assetKey)
|
'Content-Type': getMimeType(assetKey),
|
||||||
};
|
};
|
||||||
this.setResponseHeaders(res, nowRequestId, headers);
|
this.setResponseHeaders(res, nowRequestId, headers);
|
||||||
res.end(asset.data);
|
res.end(asset.data);
|
||||||
@@ -1273,7 +1320,7 @@ export default class DevServer {
|
|||||||
Object.assign(parsed.query, uri_args);
|
Object.assign(parsed.query, uri_args);
|
||||||
const path = url.format({
|
const path = url.format({
|
||||||
pathname: parsed.pathname,
|
pathname: parsed.pathname,
|
||||||
query: parsed.query
|
query: parsed.query,
|
||||||
});
|
});
|
||||||
|
|
||||||
const body = await rawBody(req);
|
const body = await rawBody(req);
|
||||||
@@ -1283,7 +1330,7 @@ export default class DevServer {
|
|||||||
path,
|
path,
|
||||||
headers: this.getNowProxyHeaders(req, nowRequestId),
|
headers: this.getNowProxyHeaders(req, nowRequestId),
|
||||||
encoding: 'base64',
|
encoding: 'base64',
|
||||||
body: body.toString('base64')
|
body: body.toString('base64'),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.output.debug(`Invoking lambda: "${assetKey}" with ${path}`);
|
this.output.debug(`Invoking lambda: "${assetKey}" with ${path}`);
|
||||||
@@ -1292,7 +1339,7 @@ export default class DevServer {
|
|||||||
try {
|
try {
|
||||||
result = await asset.fn<InvokeResult>({
|
result = await asset.fn<InvokeResult>({
|
||||||
Action: 'Invoke',
|
Action: 'Invoke',
|
||||||
body: JSON.stringify(payload)
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -1379,7 +1426,7 @@ export default class DevServer {
|
|||||||
relative: href,
|
relative: href,
|
||||||
ext,
|
ext,
|
||||||
title: href,
|
title: href,
|
||||||
base
|
base,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1391,13 +1438,13 @@ export default class DevServer {
|
|||||||
const paths = [
|
const paths = [
|
||||||
{
|
{
|
||||||
name: directory,
|
name: directory,
|
||||||
url: requestPath
|
url: requestPath,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
const directoryHtml = directoryTemplate({
|
const directoryHtml = directoryTemplate({
|
||||||
files,
|
files,
|
||||||
paths,
|
paths,
|
||||||
directory
|
directory,
|
||||||
});
|
});
|
||||||
this.setResponseHeaders(res, nowRequestId);
|
this.setResponseHeaders(res, nowRequestId);
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||||
@@ -1409,25 +1456,6 @@ export default class DevServer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Serve project directory as a static deployment.
|
|
||||||
*/
|
|
||||||
serveProjectAsStatic = async (
|
|
||||||
req: http.IncomingMessage,
|
|
||||||
res: http.ServerResponse,
|
|
||||||
nowRequestId: string
|
|
||||||
) => {
|
|
||||||
const filePath = req.url ? req.url.replace(/^\//, '') : '';
|
|
||||||
|
|
||||||
if (filePath && typeof this.files[filePath] === 'undefined') {
|
|
||||||
await this.send404(req, res, nowRequestId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setResponseHeaders(res, nowRequestId);
|
|
||||||
return serveStaticFile(req, res, this.cwd, { cleanUrls: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
async hasFilesystem(dest: string): Promise<boolean> {
|
async hasFilesystem(dest: string): Promise<boolean> {
|
||||||
const requestPath = dest.replace(/^\//, '');
|
const requestPath = dest.replace(/^\//, '');
|
||||||
if (
|
if (
|
||||||
@@ -1459,7 +1487,7 @@ function proxyPass(
|
|||||||
ws: true,
|
ws: true,
|
||||||
xfwd: true,
|
xfwd: true,
|
||||||
ignorePath: true,
|
ignorePath: true,
|
||||||
target: dest
|
target: dest,
|
||||||
});
|
});
|
||||||
|
|
||||||
proxy.on('error', (error: NodeJS.ErrnoException) => {
|
proxy.on('error', (error: NodeJS.ErrnoException) => {
|
||||||
@@ -1490,7 +1518,7 @@ function serveStaticFile(
|
|||||||
public: cwd,
|
public: cwd,
|
||||||
cleanUrls: false,
|
cleanUrls: false,
|
||||||
etag: true,
|
etag: true,
|
||||||
...opts
|
...opts,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1546,7 +1574,7 @@ async function shouldServe(
|
|||||||
const {
|
const {
|
||||||
src: entrypoint,
|
src: entrypoint,
|
||||||
config,
|
config,
|
||||||
builderWithPkg: { builder }
|
builderWithPkg: { builder },
|
||||||
} = match;
|
} = match;
|
||||||
if (typeof builder.shouldServe === 'function') {
|
if (typeof builder.shouldServe === 'function') {
|
||||||
const shouldServe = await builder.shouldServe({
|
const shouldServe = await builder.shouldServe({
|
||||||
@@ -1554,7 +1582,7 @@ async function shouldServe(
|
|||||||
files,
|
files,
|
||||||
config,
|
config,
|
||||||
requestPath,
|
requestPath,
|
||||||
workPath: devServer.cwd
|
workPath: devServer.cwd,
|
||||||
});
|
});
|
||||||
if (shouldServe) {
|
if (shouldServe) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export const version = 2;
|
|||||||
|
|
||||||
export function build({ files, entrypoint }: BuilderParams): BuildResult {
|
export function build({ files, entrypoint }: BuilderParams): BuildResult {
|
||||||
const output = {
|
const output = {
|
||||||
[entrypoint]: files[entrypoint]
|
[entrypoint]: files[entrypoint],
|
||||||
};
|
};
|
||||||
const watch = [entrypoint];
|
const watch = [entrypoint];
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ export function build({ files, entrypoint }: BuilderParams): BuildResult {
|
|||||||
export function shouldServe({
|
export function shouldServe({
|
||||||
entrypoint,
|
entrypoint,
|
||||||
files,
|
files,
|
||||||
requestPath
|
requestPath,
|
||||||
}: ShouldServeParams) {
|
}: ShouldServeParams) {
|
||||||
if (isIndex(entrypoint)) {
|
if (isIndex(entrypoint)) {
|
||||||
const indexPath = join(requestPath, basename(entrypoint));
|
const indexPath = join(requestPath, basename(entrypoint));
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
import http from 'http';
|
import http from 'http';
|
||||||
import { ChildProcess } from 'child_process';
|
import { ChildProcess } from 'child_process';
|
||||||
import { Lambda as FunLambda } from '@zeit/fun';
|
import { Lambda as FunLambda } from '@zeit/fun';
|
||||||
import { FileBlob, FileFsRef, Lambda } from '@now/build-utils';
|
import {
|
||||||
|
Builder as BuildConfig,
|
||||||
|
FileBlob,
|
||||||
|
FileFsRef,
|
||||||
|
Lambda,
|
||||||
|
PackageJson,
|
||||||
|
} from '@now/build-utils';
|
||||||
import { Output } from '../output';
|
import { Output } from '../output';
|
||||||
|
|
||||||
export interface DevServerOptions {
|
export interface DevServerOptions {
|
||||||
@@ -13,12 +19,6 @@ export interface EnvConfig {
|
|||||||
[name: string]: string | undefined;
|
[name: string]: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BuildConfig {
|
|
||||||
src: string;
|
|
||||||
use?: string;
|
|
||||||
config?: object;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BuildMatch extends BuildConfig {
|
export interface BuildMatch extends BuildConfig {
|
||||||
builderWithPkg: BuilderWithPackage;
|
builderWithPkg: BuilderWithPackage;
|
||||||
buildOutput: BuilderOutputs;
|
buildOutput: BuilderOutputs;
|
||||||
@@ -119,6 +119,7 @@ export interface BuildResult {
|
|||||||
output: BuilderOutputs;
|
output: BuilderOutputs;
|
||||||
routes: RouteConfig[];
|
routes: RouteConfig[];
|
||||||
watch: string[];
|
watch: string[];
|
||||||
|
distPath?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShouldServeParams {
|
export interface ShouldServeParams {
|
||||||
@@ -129,18 +130,10 @@ export interface ShouldServeParams {
|
|||||||
workPath: string;
|
workPath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Package {
|
|
||||||
name: string;
|
|
||||||
version: string;
|
|
||||||
scripts?: { [key: string]: string };
|
|
||||||
dependencies?: { [name: string]: string };
|
|
||||||
devDependencies?: { [name: string]: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BuilderWithPackage {
|
export interface BuilderWithPackage {
|
||||||
runInProcess?: boolean;
|
runInProcess?: boolean;
|
||||||
builder: Readonly<Builder>;
|
builder: Readonly<Builder>;
|
||||||
package: Readonly<Package>;
|
package: Readonly<PackageJson>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HttpHeadersConfig {
|
export interface HttpHeadersConfig {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Ajv from 'ajv';
|
import Ajv from 'ajv';
|
||||||
import { schema as routesSchema } from '@now/routing-utils';
|
import { routesSchema } from '@now/routing-utils';
|
||||||
import { NowConfig } from './types';
|
import { NowConfig } from './types';
|
||||||
|
|
||||||
const ajv = new Ajv();
|
const ajv = new Ajv();
|
||||||
@@ -16,16 +16,16 @@ const buildsSchema = {
|
|||||||
src: {
|
src: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
minLength: 1,
|
minLength: 1,
|
||||||
maxLength: 4096
|
maxLength: 4096,
|
||||||
},
|
},
|
||||||
use: {
|
use: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
minLength: 3,
|
minLength: 3,
|
||||||
maxLength: 256
|
maxLength: 256,
|
||||||
|
},
|
||||||
|
config: { type: 'object' },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
config: { type: 'object' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateBuilds = ajv.compile(buildsSchema);
|
const validateBuilds = ajv.compile(buildsSchema);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
writeFile,
|
writeFile,
|
||||||
statSync,
|
statSync,
|
||||||
chmodSync,
|
chmodSync,
|
||||||
createReadStream
|
createReadStream,
|
||||||
} from 'fs-extra';
|
} from 'fs-extra';
|
||||||
import pipe from 'promisepipe';
|
import pipe from 'promisepipe';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
@@ -63,7 +63,7 @@ async function installYarn(output: Output): Promise<string> {
|
|||||||
output.debug(`Downloading ${YARN_URL}`);
|
output.debug(`Downloading ${YARN_URL}`);
|
||||||
const response = await fetch(YARN_URL, {
|
const response = await fetch(YARN_URL, {
|
||||||
compress: false,
|
compress: false,
|
||||||
redirect: 'follow'
|
redirect: 'follow',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
@@ -90,7 +90,7 @@ async function installYarn(output: Output): Promise<string> {
|
|||||||
'@echo off',
|
'@echo off',
|
||||||
'@SETLOCAL',
|
'@SETLOCAL',
|
||||||
'@SET PATHEXT=%PATHEXT:;.JS;=;%',
|
'@SET PATHEXT=%PATHEXT:;.JS;=;%',
|
||||||
'node "%~dp0\\yarn" %*'
|
'node "%~dp0\\yarn" %*',
|
||||||
].join('\r\n')
|
].join('\r\n')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import { Response } from 'fetch-h2'
|
import { Response } from 'node-fetch';
|
||||||
import { DomainNotFound, InvalidDomain } from '../errors-ts';
|
import { DomainNotFound, InvalidDomain } from '../errors-ts';
|
||||||
import Client from '../client';
|
import Client from '../client';
|
||||||
import wait from '../output/wait';
|
import wait from '../output/wait';
|
||||||
|
|
||||||
type JSONResponse = {
|
type JSONResponse = {
|
||||||
recordIds: string[]
|
recordIds: string[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export default async function importZonefile(
|
export default async function importZonefile(
|
||||||
client: Client,
|
client: Client,
|
||||||
contextName: string,
|
contextName: string,
|
||||||
domain: string,
|
domain: string,
|
||||||
zonefilePath: string,
|
zonefilePath: string
|
||||||
) {
|
) {
|
||||||
const cancelWait = wait(`Importing Zone file for domain ${domain} under ${chalk.bold(contextName)}`);
|
const cancelWait = wait(
|
||||||
|
`Importing Zone file for domain ${domain} under ${chalk.bold(contextName)}`
|
||||||
|
);
|
||||||
const zonefile = readFileSync(resolve(zonefilePath), 'utf8');
|
const zonefile = readFileSync(resolve(zonefilePath), 'utf8');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -27,7 +29,7 @@ export default async function importZonefile(
|
|||||||
json: false,
|
json: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { recordIds } = await res.json() as JSONResponse;
|
const { recordIds } = (await res.json()) as JSONResponse;
|
||||||
cancelWait();
|
cancelWait();
|
||||||
return recordIds;
|
return recordIds;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import maybeGetDomainByName from './maybe-get-domain-by-name';
|
|||||||
import purchaseDomainIfAvailable from './purchase-domain-if-available';
|
import purchaseDomainIfAvailable from './purchase-domain-if-available';
|
||||||
import verifyDomain from './verify-domain';
|
import verifyDomain from './verify-domain';
|
||||||
import extractDomain from '../alias/extract-domain';
|
import extractDomain from '../alias/extract-domain';
|
||||||
|
import isWildcardAlias from '../alias/is-wildcard-alias';
|
||||||
|
|
||||||
export default async function setupDomain(
|
export default async function setupDomain(
|
||||||
output: Output,
|
output: Output,
|
||||||
@@ -34,7 +35,7 @@ export default async function setupDomain(
|
|||||||
|
|
||||||
if (info) {
|
if (info) {
|
||||||
output.debug(`Domain ${domain} found for the given context`);
|
output.debug(`Domain ${domain} found for the given context`);
|
||||||
if (!info.verified) {
|
if (!info.verified || (!info.nsVerifiedAt && isWildcardAlias(alias))) {
|
||||||
output.debug(
|
output.debug(
|
||||||
`Domain ${domain} is not verified, trying to perform a verification`
|
`Domain ${domain} is not verified, trying to perform a verification`
|
||||||
);
|
);
|
||||||
@@ -47,6 +48,15 @@ export default async function setupDomain(
|
|||||||
output.debug(`Domain ${domain} verification failed`);
|
output.debug(`Domain ${domain} verification failed`);
|
||||||
return verificationResult;
|
return verificationResult;
|
||||||
}
|
}
|
||||||
|
if (!verificationResult.nsVerifiedAt && isWildcardAlias(alias)) {
|
||||||
|
return new ERRORS.DomainNsNotVerifiedForWildcard({
|
||||||
|
domain,
|
||||||
|
nsVerification: {
|
||||||
|
intendedNameservers: verificationResult.intendedNameservers,
|
||||||
|
nameservers: verificationResult.nameservers
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
output.debug(`Domain ${domain} successfuly verified`);
|
output.debug(`Domain ${domain} successfuly verified`);
|
||||||
return maybeGetDomainByName(client, contextName, domain) as Promise<
|
return maybeGetDomainByName(client, contextName, domain) as Promise<
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import bytes from 'bytes';
|
import bytes from 'bytes';
|
||||||
import { Response } from 'fetch-h2';
|
import { Response } from 'node-fetch';
|
||||||
import { NowError } from './now-error';
|
import { NowError } from './now-error';
|
||||||
import param from './output/param';
|
import param from './output/param';
|
||||||
import cmd from './output/cmd';
|
import cmd from './output/cmd';
|
||||||
@@ -53,7 +53,7 @@ export class TeamDeleted extends NowError<'TEAM_DELETED', {}> {
|
|||||||
message: `Your team was deleted. You can switch to a different one using ${param(
|
message: `Your team was deleted. You can switch to a different one using ${param(
|
||||||
'now switch'
|
'now switch'
|
||||||
)}.`,
|
)}.`,
|
||||||
meta: {}
|
meta: {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ export class InvalidToken extends NowError<'NOT_AUTHORIZED', {}> {
|
|||||||
super({
|
super({
|
||||||
code: `NOT_AUTHORIZED`,
|
code: `NOT_AUTHORIZED`,
|
||||||
message: `The specified token is not valid`,
|
message: `The specified token is not valid`,
|
||||||
meta: {}
|
meta: {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ export class MissingUser extends NowError<'MISSING_USER', {}> {
|
|||||||
super({
|
super({
|
||||||
code: 'MISSING_USER',
|
code: 'MISSING_USER',
|
||||||
message: `Not able to load user, missing from response`,
|
message: `Not able to load user, missing from response`,
|
||||||
meta: {}
|
meta: {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,7 @@ export class DomainAlreadyExists extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DOMAIN_ALREADY_EXISTS',
|
code: 'DOMAIN_ALREADY_EXISTS',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: `The domain ${domain} already exists under a different context.`
|
message: `The domain ${domain} already exists under a different context.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ export class DomainPermissionDenied extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DOMAIN_PERMISSION_DENIED',
|
code: 'DOMAIN_PERMISSION_DENIED',
|
||||||
meta: { domain, context },
|
meta: { domain, context },
|
||||||
message: `You don't have access to the domain ${domain} under ${context}.`
|
message: `You don't have access to the domain ${domain} under ${context}.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,7 +128,7 @@ export class DomainExternal extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DOMAIN_EXTERNAL',
|
code: 'DOMAIN_EXTERNAL',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: `The domain ${domain} must point to zeit.world.`
|
message: `The domain ${domain} must point to zeit.world.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,7 +143,7 @@ export class SourceNotFound extends NowError<'SOURCE_NOT_FOUND', {}> {
|
|||||||
meta: {},
|
meta: {},
|
||||||
message: `Not able to purchase. Please add a payment method using ${cmd(
|
message: `Not able to purchase. Please add a payment method using ${cmd(
|
||||||
'now billing add'
|
'now billing add'
|
||||||
)}.`
|
)}.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,7 +156,7 @@ export class InvalidTransferAuthCode extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'INVALID_TRANSFER_AUTH_CODE',
|
code: 'INVALID_TRANSFER_AUTH_CODE',
|
||||||
meta: { domain, authCode },
|
meta: { domain, authCode },
|
||||||
message: `The provided auth code does not match with the one expected by the current registar`
|
message: `The provided auth code does not match with the one expected by the current registar`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,7 +169,7 @@ export class DomainRegistrationFailed extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DOMAIN_REGISTRATION_FAILED',
|
code: 'DOMAIN_REGISTRATION_FAILED',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,7 +185,7 @@ export class DomainNotFound extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DOMAIN_NOT_FOUND',
|
code: 'DOMAIN_NOT_FOUND',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: `The domain ${domain} can't be found.`
|
message: `The domain ${domain} can't be found.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,7 +198,7 @@ export class DomainNotVerified extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DOMAIN_NOT_VERIFIED',
|
code: 'DOMAIN_NOT_VERIFIED',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: `The domain ${domain} is not verified.`
|
message: `The domain ${domain} is not verified.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -221,7 +221,7 @@ export class DomainVerificationFailed extends NowError<
|
|||||||
domain,
|
domain,
|
||||||
nsVerification,
|
nsVerification,
|
||||||
txtVerification,
|
txtVerification,
|
||||||
purchased = false
|
purchased = false,
|
||||||
}: {
|
}: {
|
||||||
domain: string;
|
domain: string;
|
||||||
nsVerification: NSVerificationError;
|
nsVerification: NSVerificationError;
|
||||||
@@ -231,7 +231,7 @@ export class DomainVerificationFailed extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DOMAIN_VERIFICATION_FAILED',
|
code: 'DOMAIN_VERIFICATION_FAILED',
|
||||||
meta: { domain, nsVerification, txtVerification, purchased },
|
meta: { domain, nsVerification, txtVerification, purchased },
|
||||||
message: `We can't verify the domain ${domain}. Both Name Servers and DNS TXT verifications failed.`
|
message: `We can't verify the domain ${domain}. Both Name Servers and DNS TXT verifications failed.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,6 +252,31 @@ export type TXTVerificationError = {
|
|||||||
values: string[];
|
values: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This error is returned when the domain is not verified by nameservers for wildcard alias.
|
||||||
|
*/
|
||||||
|
export class DomainNsNotVerifiedForWildcard extends NowError<
|
||||||
|
'DOMAIN_NS_NOT_VERIFIED_FOR_WILDCARD',
|
||||||
|
{
|
||||||
|
domain: string;
|
||||||
|
nsVerification: NSVerificationError;
|
||||||
|
}
|
||||||
|
> {
|
||||||
|
constructor({
|
||||||
|
domain,
|
||||||
|
nsVerification,
|
||||||
|
}: {
|
||||||
|
domain: string;
|
||||||
|
nsVerification: NSVerificationError;
|
||||||
|
}) {
|
||||||
|
super({
|
||||||
|
code: 'DOMAIN_NS_NOT_VERIFIED_FOR_WILDCARD',
|
||||||
|
meta: { domain, nsVerification },
|
||||||
|
message: `The domain ${domain} is not verified by nameservers for wildcard alias.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used when a domain is validated because we tried to add it to an account
|
* Used when a domain is validated because we tried to add it to an account
|
||||||
* via API or for any other reason.
|
* via API or for any other reason.
|
||||||
@@ -264,7 +289,17 @@ export class InvalidDomain extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'INVALID_DOMAIN',
|
code: 'INVALID_DOMAIN',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: message || `The domain ${domain} is not valid.`
|
message: message || `The domain ${domain} is not valid.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NotDomainOwner extends NowError<'NOT_DOMAIN_OWNER', {}> {
|
||||||
|
constructor(message: string) {
|
||||||
|
super({
|
||||||
|
code: 'NOT_DOMAIN_OWNER',
|
||||||
|
meta: {},
|
||||||
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,7 +312,7 @@ export class InvalidDeploymentId extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'INVALID_DEPLOYMENT_ID',
|
code: 'INVALID_DEPLOYMENT_ID',
|
||||||
meta: { id },
|
meta: { id },
|
||||||
message: `The deployment id "${id}" is not valid.`
|
message: `The deployment id "${id}" is not valid.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -294,7 +329,7 @@ export class UnsupportedTLD extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'UNSUPPORTED_TLD',
|
code: 'UNSUPPORTED_TLD',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: `The TLD for domain name ${domain} is not supported.`
|
message: `The TLD for domain name ${domain} is not supported.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,7 +346,7 @@ export class DomainNotAvailable extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DOMAIN_NOT_AVAILABLE',
|
code: 'DOMAIN_NOT_AVAILABLE',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: `The domain ${domain} is not available to be purchased.`
|
message: `The domain ${domain} is not available to be purchased.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -328,7 +363,7 @@ export class DomainServiceNotAvailable extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DOMAIN_SERVICE_NOT_AVAILABLE',
|
code: 'DOMAIN_SERVICE_NOT_AVAILABLE',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: `The domain purchase is unavailable, try again later.`
|
message: `The domain purchase is unavailable, try again later.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,7 +380,7 @@ export class DomainNotTransferable extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DOMAIN_NOT_TRANSFERABLE',
|
code: 'DOMAIN_NOT_TRANSFERABLE',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: `The domain ${domain} is not available to be transferred.`
|
message: `The domain ${domain} is not available to be transferred.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,7 +396,7 @@ export class UnexpectedDomainPurchaseError extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'UNEXPECTED_DOMAIN_PURCHASE_ERROR',
|
code: 'UNEXPECTED_DOMAIN_PURCHASE_ERROR',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: `An unexpected error happened while purchasing.`
|
message: `An unexpected error happened while purchasing.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -374,7 +409,7 @@ export class DomainPaymentError extends NowError<'DOMAIN_PAYMENT_ERROR', {}> {
|
|||||||
super({
|
super({
|
||||||
code: 'DOMAIN_PAYMENT_ERROR',
|
code: 'DOMAIN_PAYMENT_ERROR',
|
||||||
meta: {},
|
meta: {},
|
||||||
message: `Your card was declined.`
|
message: `Your card was declined.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,7 +426,7 @@ export class DomainPurchasePending extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DOMAIN_PURCHASE_PENDING',
|
code: 'DOMAIN_PURCHASE_PENDING',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: `The domain purchase for ${domain} is pending.`
|
message: `The domain purchase for ${domain} is pending.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -405,7 +440,7 @@ export class UserAborted extends NowError<'USER_ABORTED', {}> {
|
|||||||
super({
|
super({
|
||||||
code: 'USER_ABORTED',
|
code: 'USER_ABORTED',
|
||||||
meta: {},
|
meta: {},
|
||||||
message: `The user aborted the operation.`
|
message: `The user aborted the operation.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -415,7 +450,7 @@ export class CertNotFound extends NowError<'CERT_NOT_FOUND', { id: string }> {
|
|||||||
super({
|
super({
|
||||||
code: 'CERT_NOT_FOUND',
|
code: 'CERT_NOT_FOUND',
|
||||||
meta: { id },
|
meta: { id },
|
||||||
message: `The cert ${id} can't be found.`
|
message: `The cert ${id} can't be found.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -428,7 +463,7 @@ export class CertsPermissionDenied extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'CERTS_PERMISSION_DENIED',
|
code: 'CERTS_PERMISSION_DENIED',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: `You don't have access to ${domain}'s certs under ${context}.`
|
message: `You don't have access to ${domain}'s certs under ${context}.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,7 +476,7 @@ export class CertOrderNotFound extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'CERT_ORDER_NOT_FOUND',
|
code: 'CERT_ORDER_NOT_FOUND',
|
||||||
meta: { cns },
|
meta: { cns },
|
||||||
message: `No cert order could be found for cns ${cns.join(' ,')}`
|
message: `No cert order could be found for cns ${cns.join(' ,')}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -459,7 +494,7 @@ export class TooManyRequests extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'TOO_MANY_REQUESTS',
|
code: 'TOO_MANY_REQUESTS',
|
||||||
meta: { api, retryAfter },
|
meta: { api, retryAfter },
|
||||||
message: `Rate limited. Too many requests to the same endpoint.`
|
message: `Rate limited. Too many requests to the same endpoint.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -493,7 +528,7 @@ export class CertError extends NowError<
|
|||||||
cns,
|
cns,
|
||||||
code,
|
code,
|
||||||
message,
|
message,
|
||||||
helpUrl
|
helpUrl,
|
||||||
}: {
|
}: {
|
||||||
cns: string[];
|
cns: string[];
|
||||||
code: CertErrorCode;
|
code: CertErrorCode;
|
||||||
@@ -503,7 +538,7 @@ export class CertError extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: `CERT_ERROR`,
|
code: `CERT_ERROR`,
|
||||||
meta: { cns, code, helpUrl },
|
meta: { cns, code, helpUrl },
|
||||||
message
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -522,7 +557,7 @@ export class CertConfigurationError extends NowError<
|
|||||||
message,
|
message,
|
||||||
external,
|
external,
|
||||||
type,
|
type,
|
||||||
helpUrl
|
helpUrl,
|
||||||
}: {
|
}: {
|
||||||
cns: string[];
|
cns: string[];
|
||||||
message: string;
|
message: string;
|
||||||
@@ -533,7 +568,7 @@ export class CertConfigurationError extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: `CERT_CONFIGURATION_ERROR`,
|
code: `CERT_CONFIGURATION_ERROR`,
|
||||||
meta: { cns, helpUrl, external, type },
|
meta: { cns, helpUrl, external, type },
|
||||||
message
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -550,7 +585,7 @@ export class DeploymentNotFound extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DEPLOYMENT_NOT_FOUND',
|
code: 'DEPLOYMENT_NOT_FOUND',
|
||||||
meta: { id, context },
|
meta: { id, context },
|
||||||
message: `Can't find the deployment ${id} under the context ${context}`
|
message: `Can't find the deployment ${id} under the context ${context}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -561,13 +596,13 @@ export class DeploymentNotFound extends NowError<
|
|||||||
*/
|
*/
|
||||||
export class DeploymentNotReady extends NowError<
|
export class DeploymentNotReady extends NowError<
|
||||||
'DEPLOYMENT_NOT_READY',
|
'DEPLOYMENT_NOT_READY',
|
||||||
{ url: string; }
|
{ url: string }
|
||||||
> {
|
> {
|
||||||
constructor({ url = '' }: { url: string }) {
|
constructor({ url = '' }: { url: string }) {
|
||||||
super({
|
super({
|
||||||
code: 'DEPLOYMENT_NOT_READY',
|
code: 'DEPLOYMENT_NOT_READY',
|
||||||
meta: { url },
|
meta: { url },
|
||||||
message: `The deployment https://${url} is not ready.`
|
message: `The deployment https://${url} is not ready.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -580,7 +615,7 @@ export class DeploymentFailedAliasImpossible extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DEPLOYMENT_FAILED_ALIAS_IMPOSSIBLE',
|
code: 'DEPLOYMENT_FAILED_ALIAS_IMPOSSIBLE',
|
||||||
meta: {},
|
meta: {},
|
||||||
message: `The deployment build has failed and cannot be aliased`
|
message: `The deployment build has failed and cannot be aliased`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -597,7 +632,7 @@ export class DeploymentPermissionDenied extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DEPLOYMENT_PERMISSION_DENIED',
|
code: 'DEPLOYMENT_PERMISSION_DENIED',
|
||||||
meta: { id, context },
|
meta: { id, context },
|
||||||
message: `You don't have access to the deployment ${id} under ${context}.`
|
message: `You don't have access to the deployment ${id} under ${context}.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -610,7 +645,7 @@ export class DeploymentTypeUnsupported extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DEPLOYMENT_TYPE_UNSUPPORTED',
|
code: 'DEPLOYMENT_TYPE_UNSUPPORTED',
|
||||||
meta: {},
|
meta: {},
|
||||||
message: `This region only accepts Serverless Docker Deployments`
|
message: `This region only accepts Serverless Docker Deployments`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -624,7 +659,7 @@ export class InvalidAlias extends NowError<'INVALID_ALIAS', { alias: string }> {
|
|||||||
super({
|
super({
|
||||||
code: 'INVALID_ALIAS',
|
code: 'INVALID_ALIAS',
|
||||||
meta: { alias },
|
meta: { alias },
|
||||||
message: `The given alias ${alias} is not valid`
|
message: `The given alias ${alias} is not valid`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -638,7 +673,7 @@ export class AliasInUse extends NowError<'ALIAS_IN_USE', { alias: string }> {
|
|||||||
super({
|
super({
|
||||||
code: 'ALIAS_IN_USE',
|
code: 'ALIAS_IN_USE',
|
||||||
meta: { alias },
|
meta: { alias },
|
||||||
message: `The alias is already in use`
|
message: `The alias is already in use`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -653,7 +688,7 @@ export class CertMissing extends NowError<'ALIAS_IN_USE', { domain: string }> {
|
|||||||
super({
|
super({
|
||||||
code: 'ALIAS_IN_USE',
|
code: 'ALIAS_IN_USE',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: `The alias is already in use`
|
message: `The alias is already in use`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -666,7 +701,7 @@ export class ForbiddenScaleMinInstances extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'FORBIDDEN_SCALE_MIN_INSTANCES',
|
code: 'FORBIDDEN_SCALE_MIN_INSTANCES',
|
||||||
meta: { url, max },
|
meta: { url, max },
|
||||||
message: `You can't scale to more than ${max} min instances with your current plan.`
|
message: `You can't scale to more than ${max} min instances with your current plan.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -679,7 +714,7 @@ export class ForbiddenScaleMaxInstances extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'FORBIDDEN_SCALE_MAX_INSTANCES',
|
code: 'FORBIDDEN_SCALE_MAX_INSTANCES',
|
||||||
meta: { url, max },
|
meta: { url, max },
|
||||||
message: `You can't scale to more than ${max} max instances with your current plan.`
|
message: `You can't scale to more than ${max} max instances with your current plan.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -692,7 +727,7 @@ export class InvalidScaleMinMaxRelation extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'INVALID_SCALE_MIN_MAX_RELATION',
|
code: 'INVALID_SCALE_MIN_MAX_RELATION',
|
||||||
meta: { url },
|
meta: { url },
|
||||||
message: `Min number of instances can't be higher than max.`
|
message: `Min number of instances can't be higher than max.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -705,7 +740,7 @@ export class NotSupportedMinScaleSlots extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'NOT_SUPPORTED_MIN_SCALE_SLOTS',
|
code: 'NOT_SUPPORTED_MIN_SCALE_SLOTS',
|
||||||
meta: { url },
|
meta: { url },
|
||||||
message: `Cloud v2 does not yet support setting a non-zero min scale setting.`
|
message: `Cloud v2 does not yet support setting a non-zero min scale setting.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -718,7 +753,7 @@ export class VerifyScaleTimeout extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'VERIFY_SCALE_TIMEOUT',
|
code: 'VERIFY_SCALE_TIMEOUT',
|
||||||
meta: { timeout },
|
meta: { timeout },
|
||||||
message: `Instance verification timed out (${timeout}ms)`
|
message: `Instance verification timed out (${timeout}ms)`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -731,7 +766,7 @@ export class CantParseJSONFile extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'CANT_PARSE_JSON_FILE',
|
code: 'CANT_PARSE_JSON_FILE',
|
||||||
meta: { file },
|
meta: { file },
|
||||||
message: `Can't parse json file`
|
message: `Can't parse json file`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -744,7 +779,20 @@ export class CantFindConfig extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'CANT_FIND_CONFIG',
|
code: 'CANT_FIND_CONFIG',
|
||||||
meta: { paths },
|
meta: { paths },
|
||||||
message: `Can't find a configuration file in the given locations.`
|
message: `Can't find a configuration file in the given locations.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WorkingDirectoryDoesNotExist extends NowError<
|
||||||
|
'CWD_DOES_NOT_EXIST',
|
||||||
|
{}
|
||||||
|
> {
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
code: 'CWD_DOES_NOT_EXIST',
|
||||||
|
meta: {},
|
||||||
|
message: 'The current working directory does not exist.',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -754,7 +802,7 @@ export class FileNotFound extends NowError<'FILE_NOT_FOUND', { file: string }> {
|
|||||||
super({
|
super({
|
||||||
code: 'FILE_NOT_FOUND',
|
code: 'FILE_NOT_FOUND',
|
||||||
meta: { file },
|
meta: { file },
|
||||||
message: `Can't find a file in provided location '${file}'.`
|
message: `Can't find a file in provided location '${file}'.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -767,7 +815,7 @@ export class RulesFileValidationError extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'PATH_ALIAS_VALIDATION_ERROR',
|
code: 'PATH_ALIAS_VALIDATION_ERROR',
|
||||||
meta: { location, message },
|
meta: { location, message },
|
||||||
message: `The provided rules format in file for path alias are invalid`
|
message: `The provided rules format in file for path alias are invalid`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -777,7 +825,7 @@ export class NoAliasInConfig extends NowError<'NO_ALIAS_IN_CONFIG', {}> {
|
|||||||
super({
|
super({
|
||||||
code: 'NO_ALIAS_IN_CONFIG',
|
code: 'NO_ALIAS_IN_CONFIG',
|
||||||
meta: {},
|
meta: {},
|
||||||
message: `There is no alias set up in config file.`
|
message: `There is no alias set up in config file.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -790,7 +838,7 @@ export class InvalidAliasInConfig extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'INVALID_ALIAS_IN_CONFIG',
|
code: 'INVALID_ALIAS_IN_CONFIG',
|
||||||
meta: { value },
|
meta: { value },
|
||||||
message: `Invalid alias option in configuration.`
|
message: `Invalid alias option in configuration.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -803,7 +851,7 @@ export class RuleValidationFailed extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'RULE_VALIDATION_FAILED',
|
code: 'RULE_VALIDATION_FAILED',
|
||||||
meta: { message },
|
meta: { message },
|
||||||
message: `The server validation for rules failed`
|
message: `The server validation for rules failed`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -816,7 +864,7 @@ export class InvalidMinForScale extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'INVALID_MIN_FOR_SCALE',
|
code: 'INVALID_MIN_FOR_SCALE',
|
||||||
meta: { value },
|
meta: { value },
|
||||||
message: `Invalid <min> parameter "${value}". A number or "auto" were expected`
|
message: `Invalid <min> parameter "${value}". A number or "auto" were expected`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -829,7 +877,7 @@ export class InvalidArgsForMinMaxScale extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'INVALID_ARGS_FOR_MIN_MAX_SCALE',
|
code: 'INVALID_ARGS_FOR_MIN_MAX_SCALE',
|
||||||
meta: { min },
|
meta: { min },
|
||||||
message: `Invalid number of arguments: expected <min> ("${min}") and [max]`
|
message: `Invalid number of arguments: expected <min> ("${min}") and [max]`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -842,7 +890,7 @@ export class InvalidMaxForScale extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'INVALID_MAX_FOR_SCALE',
|
code: 'INVALID_MAX_FOR_SCALE',
|
||||||
meta: { value },
|
meta: { value },
|
||||||
message: `Invalid <max> parameter "${value}". A number or "auto" were expected`
|
message: `Invalid <max> parameter "${value}". A number or "auto" were expected`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -852,7 +900,7 @@ export class InvalidCert extends NowError<'INVALID_CERT', {}> {
|
|||||||
super({
|
super({
|
||||||
code: 'INVALID_CERT',
|
code: 'INVALID_CERT',
|
||||||
meta: {},
|
meta: {},
|
||||||
message: `The provided custom certificate is invalid and couldn't be added`
|
message: `The provided custom certificate is invalid and couldn't be added`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -865,7 +913,7 @@ export class DNSPermissionDenied extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DNS_PERMISSION_DENIED',
|
code: 'DNS_PERMISSION_DENIED',
|
||||||
meta: { domain },
|
meta: { domain },
|
||||||
message: `You don't have access to the DNS records of ${domain}.`
|
message: `You don't have access to the DNS records of ${domain}.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -875,7 +923,7 @@ export class DNSInvalidPort extends NowError<'DNS_INVALID_PORT', {}> {
|
|||||||
super({
|
super({
|
||||||
code: 'DNS_INVALID_PORT',
|
code: 'DNS_INVALID_PORT',
|
||||||
meta: {},
|
meta: {},
|
||||||
message: `Invalid <port> parameter. A number was expected`
|
message: `Invalid <port> parameter. A number was expected`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -888,7 +936,7 @@ export class DNSInvalidType extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DNS_INVALID_TYPE',
|
code: 'DNS_INVALID_TYPE',
|
||||||
meta: { type },
|
meta: { type },
|
||||||
message: `Invalid <type> parameter "${type}". Expected one of A, AAAA, ALIAS, CAA, CNAME, MX, SRV, TXT`
|
message: `Invalid <type> parameter "${type}". Expected one of A, AAAA, ALIAS, CAA, CNAME, MX, SRV, TXT`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -901,7 +949,7 @@ export class DNSConflictingRecord extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'DNS_CONFLICTING_RECORD',
|
code: 'DNS_CONFLICTING_RECORD',
|
||||||
meta: { record },
|
meta: { record },
|
||||||
message: ` A conflicting record exists "${record}".`
|
message: ` A conflicting record exists "${record}".`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -924,7 +972,7 @@ export class DomainRemovalConflict extends NowError<
|
|||||||
pendingAsyncPurchase,
|
pendingAsyncPurchase,
|
||||||
resolvable,
|
resolvable,
|
||||||
suffix,
|
suffix,
|
||||||
transferring
|
transferring,
|
||||||
}: {
|
}: {
|
||||||
aliases: string[];
|
aliases: string[];
|
||||||
certs: string[];
|
certs: string[];
|
||||||
@@ -942,9 +990,9 @@ export class DomainRemovalConflict extends NowError<
|
|||||||
pendingAsyncPurchase,
|
pendingAsyncPurchase,
|
||||||
suffix,
|
suffix,
|
||||||
transferring,
|
transferring,
|
||||||
resolvable
|
resolvable,
|
||||||
},
|
},
|
||||||
message
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -957,7 +1005,7 @@ export class DomainMoveConflict extends NowError<
|
|||||||
message,
|
message,
|
||||||
pendingAsyncPurchase,
|
pendingAsyncPurchase,
|
||||||
resolvable,
|
resolvable,
|
||||||
suffix
|
suffix,
|
||||||
}: {
|
}: {
|
||||||
message: string;
|
message: string;
|
||||||
pendingAsyncPurchase: boolean;
|
pendingAsyncPurchase: boolean;
|
||||||
@@ -969,9 +1017,9 @@ export class DomainMoveConflict extends NowError<
|
|||||||
meta: {
|
meta: {
|
||||||
pendingAsyncPurchase,
|
pendingAsyncPurchase,
|
||||||
resolvable,
|
resolvable,
|
||||||
suffix
|
suffix,
|
||||||
},
|
},
|
||||||
message
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -981,17 +1029,23 @@ export class InvalidEmail extends NowError<'INVALID_EMAIL', { email: string }> {
|
|||||||
super({
|
super({
|
||||||
code: 'INVALID_EMAIL',
|
code: 'INVALID_EMAIL',
|
||||||
message,
|
message,
|
||||||
meta: { email }
|
meta: { email },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AccountNotFound extends NowError<'ACCOUNT_NOT_FOUND', { email: string }> {
|
export class AccountNotFound extends NowError<
|
||||||
constructor(email: string, message: string = `Please sign up: https://zeit.co/signup`) {
|
'ACCOUNT_NOT_FOUND',
|
||||||
|
{ email: string }
|
||||||
|
> {
|
||||||
|
constructor(
|
||||||
|
email: string,
|
||||||
|
message: string = `Please sign up: https://zeit.co/signup`
|
||||||
|
) {
|
||||||
super({
|
super({
|
||||||
code: 'ACCOUNT_NOT_FOUND',
|
code: 'ACCOUNT_NOT_FOUND',
|
||||||
message,
|
message,
|
||||||
meta: { email }
|
meta: { email },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1004,7 +1058,7 @@ export class InvalidMoveDestination extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'INVALID_MOVE_DESTINATION',
|
code: 'INVALID_MOVE_DESTINATION',
|
||||||
message: `Invalid move destination "${destination}"`,
|
message: `Invalid move destination "${destination}"`,
|
||||||
meta: { destination }
|
meta: { destination },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1017,7 +1071,7 @@ export class InvalidMoveToken extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'INVALID_MOVE_TOKEN',
|
code: 'INVALID_MOVE_TOKEN',
|
||||||
message: `Invalid move token "${token}"`,
|
message: `Invalid move token "${token}"`,
|
||||||
meta: { token }
|
meta: { token },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1027,7 +1081,7 @@ export class NoBuilderCacheError extends NowError<'NO_BUILDER_CACHE', {}> {
|
|||||||
super({
|
super({
|
||||||
code: 'NO_BUILDER_CACHE',
|
code: 'NO_BUILDER_CACHE',
|
||||||
message: 'Could not find cache directory for now-builders.',
|
message: 'Could not find cache directory for now-builders.',
|
||||||
meta: {}
|
meta: {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1040,7 +1094,7 @@ export class BuilderCacheCleanError extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'BUILDER_CACHE_CLEAN_FAILED',
|
code: 'BUILDER_CACHE_CLEAN_FAILED',
|
||||||
message: `Error cleaning builder cache: ${message}`,
|
message: `Error cleaning builder cache: ${message}`,
|
||||||
meta: { path }
|
meta: { path },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1057,7 +1111,7 @@ export class LambdaSizeExceededError extends NowError<
|
|||||||
).toLowerCase()}) exceeds the maximum size limit (${bytes(
|
).toLowerCase()}) exceeds the maximum size limit (${bytes(
|
||||||
maxLambdaSize
|
maxLambdaSize
|
||||||
).toLowerCase()}). Learn more: https://zeit.co/docs/v2/deployments/concepts/lambdas/#maximum-bundle-size`,
|
).toLowerCase()}). Learn more: https://zeit.co/docs/v2/deployments/concepts/lambdas/#maximum-bundle-size`,
|
||||||
meta: { size, maxLambdaSize }
|
meta: { size, maxLambdaSize },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1076,7 +1130,7 @@ export class MissingDotenvVarsError extends NowError<
|
|||||||
} else {
|
} else {
|
||||||
message = [
|
message = [
|
||||||
`The following env vars are not defined in ${code(type)} file:`,
|
`The following env vars are not defined in ${code(type)} file:`,
|
||||||
...missing.map(name => ` - ${JSON.stringify(name)}`)
|
...missing.map(name => ` - ${JSON.stringify(name)}`),
|
||||||
].join('\n');
|
].join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1085,17 +1139,20 @@ export class MissingDotenvVarsError extends NowError<
|
|||||||
super({
|
super({
|
||||||
code: 'MISSING_DOTENV_VARS',
|
code: 'MISSING_DOTENV_VARS',
|
||||||
message,
|
message,
|
||||||
meta: { type, missing }
|
meta: { type, missing },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DeploymentsRateLimited extends NowError<'DEPLOYMENTS_RATE_LIMITED', {}> {
|
export class DeploymentsRateLimited extends NowError<
|
||||||
|
'DEPLOYMENTS_RATE_LIMITED',
|
||||||
|
{}
|
||||||
|
> {
|
||||||
constructor(message: string) {
|
constructor(message: string) {
|
||||||
super({
|
super({
|
||||||
code: 'DEPLOYMENTS_RATE_LIMITED',
|
code: 'DEPLOYMENTS_RATE_LIMITED',
|
||||||
meta: {},
|
meta: {},
|
||||||
message
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1105,7 +1162,7 @@ export class BuildsRateLimited extends NowError<'BUILDS_RATE_LIMITED', {}> {
|
|||||||
super({
|
super({
|
||||||
code: 'BUILDS_RATE_LIMITED',
|
code: 'BUILDS_RATE_LIMITED',
|
||||||
meta: {},
|
meta: {},
|
||||||
message
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1115,47 +1172,66 @@ export class ProjectNotFound extends NowError<'PROJECT_NOT_FOUND', {}> {
|
|||||||
super({
|
super({
|
||||||
code: 'PROJECT_NOT_FOUND',
|
code: 'PROJECT_NOT_FOUND',
|
||||||
meta: {},
|
meta: {},
|
||||||
message: `There is no project for "${nameOrId}"`
|
message: `There is no project for "${nameOrId}"`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AliasDomainConfigured extends NowError<'DOMAIN_CONFIGURED', {}> {
|
export class AliasDomainConfigured extends NowError<'DOMAIN_CONFIGURED', {}> {
|
||||||
constructor({ message }: { message: string; }) {
|
constructor({ message }: { message: string }) {
|
||||||
super({
|
super({
|
||||||
code: 'DOMAIN_CONFIGURED',
|
code: 'DOMAIN_CONFIGURED',
|
||||||
meta: {},
|
meta: {},
|
||||||
message
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MissingBuildScript extends NowError<'MISSING_BUILD_SCRIPT', {}> {
|
export class MissingBuildScript extends NowError<'MISSING_BUILD_SCRIPT', {}> {
|
||||||
constructor({ message }: { message: string; }) {
|
constructor({ message }: { message: string }) {
|
||||||
super({
|
super({
|
||||||
code: 'MISSING_BUILD_SCRIPT',
|
code: 'MISSING_BUILD_SCRIPT',
|
||||||
meta: {},
|
meta: {},
|
||||||
message
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ConflictingFilePath extends NowError<'CONFLICTING_FILE_PATH', {}> {
|
export class ConflictingFilePath extends NowError<'CONFLICTING_FILE_PATH', {}> {
|
||||||
constructor({ message }: { message: string; }) {
|
constructor({ message }: { message: string }) {
|
||||||
super({
|
super({
|
||||||
code: 'CONFLICTING_FILE_PATH',
|
code: 'CONFLICTING_FILE_PATH',
|
||||||
meta: {},
|
meta: {},
|
||||||
message
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ConflictingPathSegment extends NowError<'CONFLICTING_PATH_SEGMENT', {}> {
|
export class ConflictingPathSegment extends NowError<
|
||||||
constructor({ message }: { message: string; }) {
|
'CONFLICTING_PATH_SEGMENT',
|
||||||
|
{}
|
||||||
|
> {
|
||||||
|
constructor({ message }: { message: string }) {
|
||||||
super({
|
super({
|
||||||
code: 'CONFLICTING_PATH_SEGMENT',
|
code: 'CONFLICTING_PATH_SEGMENT',
|
||||||
meta: {},
|
meta: {},
|
||||||
message
|
message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BuildError extends NowError<'BUILD_ERROR', {}> {
|
||||||
|
constructor({
|
||||||
|
message,
|
||||||
|
meta,
|
||||||
|
}: {
|
||||||
|
message: string;
|
||||||
|
meta: { entrypoint: string };
|
||||||
|
}) {
|
||||||
|
super({
|
||||||
|
code: 'BUILD_ERROR',
|
||||||
|
meta,
|
||||||
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { CantParseJSONFile, CantFindConfig } from './errors-ts';
|
import {
|
||||||
|
CantParseJSONFile,
|
||||||
|
CantFindConfig,
|
||||||
|
WorkingDirectoryDoesNotExist,
|
||||||
|
} from './errors-ts';
|
||||||
import humanizePath from './humanize-path';
|
import humanizePath from './humanize-path';
|
||||||
import readJSONFile from './read-json-file';
|
import readJSONFile from './read-json-file';
|
||||||
import readPackage from './read-package';
|
import readPackage from './read-package';
|
||||||
@@ -8,14 +12,25 @@ import { Output } from './output';
|
|||||||
|
|
||||||
let config: Config;
|
let config: Config;
|
||||||
|
|
||||||
export default async function getConfig(output: Output, configFile?: string) {
|
export default async function getConfig(
|
||||||
const localPath = process.cwd();
|
output: Output,
|
||||||
|
configFile?: string
|
||||||
|
): Promise<Config | Error> {
|
||||||
// If config was already read, just return it
|
// If config was already read, just return it
|
||||||
if (config) {
|
if (config) {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let localPath: string;
|
||||||
|
try {
|
||||||
|
localPath = process.cwd();
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
return new WorkingDirectoryDoesNotExist();
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
// First try with the config supplied by the user via --local-config
|
// First try with the config supplied by the user via --local-config
|
||||||
if (configFile) {
|
if (configFile) {
|
||||||
const localFilePath = path.resolve(localPath, configFile);
|
const localFilePath = path.resolve(localPath, configFile);
|
||||||
@@ -27,8 +42,7 @@ export default async function getConfig(output: Output, configFile?: string) {
|
|||||||
return localConfig;
|
return localConfig;
|
||||||
}
|
}
|
||||||
if (localConfig !== null) {
|
if (localConfig !== null) {
|
||||||
const castedConfig = localConfig;
|
config = localConfig;
|
||||||
config = castedConfig;
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ export default function handleError(
|
|||||||
|
|
||||||
if ((<APIError>error).status === 403) {
|
if ((<APIError>error).status === 403) {
|
||||||
console.error(
|
console.error(
|
||||||
errorOutput('Authentication error. Run `now login` to log-in again.')
|
errorOutput(
|
||||||
|
error.message ||
|
||||||
|
'Authentication error. Run `now login` to log-in again.'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
} else if ((<APIError>error).status === 429) {
|
} else if ((<APIError>error).status === 429) {
|
||||||
// Rate limited: display the message from the server-side,
|
// Rate limited: display the message from the server-side,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { homedir } from 'os';
|
import { homedir } from 'os';
|
||||||
import { resolve as resolvePath, join, basename } from 'path';
|
import { resolve as resolvePath } from 'path';
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import qs from 'querystring';
|
import qs from 'querystring';
|
||||||
import { parse as parseUrl } from 'url';
|
import { parse as parseUrl } from 'url';
|
||||||
@@ -7,24 +7,22 @@ import bytes from 'bytes';
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import retry from 'async-retry';
|
import retry from 'async-retry';
|
||||||
import { parse as parseIni } from 'ini';
|
import { parse as parseIni } from 'ini';
|
||||||
import { createReadStream } from 'fs';
|
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
|
import fetch from 'node-fetch';
|
||||||
import { URLSearchParams } from 'url';
|
import { URLSearchParams } from 'url';
|
||||||
import {
|
import {
|
||||||
staticFiles as getFiles,
|
staticFiles as getFiles,
|
||||||
npm as getNpmFiles,
|
npm as getNpmFiles,
|
||||||
docker as getDockerFiles
|
docker as getDockerFiles,
|
||||||
} from './get-files';
|
} from './get-files';
|
||||||
import Agent from './agent.ts';
|
|
||||||
import ua from './ua.ts';
|
import ua from './ua.ts';
|
||||||
import hash from './hash';
|
import processDeployment from './deploy/process-deployment.ts';
|
||||||
import highlight from './output/highlight';
|
import highlight from './output/highlight';
|
||||||
import createOutput from './output';
|
import createOutput from './output';
|
||||||
import { responseError } from './error';
|
import { responseError } from './error';
|
||||||
|
import stamp from './output/stamp';
|
||||||
// How many concurrent HTTP/2 stream uploads
|
import { BuildError } from './errors-ts';
|
||||||
const MAX_CONCURRENT = 50;
|
|
||||||
|
|
||||||
// Check if running windows
|
// Check if running windows
|
||||||
const IS_WIN = process.platform.startsWith('win');
|
const IS_WIN = process.platform.startsWith('win');
|
||||||
@@ -39,14 +37,8 @@ export default class Now extends EventEmitter {
|
|||||||
this._forceNew = forceNew;
|
this._forceNew = forceNew;
|
||||||
this._output = createOutput({ debug });
|
this._output = createOutput({ debug });
|
||||||
this._apiUrl = apiUrl;
|
this._apiUrl = apiUrl;
|
||||||
this._agent = new Agent(apiUrl, { debug });
|
|
||||||
this._onRetry = this._onRetry.bind(this);
|
this._onRetry = this._onRetry.bind(this);
|
||||||
this.currentTeam = currentTeam;
|
this.currentTeam = currentTeam;
|
||||||
const closeAgent = () => {
|
|
||||||
this._agent.close();
|
|
||||||
process.removeListener('nowExit', closeAgent);
|
|
||||||
};
|
|
||||||
process.on('nowExit', closeAgent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(
|
async create(
|
||||||
@@ -61,7 +53,6 @@ export default class Now extends EventEmitter {
|
|||||||
nowConfig = {},
|
nowConfig = {},
|
||||||
hasNowJson = false,
|
hasNowJson = false,
|
||||||
sessionAffinity = 'random',
|
sessionAffinity = 'random',
|
||||||
isFile = false,
|
|
||||||
atlas = false,
|
atlas = false,
|
||||||
|
|
||||||
// Latest
|
// Latest
|
||||||
@@ -73,22 +64,40 @@ export default class Now extends EventEmitter {
|
|||||||
quiet = false,
|
quiet = false,
|
||||||
env,
|
env,
|
||||||
build,
|
build,
|
||||||
followSymlinks = true,
|
|
||||||
forceNew = false,
|
forceNew = false,
|
||||||
target = null
|
target = null,
|
||||||
|
deployStamp,
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const { log, warn, time } = this._output;
|
const opts = { output: this._output, hasNowJson };
|
||||||
|
const { log, warn, debug } = this._output;
|
||||||
const isBuilds = type === null;
|
const isBuilds = type === null;
|
||||||
|
|
||||||
let files = [];
|
let files = [];
|
||||||
|
let hashes = {};
|
||||||
const relatives = {};
|
const relatives = {};
|
||||||
let engines;
|
let engines;
|
||||||
|
let deployment;
|
||||||
|
let requestBody = {};
|
||||||
|
|
||||||
await time('Getting files', async () => {
|
if (isBuilds) {
|
||||||
const opts = { output: this._output, hasNowJson };
|
requestBody = {
|
||||||
|
token: this._token,
|
||||||
|
teamId: this.currentTeam,
|
||||||
|
env,
|
||||||
|
build,
|
||||||
|
public: wantsPublic || nowConfig.public,
|
||||||
|
name,
|
||||||
|
project,
|
||||||
|
meta,
|
||||||
|
regions,
|
||||||
|
force: forceNew,
|
||||||
|
};
|
||||||
|
|
||||||
if (type === 'npm') {
|
if (target) {
|
||||||
|
requestBody.target = target;
|
||||||
|
}
|
||||||
|
} else if (type === 'npm') {
|
||||||
files = await getNpmFiles(paths[0], pkg, nowConfig, opts);
|
files = await getNpmFiles(paths[0], pkg, nowConfig, opts);
|
||||||
|
|
||||||
// A `start` or `now-start` npm script, or a `server.js` file
|
// A `start` or `now-start` npm script, or a `server.js` file
|
||||||
@@ -108,9 +117,7 @@ export default class Now extends EventEmitter {
|
|||||||
engines = nowConfig.engines || pkg.engines;
|
engines = nowConfig.engines || pkg.engines;
|
||||||
forwardNpm = forwardNpm || nowConfig.forwardNpm;
|
forwardNpm = forwardNpm || nowConfig.forwardNpm;
|
||||||
} else if (type === 'static') {
|
} else if (type === 'static') {
|
||||||
if (isFile) {
|
if (paths.length === 1) {
|
||||||
files = [resolvePath(paths[0])];
|
|
||||||
} else if (paths.length === 1) {
|
|
||||||
files = await getFiles(paths[0], nowConfig, opts);
|
files = await getFiles(paths[0], nowConfig, opts);
|
||||||
} else {
|
} else {
|
||||||
if (!files) {
|
if (!files) {
|
||||||
@@ -128,30 +135,23 @@ export default class Now extends EventEmitter {
|
|||||||
}
|
}
|
||||||
} else if (type === 'docker') {
|
} else if (type === 'docker') {
|
||||||
files = await getDockerFiles(paths[0], nowConfig, opts);
|
files = await getDockerFiles(paths[0], nowConfig, opts);
|
||||||
} else if (isBuilds) {
|
|
||||||
opts.isBuilds = isBuilds;
|
|
||||||
|
|
||||||
if (isFile) {
|
|
||||||
files = [resolvePath(paths[0])];
|
|
||||||
} else if (paths.length === 1) {
|
|
||||||
files = await getFiles(paths[0], {}, opts);
|
|
||||||
} else {
|
|
||||||
if (!files) {
|
|
||||||
files = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const path of paths) {
|
const uploadStamp = stamp();
|
||||||
const list = await getFiles(path, {}, opts);
|
|
||||||
files = files.concat(list);
|
|
||||||
|
|
||||||
for (const file of list) {
|
if (isBuilds) {
|
||||||
relatives[file] = path;
|
deployment = await processDeployment({
|
||||||
}
|
now: this,
|
||||||
}
|
output: this._output,
|
||||||
}
|
hashes,
|
||||||
}
|
paths,
|
||||||
|
requestBody,
|
||||||
|
uploadStamp,
|
||||||
|
deployStamp,
|
||||||
|
quiet,
|
||||||
|
nowConfig,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
// Read `registry.npmjs.org` authToken from .npmrc
|
// Read `registry.npmjs.org` authToken from .npmrc
|
||||||
let authToken;
|
let authToken;
|
||||||
|
|
||||||
@@ -160,82 +160,9 @@ export default class Now extends EventEmitter {
|
|||||||
(await readAuthToken(paths[0])) || (await readAuthToken(homedir()));
|
(await readAuthToken(paths[0])) || (await readAuthToken(homedir()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashes = await time('Computing hashes', () => {
|
requestBody = {
|
||||||
const pkgDetails = Object.assign({ name }, pkg);
|
token: this._token,
|
||||||
return hash(files, pkgDetails);
|
teamId: this.currentTeam,
|
||||||
});
|
|
||||||
|
|
||||||
this._files = hashes;
|
|
||||||
|
|
||||||
const deployment = await this.retry(async bail => {
|
|
||||||
// Flatten the array to contain files to sync where each nested input
|
|
||||||
// array has a group of files with the same sha but different path
|
|
||||||
const files = await time(
|
|
||||||
'Get files ready for deployment',
|
|
||||||
Promise.all(
|
|
||||||
Array.prototype.concat.apply(
|
|
||||||
[],
|
|
||||||
await Promise.all(
|
|
||||||
Array.from(this._files).map(async ([sha, { data, names }]) => {
|
|
||||||
const statFn = followSymlinks ? fs.stat : fs.lstat;
|
|
||||||
|
|
||||||
return names.map(async name => {
|
|
||||||
const getMode = async () => {
|
|
||||||
const st = await statFn(name);
|
|
||||||
return st.mode;
|
|
||||||
};
|
|
||||||
|
|
||||||
const mode = await getMode();
|
|
||||||
const multipleStatic = Object.keys(relatives).length !== 0;
|
|
||||||
|
|
||||||
let file;
|
|
||||||
|
|
||||||
if (isFile) {
|
|
||||||
file = basename(paths[0]);
|
|
||||||
} else if (multipleStatic) {
|
|
||||||
file = toRelative(name, join(relatives[name], '..'));
|
|
||||||
} else {
|
|
||||||
file = toRelative(name, paths[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
sha,
|
|
||||||
size: data.length,
|
|
||||||
file,
|
|
||||||
mode
|
|
||||||
};
|
|
||||||
});
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// This is a useful warning because it prevents people
|
|
||||||
// from getting confused about a deployment that renders 404.
|
|
||||||
if (
|
|
||||||
files.length === 0 ||
|
|
||||||
files.every(item => item.file.startsWith('.'))
|
|
||||||
) {
|
|
||||||
warn(
|
|
||||||
'There are no files (or only files starting with a dot) inside your deployment.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryProps = {};
|
|
||||||
const requestBody = isBuilds
|
|
||||||
? {
|
|
||||||
version: 2,
|
|
||||||
env,
|
|
||||||
build,
|
|
||||||
public: wantsPublic || nowConfig.public,
|
|
||||||
name,
|
|
||||||
project,
|
|
||||||
files,
|
|
||||||
meta,
|
|
||||||
regions
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
env,
|
env,
|
||||||
build,
|
build,
|
||||||
meta,
|
meta,
|
||||||
@@ -246,188 +173,43 @@ export default class Now extends EventEmitter {
|
|||||||
description,
|
description,
|
||||||
deploymentType: type,
|
deploymentType: type,
|
||||||
registryAuthToken: authToken,
|
registryAuthToken: authToken,
|
||||||
files,
|
|
||||||
engines,
|
engines,
|
||||||
scale,
|
scale,
|
||||||
sessionAffinity,
|
sessionAffinity,
|
||||||
limits: nowConfig.limits,
|
limits: nowConfig.limits,
|
||||||
atlas
|
atlas,
|
||||||
|
config: nowConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Object.keys(nowConfig).length > 0) {
|
deployment = await processDeployment({
|
||||||
if (isBuilds) {
|
legacy: true,
|
||||||
// These properties are only used inside Now CLI and
|
now: this,
|
||||||
// are not supported on the API.
|
output: this._output,
|
||||||
const exclude = ['github', 'scope'];
|
hashes,
|
||||||
|
paths,
|
||||||
// Request properties that are made of a combination of
|
requestBody,
|
||||||
// command flags and config properties were already set
|
uploadStamp,
|
||||||
// earlier. Here, we are setting request properties that
|
deployStamp,
|
||||||
// are purely made of their equally-named config property.
|
quiet,
|
||||||
for (const key of Object.keys(nowConfig)) {
|
env,
|
||||||
const value = nowConfig[key];
|
nowConfig,
|
||||||
|
|
||||||
if (!requestBody[key] && !exclude.includes(key)) {
|
|
||||||
requestBody[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
requestBody.config = nowConfig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBuilds) {
|
|
||||||
if (forceNew) {
|
|
||||||
queryProps.forceNew = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target) {
|
|
||||||
requestBody.target = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFile) {
|
|
||||||
requestBody.routes = [
|
|
||||||
{
|
|
||||||
src: '/',
|
|
||||||
dest: `/${files[0].file}`
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = qs.stringify(queryProps);
|
|
||||||
const version = isBuilds ? 'v9' : 'v4';
|
|
||||||
|
|
||||||
const res = await this._fetch(
|
|
||||||
`/${version}/now/deployments${query ? `?${query}` : ''}`,
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
body: requestBody
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// No retry on 4xx
|
|
||||||
let body;
|
|
||||||
|
|
||||||
try {
|
|
||||||
body = await res.json();
|
|
||||||
} catch (err) {
|
|
||||||
throw new Error(
|
|
||||||
`Unexpected response error: ${err.message} (${
|
|
||||||
res.status
|
|
||||||
} status code)`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.status === 429) {
|
|
||||||
if (body.error && body.error.code === 'builds_rate_limited') {
|
|
||||||
const err = new Error(body.error.message);
|
|
||||||
err.status = res.status;
|
|
||||||
err.retryAfter = 'never';
|
|
||||||
err.code = body.error.code;
|
|
||||||
|
|
||||||
return bail(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
let msg = 'You have been creating deployments at a very fast pace. ';
|
|
||||||
|
|
||||||
if (body.error && body.error.limit && body.error.limit.reset) {
|
|
||||||
const { reset } = body.error.limit;
|
|
||||||
const difference = reset * 1000 - Date.now();
|
|
||||||
|
|
||||||
msg += `Please retry in ${ms(difference, { long: true })}.`;
|
|
||||||
} else {
|
|
||||||
msg += 'Please slow down.';
|
|
||||||
}
|
|
||||||
|
|
||||||
const err = new Error(msg);
|
|
||||||
|
|
||||||
err.status = res.status;
|
|
||||||
err.retryAfter = 'never';
|
|
||||||
|
|
||||||
return bail(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the deployment domain is missing a cert, bail with the error
|
|
||||||
if (
|
|
||||||
res.status === 400 &&
|
|
||||||
body.error &&
|
|
||||||
body.error.code === 'cert_missing'
|
|
||||||
) {
|
|
||||||
bail(await responseError(res, null, body));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
res.status === 400 &&
|
|
||||||
body.error &&
|
|
||||||
body.error.code === 'missing_files'
|
|
||||||
) {
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.status === 404 && body.error && body.error.code === 'not_found') {
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.status >= 400 && res.status < 500) {
|
|
||||||
const err = new Error();
|
|
||||||
|
|
||||||
if (body.error) {
|
|
||||||
const { code, unreferencedBuildSpecs } = body.error;
|
|
||||||
|
|
||||||
if (code === 'env_value_invalid_type') {
|
|
||||||
const { key } = body.error;
|
|
||||||
err.message =
|
|
||||||
`The env key ${key} has an invalid type: ${typeof env[key]}. ` +
|
|
||||||
'Please supply a String or a Number (https://err.sh/now/env-value-invalid-type)';
|
|
||||||
} else if (code === 'unreferenced_build_specifications') {
|
|
||||||
const count = unreferencedBuildSpecs.length;
|
|
||||||
const prefix = count === 1 ? 'build' : 'builds';
|
|
||||||
|
|
||||||
err.message =
|
|
||||||
`You defined ${count} ${prefix} that did not match any source files (please ensure they are NOT defined in ${highlight(
|
|
||||||
'.nowignore'
|
|
||||||
)}):` +
|
|
||||||
`\n- ${unreferencedBuildSpecs
|
|
||||||
.map(item => JSON.stringify(item))
|
|
||||||
.join('\n- ')}`;
|
|
||||||
} else {
|
|
||||||
Object.assign(err, body.error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err.message = 'Not able to create deployment';
|
|
||||||
}
|
|
||||||
|
|
||||||
return bail(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.status !== 200) {
|
|
||||||
throw new Error(body.error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [name, value] of res.headers.entries()) {
|
|
||||||
if (name.startsWith('x-now-warning-')) {
|
|
||||||
this._output.warn(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return body;
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// We report about files whose sizes are too big
|
// We report about files whose sizes are too big
|
||||||
let missingVersion = false;
|
let missingVersion = false;
|
||||||
|
|
||||||
if (deployment.warnings) {
|
if (deployment && deployment.warnings) {
|
||||||
let sizeExceeded = 0;
|
let sizeExceeded = 0;
|
||||||
|
|
||||||
deployment.warnings.forEach(warning => {
|
deployment.warnings.forEach(warning => {
|
||||||
if (warning.reason === 'size_limit_exceeded') {
|
if (warning.reason === 'size_limit_exceeded') {
|
||||||
const { sha, limit } = warning;
|
const { sha, limit } = warning;
|
||||||
const n = hashes.get(sha).names.pop();
|
const n = hashes[sha].names.pop();
|
||||||
|
|
||||||
warn(`Skipping file ${n} (size exceeded ${bytes(limit)}`);
|
warn(`Skipping file ${n} (size exceeded ${bytes(limit)}`);
|
||||||
|
|
||||||
hashes.get(sha).names.unshift(n); // Move name (hack, if duplicate matches we report them in order)
|
hashes[sha].names.unshift(n); // Move name (hack, if duplicate matches we report them in order)
|
||||||
sizeExceeded++;
|
sizeExceeded++;
|
||||||
} else if (warning.reason === 'node_version_not_found') {
|
} else if (warning.reason === 'node_version_not_found') {
|
||||||
warn(`Requested node version ${warning.wanted} is not available`);
|
warn(`Requested node version ${warning.wanted} is not available`);
|
||||||
@@ -445,19 +227,10 @@ export default class Now extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deployment.error && deployment.error.code === 'missing_files') {
|
|
||||||
this._missing = deployment.error.missing || [];
|
|
||||||
this._fileCount = files.length;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isBuilds && !quiet && type === 'npm' && deployment.nodeVersion) {
|
if (!isBuilds && !quiet && type === 'npm' && deployment.nodeVersion) {
|
||||||
if (engines && engines.node && !missingVersion) {
|
if (engines && engines.node && !missingVersion) {
|
||||||
log(
|
log(
|
||||||
chalk`Using Node.js {bold ${
|
chalk`Using Node.js {bold ${deployment.nodeVersion}} (requested: {dim \`${engines.node}\`})`
|
||||||
deployment.nodeVersion
|
|
||||||
}} (requested: {dim \`${engines.node}\`})`
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
log(chalk`Using Node.js {bold ${deployment.nodeVersion}} (default)`);
|
log(chalk`Using Node.js {bold ${deployment.nodeVersion}} (default)`);
|
||||||
@@ -472,81 +245,90 @@ export default class Now extends EventEmitter {
|
|||||||
return deployment;
|
return deployment;
|
||||||
}
|
}
|
||||||
|
|
||||||
upload({ atlas = false, scale = {} } = {}) {
|
async handleDeploymentError(error, { hashes, env }) {
|
||||||
const { debug, time } = this._output;
|
if (error.status === 429) {
|
||||||
debug(`Will upload ${this._missing.length} files`);
|
if (error.code === 'builds_rate_limited') {
|
||||||
|
const err = new Error(error.message);
|
||||||
|
err.status = error.status;
|
||||||
|
err.retryAfter = 'never';
|
||||||
|
err.code = error.code;
|
||||||
|
|
||||||
this._agent.setConcurrency({
|
return err;
|
||||||
maxStreams: MAX_CONCURRENT,
|
|
||||||
capacity: this._missing.length
|
|
||||||
});
|
|
||||||
|
|
||||||
time(
|
|
||||||
'Uploading files',
|
|
||||||
Promise.all(
|
|
||||||
this._missing.map(sha =>
|
|
||||||
retry(
|
|
||||||
async bail => {
|
|
||||||
const file = this._files.get(sha);
|
|
||||||
const fPath = file.names[0];
|
|
||||||
const stream = createReadStream(fPath);
|
|
||||||
const { data } = file;
|
|
||||||
|
|
||||||
const fstreamPush = stream.push;
|
|
||||||
|
|
||||||
let uploadedSoFar = 0;
|
|
||||||
stream.push = chunk => {
|
|
||||||
// If we're about to push the last chunk, then don't do it here
|
|
||||||
// But instead, we'll "hang" the progress bar and do it on 200
|
|
||||||
if (chunk && uploadedSoFar + chunk.length < data.length) {
|
|
||||||
this.emit('uploadProgress', chunk.length);
|
|
||||||
uploadedSoFar += chunk.length;
|
|
||||||
}
|
}
|
||||||
return fstreamPush.call(stream, chunk);
|
|
||||||
};
|
|
||||||
|
|
||||||
const url = atlas ? '/v1/now/images' : '/v2/now/files';
|
let msg = 'You have been creating deployments at a very fast pace. ';
|
||||||
const additionalHeaders = atlas
|
|
||||||
? {
|
|
||||||
'x-now-dcs': Object.keys(scale).join(',')
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
const res = await this._fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/octet-stream',
|
|
||||||
'x-now-digest': sha,
|
|
||||||
'x-now-size': data.length,
|
|
||||||
...additionalHeaders
|
|
||||||
},
|
|
||||||
body: stream
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (error.limit && error.limit.reset) {
|
||||||
// What we want
|
const { reset } = error.limit;
|
||||||
this.emit('uploadProgress', file.data.length - uploadedSoFar);
|
const difference = reset * 1000 - Date.now();
|
||||||
this.emit('upload', file);
|
|
||||||
} else if (res.status > 200 && res.status < 500) {
|
msg += `Please retry in ${ms(difference, { long: true })}.`;
|
||||||
// If something is wrong with our request, we don't retry
|
|
||||||
return bail(await responseError(res, `Failed to upload file with status: ${res.status}`));
|
|
||||||
} else {
|
} else {
|
||||||
// If something is wrong with the server, we retry
|
msg += 'Please slow down.';
|
||||||
throw await responseError(res, 'Failed to upload file');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const err = new Error(msg);
|
||||||
|
|
||||||
|
err.status = error.status;
|
||||||
|
err.retryAfter = 'never';
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the deployment domain is missing a cert, bail with the error
|
||||||
|
if (error.status === 400 && error.code === 'cert_missing') {
|
||||||
|
return responseError(error, null, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.status === 400 && error.code === 'missing_files') {
|
||||||
|
this._missing = error.missing || [];
|
||||||
|
this._fileCount = hashes.length;
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.status === 404 && error.code === 'not_found') {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.status >= 400 && error.status < 500) {
|
||||||
|
const err = new Error();
|
||||||
|
|
||||||
|
const { code, unreferencedBuildSpecs } = error;
|
||||||
|
|
||||||
|
if (code === 'env_value_invalid_type') {
|
||||||
|
const { key } = error;
|
||||||
|
err.message =
|
||||||
|
`The env key ${key} has an invalid type: ${typeof env[key]}. ` +
|
||||||
|
'Please supply a String or a Number (https://err.sh/now-cli/env-value-invalid-type)';
|
||||||
|
} else if (code === 'unreferenced_build_specifications') {
|
||||||
|
const count = unreferencedBuildSpecs.length;
|
||||||
|
const prefix = count === 1 ? 'build' : 'builds';
|
||||||
|
|
||||||
|
err.message =
|
||||||
|
`You defined ${count} ${prefix} that did not match any source files (please ensure they are NOT defined in ${highlight(
|
||||||
|
'.nowignore'
|
||||||
|
)}):` +
|
||||||
|
`\n- ${unreferencedBuildSpecs
|
||||||
|
.map(item => JSON.stringify(item))
|
||||||
|
.join('\n- ')}`;
|
||||||
|
} else {
|
||||||
|
Object.assign(err, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle build errors
|
||||||
|
if (error.id && error.id.startsWith('bld_')) {
|
||||||
|
return new BuildError({
|
||||||
|
meta: {
|
||||||
|
entrypoint: error.entrypoint,
|
||||||
},
|
},
|
||||||
{
|
});
|
||||||
retries: 3,
|
|
||||||
randomize: true,
|
|
||||||
onRetry: this._onRetry
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
)
|
return new Error(error.message);
|
||||||
)
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
this.emit('complete');
|
|
||||||
})
|
|
||||||
.catch(err => this.emit('error', err));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async listSecrets() {
|
async listSecrets() {
|
||||||
@@ -589,7 +371,7 @@ export default class Now extends EventEmitter {
|
|||||||
{
|
{
|
||||||
retries: 3,
|
retries: 3,
|
||||||
minTimeout: 2500,
|
minTimeout: 2500,
|
||||||
onRetry: this._onRetry
|
onRetry: this._onRetry,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -597,7 +379,7 @@ export default class Now extends EventEmitter {
|
|||||||
if (!app && !Object.keys(meta).length) {
|
if (!app && !Object.keys(meta).length) {
|
||||||
// Get the 35 latest projects and their latest deployment
|
// Get the 35 latest projects and their latest deployment
|
||||||
const query = new URLSearchParams({ limit: 35 });
|
const query = new URLSearchParams({ limit: 35 });
|
||||||
const projects = await fetchRetry(`/projects/list?${query}`);
|
const projects = await fetchRetry(`/v2/projects/?${query}`);
|
||||||
|
|
||||||
const deployments = await Promise.all(
|
const deployments = await Promise.all(
|
||||||
projects.map(async ({ id: projectId }) => {
|
projects.map(async ({ id: projectId }) => {
|
||||||
@@ -647,7 +429,7 @@ export default class Now extends EventEmitter {
|
|||||||
{
|
{
|
||||||
retries: 3,
|
retries: 3,
|
||||||
minTimeout: 2500,
|
minTimeout: 2500,
|
||||||
onRetry: this._onRetry
|
onRetry: this._onRetry,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -697,7 +479,7 @@ export default class Now extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const url = `/${
|
const url = `/${
|
||||||
isBuilds ? 'v9' : 'v5'
|
isBuilds ? 'v10' : 'v5'
|
||||||
}/now/deployments/${encodeURIComponent(id)}`;
|
}/now/deployments/${encodeURIComponent(id)}`;
|
||||||
|
|
||||||
return this.retry(
|
return this.retry(
|
||||||
@@ -727,7 +509,7 @@ export default class Now extends EventEmitter {
|
|||||||
|
|
||||||
await this.retry(async bail => {
|
await this.retry(async bail => {
|
||||||
const res = await this._fetch(url, {
|
const res = await this._fetch(url, {
|
||||||
method: 'DELETE'
|
method: 'DELETE',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
@@ -748,7 +530,7 @@ export default class Now extends EventEmitter {
|
|||||||
return retry(fn, {
|
return retry(fn, {
|
||||||
retries,
|
retries,
|
||||||
maxTimeout,
|
maxTimeout,
|
||||||
onRetry: this._onRetry
|
onRetry: this._onRetry,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,9 +538,7 @@ export default class Now extends EventEmitter {
|
|||||||
this._output.debug(`Retrying: ${err}\n${err.stack}`);
|
this._output.debug(`Retrying: ${err}\n${err.stack}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {}
|
||||||
this._agent.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
return this._id;
|
return this._id;
|
||||||
@@ -802,14 +582,21 @@ export default class Now extends EventEmitter {
|
|||||||
|
|
||||||
opts.headers = opts.headers || {};
|
opts.headers = opts.headers || {};
|
||||||
opts.headers.accept = 'application/json';
|
opts.headers.accept = 'application/json';
|
||||||
opts.headers.authorization = `Bearer ${this._token}`;
|
opts.headers.Authorization = `Bearer ${this._token}`;
|
||||||
opts.headers['user-agent'] = ua;
|
opts.headers['user-agent'] = ua;
|
||||||
|
|
||||||
|
if (
|
||||||
|
opts.body &&
|
||||||
|
typeof opts.body === 'object' &&
|
||||||
|
opts.body.constructor === Object
|
||||||
|
) {
|
||||||
|
opts.body = JSON.stringify(opts.body);
|
||||||
|
opts.headers['Content-Type'] = 'application/json';
|
||||||
|
}
|
||||||
|
|
||||||
return this._output.time(
|
return this._output.time(
|
||||||
`${opts.method || 'GET'} ${this._apiUrl}${_url} ${JSON.stringify(
|
`${opts.method || 'GET'} ${this._apiUrl}${_url} ${opts.body || ''}`,
|
||||||
opts.body
|
fetch(`${this._apiUrl}${_url}`, opts)
|
||||||
) || ''}`,
|
|
||||||
this._agent.fetch(_url, opts)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -827,8 +614,8 @@ export default class Now extends EventEmitter {
|
|||||||
opts = Object.assign({}, opts, {
|
opts = Object.assign({}, opts, {
|
||||||
body: JSON.stringify(opts.body),
|
body: JSON.stringify(opts.body),
|
||||||
headers: Object.assign({}, opts.headers, {
|
headers: Object.assign({}, opts.headers, {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const res = await this._fetch(url, opts);
|
const res = await this._fetch(url, opts);
|
||||||
@@ -875,6 +662,7 @@ function hasNpmStart(pkg) {
|
|||||||
|
|
||||||
function hasFile(base, files, name) {
|
function hasFile(base, files, name) {
|
||||||
const relative = files.map(file => toRelative(file, base));
|
const relative = files.map(file => toRelative(file, base));
|
||||||
|
console.log(731, relative);
|
||||||
return relative.indexOf(name) !== -1;
|
return relative.indexOf(name) !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import inquirer from 'inquirer';
|
|||||||
import stripAnsi from 'strip-ansi';
|
import stripAnsi from 'strip-ansi';
|
||||||
import eraseLines from '../output/erase-lines';
|
import eraseLines from '../output/erase-lines';
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-unassigned-import
|
|
||||||
import './patch-inquirer';
|
import './patch-inquirer';
|
||||||
|
|
||||||
function getLength(string) {
|
function getLength(string) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable import/no-unresolved */
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import pkg from '../../package.json';
|
import pkg from '../../package.json';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { exists } from 'fs-extra';
|
import { exists } from 'fs-extra';
|
||||||
|
import { PackageJson } from '@now/build-utils';
|
||||||
|
|
||||||
import Client from './client';
|
import Client from './client';
|
||||||
import { Config } from '../types';
|
import { Config } from '../types';
|
||||||
import { Package } from './dev/types';
|
|
||||||
import { CantParseJSONFile, ProjectNotFound } from './errors-ts';
|
import { CantParseJSONFile, ProjectNotFound } from './errors-ts';
|
||||||
import getProjectByIdOrName from './projects/get-project-by-id-or-name';
|
import getProjectByIdOrName from './projects/get-project-by-id-or-name';
|
||||||
|
|
||||||
@@ -26,14 +27,14 @@ export default async function preferV2Deployment({
|
|||||||
hasServerfile,
|
hasServerfile,
|
||||||
pkg,
|
pkg,
|
||||||
localConfig,
|
localConfig,
|
||||||
projectName
|
projectName,
|
||||||
}: {
|
}: {
|
||||||
client?: Client,
|
client?: Client;
|
||||||
hasDockerfile: boolean,
|
hasDockerfile: boolean;
|
||||||
hasServerfile: boolean,
|
hasServerfile: boolean;
|
||||||
pkg: Package | CantParseJSONFile | null,
|
pkg: PackageJson | CantParseJSONFile | null;
|
||||||
localConfig: Config | undefined,
|
localConfig: Config | undefined;
|
||||||
projectName?: string
|
projectName?: string;
|
||||||
}): Promise<null | string> {
|
}): Promise<null | string> {
|
||||||
if (localConfig && localConfig.version) {
|
if (localConfig && localConfig.version) {
|
||||||
// We will prefer anything that is set here
|
// We will prefer anything that is set here
|
||||||
@@ -52,10 +53,14 @@ export default async function preferV2Deployment({
|
|||||||
const { scripts = {} } = pkg;
|
const { scripts = {} } = pkg;
|
||||||
|
|
||||||
if (!scripts.start && !scripts['now-start']) {
|
if (!scripts.start && !scripts['now-start']) {
|
||||||
return `Deploying to Now 2.0, because ${highlight('package.json')} is missing a ${cmd('start')} script. ${INFO}`;
|
return `Deploying to Now 2.0, because ${highlight(
|
||||||
|
'package.json'
|
||||||
|
)} is missing a ${cmd('start')} script. ${INFO}`;
|
||||||
}
|
}
|
||||||
} else if (!pkg && !hasDockerfile) {
|
} else if (!pkg && !hasDockerfile) {
|
||||||
return `Deploying to Now 2.0, because no ${highlight('Dockerfile')} was found. ${INFO}`;
|
return `Deploying to Now 2.0, because no ${highlight(
|
||||||
|
'Dockerfile'
|
||||||
|
)} was found. ${INFO}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client && projectName) {
|
if (client && projectName) {
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import path from 'path';
|
|||||||
import { CantParseJSONFile } from './errors-ts';
|
import { CantParseJSONFile } from './errors-ts';
|
||||||
import readJSONFile from './read-json-file';
|
import readJSONFile from './read-json-file';
|
||||||
import { Config } from '../types';
|
import { Config } from '../types';
|
||||||
import { Package } from './dev/types';
|
import { PackageJson } from '@now/build-utils';
|
||||||
|
|
||||||
interface CustomPackage extends Package {
|
interface CustomPackage extends PackageJson {
|
||||||
now?: Config
|
now?: Config;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function readPackage(file?: string) {
|
export default async function readPackage(file?: string) {
|
||||||
@@ -17,7 +17,7 @@ export default async function readPackage(file?: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
return result as CustomPackage
|
return result as CustomPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default async (sentry, error, apiUrl, configFiles) => {
|
|||||||
if (user) {
|
if (user) {
|
||||||
const spec = {
|
const spec = {
|
||||||
email: user.email,
|
email: user.email,
|
||||||
id: user.uid
|
id: user.uid,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (user.username) {
|
if (user.username) {
|
||||||
@@ -44,7 +44,7 @@ export default async (sentry, error, apiUrl, configFiles) => {
|
|||||||
scope.setExtra('scopeError', {
|
scope.setExtra('scopeError', {
|
||||||
name: scopeError.name,
|
name: scopeError.name,
|
||||||
message: scopeError.message,
|
message: scopeError.message,
|
||||||
stack: scopeError.stack
|
stack: scopeError.stack,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +81,8 @@ export default async (sentry, error, apiUrl, configFiles) => {
|
|||||||
// Report information about the version of `node` being used
|
// Report information about the version of `node` being used
|
||||||
scope.setExtra('node', {
|
scope.setExtra('node', {
|
||||||
execPath: process.execPath,
|
execPath: process.execPath,
|
||||||
version: process.version
|
version: process.version,
|
||||||
|
platform: process.platform,
|
||||||
});
|
});
|
||||||
|
|
||||||
sentry.captureException(error);
|
sentry.captureException(error);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Response } from 'fetch-h2';
|
import { Response } from 'node-fetch';
|
||||||
import { APIError } from './errors-ts';
|
import { APIError } from './errors-ts';
|
||||||
|
|
||||||
export default async function responseError(
|
export default async function responseError(
|
||||||
|
|||||||
1
packages/now-cli/test/dev-builder.unit.js
vendored
1
packages/now-cli/test/dev-builder.unit.js
vendored
@@ -1,5 +1,4 @@
|
|||||||
import test from 'ava';
|
import test from 'ava';
|
||||||
|
|
||||||
import { filterPackage } from '../src/util/dev/builder-cache';
|
import { filterPackage } from '../src/util/dev/builder-cache';
|
||||||
|
|
||||||
test('[dev-builder] filter install "latest", cached canary', async t => {
|
test('[dev-builder] filter install "latest", cached canary', async t => {
|
||||||
|
|||||||
1
packages/now-cli/test/dev-router.unit.js
vendored
1
packages/now-cli/test/dev-router.unit.js
vendored
@@ -1,5 +1,4 @@
|
|||||||
import test from 'ava';
|
import test from 'ava';
|
||||||
|
|
||||||
import devRouter from '../src/util/dev/router';
|
import devRouter from '../src/util/dev/router';
|
||||||
|
|
||||||
test('[dev-router] 301 redirection', async t => {
|
test('[dev-router] 301 redirection', async t => {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user